Skip to content

fix(services): create working_dir and accept a keg name - #797

Open
rustytrees wants to merge 1 commit into
indaco:mainfrom
rustytrees:fix/service-workdir-and-name-resolution
Open

fix(services): create working_dir and accept a keg name#797
rustytrees wants to merge 1 commit into
indaco:mainfrom
rustytrees:fix/service-workdir-and-name-resolution

Conversation

@rustytrees

Copy link
Copy Markdown

Fixes #796 — both halves.

resolveLabel: accept either name

Services are stored under their launchd label with the formula in keg_name, and the lifecycle lookup queried name only. Now either resolves, with three properties I thought were worth pinning rather than assuming:

  • The label still wins outright. If one service's label happens to be another's keg_name, an exact request is never reinterpreted.
  • Ambiguity is refused, not guessed. A formula registering two services (postgresql@16 → two labels) returns AmbiguousService and tells you to name one, instead of silently picking the first row.
  • hasService matches the same way, so status and start agree on what exists — otherwise status mosquitto would still say "no such service" for something start mosquitto could now act on.

register: create working_dir

Three lines, guarded by the fact that plist_mod.validate has already confined the path to the keg or the prefix by that point, so createDirPath cannot be aimed outside. cli/install.zig already does this for the log directory; this is the same treatment for the other declared path.

The failure mode is what makes this worth fixing rather than documenting: launchd returns EX_CONFIG before exec, so StandardErrorPath never gets created and the user is left with errored, an empty log directory, and no indication of which path was wrong. I lost a while to it on a real install before thinking to check launchctl list.

Deliberately not included

Seeding a missing config file named by a service.run argument. mosquitto also needs etc/mosquitto/mosquitto.conf, which nothing creates — but inventing config content is a different class of decision from creating a directory the plist already declares, and I did not want to smuggle it in here. Flagged in #796 if you want it.

Verification

  • zig build test — full suite green
  • ./scripts/lint-spawn-invariants.sh — clean
  • ./scripts/smokes/smoke_security.sh — 8/8 (touches the plist validator's neighbourhood)

Four regression tests, and I checked they fail against the current code rather than passing vacuously — the working_dir one fails with FileNotFound, which is the same shape as the real failure.

Two independent bugs, both hit by `malt services start mosquitto`.

**`ServiceNotFound` on a service that `services list` shows.** Services are
registered under their launchd label (`com.malt.mosquitto`) while `keg_name`
holds the formula (`mosquitto`), and the lifecycle lookup queried `name` only.
Every other verb takes the formula name — `install mosquitto`,
`uninstall mosquitto` — so the one place that demanded the label was the one
place a user would not think to look it up. `resolveLabel` now accepts either.
The label still wins outright, so an exact request is never reinterpreted, and
a formula registering two services says so instead of picking one. `hasService`
matches the same way so `status` and `start` agree on what exists.

**Registered services whose working_dir does not exist cannot start.** launchd
fails the spawn with EX_CONFIG (78) *before* exec when WorkingDirectory is
missing, so StandardErrorPath is never created either: the user gets a bare
"errored" and an empty log directory, with nothing anywhere saying which path
was wrong. `cli/install.zig` already pre-creates the log directory for exactly
this reason — the working dir was the half that got missed, and nothing else in
the install path creates it. mosquitto is a plain example: it declares
`working_dir var/mosquitto`, has no post_install, and its install step is a
bare cmake install, so that directory never exists on a fresh install.
`plist_mod.validate` has already confined the path to the keg or the prefix by
the time we get here, so creating it is in-bounds.

Both regression tests fail against the previous code — the working_dir one
with FileNotFound, which is the same shape as the real failure.
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.

services: start/status reject the keg name, and a missing working_dir fails with no diagnostic

1 participant