troglobit / finit

Fast init for Linux. Cookies included
https://troglobit.com/projects/finit/
MIT License
622 stars 61 forks source link

Don't override BLOCK_USER on service reload #335

Open JackNewman12 opened 1 year ago

JackNewman12 commented 1 year ago

This might be more a personal choice, but at least how we are using finit if we stop a service manually (only done during debugging) we want it to stay stopped regardless of other things happening in the system.

I think this makes sense and follows systemd where reloading a config doesn't suddenly start the service (or other unrelated services).

Sorry for all the pull request spam, I just wanted to keep all the changes I've done isolated from each other. Feel free to reject any of these.

JackNewman12 commented 1 year ago

Actually on my drive home I had a few more thoughts about this. It might be better if I first explain how we use finit and why I wanted it to behave like this.

Most of our services are in a ro filesystem /etc/finit.d, any services that need to be dynamically added/removed are thrown in /var/finit.d (which is symlinked to /etc/finit.d/enabled). This is all working perfectly for us, we can add/remove services as needed then sighup finit to do all the hard work.

However when debugging I'd like to disable a service. Unfortunately disabling a service in /var/finit.d causes it to get deleted, and it is not possible to disable a service in /etc/finit.d

Perhaps if services had a "disabled" flag that might be more appropriate. Thanks for your time. Let us know what you think.

troglobit commented 1 year ago

First of all, no need to apologize. I really appreciate getting PRs for my projects, in particular when they come with motivation and are split up in logical chunks like you've done! :-)

Now, Finit is not systemd and we don't strive to replace or replicate all behaviors of it. But I have to admit, this is one of the cases I've run into myself a few times. However, there are lots of users out there that expect the existing behavior. My former employer, for instance, switches between various system configurations and swaps in/out a lot of services, activating all changes with initctl reload should behave the same as rebooting the device, only quicker. Breaking those use-cases is a big no-no.

I didn't fully understand your setup, have you considered using an overlay over /etc? That's what we do in a few projects. It's quite handy to be able to use initctl enable|disable foo to manage symlinks from /etc/finit.d/availble/ -> /etc/finit.d/enabled/foo. We make sure to have all possible services pre-configured in /etc/finit.d/availble/ and control their behavior by their own respective configurations files in /etc/, and command line args infused from /etc/default/foo.

I need to give this one a think, and talk to my colleague again, to see what we can do. I'd very much like to support both existing and new use-cases, and breaking changes I try to do only with major version numbers. E.g., file format change is planned for 5.0 and maybe this could be postponed for that too.

troglobit commented 1 year ago

I've now discussed this too with my colleague. He agrees with me, and we will not accept this PR right now.

However, he reacted to the following paragraphs (my emphasis added) and suggested two things:

  1. I need to update and clarify the documentation on /etc/finit.d/{available,enabled}
  2. An alternate approach for you

Most of our services are in a ro filesystem /etc/finit.d, any services that need to be dynamically added/removed are thrown in /var/finit.d (which is symlinked to /etc/finit.d/enabled). This is all working perfectly for us, we can add/remove services as needed then sighup finit to do all the hard work.

However when debugging I'd like to disable a service. Unfortunately disabling a service in /var/finit.d causes it to get deleted, and it is not possible to disable a service in /etc/finit.d

The idea is to tweak your setup slightly, to this:

  1. Keep all your static services and finit configuration in /etc/finit.d/*.conf

  2. Set up the following symlinks for dynamic services in your rootfs:

    • /etc/finit.d/available -> /var/finit.d/available
    • /etc/finit.d/enabled -> /var/finit.d/enabled
  3. At boot, copy from a template directory (or generate, however you do it) all possible dynamic services to /var/finit.d/available/*.conf

  4. To enable services, either create the relative symlinks yourself from at boot (4) or use initctl enable foo when needed, e.g.

       cd /var/finit.d/enabled
       ln -s ../available/foo.conf .
  5. For your debugging purposes, disabling a service with initctl disable foo, would then only remove the symlink in /var/finit.d/enabled, so it would be possible to re-enable it again

JackNewman12 commented 1 year ago

Cheers. I got this working and here is some of my brain vomit:

Thanks for all your time and effort, have a great weekend.

troglobit commented 1 year ago

I've scheduled this PR for v5.0 now. It'll still require some extra logic to go in, i.e., something along the lines of initctl --reset reload to reset internal "states" like user block.

  • I still like using init.d/ and init.d/enabled as two separate conf directories (Same idea as Feature request, Allow additional directories included in config with include /etc/finit.d/*.conf #184).

    • I feel like having service enable/disable support without having to convert all of our services to dynamic services makes sense in my head. Although I guess you would never want to disable a static service (except when debugging).
    • I wonder if something like '/tmp/forcestop' (name pending) could be a good workaround similar to '/tmp/norespawn' .
    • It can be little unclear which services are built-in, and which ones have been generated when they all exist in the same folder. Maybe this doesn't really matter in practice.

The norespawn is an old hack from Finit v1 that should be dropped. The official way of interacting with Finit is via initctl.

By built-in here I guess you mean what's on your read-only filesystem at boot? Finit has it's own built-in services as well, e.g., keventd. Those are real confusing and will be phased out over time (5.0 will be a lot of changes!). But in your case, I'd say that's a you problem ;-)

  • It would be nice if initctl could show disabled services. It can be hard to parse 'initctl enable' and compare it with the currently running services.

    • Note I have modified initctl status to color the state of all services. This makes it really easy to spot Green/Yellow/White services at a glance.

Good point, never thought about that.

Color is one way to go, but maybe some other marker as well since there are color blind people (over represented in our field).

I've recently added a lot of JSON output support to initctl, for purposes of scripting (jq is awesome!) and for piping to web interfaces and the likes. The ls output is one of the last commands to not support JSON output, so that's probably what I'll do. Then you could use jq to compare the two lists of enabled/available.

troglobit commented 1 year ago

There, as of c29589c we now have initctl --json ls, which means you can do stuff like this:

root@anarchy:~# initctl ls --json |jq '.available - .enabled'
[
  "chronyd.conf",
  "dnsmasq.conf",
  "gdbserver.conf",
  "inadyn.conf",
  "inetd.conf",
  "isisd.conf",
  "lldpd.conf",
  "mstpd.conf",
  "ntpd.conf",
  "ospf6d.conf",
  "ospfd.conf",
  "querierd.conf",
  "ripd.conf",
  "ripng.conf",
  "sshd.conf",
  "syslogd.conf",
  "telnetd.conf",
  "uftpd.conf",
  "wpa_supplicant.conf",
  "zebra.conf"
]
JackNewman12 commented 1 year ago

Looking good! Excited to see where this goes.

I'm wondering if parsing available/*.conf so that all services are listed would be a worthwhile change alongside this. Anything that is only listed in the available folder can have a BLOCK_DISABLED flag set, and the extra overhead of having to scan a few more files should be minimal since I imagine most people are not reloading constantly.

Edit: actually I realised this might not make sense. You can have multiple services in a conf file. So really you are enabling/disabling groups of services, and it might not make sense why you can see ServiceA in the list, but you'd have to enable SomethingElse.conf to get it running.

troglobit commented 1 year ago

Yeah, we can't scan available/*.conf, it is defined that we only read finit.conf, finit.d/*.conf and finit.d/enabled/*.conf.

By we I mean PID 1, the initctl tool does not scan the contents of these files, it queries Finit over /run/finit/socket for its current set of running services.

The initctl ls|enable|disable|et al are just glorfied wrappers of ls, ln and rm that sprinkle some policy on top.