Documentation
¶
Overview ¶
Package launchagent keeps a program running across logouts and restarts.
A program a person started in a terminal ends with the terminal, and a program that ends is a queue that stops, a watcher that stops watching, a sync that silently falls behind. Something has to tell macOS to start it again, and there are two ways to say so:
Install writes a plist into ~/Library/LaunchAgents, which is path and file work — no cgo, nothing macOS-only — and works for anything, including a bare executable in /usr/local/bin and a macOS older than 13. It is also invisible to the person who owns the machine: the item shows up in System Settings as a reverse-DNS label with no name and no icon, and nothing tells the program when they switch it off.
Enable prefers SMAppService when the process is inside an application bundle, through github.com/go-macos/servicemanagement. That is what Apple supports since macOS 13, it appears under the application's own name, and the person can turn it off — which State can then report, and which the plist can never tell you.
Enable, State and Disable choose between the two and say which one they used. Install, Remove, Installed, Path and Dir are the plist alone, unchanged: they still mean exactly what they meant, so a caller that wants the file and nothing else still gets it.
Everything here is CGO_ENABLED=0 and builds and is tested on every platform; off darwin, servicemanagement reports itself unsupported and the plist is what is left, which is what lets a cross-compiling build write one.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Disable ¶
Disable stops the program starting at login, whichever way it is held — and it takes away BOTH, because a program that gained a bundle after shipping a plist has two registrations and removing one of them leaves it starting at login anyway.
A registration that is not there is not an error: asking for it to be gone and finding it gone is the outcome wanted. SMAppService is asked for its status first for exactly that reason: -unregister: on something never registered is reported by macOS as an error (SMAppServiceErrorDomain 22, "Invalid argument") rather than as the no-op it is.
func Install ¶
Install writes the agent and reports where it put it.
It does not ask launchd to load it: that is a running session's business, and a build or an installer has no session to speak for. RunAtLoad covers the next login, which is the case this exists for.
Types ¶
type Method ¶
type Method int
Method is the mechanism actually holding a startup registration.
It is reported rather than assumed because the two are not interchangeable and a caller has things to say about which one it got: a plist is invisible to the person who owns the machine, and an SMAppService registration can be switched off by them at any moment.
const ( // MethodPlist is a plist in ~/Library/LaunchAgents — what [Install] // writes. It works for anything, including a bare executable in // /usr/local/bin, and macOS shows it to the person as a reverse-DNS label // with no name and no icon. MethodPlist Method = iota // MethodAppService is SMAppService, the supported path since macOS 13. It // requires an application bundle, it appears in System Settings under the // application's own name, and the person can switch it off there — which // [State] can then report. MethodAppService )
type Registration ¶
type Registration struct {
// Method is the mechanism holding it.
Method Method
// Enabled reports whether the program will actually start at the next
// login. It is false for a registration waiting to be approved: macOS
// holds it and will not run it.
Enabled bool
// Path is the plist, when Method is [MethodPlist]. It is the path
// [Install] returns, and it is set even when nothing is installed there,
// so a caller can say where it looked.
Path string
// Advice is what to tell the person, or "" when there is nothing for them
// to do. It is non-empty exactly when macOS is waiting on them — most
// often because they switched this item off before, in which case it
// stays off whatever this program does.
Advice string
// Fallback is the SMAppService failure that made [Enable] write a plist
// instead, and nil when there was none.
//
// It is carried rather than swallowed. Falling back is the right
// behaviour — a program that was working must not stop working the day it
// gains a bundle — but a fallback is also how "the plist is not in the
// bundle" looks from the outside, and a caller that never sees this error
// never finds that out. Log it; do not fail on it.
Fallback error
}
Registration is what Enable did, or what State found.
func Enable ¶
func Enable(s Spec) (Registration, error)
Enable makes the program start at login by whichever mechanism this process can actually use, and reports which one that was.
Inside an application bundle it prefers SMAppService: it is what Apple supports, it puts the item in System Settings under the application's own name, and it lets the person turn it off — which is a feature, not a problem, provided the program can find out. Everywhere else, and whenever SMAppService refuses, it writes the plist Install writes.
A refusal is reported in Registration.Fallback rather than returned as an error. The commonest one is a bundle that does not ship Contents/Library/LaunchAgents/LABEL.plist — a real defect, but not a reason to leave a program that used to start at login no longer starting at login.
A nil error does NOT mean the program will run. Read Registration.Enabled, and show Registration.Advice when it is there: a registration awaiting approval is held by macOS and idle, and saying nothing about it is how a person ends up with a feature that silently does nothing.
func State ¶
func State(label string) (Registration, error)
State reports whether the program is set to start at login, and how.
It asks SMAppService first when there is a bundle to ask from, and falls through to the plist when macOS holds no registration. That order matters for a program that shipped a plist before it gained a bundle: both can exist at once, and the SMAppService one is the one macOS acts on.
type Spec ¶
type Spec struct {
// Label identifies the agent to launchd, in reverse-DNS form. It is also
// the plist's file name, and what Remove and Installed are given.
Label string
// Program is the executable, and Args what follows it. The program is
// named absolutely: launchd starts it from no particular directory and
// with no particular PATH.
Program string
Args []string
// WorkingDir is where the program runs. Empty leaves launchd's choice.
WorkingDir string
// RunAtLoad starts it when the user logs in, which is what makes this
// survive a restart rather than merely a terminal closing.
RunAtLoad bool
// KeepAlive starts it again when it exits. A program that means to
// finish should leave this false, or launchd will fight it.
KeepAlive bool
// StdoutPath and StderrPath are where its output goes. A service with
// nowhere to write is a service nobody can diagnose.
StdoutPath string
StderrPath string
}
Spec is the program to keep running.