Repository navigation
Start the scheduler service without a console session - #1
Conversation
ServiceInstallation hardcoded gui/<uid>, which only exists while the user is logged in at the console. Over ssh after a reboot, install, start and update failed with 'launchctl bootstrap failed (125)'. - install/start/update load into user/<uid> when gui/<uid> is absent; the plist adds LimitLoadToSessionType [Aqua, Background] so that domain accepts it - stop boots out every domain that holds the label; start kickstarts the loaded one - a launchd-started scheduler (XPC_SERVICE_NAME == label) that finds the latch held waits for it instead of exiting 75 into a KeepAlive respawn loop, so a later console login hands over cleanly; a manual service run still fails fast
CerebralCoding
left a comment
There was a problem hiding this comment.
Thanks for the contribution and the detailed reproduction and testing. The background-domain fallback looks reasonable, and retaining the scheduler lock as the single-instance guard keeps the change focused.
I reviewed the patch and ran the full test suite with Swift 6.4: all 219 tests passed, along with strict formatting. I didn’t reproduce the MCP failures in this run.
Before merging, I’d like to address the existing-installation limitation you noted. ServiceInstallation.configuration adds the required LimitLoadToSessionType setting, but ServiceUpdate.apply only replaces the executable and updates its receipt. The official installer also takes that update path for existing installations, so upgrading would leave those users unable to start headlessly.
Could you extend the update path to refresh the installed launch-agent configuration while preserving the selected queue? Please include a regression test for an installed plist missing this setting, and include the plist in failure recovery so a failed restart restores the original configuration alongside the executable.
The actual console-login transition remains an outstanding integration check. I haven’t exercised that here, so the passing tests shouldn’t be taken as verification of that transition. Thanks for explicitly calling out that limitation.
Updates replaced only the executable, so an installation made before LimitLoadToSessionType was added could not start without a console session. apply now rewrites the installed plist for the queue it already serves, restarts a loaded service when the plist changed, and restores the original plist alongside the executable if the restart fails.
|
Addressed in 13f289d. 8 new tests: stale plist with a changed and an unchanged binary, stopped service, identical plist (file untouched), failed-restart recovery for both binary cases, unwritable plist directory, and queue preservation. Full suite: 226 of 227 passed. The one failure, Not exercised: the real |
latch service startfails on a Mac nobody has logged into at the console.ServiceInstallation.domainis hardcoded togui/<uid>, and that domain only exists while someone is logged in at the console. Our Studio rebooted after a power cut, has no auto-login, and is driven entirely over ssh:latch service runworks fine in the same state (sensors read, jobs admit and run), so only the launchd plumbing needs the GUI.Changes
ServiceInstallation.domaingui/<uid>when it exists (launchctl print gui/<uid>succeeds), elseuser/<uid>LimitLoadToSessionType = [Aqua, Background]. WithoutBackground,bootstrap user/<uid>fails with5: Input/output error(tested on macOS 27)stop/startstopboots out every domain holding the label;startkickstarts whichever one has it loadedSchedulerService.runXPC_SERVICE_NAMEequals the label) and the latch is held, it waits onservice.lockinstead of exiting 75. Otherwise a console login would load a second copy intogui/<uid>that exits 75 andKeepAliverespawns it every 10 s. A manuallatch service runstill fails fast with 75Heads-up: this changes what
installwrites to the plist (the newLimitLoadToSessionTypekey). Versions, the service revision and schemas are unchanged. A plist from an existing install doesn't have the key, so on those installsstartwith no console session still fails as it does today until the plist is reinstalled.Verified
LimitLoadToSessionType = Backgroundloads intouser/501, keeps running across ssh sessions, and launchd setsXPC_SERVICE_NAMEto its label. Without the key, the bootstrap fails with error 5.service runholding it, a plain secondservice runexits 75 (unchanged). A second one withXPC_SERVICE_NAME=<label>logsanother scheduler holds this latch; waiting, then takes overservice.jsonwithin 3 s of the holder exiting.swift format lint --strictis clean.swift test: my new tests pass in every run. Each full run had 1 to 3 differentMCPTestsfailures onlatch_wait/ "MCP message timed out", and unmodifiedmain(78da077) also failed one (protocol tasks and completed output survive reconnecting).MCPTestsalone passes 51/51.Not tested: an actual console login while the
user/<uid>copy runs. I couldn't log in on that box, so the takeover is verified only with the simulated launchd environment above.