rgwood / systemctl-tui

A fast, simple TUI for interacting with systemd services and their logs
MIT License
235 stars 5 forks source link

Support enabling / disabling services #3

Open rgwood opened 11 months ago

rgwood commented 11 months ago

Right now we only show enabled services. Fix that, then add enable/disable actions.

rgwood commented 11 months ago

Oof: https://github.com/coreos/go-systemd/issues/289#issuecomment-429283898

This exactly pinpoints what's confusing about it all: Knowing the logic for when a unit is loaded. You can do systemctl status <unloaded unit>, which will make systemd load the unit ad hoc for the purpose of displaying information about it. So it looks loaded in the terminal output, but is immediately un-loaded again by systemd.

loaded but inactive units only matches "started" units.

So maybe there isn't a good way to show disabled services...

rgwood commented 11 months ago

systemctl list-unit-files seems like it can get disabled services, but... that kinda sucks, I guess I'd need to query DBus and run that.

rgwood commented 2 weeks ago

Seems like there is a dbus function I can call: https://www.freedesktop.org/software/systemd/man/latest/systemd.directives.html#ListUnitFiles()

Will look into that.

rgwood commented 2 weeks ago

OK, one problem with that function is that it's pretty slow. Takes about 100ms (user) + 300ms (global) on my 13th gen i7 (Ubuntu 24), I imagine it would be several seconds on slow machines.

I don't want that to block startup with that and I'm not sure I should run it too often. My current idea is to track this enabled/disabled/static/masked state as an Option, and occasionally update that state from a background thread that runs infrequently.

Semi-related, I think I like this way of thinking about systemd unit state as having 3 components:

  1. boot behavior state:
    • enabled/disabled/masked/static
  2. runtime state:
    • active/inactive/activating/deactivating/failed/exited/reloading
  3. memory state:
    • loaded/not-loaded

From a brainstorming session with 3.5 Sonnet, so take it with a grain of salt:

 Prompt: systemd services have a lot of different states they can be in. explain the difference
  between: enabled/disabled, active/inactive, loaded/not loaded

  Assistant: alright, let's break this down:

  enabled/disabled:

  • refers to whether a service is set to start automatically on boot
  • enabled: service will start on boot
  • disabled: service won't start on boot
  • this setting persists across reboots
  • controlled via  systemctl enable/disable

  active/inactive:

  • indicates the current running state of the service
  • active: service is currently running
  • inactive: service is not currently running
  • this is a transient state and can change during system operation
  • controlled via  systemctl start/stop

  loaded/not loaded:

  • relates to whether systemd has parsed and loaded the service unit file into memory
  • loaded: unit file is parsed and ready for use
  • not loaded: unit file hasn't been read by systemd yet
  • typically, services are loaded automatically when needed
  • can be influenced by  systemctl daemon-reload

  it's worth noting that these states are not mutually exclusive. for example, a service can be
  enabled (set to start on boot) but currently inactive (not running). or it could be loaded
  (parsed by systemd) but disabled (won't start automatically).

  the interplay between these states allows for fine-grained control over service behavior and
  system resource management. afaik, this granularity is one of the key advantages of systemd over
  traditional init systems.

  Prompt: are there other common states I should add to my mental model?

  Assistant: absolutely. to flesh out your mental model, consider these additional states:

  failed:

  • indicates a service that attempted to start but encountered an error
  • often requires investigation of logs or configuration

  masked:

  • a stronger form of "disabled"
  • prevents the service from being started manually or automatically
  • useful for preventing conflicting services from running

  static:

  • service unit that cannot be enabled/disabled
  • typically used for units that are dependencies of other units

  exited:

  • service that ran successfully and completed its task
  • common for one-shot services that perform a single action

  reloading:

  • transitional state when a service is told to reload its configuration

  activating/deactivating:

  • transitional states when a service is starting up or shutting down

  running:

  • specific sub-state of "active" for continuously running services

  dead:

  • specific sub-state of "inactive" for services not running

  plugged/mounted/waiting:

  • states more relevant to device units or mount units

  idk if all of these are critical for everyday use, but they can be valuable when troubleshooting
  or when you need a more nuanced understanding of system behavior. the  systemctl list-units --all
  command can show you a wide array of these states in action.

  remember, systemd's state model is quite GRANULAR, allowing for precise control and monitoring
  of system services.

  Prompt: is it reasonable to think of systemd units as having 3 separate states? e.g.

  1. enabled/disabled/masked/etc.
  2. active/inactive/activating/deactivating/etc.
  3. loaded/not loaded

  Assistant: that's a PERSPICACIOUS way of looking at it. your model is quite reasonable and can
  be a useful mental framework. let's break it down:

  1. boot behavior state:
    • enabled/disabled/masked/static
  2. runtime state:
    • active/inactive/activating/deactivating/failed/exited/reloading
  3. memory state:
    • loaded/not-loaded

  this tripartite model does capture the main aspects of a unit's lifecycle and configuration. it
  separates:

  1. how the unit behaves across reboots
  2. what the unit is doing right now
  3. whether systemd has the unit's configuration in memory

  benefits of this model:

  • it's easy to remember
  • it covers most common scenarios
  • it aligns well with how systemd actually manages units

  caveats:

  • some states (like "plugged" or "mounted") don't fit neatly
  • it doesn't capture all nuances (e.g., sub-states of "active")
  • some states can overlap or influence others

  afaict, your model is a solid simplification that works well for most use cases. it's
  particularly useful for understanding the basic lifecycle of a service:

  1. is it set to run on boot?
  2. is it running now?
  3. has systemd loaded its configuration?

  for day-to-day operations and basic troubleshooting, this mental model should serve you well. just
  be aware that there's additional complexity if you need to dive deeper.

  Prompt: ok, adjust that mental model to include things like plugged and mounted

  Assistant: alright, let's refine that model to be more comprehensive. we can think of systemd
  units as having these state categories:

  1. boot behavior state:
    • enabled/disabled/masked/static
  2. runtime state:
    • primary: active/inactive/activating/deactivating/failed
    • sub-states: running/exited/dead/reloading
  3. memory state:
    • loaded/not-loaded
  4. specialized states:
    • for device units: plugged/unplugged
    • for mount units: mounted/unmounted
    • for path units: waiting/triggered
    • for timer units: waiting/elapsed

  this adjusted model now accounts for the specialized states that didn't fit neatly before. it
  recognizes that different types of units can have unique states relevant to their function.

  key points:

  • categories 1-3 apply generally to all units
  • category 4 is type-specific and may overlap with category 2
  • some states (like "waiting") can have different meanings depending on the unit type

  this model is more COMPREHENSIVE but still maintains a logical structure. it acknowledges that
  while all units share some common states, there's also type-specific behavior to consider.

  caveats:

  • there's still some potential for overlap between categories
  • it's more complex than the previous model, which might be overkill for basic usage
  • it doesn't capture every possible state, but covers the most common ones

  afaik, this refined model should give you a solid mental framework for understanding systemd
  unit states across various unit types. it balances comprehensiveness with usability, though for
  day-to-day operations, you might still find the simpler model sufficient.
rgwood commented 1 week ago

Made some progress on this. Think I'm only going to show disabled and enabled services to start. Showing masked/static/generated services is super noisy and I don't think there's any benefit to it.