Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

modules/supervisord: init, modules/openssh: init #203

Open
wants to merge 28 commits into
base: master
Choose a base branch
from

Conversation

zhaofengli
Copy link
Contributor

This PR adds support for running services with supervisord as well as an OpenSSH module. I wanted to run an SSH server (#32, #156) that gets automatically launched when Nix-on-Droid starts, complete with process supervision.

The supervisord.programs.<name> options are designed to resemble systemd.services.<name>:

{ pkgs, ... }:
{
  supervisord.programs.sshd = {
    path = [ pkgs.openssh ];
    autoRestart = true;
    script = ''
      ssh-keygen ...

      exec ${pkgs.openssh}/bin/sshd -D -f /etc/ssh/sshd_config
    '';
  };
}

Fixes #54.

Copy link
Collaborator

@Gerschtli Gerschtli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks cool, will continue the review later :)

modules/supervisord.nix Outdated Show resolved Hide resolved
modules/supervisord.nix Show resolved Hide resolved
modules/supervisord.nix Show resolved Hide resolved
modules/supervisord.nix Show resolved Hide resolved
modules/supervisord.nix Outdated Show resolved Hide resolved
Copy link
Contributor Author

@zhaofengli zhaofengli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review! I'll squash the commits once everything is ready.

modules/supervisord.nix Show resolved Hide resolved
modules/supervisord.nix Outdated Show resolved Hide resolved
modules/supervisord.nix Show resolved Hide resolved
modules/supervisord.nix Outdated Show resolved Hide resolved
modules/supervisord.nix Show resolved Hide resolved
Copy link
Collaborator

@Gerschtli Gerschtli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please also mention the new modules in the changelog :) Would be great to have some tests for these new options (if feasible with reasonable effort).

Wanted to implement something like that since a very long time, I am glad you took the effort. Thank you a lot :)

modules/environment/login/login-inner.nix Outdated Show resolved Hide resolved
${lib.optionalString config.supervisord.enable ''
set +e
if [ ! -e "${config.supervisord.socketPath}" ]; then
${config.supervisord.package}/bin/supervisord -c /etc/supervisord.conf
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are starting the daemon but you should also stop it, when every shell is closed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense to do, but I'm not sure what the cleanest way to do that is.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can see myself doing an 'Acquire wakelock', closing all shells and still expecting services to run, so not sure what's the ideal UX here. My vote is "notification is present <=> services are running".

What's the current situation?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is worth providing solutions to both use cases, so there could be an option to disable automatic shutdown after last session closes (I think using automatic shutdown as default sounds safer than the other way around).

But also not quite sure where such a shutdown could be implemented..

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could replace exec proot in /bin/login with a fork and wait approach while redirecting process signals to the proot process, enabling us to run cleanup stuff after a session is closed. Counting of proot processes could then be used to shutdown supervisord. I can not think of a downside of this approach (and I don't see an alternative).

modules/services/openssh.nix Outdated Show resolved Hide resolved
modules/supervisord.nix Show resolved Hide resolved
modules/supervisord.nix Show resolved Hide resolved
modules/supervisord.nix Show resolved Hide resolved
modules/supervisord.nix Outdated Show resolved Hide resolved
modules/services/openssh.nix Show resolved Hide resolved
modules/services/openssh.nix Outdated Show resolved Hide resolved
modules/services/openssh.nix Show resolved Hide resolved
@Gerschtli
Copy link
Collaborator

For completion, it would be great to have native options to add ssh pub keys in /etc/ssh/authorized_keys.d. But could be done in a separate PR, however you like :)

@Gerschtli
Copy link
Collaborator

Tested it on device: Running supervisorctl stop sshd does not stop the sshd process when there is an active ssh session.. Does not look like that would be expected behaviour.

Copy link
Contributor Author

@zhaofengli zhaofengli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested it on device: Running supervisorctl stop sshd does not stop the sshd process when there is an active ssh session.. Does not look like that would be expected behaviour.

It's expected behavior because sshd forks a separate process to handle each connection. You can try by stopping sshd on NixOS (or any other distro): Existing sessions keep running but you can't establish new ones.

But yes, we do need to consider whether to want to terminate everything when all foreground Nix-on-Droid sessions exit. In regular Termux my manually started sshd processes still run when all sessions end.

modules/supervisord.nix Show resolved Hide resolved
'';
type = types.str;
};
script = lib.mkOption {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

command is the direct way of specifying what to run (systemd.services.<name>.serviceConfig.ExecStart) and works fine for simple services that don't require much setup. I think there's no need to involve a script when setting command directly is sufficient (also avoid the exec pitfalls).

modules/supervisord.nix Show resolved Hide resolved
modules/services/openssh.nix Outdated Show resolved Hide resolved
${lib.optionalString config.supervisord.enable ''
set +e
if [ ! -e "${config.supervisord.socketPath}" ]; then
${config.supervisord.package}/bin/supervisord -c /etc/supervisord.conf
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense to do, but I'm not sure what the cleanest way to do that is.

@Gerschtli
Copy link
Collaborator

It's expected behavior because sshd forks a separate process to handle each connection. You can try by stopping sshd on NixOS (or any other distro): Existing sessions keep running but you can't establish new ones.

Okay, but there is still a sshd process running even after the last ssh connection is closed when the sshd was stopped while session where open.

modules/supervisord.nix Outdated Show resolved Hide resolved
modules/supervisord.nix Outdated Show resolved Hide resolved
@zhaofengli
Copy link
Contributor Author

There is another UX issue with the current approach. Currently proot waits for all traced processes to terminate, so the first Nix-on-Droid session which starts supervisord won't terminate after exiting the shell. If this isn't desirable, we can start supervisord in a separate proot invocation.

@Gerschtli
Copy link
Collaborator

Why not detach the process from the invoking session with e.g. disown?

@zhaofengli
Copy link
Contributor Author

Why not detach the process from the invoking session with e.g. disown?

Sadly this can't work because proot needs to ptrace all processes in the session to perform its hooking, so it has to be there as long as any child processes exist. There doesn't appear to be an option to have proot itself fork into the background if the "main process" exits.

@t184256
Copy link
Collaborator

t184256 commented Oct 11, 2022

Random idea, feel free to disregard:

login -> proot -> login-inner -> session
       \
        -> (supervisord overseeing process) -> proot -> login-inner -> supervisord

@zhaofengli
Copy link
Contributor Author

Random idea, feel free to disregard:

login -> proot -> login-inner -> session
       \
        -> (supervisord overseeing process) -> proot -> login-inner -> supervisord

That's what I meant by "start supervisord in a separate proot invocation." If this is desired I'll implement it later this week.

@t184256
Copy link
Collaborator

t184256 commented Oct 11, 2022

I still somehow thought that meant starting it from proot with extra precautions. Sorry for the noise then.

As for desireability, I'm not sure. Sounds rather involved to me wrt starting/stopping it (via signals?). If you figure out some alternative that simplifies things at the expense of, say, some support from the app, I'm open for discussing it further.

@Gerschtli
Copy link
Collaborator

Hey @zhaofengli , is there anything where I could support you with this PR? :)

@zhaofengli
Copy link
Contributor Author

Sorry, got distracted and forgot about this :/ Termux does appear to have a special type of session for background tasks:

Termux notification with background tasks active

One integration is implemented in Termux:Widgets, where you can drop a shell script in ~/.shortcuts/tasks and add a shortcut that launches the script in the background. We should look into whether it's possible to start such a task with /system/bin/am start or similar outside PRoot. If so, then things should be cleaner with the user still having a persistent notification even with no interactive sessions active.

@diamondburned
Copy link

Is there a way to test this PR without rebuilding the fork? Maybe a way to patch these modules into the config?

@Gerschtli
Copy link
Collaborator

Yes, either use @zhaofengli's fork as channel/flake input or create your own fork of nix-on-droid and rebase his branch onto latest master to keep all changes that were merged in the meantime. No need for a new bootstrap zip because there were no changes :)

@diamondburned
Copy link

create your own fork of nix-on-droid and rebase his branch onto latest master to keep all changes that were merged in the meantime

Sorry for being off-topic; how would I do this, exactly? I'm currently not using Flakes. Do I swap the Nix channel with one that points to this commit?

@Gerschtli
Copy link
Collaborator

nix-channel --list shows you the current nix-on-droid url. That's a url with repo owner, repo name and rev (probably master or release-22.11). Replace owner and rev and set within .nix-channels, afterwards run nix-channel --update and nix-on-droid switch :)

fi
'')}

exec ${cfg.package}/bin/sshd -D -f /etc/ssh/sshd_config
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should probably add the -e flag so log output goes to stderr, which supervisord can actually pick up (unlike syslog).

@t184256 t184256 mentioned this pull request Feb 7, 2024
@expenses
Copy link

expenses commented Jun 1, 2024

What's the status of this PR? I've been using it for some basic services and I'm pretty happy with it. Would be good to get it merged if possible.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Init daemon to manage all running processes
6 participants