【macOS】命令 - Launchctl

Posted by 西维蜀黍 on 2020-11-21, Last Modified on 2022-04-11

What is launchd?

Wikipedia defines launchd as “a unified, open-source service management framework for starting, stopping and managing daemons, applications, processes, and scripts. Written and designed by Dave Zarzycki at Apple, it was introduced with Mac OS X Tiger and is licensed under the Apache License.”

launchctl

launchctl 管理OS X的启动脚本,控制启动计算机时需要开启的服务。也可以设置定时执行特定任务的脚本,就像Linux crontab一样, 通过加装*.plist文件执行相应命令。

Job Definitions

The behavior of a daemon/agent is specified in a special XML file called a property list. Depending on where it is stored it will be treated as a daemon or an agent.

Job definitions crucial for the operation of the operating system are stored below /System/Library. You should never need to create a daemon or agent in these directories. Third-Party definitions which are relevant for every user are stored below /Library. Job definitions for a specific user are stored below the respective user’s Library directory.

Type Location Run on behalf of
User Agents ~/Library/LaunchAgents Currently logged in user
Global Agents /Library/LaunchAgents Currently logged in user
Global Daemons /Library/LaunchDaemons root or the user specified with the key UserName
System Agents /System/Library/LaunchAgents Currently logged in user
System Daemons /System/Library/LaunchDaemons root or the user specified with the key UserName

Daemons and Agents

launchd differentiates between agents and daemons. The main difference is that an agent is run on behalf of the logged in user while a daemon runs on behalf of the root user or any user you specify with the UserName key.

通过brew安装的软件,通常都会有一个*.plist文件,所以通过launchctl可以使brew安装的软件开机自启动。

比如,执行

$ brew services start redis

brew 其实会将 homebrew.mxcl.redis.plist 文件添加到 ~/Library/LaunchAgents 中。

Example

The following example shows a complete job definition with only three keys:

  • Label This key is required for every job definition. It identifies the job and has to be unique for the launchd instance. Theoretically it is possible for an agent to have the same label as a daemon, as daemons are loaded by the root launchd whereas agents are loaded by a user launchd, but it is not recommended.
  • Program This key defines what to start, in this case a shell script /Users/Me/Scripts/cleanup.sh.
  • RunAtLoad This is one of several optional keys specifying when the job should be run, in this case right after it has been loaded.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
	<dict>
		<key>Label</key>
		<string>com.example.app</string>
		<key>Program</key>
		<string>/Users/Me/Scripts/cleanup.sh</string>
		<key>RunAtLoad</key>
		<true/>
	</dict>
</plist>

e.g.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
   <key>Label</key>
   <string>com.user.loginbashscript</string>
   <key>ProgramArguments</key>
   <array><string>/path/to/executable/script.sh</string></array>
   <key>RunAtLoad</key>
   <true/>
</dict>
</plist>

Load the specified configuration files or directories of configuration files. Jobs that are not on-demand will be started as soon as possible. All specified jobs will be loaded before any of them are allowed to start.

Note that per-user configuration files (LaunchAgents) must be owned by root (if they are located in /Library/LaunchAgents) or the user loading them (if they are located in $HOME/Library/LaunchAgents).

All system-wide daemons (LaunchDaemons) must be owned by root. Configuration files must disallow group and world writes. These restrictions are in place for security reasons, as allowing writability to a launchd configuration file allows one to specify which executable will be launched.

Config

Refer to https://www.launchd.info/

List Labels

  • With no arguments, list all of the jobs loaded into launchd in three columns.
  • The first column displays the PID of the job if it is running.
  • The second column displays the last exit status of the job. If the number in this column is negative, it represents the negative of the signal which stopped the job. Thus, “-15” would indicate that the job was terminated with SIGTERM.
  • The third column is the job’s label. If [label] is specified, prints information about the requested job.
# Show all loaded agents/daemons, with the PID if the process they specify is currently running, and the exit code returned the last time they ran:
$ launchctl list [label]
PID	Status	Label
-	0	com.apple.SafariHistoryServiceAgent
2037	0	com.apple.progressd
-	0	com.google.keystone.user.xpcservice
2055	0	com.apple.cloudphotod
1188	0	com.apple.Finder
...

Example

We issue the command launchctl list, which returns a long list of currently loaded jobs.

$ launchctl list | grep com.example.app -	2	com.example.app
  • We received one line of output, which means that our job is currently loaded.
  • The first colum contains the current process id. We received a - instead of a number, which means that while the job is loaded it is currently not running. Any numeric value in this column would indicate a running job.
  • The second column displays the last exit code. A value of 0 indicates that the job finished successfully, a positive number that the job has reported an error, a negative number that the process was terminated because it received a signal.

launchctl also accepts the Label of a job as an optional argument. This results in a more detailed output in a JSON-like format.

To find out if a job’s Disabled key has been overridden we query the override database using the xpath command line utility. Its first argument is the name of the file to query, its second argument the query itself. You have to replace the job label in the query.

Enable a Agent Onboot

# Activate a user-specific agent to be loaded into launchd whenever the user logs in: 
# e.g., sudo launchctl load /System/Library/LaunchDaemons/homebrew.mxcl.mysql.plist
$ launchctl load ~/Library/LaunchAgents/<LABEL>.plist

# Activate an agent which requires root privileges to run and/or should be loaded whenever any user logs in (note the absence of ~ in the path):
$ sudo launchctl load /Library/LaunchAgents/<LABEL>.plist

# Activate a system-wide daemon to be loaded whenever the system boots up (even if no user logs in):
$ sudo launchctl load /Library/LaunchDaemons/<LABEL>.plist

When launchd is about to load a job it will check if it has the Disabled key set. Disabled jobs will not be loaded. But the value of this key can be overridden.

Permanently disabling/enabling a job

$ launchctl unload -w ~/Library/LaunchAgents/com.example.app.plist

# -w flag permanently adds the plist to the Launch Daemon
$ sudo launchctl load -w /Library/LaunchDaemons/com.startup.plist

Disable a Agent OnBoot

# Unload a currently loaded agent, e.g. to make changes (note: the plist file is automatically loaded into launchd after a reboot and/or logging in):
$ launchctl unload ~/Library/LaunchAgents/<LABEL>.plist

启动/停止

Manually Start

Start the specified job by label. The expected use of this subcommand is for debugging and testing so that one can manually kickstart an on-demand server.

# Manually run a known (loaded) agent/daemon, even if it is not the right time (note: this command uses the agent's label, rather than the filename)
$ launchctl start <LABEL>

Manually Stop

Stop the specified job by label. If a job is on-demand, launchd may immediately restart the job if launchd finds any criteria that is satisfied.

# Manually kill the process associated with a known agent/daemon, if it is running
$ launchctl stop <LABEL>

Run a Bash Script Onboot via launchctl

Refer to https://swsmile.info/post/macos-startup-onboot/

Reference