Ubuntu

cronが送ってくれるメールの調整

日々、自動で色々な処理を行ってくれている便利なcron。
送ってくれるメールについてちょっと調整したいと思った。



広告


メールの調整

メールの内容

cronは実行したコマンドの出力を全てメールで送ってくれる、とされている。
ここでいう出力は、STDOUTとSTDERRへの出力と思われる。

メールの宛先

メールの宛先を何も指定していないとき、cronファイルの持ち主にメールが送られる。
/etc/crontabや/etc/cron.d/*にあるファイルでは、実行ユーザーを指定しており、そのユーザーにメールが送られる。

MAILTO変数には、メール送信先のユーザー名を登録できるとあるけれど、メールアドレスを登録することもできるようだった。
実際にメールアドレスを入れてみると、そこにメールが送られた。

/etc/cron.dにあるファイルも実行されるが、/etc/crontabに定義した環境変数は引き継がれない。
それぞれのファイルに必要な設定を入れておく。

Postfixを導入している環境でメールが送信されたとき、
 root@hostname.example.net
のように、ホスト名が付いていた。このメールを受け取れるサーバーがないので、以下で強制的にサーバー名を付けるようにした。

/etc/postfix/main.cf

myorigin = example.net

色々なやり方があるのだと思うけれども、この環境ではこれで root@example.net にメールが送られてくるようになった。

失敗したときだけ教えて

短いスパンでコマンドを実行するので、失敗したときだけ教えて欲しい。
Server Fault / Cron: Only get errors in emails?

この場合、色々なやり方があるけれど、moreutilsというパッケージを利用するのが簡単。
https://joeyh.name/code/moreutils/
https://git.joeyh.name/index.cgi/moreutils.git/

$ sudo apt install moreutils

このパッケージに含まれるchronicというコマンドを利用する。
Ubuntu manuals / chronic - runs a command quietly unless it fails

/etc/crontab

0 1 * * * root chronic backup

今回はまだ試していないのだけれど…
このコマンドに -v を付けると、STDOUTとSTDERRを区別し、RETVALも報告してくれるようになる。
また -e を付けると、STDERRの出力長さが0以外の時にトリガーされる。

尚、backup 1>/dev/null といった指定もできるようだけれども、それで事足りる用途なのであればOK。
エラーが発生したときには標準出力への出力も見たい、といった場合にchronicが良いのだろうと思われる。

何も教えてくれなくていい

実行結果を何か別の方法で確認する、あるいは、どうなっても気にならないというケース。

「指定されている全てのジョブの報告はいらない」という場合には、MAILTO=""で良い。
でも、なかなか全部いらないという話にはならないのかな、と思いやり方を検索してみた。
Super User / crontab still sending emails even with > /dev/null

以下を指定すれば良いようだ。

/etc/crontab

0 1 * * * root backup > /dev/null 2>&1

これで、STDOUTとSTDERRの両方が/dev/nullに出力されるので、メールは送られてこなくなる。

うまくいったときだけ教えて

多くは失敗したときだけ教えてくれれば事足りるのだろうけれども、逆はどうかというと、STDOUTとSTDERRに都合の良い文字列だけを出力すれば良いのだなと気付いた。

ちょうどいい題材がある、というか、まさにこのことを解決したくて色々と調べていた。

Z-Pushでカレンダー共有をすることについて改めて学習し直したところ、デバイスを追加する度にスクリプトを走らせて、アクセス権を設定し直さなければならないことが分かった。

でも、それを都度手で実行するのは面倒。特に、同居していない親族が新しいデバイスを使い始めたときに「もしもし?新しいデバイスを追加したんだけど」→「はいはい、じゃあスクリプトを実行するね」→(PCの電源を入れる)→(スクリプトを実行する)→「スクリプト実行したから確認してみて」→「了解」っていうやりとりはやっていられない。

cronで1時間おきにスクリプトを実行することにしたが、デバイスを買ったときに1時間待つのはちょっと長い、それでいてかなりの数のメールが送られてくる。

設定をちゃんと済ませれば、常に動作し続けることは保証される。
となると、知りたいのは「デバイスを追加したのでアクセス権の設定をしたよ」ということだけ、これだけを知らせて欲しい。

アクセス権を付けるスクリプトは、実行するとほとんど空振り。
この時に、STDOUTに以下のメッセージが出る。

Failed adding folder 'hogeuser' for user 'hogewife' ...

新しいデバイスが見つかったときにだけ、実際の登録処理が走って、STDOUTに以下のメッセージが出る。

Successfully added folder 'hogeuser' for user 'hogewife' on device 'androidNNNNNNNNNN'.

本来の解決策を考えると…

  • デバイスの一覧を取得して保管。
  • 前回実行時のデバイス一覧と比較し、増分があれば、利用ユーザー(アカウント)に計画したカレンダーへのアクセス権を設定。
  • 長期間アクセスしてきていないデバイスを削除。

というような処理を組んで、必要なメッセージだけをSTDOUTとかSTDERRに出力すること。

でも、デバイス数は大した数でもない。
そこで、定期的に只々アクセス権を設定して、実際にデバイスに設定をしたときだけその結果を伝える、というスクリプトに改造する。

/path/to/script/z-push-entry.sh

#!/bin/bash
cd $(dirname $0)

z-push-admin -a addshared -u hogewife   -n HOGEUSER -o hogeuser -t calendar -f XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX2b0000000000 -g 1 >  z-push-entry-result.txt
z-push-admin -a addshared -u hogechild1 -n HOGEUSER -o hogeuser -t calendar -f XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX2b0000000000 -g 1 >> z-push-entry-result.txt
z-push-admin -a addshared -u hogechild2 -n HOGEUSER -o hogeuser -t calendar -f XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX2b0000000000 -g 1 >> z-push-entry-result.txt
z-push-admin -a addshared -u hogemother -n HOGEUSER -o hogeuser -t calendar -f XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX2b0000000000 -g 1 >> z-push-entry-result.txt

grep Successfully z-push-entry-result.txt

※赤文字を追加。

z-push-adminは結果をすべてSTDOUTに出してくれているので、スクリプトがあるディレクトリにファイルを作り、そこに中身を出力しておく。
できあがったファイルをgrepし、Successfully が出力されている行をSTDOUTに出力すると、それがメールが送られてくる。

空振った場合にはgrepは何も出力しないので、メールは送られてこない。

これなら、cronの実行頻度をもう少し上げてもいいかもしれない。

cronについて

いままでmanページをちゃんと読んだことがなかったが、日本語に翻訳されている。
Ubuntu manuals / cron - 予定されたコマンドを実行するデーモン
Ubuntu manuals / crontab - cronを駆動するための一覧表
Ubuntu manuals / run-parts - ディレクトリにあるスクリプト・プログラムの実行
Ubuntu manuals / anacron - runs command periodically
Ubuntu manuals / anacrontab - monotonic jobs

仕組み

仕組みについて自分なりに整理したのがこちら。

  • cronは1分ごとに起動し、crontabが変更されていれば読み直し、crontabをすべて調べて実行すべきコマンドを実行する。
  • すべての出力をcrontabの所有者、または、MAILTO環境変数に名前が書かれたユーザーにメールで送信する。
    MAILTO=""の場合はメールを送信しない。
  • cronは以下のcrontabファイルを読み込む。
    • CRONファイル
      /var/spool/cron/crontabs
    • システムCRONファイル
      /etc/crontab, /etc/cron.d にあるファイル
  • システムCRONファイルは、crontabコマンドでは編集する必要はない。
    ユーザー名のフィールドを含む。
  • /etc/cron.d/* はパッケージに基づく名前が付けられるべき。
    /etc/crontabとは独立しており、環境変数の継承はしない。
  • 管理者は一般に/etc/crontabを使用すべき。

/etc/cron.dに幾つかファイルが置かれている。
今までインストールしたパッケージに含まれていたもので、/etc/crontabと同じような指定ができるようになっている。

CRONファイル

システムCRONファイル、CRONファイルについては以下が書かれていた。

  • コメント
    • 先頭が # の行はコメント行として無視される。
    • コマンド行、環境変数行にコメントを入れることはできない。
  • 環境変数
    • SHELLは/bin/sh、PATHは/usr/bin:/binに設定される。
    • name = value の形式で書く。=の両端の空白はあってもなくても良い。
    • valueをダブルクォーテーションで囲むと、先頭・末尾に空白を付けられる。
    • 環境変数の置き換えは行われない。
    • debianでは/etc/security/pam_env.confを読み込むが、上記を上書きしない。

デフォルトで定期実行されるもの

デフォルトでは、以下のタイミングでcronによりrun-partsが実行され、run-partsによりディレクトリに置いてあるスクリプトが実行される。

実行されるディレクトリ実行タイミング実行ユーザー備考
/etc/cron.hourly毎時17分root
/etc/cron.daily毎日6時26分rootanacronがインストールされていればそれに任せる
/etc/cron.weekly毎週日曜日6時47分rootanacronがインストールされていればそれに任せる
/etc/cron.monthly毎月1日6時52分rootanacronがインストールされていればそれに任せる

/usr/sbin/anacronが存在しない場合に、run-partsで各ディレクトリにあるコマンドが実行される。

anacron

anacronはサーバーにはインストールされていなかったが、デスクトップにはインストールされていた。
なので、デスクトップ環境では、anacronによって daily, weekly, monthly にあるスクリプトが実行される。

ということで、anacronはどのようなもので、どのような設定がされているのか調べてみた。

anacronはsystemdのユニットとして登録されていて、タイマーユニットにより多分7:30~23:30まで1時間ごとに動いているようだ。

[Timer]
OnCalendar=*-*-* 07..23:30

※DayOfWeek Year-Month-Day Hour:Minute:second

/etc/anacrontabには、

  • cron.dailyという名前、最大1日遅れても実行、ジョブの開始に5分待って、/etc/cron.dailyをrun-partsで実行。
  • cron.weeklyという名前、最大7日遅れても実行、ジョブの開始に10分待って、/etc/cron.weeklyをrun-partsで実行。
  • cron.monthlyという名前、@monthly遅れても実行、ジョブの開始に15分待って、/etc/cron.monthlyをrun-partsで実行。

というジョブが定義されていた。

この動きを見ると、定期的、あるいは、時々電源を入れるパソコンにはちょうど良い動きをするように見える。
デスクトップ環境にインストールされているのは、こういう理由からであろう。

さいごに

日頃、cronは「とりあえず動いてくれればいいや」と、見よう見まねで設定していながら、ちゃんと動いていて欲しかったり、都合良く報告が欲しかったりする。
実は、動かすところについては過去にちょっと調べていて、メールによる報告も使っていたのだけれど、どういう動作何かをちゃんと調べていなかった。

同じように、なんとなく使っているけれども、ちゃんと分かっていないものにリダイレクトがある。
探してみると、きっちりとまとめてくれている記事があった。
Qiita / Bashのリダイレクト(>>&)とパイプ等についてのまとめ

bashについてもそうなんだけれど、知っていればもっとスマートに書けるだろうし、何をしているのかを読むことができるようになるだろう。
ちゃんと調べることは大事だなと改めて思うのだった。

広告

コメントはこちらから お気軽にどうぞ ~ 投稿に関するご意見・感想・他