Mac

Mac でlaunchdを使って定期的にスクリプトを実行する方法

Mac miniサーバーで定期的にスクリプト処理を実行させたいケースが発生しました。
通常はcrontabを使うのですが、Macではlaunchdを使う方が良いとのことです。
crontabでも出来るとのことですが、アクセス権などの制限に引っかかる可能性があり、せっかくなのでlaunchdを使うことにしました。

目的

pythonスクリプトを定期的に実行するためのlaunchdの設定方法を記載します。
pythonスクリプトでは、外部のAPIを叩いてIPアドレスを取得し、取得したIPアドレスを元にDNSのAレコードを更新する処理を行います。

0. 環境

  • Mac mini (macOS Ventura 13.6)
  • Python 3.11.4

1. launchdの設定ファイルを作成する

設定方法は以下のフォルダにXML形式のplistファイルを作成します。
以下のFileNameは任意の名前で、拡張子はplistにします。

フォルダ説明
~/Library/LaunchAgents/<FileName>.plistユーザーレベルのlaunchdジョブ
/Library/LaunchAgents/<FileName>.plistシステム全体のユーザーレベルのlaunchdジョブ
/Library/LaunchDaemons/<FileName>.plistシステム全体のrootレベルのlaunchdジョブ

上記の「ユーザーレベルのlaunchdジョブ」と「システム全体のユーザーレベルのlaunchdジョブ」はエージェントとなり、ユーザーがログインしているときに実行されます。
システム全体のrootレベルのlaunchdジョブ」はデーモンとなり、ユーザーがログインしていないときでも実行されます。
今回のケースはサーバーのように使用しているため、ログインしていない時でも実行したいので、/Library/LaunchDaemons/<FileName>.plistに作成します。

2. plistファイルの作成

以下のような内容でplistファイルを作成します。

sudo vi /Library/LaunchDaemons/launched.python.dns.update.plist

ファイルの内容は以下となります。

<?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>launched.python.dns.update</string>

	<key>ProgramArguments</key>
	<array>
		<string>python</string>
		<string>/Users/tomo/Scripts/python.com_dns_update.py</string>
	</array>

	<key>StartCalendarInterval</key>
	<dict>
		<key>Hour</key>
		<integer>0</integer>
		<key>Minute</key>
		<integer>0</integer>
	</dict>

	<key>StandardOutPath</key>
	<string>/Users/tomo/Develop/Scripts/python.com_dns_update/dice_script.out</string>

	<key>StandardErrorPath</key>
	<string>/Users/tomo/Develop/Scripts/python.com_dns_update/dice_script.err</string>
</dict>
</plist>

簡潔に上記のKeyを説明すると以下のようになります。

Key記述方法説明
Label任意の名前launchdジョブの名前で、任意の名前を指定します。
ProgramArguments配列形式実行するコマンドと引数を指定します。
StartCalendarInterval辞書形式実行する時間を指定します。
StandardOutPathファイルパス標準出力の保存先を指定します。
StandardErrorPathファイルパス標準エラーの保存先を指定します。

今回は定期時間になったら実行するため「StartCalendarInterval」を使用しておりますが、以下のように実行するパターンが他にもあります。

  • 「StartInterval」: 指定した秒数ごとに実行
  • 「WatchPaths」: 指定したパスに変更があった場合に実行
  • 「QueueDirectories」: 指定したパスにファイルが追加された場合に実行
  • 「KeepAlive」: 指定したプロセスが終了した場合に実行
  • 「StartOnMount」: 指定したボリュームがマウントされた場合に実行

このあたりは以下のWikipediaに記載があるようです。

https://ja.wikipedia.org/wiki/Launchd

またファイルの作成は直接作成する他に、以下のサイトから作成する事も可能です。

https://launched.zerowidth.com

3. plistファイルの権限を変更する

作成したplistファイルの権限を変更します。

sudo chmod 644 /Library/LaunchDaemons/launched.python.dns.update.plist

権限を変更した後、所有者をrootに変更します。

sudo chown root:wheel /Library/LaunchDaemons/launched.python.dns.update.plist

4. launchdを読み込み、定期処理を開始する

launchdにplistファイルを読み込ませます。

sudo launchctl load /Library/LaunchDaemons/launched.python.dns.update.plist

5. launchdの確認

launchdに読み込まれているか確認します。

sudo launchctl list | grep launched.python.dns.update

出力結果は以下のようになります。

- 0	launched.python.dns.update

6. launchdのアンロードして、定期処理を停止する

定期処理を行わないようにするには、launchdをアンロードします。

sudo launchctl unload /Library/LaunchDaemons/launched.python.dns.update.plist