Linuxからプロセスの動作状況をスマホに通知する
計算機をn時間とかx日とか動かす作業をしている時に、タスクの終了状況をどうやって確認するか。
いちいちps -x
とかtop
とかやるのもいいけど、もっとパソコンらしいラクチンな方法があるはず。
というわけで、自分の思考と試行の備忘録をここに記す。
プロセス監視の対象同定
ps
で現在動いているプロセスを取得できる。これを定期的に実行してあげればよさそうである。だが、問題はどうやってプロセスを同定するか?
個人的には、以下の3つの用途が考えられた。
- 単純にコマンドを渡してあげて、実行すると同時に監視を開始
- 実行中のプロセスに対し、PIDで同定する
- 実行中のプロセスに対し、実行時のコマンド名で対応する
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
使った結果
自画自賛もいいとこだけど便利すぎだこれ pic.twitter.com/8vYM4PleDj
— ありか (@arika_nekowiz) 2017年6月18日
定期的にVPNとSSH繋いで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が導入されていない場合についても記述されている。