cronでメール以外の方法で結果を通知する
cronって便利なんだけど、結果を確認するのがめんどくさい。
メールを全部見るのも今っぽくない。
でもRundeckとか他のジョブスケジューラを使い始めると、実行時に依存するものが増えるのでそれはそれで微妙。
じゃあ、メールで送っている内容と同じようなものを別のプログラムが取得できるか、ということ。
ソースコード確認
最初に考えたのがコマンドからパイプで繋いで最後に別のプログラムが情報をとる、というもの。
標準出力、標準エラー出力は取得可能。 実行したコマンドは、、、?
実行したコマンドはパイプで繋いでる数が少なくて時間がかかればプロセスリストから見つけられるけど、なんか違う。
じゃあCronがメールの件名につけている実行したコマンドはどうやって?ということでソース(とりあえずcronie
)をおってみた。
cronie.git - Unnamed repository; edit this file to name it for gitweb.
コマンドを実行してその結果のメールを組み立てる部分。
cronie.git - Unnamed repository; edit this file to name it for gitweb.
entry->cmd
という形で取得しているので、内部的にcronのファイルを構造化して、それを実行中に保持しているに過ぎないということ。
ちなみにentry
はstruct
cronie.git - Unnamed repository; edit this file to name it for gitweb.
外部のコマンドがうまーくやるにはちょっと厳し目か。
crond
次に考えたのがcrond
コマンドの実行時オプション。何か良さげなものがないか?
% man crond
途中いっぱい省略するけど、-m
オプションで実行可能なコマンドを渡すと、本来メール送信用に組み立てたテキストを、標準入力に渡すよ、という感じ。
OPTIONS -m This option allows you to specify a shell command to use for sending Cron mail output instead of using sendmail(8) This command must accept a fully formatted mail message (with headers) on standard input and send it as a mail message to the recipients specified in the mail headers. Specifying the string off (i.e., crond -m off) will disable the sending of mail.
実験
検証環境
% cat /etc/redhat-release CentOS Linux release 7.2.1511 (Core) % ruby -v ruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-linux]
結果を受け取るプログラム
rubyで受け取ってみる。
/tmp/mail.rb
#!/home/admin/.rbenv/shims/ruby text = STDIN.read File.open "/tmp/stdin.mail.#{Time.now.to_i}", "w" do |writer| writer.puts text end
とりあえずでこやつには実行権限も付与。
% chmod 775 /tmp/mail.rb
crondの起動時オプション
/etc/sysconfig/crond
で起動時のオプションを指定できる。
# Settings for the CRON daemon. # CRONDARGS= : any extra command-line startup arguments for crond CRONDARGS="-m /tmp/mail.rb"
cronのジョブ
とりあえず適当に、標準出力がちょっと長くなりそうなやつを。
/etc/cron.d/my.admin
MAILTO=sugilog@test.example * * * * * root /usr/bin/sar -P ALL
実行
まずはcrondを再起動
% systemctl restart crond % ps aux | grep crond | grep -v grep root 5948 0.0 0.0 126332 1712 ? Ss 10:52 0:00 /usr/sbin/crond -n -m /tmp/mail.rb
cronの実行結果を確認してみる。
From: "(Cron Daemon)" <root> To: sugilog@test.example Subject: Cron <root@sugilog> /usr/bin/sar -P ALL Content-Type: text/plain; charset=UTF-8 Auto-Submitted: auto-generated Precedence: bulk X-Cron-Env: <XDG_SESSION_ID=70> X-Cron-Env: <XDG_RUNTIME_DIR=/run/user/0> X-Cron-Env: <LANG=ja_JP.UTF-8> X-Cron-Env: <MAILTO=sugilog@test.example> X-Cron-Env: <SHELL=/bin/sh> X-Cron-Env: <HOME=/root> X-Cron-Env: <PATH=/usr/bin:/bin> X-Cron-Env: <LOGNAME=root> X-Cron-Env: <USER=root> Linux 3.10.0-327.22.2.el7.x86_64 (sugilog) 2016年12月20日 _x86_64_ (4 CPU) 07時37分20秒 LINUX RESTART 07時40分01秒 CPU %user %nice %system %iowait %steal %idle (以下省略)
その他
ちなみに、-m
で渡したスクリプトがイケてなくと何かしら失敗していると、ログにそれっぽい出力がある。
Dec 20 10:47:01 sugilog CROND[5604]: (root) CMD (time /usr/bin/sar -P ALL) Dec 20 10:47:01 sugilog CROND[5603]: (root) MAIL (mailed 8204 bytes of output but got status 0x0001#012)
あとは
cronから標準入力で渡されるテキストはメール形式なので、メールとしてパースすれば、構造化されたデータとして扱える。
例えばruby。
require "rubygems" require "mail" text = STDIN.read message = Mail.new(text) message.header_fields.each do |field| puts "#{field.name} = #{field.value}" end puts message.body
From = "(Cron Daemon)" <root> To = sugilog@test.example Subject = Cron <root@sugilog> /usr/bin/sar -P ALL Content-Type = text/plain; charset=UTF-8 Auto-Submitted = auto-generated Precedence = bulk X-Cron-Env = <XDG_SESSION_ID=70> X-Cron-Env = <XDG_RUNTIME_DIR=/run/user/0> X-Cron-Env = <LANG=ja_JP.UTF-8> X-Cron-Env = <MAILTO=sugilog@test.example> X-Cron-Env = <SHELL=/bin/sh> X-Cron-Env = <HOME=/root> X-Cron-Env = <PATH=/usr/bin:/bin> X-Cron-Env = <LOGNAME=root> X-Cron-Env = <USER=root> Linux 3.10.0-327.22.2.el7.x86_64 (sugilog) 2016年12月20日 _x86_64_ (4 CPU) 07時37分20秒 LINUX RESTART 07時40分01秒 CPU %user %nice %system %iowait %steal %idle (以下省略)