macOS 通過啟動硬盤指定目錄下的配置文件,來完成啟動任務(wù)。這些文件為plist,本質(zhì)上是XML。
0x00 Launchd 目錄配置
Mac下Launchd的plist文件放置的目錄有
由用戶自己定義的任務(wù)項
~/Library/LaunchAgents
由管理員為用戶定義的任務(wù)項
/Library/LaunchAgents
由管理員定義的守護進程任務(wù)項
/Library/LaunchDaemons
由 macOS 為用戶定義的任務(wù)項
/System/Library/LaunchAgents
由 macOS 定義的守護進程任務(wù)項
/System/Library/LaunchDaemons
其中 LaunchDaemons 和 LaunchAgents 的區(qū)別是什么?
- LaunchDaemons 是用戶未登陸前就啟動的服務(wù)(守護進程)需要 root 權(quán)限, 即 sudo xx
- LaunchAgents 是用戶登陸后啟動的服務(wù)(守護進程)
0x01 Launchd Plist 配置
| 標簽 | 必填 | 說明 |
|---|---|---|
| Label | 是 | 標識符,用來表示該任務(wù)的唯一性 |
| Program | 是 | 程序名稱,用來說明運行哪個程序、腳本 |
| ProgramArguments | 是 | 同上,與Program二選一或一起使用,只是可以運行多個程序、可帶參數(shù) |
| WatchPaths | 否 | 監(jiān)控路徑,當路徑文件有變化是運行程序,也是數(shù)組 |
| RunAtLoad | 否 | 是否在加載的同時啟動 |
| StartCalendarInterval | 否 | 運行的時間,單個時間點使用dict,多個時間點使用 array -> dict |
| StartInterval | 否 | 時間間隔,與StartCalendarInterval使用其一,單位為秒 |
| StandardInPath、StandardOutPath、StandardErrorPath | 否 | 標準的輸入輸出錯誤文件,這里建議不要使用.log作為后綴,會打不開里面的信息 |
兩種指定要執(zhí)行命令的方法:
使用 Program 和 ProgramArguments
- Program, 運行命令或要執(zhí)行文件路徑
- ProgramArguments, 執(zhí)行時傳入?yún)?shù)
只使用 ProgramArguments
- ProgramArguments 的每個參數(shù)為要執(zhí)行的命令或文件路徑,其它參數(shù)為傳入?yún)?shù)
兩種設(shè)置執(zhí)行時間的方法:
- StartCalendarInterval 使用元素Minute, Hour, Day, Month, Weekday指定執(zhí)行時間,如:
<!-- 每天的9:30執(zhí)行 -->
<key>StartCalendarInterval</key>
<dict>
<key>Minute</key>
<integer>30</integer>
<key>Hour</key>
<integer>9</integer>
</dict>
- StartInterval 設(shè)置執(zhí)行的時間間隔,單位為秒
<!-- 每小時執(zhí)行一次 -->
<key>StartInterval</key>
<integer>3600</integer>
0x02 Launchd 定時任務(wù)
編寫我的 shell 腳本文件
功能是在 macOS 開機后自動執(zhí)行下面的腳本, 開啟內(nèi)網(wǎng)穿透的功能, 可以忽略我的腳本內(nèi)容, 替換成你想用的腳本
# launch.sh
#! /bin/bash
export ZWY_HOME=/Users/mb
export ZWY_DIR=$ZWY_HOME/ZwyShell
export ZWY_DIR_FeiGeNAT=$ZWY_HOME/ZwyShell/darwin_amd64_client
export PATH=$PATH:$ZWY_DIR:$ZWY_DIR_FeiGeNAT
LOG_PATH=$ZWY_DIR/launch/launch.log
echo "====start zwy launch====" >> $LOG_PATH
echo $(date +"%Y-%m-%d %H:%M:%S") >> $LOG_PATH
echo "npc is in "`which npc` >> $LOG_PATH
npc -server=nps.xxxx.press:8024 -vkey=zwy -type=tcp > $ZWY_DIR_FeiGeNAT/npc_home.log
注意:
可以使用 chmod a+x launch.sh 給權(quán)限變成可執(zhí)行文件, 也可以用 bash 命令調(diào)用
編寫 Plist 文件
在 ~/Downloads 下面創(chuàng)建一個 Plist 文件, 都寫好后, 再復(fù)制到想要的權(quán)限文件夾下面
<?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.zwy.home</string>
<key>ProgramArguments</key>
<array>
<string>bash</string>
<string>/Users/mb/ZwyShell/launch/launch.sh</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>Version</key>
<string>3</string>
</dict>
</plist>
可以用 plutil -lint com.zwy.home.plist 來驗證 Plist 格式是否正確
加載命令
launchctl 是一個統(tǒng)一的服務(wù)管理框架,啟動、停止和管理守護進程、應(yīng)用程序、進程和腳本
# 加載任務(wù), -w選項會將plist文件中無效的key覆蓋掉,建議加上
launchctl load -w com.zwy.home.plist
launchctl start com.zwy.home
# 刪除任務(wù)
launchctl stop com.zwy.home.plist
launchctl unload -w com.zwy.home
# 查看任務(wù)列表, 使用 grep '任務(wù)部分名字' 過濾
launchctl list | grep 'com.zwy.home.plist'
經(jīng)驗
如果執(zhí)行 launchctl load 命令出現(xiàn) com.zwy.home.plist: Path had bad ownership/permissions,需要為 plist 文件賦予600 權(quán)限或者 root 權(quán)限, 都可以嘗試一下:
sudo chmod 600 path/com.zwy.home.plist
sudo chown root:wheel path/com.zwy.home.plist
執(zhí)行 launchctl list 查看到你的執(zhí)行結(jié)果, 其他錯誤可以用 launchctl error <insert numerical error code here>來查看具體錯誤:
$ launchctl list | grep 'com.zwy.home'
- 78 com.zwy.home # 錯誤結(jié)果
- 0 com.zwy.home # 正確結(jié)果
執(zhí)行 launchctl list 具體描述如下:
1230 - com.apple.speech.speechsynthesisd
353 - com.apple.security.cloudkeychainproxy3
255 - com.apple.secd
- 0 com.apple.sbd
第一列表示進程號,如果有在結(jié)果中羅列,但沒有數(shù)字而只是一個橫線,標志雖然已經(jīng)loaded, 但沒有運行
第二列是上次退出的狀態(tài)號(the last exit code), 0表示成功,正數(shù)表示錯誤退出,負數(shù)表示收到信號后退出
如果 shell腳本輸出如果有中文,中文部分亂碼,則需要在shell腳本中指定編碼:
#!/bin/bash
LANG=en_US.UTF-8
export LANG
echo "你好賈維斯"
2024-07-10 補充
有時候會遇到 exit code 為 1, 表示沒有權(quán)限, 在我的這里, 實際上是 bash 沒有權(quán)限訪問磁盤文件, 在系統(tǒng)設(shè)置里添加自己的 bash 為完全磁盤訪問權(quán)限即可, 查看 bash 在哪里, 用which bash
2025-03-12 補充
遇到了 exit code 126 表示沒有磁盤權(quán)限, 我放到了 /Documents 下, 用 bash 執(zhí)行了命令, 解決這個需要給 bash 完全磁盤權(quán)限