Linuxからプロセスの動作状況をスマホに通知する

計算機をn時間とかx日とか動かす作業をしている時に、タスクの終了状況をどうやって確認するか。
いちいちps -xとかtopとかやるのもいいけど、もっとパソコンらしいラクチンな方法があるはず。
というわけで、自分の思考と試行の備忘録をここに記す。

プロセス監視の対象同定

psで現在動いているプロセスを取得できる。これを定期的に実行してあげればよさそうである。だが、問題はどうやってプロセスを同定するか?
個人的には、以下の3つの用途が考えられた。

  1. 単純にコマンドを渡してあげて、実行すると同時に監視を開始
  2. 実行中のプロセスに対し、PIDで同定する
  3. 実行中のプロセスに対し、実行時のコマンド名で対応する

1だけだと実行し忘れた時に捕捉する手段がないし、2,3だけだと二度手間になる。
1の場合、普通に引数を合成してやって、$!でPIDを取得してやればよさそうだ。*1
上の選択肢の分岐は第1引数で指定してやる仕組みにすることで対応する。

#引数に応じて捕捉条件を変動させる
action=$1
greptx=""
tasktype=""
case "${action}" in
	"-p" )
		greptx="^\s{0,}${2} "
		tasktype="PID[${greptx}]";;
	"-c" )
		greptx=" ${2}"
		tasktype="COMMAND[${greptx}]";;
	"-d" )
		taskstr="$2 $3 $4 $5 $6 $7 $8 $9"
		${taskstr} &
		tasktype="JOB[${taskstr}]"
		greptx="^\s{0,}${!} ";;
	* )
		echo -e "\033[31m[ERROR]\033[0m Missing argument."
		exit 2;;
esac

-pならPID指定、-cならCOMMAND指定、-dなら引数を合成してバックグラウンド実行、その後PIDを取得する。


プロセス監視を定期的に行う方法

さて、これで目的のPIDないしCMDは取得できた。後はpsを定期的に実行してやるだけである。
この際、余計な項目を表示させないようps -e o pid,cmdを使ってやる。
これをgrepで条件一致させてやる。この時前述の変数greptxを使用するわけだ。
もちろん、grep自身や実行中のshell scriptの名前が引っかかってしまってはしょうもないので、これはgrep -vで除外する。*2

#ここで、ファイル名はprcliveである(grepの除外条件にファイル名を使っている)
interval=5
while true
do
	isAlive=`ps -e o pid,cmd | egrep "${greptx}" | grep -v "grep" | grep -v "prclive" | wc -l`
	if [ $isAlive -ge 1 ]; then
		# running
		echo "${tasktype} is Running…"
	else
		# dead
		echo -e "\033[32m${tasktype}\033[0m is Finished."
		break;
	fi
	sleep $interval
done

無限ループを発生させ、$intervalの間隔で実行中のタスク一覧を取得する。
grepに引っかかる=生存中なのでrunning, いないなら終了しているのでdeadという寸法だ。
とりあえず、これでタスクの定期監視は達成できた。


問題点:通知手段が貧弱

これで完成でいいかというと決してそんなことは無いであろう。
そもそもコマンドライン上にプロセスの監視状況を出すだけならtopでいい。
せっかくプログラムを書いているのだから、もっといい通知手段を使いたいものだ。
notify-sendという手もあるが、そもそも実行中のマシンにモニターをつなげてないし、遠隔操作も普通に行う。
必然的に別の方法を探さないといけないことになる。


どうやってスマホに通知するか

最初は専用のアプリを作ったりとか考えたが、明らかに苦労の割に成果が見合わないだろうということで却下。
VPN接続を維持したままとかそういうのも無しの方向で考えた結果、メール送信が一番シンプルでいいだろうということに。
GmailとかならPC/スマホ両方でお手軽に通知出せるので、余計な車輪の再発明をしなくて済むのが大きい。


CUIでメール送信

.mailrcに情報書き込み

メール送信プログラムとして、CUIで操作できてroot権限が不要(ここ大事)なmailxを使用することにした。
幸い標準でinstallされていたが、ないなら

sudo yum install mailx

してやる必要がある。
さて、初期設定のままだと引数がいっぱいになってしまうので設定ファイルに共通設定を書き込んでしまおう。
設定ファイルはvi ~/.mailrcで開く。
[]で囲った部分は適宜自分のものに読み替えて欲しい。

set smtp-use-starttls
set smtp-auth=login
set smtp=smtp://smtp.gmail.com:587
set smtp-auth-user="[your-mail-address]@gmail.com"
set smtp-auth-password="[your-password]"
set from="[your-username]"
set ssl-verify=ignore
set nss-config-dir=/home/[USER]/certs/



nss-config-dirに指定してやるための証明書確保

証明書がないと「Error in certificate: Peer's certificate issuer is not recognized.」というエラーが発生する。
ここがハマり所で、気づくのに数時間かかったので忘れないうちにメモ書きしておく。
まず、certutilがinstallされているか確認しておこう。ないなら

sudo yum install nss-tools

が必要。
firefoxがインストールされているなら、その証明書をかっぱらうと簡単に設定ができる。
~/.mozilla/firefox/[xxxxxxxx].default/フォルダ内にある、key3.db,cert8.db,secmod.db
~/certsにコピーする。
その後、ホームディレクトリに戻って以下のコマンドを順番に実行していく。*3

~]$ certutil -L -n 'Google Internet Authority G2'  -d certs -a > google.cert.asc
~]$ certutil -A -t "C,," -n 'Google Internet Authority G2'  -d certs -i google.cert.asc



テスト送信をしてみる

以上の準備が整ったら、以下のコマンドを試しに実行してみる。

echo test | mailx -s "subject: testmail" [want-sendto-address]@gmail.com

エラーが発生しなければ成功。


プロセス監視アプリに組み込む

これで準備は全て完了した。後はプログラムに組み込むだけだ。

sendto="[want-sendto-address]@gmail.com"
interval=5
while true
do
	tasktype=`ps -e o pid,cmd | egrep "${greptx}" | grep -v "grep" | grep -v "prclive"`
	isAlive=`ps -e o pid,cmd | egrep "${greptx}" | grep -v "grep" | grep -v "prclive" | wc -l`
	if [ $isAlive -ge 1 ]; then
		# running (nothing to do)
	else
		# dead
		dat=`date +"%Y/%m/%d %H:%M:%S"`
		`echo -e "${tasktype} is finished. (at: ${dat})" | mailx -s "Finished: [${tasktype}]" $sendto `
		break;
	fi
	sleep $interval
done



使った結果


定期的にVPNSSH繋いでtopして閉じるクソ面倒な所業から解放された喜びがすごい。
こうしてますますヒキニート化していくのであった。完。
(休日の最中でも容赦なく通知音が鳴って現実に引き戻されるわけでもあるが)

*1:参考: [Linux][shell] PIDを取得する方法まとめ - Qiita

*2:参考: 簡易プロセス死活監視スクリプト - Qiita

*3:参考: ssl - smtp.gmail.com from bash gives "Error in certificate: Peer's certificate issuer is not recognized." - Server Fault。 ただし、Google Internet Authorityの後ろにG2をつけてやる必要があった。 Firefoxが導入されていない場合についても記述されている。