【MacOS・LaunchAgent】ログイン時にスクリプトを実行する
はじめに
業務中に毎回、SQLクエリを叩く作業があり、PCのログイン時に処理が実行され、結果が自動で表示されたら便利だと思ったので、色々と調べて実行してみることにしました。今回はLaunchAgentという方法を使用して、実装していきたいと思います。
Launch Agentについて
LaunchAgentはmacOSのlaunchdで管理されるプロセスの一種です。ユーザーがログインしたときにロードされる為、この仕組を利用してユーザーログイン時にスクリプトを実行が可能となります。
エージェント(LaunchAgents)とデーモン(LaunchDaemons)の違いについて
エージェントとデーモンは共に、macOSでバックグラウンドプロセスを自動化するための仕組みですが、いくつか異なる点があります。デーモンは対話型ユーザーのセッションの直下ではなく、バックグラウンドプロセスとして実行され、システム全体に関わるタスクや、ユーザーセッションに依存しない処理に適しています。エージェントも同様にバックグラウンドプロセスの一種で、デーモンはシステムのプロセスとして実行されますが、エージェントは特定のユーザーのプロセスとして実行されます。エージェントはユーザーセッションに関連するタスク(ユーザーログイン後のタスク実行など)や、GUIアプリケーションの実行に適しています。つまり、エージェントがユーザーログイン時に起動するのに対して、デーモンはシステム起動時に起動します。
launchdで管理されているデーモンとエージェントのフォルダ構成
フォルダ | 目的 |
---|---|
/System/Library/LaunchDaemons | システムが提供するデーモン |
/Library/LaunchDaemons | 他社製のシステムデーモン |
/System/Library/LaunchAgents | システムが提供するエージェント。すべてのユーザに対してユーザごとに適用される |
/Library/LaunchAgents | 他社製のエージェント。すべてのユーザに対してユーザごとに適用される |
~/Library/LaunchAgents | 他社製のエージェント。ログイン中のユーザにのみ適用される |
ユーザーがログインしていなくてもプログラムを起動したい場合は、デーモンで処理する必要があります。通常、システム起動時に実行されるデーモン用のplistファイルを/Library/LaunchDaemons
に配置します。セキュリティ上の理由から、plistファイルには適切な権限とオーナーシップを設定する必要がある為、設定ファイルの権限は644、所有者はroot:wheel
にします。
sudo chown root:wheel /Library/LaunchDaemons/your_daemon.plist
sudo chmod 644 /Library/LaunchDaemons/your_daemon.plist
システム起動時に自動的に実行されるデーモンは、システムのセキュリティに影響を与える可能性があるため、信頼できるソースからのデーモンのみに限定します。
今回はログイン後に設定したスクリプトを起動させたい為、エージェントとして~/Library/LaunchAgents
に処理を記述していきます。
スクリプトの生成
まず、設定ファイルを追加する前に、実施したいスクリプトを生成します。
#!/bin/bash
# SSHトンネルを開始
ssh -f -N -L 3306:demo-crm-db.cdmy:<サーバのポート番号> demo-api
# クエリを実行
mysql -u <your_username> -p <your_password> -h 127.0.0.1 -P 3306 -e <クエリ>
# 自動的にクエリ結果を表示する
echo "実行されました。"
設定ファイルを記述後、ファイル権限を追加します。
chmod +x fetch_code.sh
ls -la
-rwxr-xr-x 1 staff 45 12 2 11:52 fetch_code.sh
パスワードの管理方法について
今回は、SQLクエリを実行している為、セキュリティの観点から、データベースのユーザー名やパスワードをスクリプトに直書きするのは避けるべきです。ySQL の設定ファイルmy.cnf
を利用してユーザー名とパスワードを安全に格納し、スクリプトから参照するように変更します。
[client]
user=your_username
password=your_password
host=
port=
ファイルの権限を600に設定して、他のユーザーから読み取れないようにします。
MySQLクライアントは ~/.my.cnf を自動的に参照するため、これでスクリプト内で-u
と-p
の省略が可能です。
chmod 600 .my.cnf
plistファイルの作成
スクリプトをLaunchdに登録するための設定ファイル(plist)を~/Library/LaunchAgents/
ディレクトリに作成します。.plist
はMac OSにおいてオブジェクトの永続化に用いられるファイル形式でメモリ上に生成したオブジェクトをファイルに書き込みプログラムが終了しても内容を保持します。
ファイルを作成します。
nano ~/Library/LaunchAgents/fetchCode.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>
<!-- ラベル(任意の名前でOK) -->
<key>Label</key>
<string>fetchCode</string>
<!-- スクリプトのパス -->
<key>ProgramArguments</key>
<array>
<string>/Users/<your_username>/scripts/fetch_code.sh</string>
</array>
<!-- 実行ディレクトリ -->
<key>WorkingDirectory</key>
<string>/Users/<your_username>/scripts</string>
<!-- PC起動時に実行 -->
<key>RunAtLoad</key>
<true/>
<!-- 標準出力/エラー出力 -->
<key>StandardOutPath</key>
<string>/Users/<your_username>/Library/Logs/fetchCode.out</string>
<key>StandardErrorPath</key>
<string>/Users/<your_username>/Library/Logs/fetchCode.err</string>
</dict>
</plist>
label
launchdプロセスがデーモンを識別するために一意の文字列を指定します。
ProgramArguments
実行ファイルに引数を渡したい時に設定します。今回はスクリプトが特定のディレクトリで動作することを前提にしており、スクリプトのパスを指定(このパスは絶対バスで記載する必要があります)し<your_username>
を自分のユーザー名に置き換えます。
ProgramArguments
は最初の要素に実行するプログラムのパス、それ以降に引数を指定しており、配列で指定する必要があります。
WorkingDirectory
実行ディレクトリのパスを指定します。
RunAtLoad
実行タイミングを指定します。RunAtLoad
をtrue
に設定することで、サービスのロード時(OS起動時を含む)に自動的にサービスが起動します。
StandardOutPath
スクリプトやコマンドがエラーを起こしている場合に、詳細なエラーを表示させるために標準出力と標準エラー出力を設定します。
シンタックスエラーがないかチェックし、
plutil -lint fetchCode.plist
fetchCode.plist: OK
Launch Agentのロード
launchctl
コマンドを使って、上記の.plist
ファイルを手動でロードしてみます。
launchctl load ~/Library/LaunchAgents/fetchCode.plist
エラーが出力された場合は、ファイルの権限を確認します。
Load failed: 5: Input/output error
Try running `launchctl bootstrap` as root for richer errors.
ls -l
-rw-r--r--@ 1 staff 636 12 2 11:54 fetchCode.plist
実行権限がない場合は以下で追加します。
chmod +x /Users/<username>/scripts/fetchCode.plist
または、標準出力と標準エラー出力を設定してデバッグを行い、エラー箇所を特定します。
cat /Users/takahashi_masaki/Library/Logs/fetchCode.out
cat /Users/takahashi_masaki/Library/Logs/fetchCode.err
修正後にアンロードを行い、動作確認を行います。
launchctl unload ~/Library/LaunchAgents/fetchCode.plist
launchctl load ~/Library/LaunchAgents/fetchCode.plist
最後に
ここまでお読みいただきありがとうございました。
参考文献
Discussion