ごらくらいふ

プログラミングしたりゲームしたり

cron実行時のPATHがどこで決められるのか調査した

経緯

  • CentOS鯖が2本ある
  • Let's encryptの自動更新をしたくてシェルスクリプトを書き、cronに食わせた
  • 2本の内1本から「コマンドが見つかりません」とエラーメールが飛んできた

環境

原因

両方とも失敗していたが片方しかrootメールアドレスを設定していなかった。

結論

PATHMAILTOcrontab -eで一緒に書いちゃった方がいい。

デフォルト値はCronのコード内で定義されていた。

PATHはどんな値?

cron実行中の環境変数

crontabの環境変数PATHについて調べてみた · DQNEO起業日記

# crontab -e
*/1 * * * * printenv > /tmp/printenv.txt
# cat /tmp/printenv.txt
SHELL=/bin/sh
USER=root
PATH=/usr/bin:/bin
PWD=/root
LANG=ja_JP.UTF-8
SHLVL=1
HOME=/root
LOGNAME=root

出た

ユーザの環境変数

Q.ユーザー環境変数は引き継がれるの? A.されない

Crontabの環境変数はどうなっているのか、調べる - それマグで!

まぁカスタムしたPATHとか.bash_profileに書いてあるし、cronが使えるとは思ってない。

ただ、/root/binも放り出されているなんて…

システム的な初期値は何?

# grep PATH /etc/init.d/functions
PATH="/sbin:/usr/sbin:/bin:/usr/bin"
export PATH

「CentOS でデフォルトで設定される環境変数について」(1) Linux Square − @IT

まったく一致しない……。

もはや答えはソースを見るしかない

# whereis -b crontab | cut -d' ' -f2 | xargs rpm -qf
cronie-1.4.4-15.el6_7.1.x86_64

2018/01/18 追記: fedorahosted.orgが引退した結果使用できなくなったリンク( https://git.fedorahosted.org/git/cronie.git )を差し替え

# git clone https://github.com/cronie-crond/cronie.git
# cd cronie
# git tag | grep 1.4.4 | xargs git checkout
# find . -type d | grep -v '.git'
.
./anacron
./contrib
./man
./pam
./src

cron の意外な落とし穴! - もろず blog

問題のPATHを探す

# grep '/usr/bin:/bin' src/*
src/pathnames.h:# define _PATH_DEFPATH "/usr/bin:/bin"

幸運なことにあからさまな要素がHit。 この値が利用される箇所を確認する。

# grep -n '_PATH_DEFPATH' src/*
src/cron.c:177: if (putenv("PATH=" _PATH_DEFPATH) < 0) {
src/entry.c:299:                if (glue_strings(envstr, sizeof envstr, "PATH", _PATH_DEFPATH, '=')) {
src/pathnames.h:61:#ifndef _PATH_DEFPATH
src/pathnames.h:62:# define _PATH_DEFPATH "/usr/bin:/bin"
cron.c 134行目から
int main(int argc, char *argv[]) {

    /* 省略 */

    if (putenv("PATH=" _PATH_DEFPATH) < 0) {
        log_it("CRON", pid, "DEATH", "can't putenv PATH", errno);
        exit(1);
    }

    /* 省略 */

}

大当たりだ。

C言語なんてめっきり触っておらず("PATH=" _PATH_DEFPATH)なる記法にウッときた。 (業務でC言語に振れたのはせいぜいがObjective-Csqlite直接使ったときくらい)

参考リンク

起動デーモン(rc.d)

cron

C言語

rootメールアドレス設定