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が導入されていない場合についても記述されている。

Wiztools with (some useful)tools

いろんな便利なツールを使ってwiztoolsは成り立っています。
このタイトルで記事を書きたかっただけでしょとかそういうことは言ってはいけない

Javascript関連

jQuery

https://jquery.com/
基本。概略を説明すると、DOMの操作がすごくラクチンになる。

// typeがnumberであるinput要素にhighlightクラスを追加
$("input[type=number]").attr("class", "highlight");
jQuery UI

https://jqueryui.com/
jQueryをベースとするUI用ライブラリ。
他にもあるんだろうけど今回はこれを採用。
以下に示したDialogの他に、autocompleteなども使用している。
ていうかサイト作り始めの時jQueryすらロクにわかってなかったから仕方がない。

$(function(){
  // 予め用意してあるdiv要素をdialogとして扱う
  $("div#mydialog").dialog({
    autoOpen: false,
    modal: true,
    buttons: { "OK": function(){ $("div#mydialog").dialog("close"); } }
  });
})

function pushButton(){
  // 用意しておいたdialogを開く
  $("duv#mydialog").dialog("open")
}
jQuery cookie

https://github.com/carhartl/jquery-cookie
Cookieを簡単に操作するためのjqueryプラグイン
webstorage使ったほうがいいかも。

chosen

https://harvesthq.github.io/chosen/
サイトの性質上、試走先を選択するコンボボックスがどうしても縦長になってしまいどうにも使いにくかったので導入。
お手軽に綺麗なコンボボックスを導入できるため、使用すると幸せになれる。

/*
<link type="text/css" rel="stylesheet"
          href="https://cdnjs.cloudflare.com/ajax/libs/chosen/1.6.2/chosen.min.css" />
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/chosen/1.6.2/chosen.jquery.min.js"></script>
*/
$("select.chosen").chosen();
html5sortable

http://farhadi.ir/projects/html5sortable/
ユーザーが要素を並び替えることのできるフィールドを用意するために導入。
iOSでは無問題だが、Androidだと並び替えできないのにタップスクロールが不可になる欠点があるため留意が必要。

/*
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/html5sortable/0.4.2/html.sortable.min.js"></script>
*/
// 並び替え可能にする
$("div#abletosort").sortable();

// 並び替えられた結果を配列で取得
var sort_array = $('div#abletosort').sortable('toArray');

連携ツール

Visual Studio

以前はエディタとしてVSを使っていた。jsもhtmlも普通に書けるのはやはり統合開発環境と言うべきか。
後述のWebstormを導入したので最近はお役御免。

Webstorm

有料の優良ソフト(だれうま)。学生なら無料DL可能。
非常に重いがそれだけの機能はある。補完機能はVSでは味わえない有能さ。

git

バージョン管理といえばこれ。
webhook機能を使って、master/devブランチにpushがあった場合自動でサーバーのプログラムを走らせている。
サーバー上でgit initとremote設定を行っておき、push時に蹴られるphpでpullをさせれば自動更新システムの完成。
FTPとか全然使わなくなるのでおすすめ。

twitter

連携ツールと言っていいのかわからないけど一応。
前述の自動更新システムにcommit messageをツイートする機能を作ればあら簡単、自動告知シスt(ry

iPhone + jqueryUI Dialog

jQueryUI dialogはお手軽にdialog boxを表示させることができるが、妙な欠点もいくつかある。
その一つが、iphoneのブラウザでdialogを開いた時の挙動。

開いたダイアログの中にinputやselectタグが存在すると、自動的にフォーカスが合う。
そして、iOSの仕様でクソ使いにくいoption一覧をご親切にも勝手に出してくれるというわけである。

具体的には、こんな感じのコードが問題となる。 ex: https://jsfiddle.net/uoccz7La/

<!-- HTML部分 -->
<!-- iOSでこのダイアログを開くと意図した動作をしない -->
<div id="dialog" title="badtips">
    <p>select your favorite number.</p>
    <select id="selectitem"></select>
</div>
/* javascript部分 */
$(function(){
    $("#dialog").dialog({
        width: 450,
        open: function(){
            var add_html = "";
            for(var i=1; i <= 10; i++){
                add_html += "<option value=" + i + ">" + i + "</option>"
            }
            $("#selectitem").html(add_html);
        }
    })
})

これをiPhoneで開いてダイアログを開くと、以下の画像のような結果となる。
要はopen時に内容が足される前のselectを開いてしまっている。

これを回避するには、フォーカスがselectにあわないようにしてあげればよい。
すなわち、以下のような付け足しでOK。

<!-- HTML部分 -->
<div id="dialog" title="bettertips">
    <span class="ui-helper-hidden-accessible"><input type="image"/></span> <!-- dummy -->
    <p>select your favorite number.</p>
    <select id="selectitem"></select>
</div>