cron 的 PATH 在哪里设置?

ter*_*don 41 cron path

Cron 不使用其 crontab 所在用户的路径,而是拥有自己的路径。可以通过PATH=/foo/bar在 crontab 的开头添加来轻松更改,经典的解决方法是始终使用 cron 运行的命令的绝对路径,但是 cron 的默认 PATH 在哪里定义的?

我在我的 Arch 系统(cronie 1.5.1-1)上创建了一个包含以下内容的 crontab,并在 Ubuntu 16.04.3 LTS 机器上进行了测试,结果相同:

$ crontab -l
* * * * * echo "$PATH" > /home/terdon/fff
Run Code Online (Sandbox Code Playgroud)

打印出来的:

$ cat fff
/usr/bin:/bin
Run Code Online (Sandbox Code Playgroud)

但为什么?默认的系统范围路径设置在 中/etc/profile,但包括其他目录:

$ grep PATH= /etc/profile
PATH="/usr/local/sbin:/usr/local/bin:/usr/bin"
Run Code Online (Sandbox Code Playgroud)

/etc/environmentor 中没有其他相关内容,/etc/profile.d我认为其他文件可能会被 cron 读取:

$ grep PATH= /etc/profile.d/* /etc/environment
/etc/profile.d/jre.sh:export PATH=${PATH}:/usr/lib/jvm/default/bin
/etc/profile.d/mozilla-common.sh:export MOZ_PLUGIN_PATH="/usr/lib/mozilla/plugins"
/etc/profile.d/perlbin.sh:[ -d /usr/bin/site_perl ] && PATH=$PATH:/usr/bin/site_perl
/etc/profile.d/perlbin.sh:[ -d /usr/lib/perl5/site_perl/bin ] && PATH=$PATH:/usr/lib/perl5/site_perl/bin
/etc/profile.d/perlbin.sh:[ -d /usr/bin/vendor_perl ] && PATH=$PATH:/usr/bin/vendor_perl
/etc/profile.d/perlbin.sh:[ -d /usr/lib/perl5/vendor_perl/bin ] && PATH=$PATH:/usr/lib/perl5/vendor_perl/bin
/etc/profile.d/perlbin.sh:[ -d /usr/bin/core_perl ] && PATH=$PATH:/usr/bin/core_perl
Run Code Online (Sandbox Code Playgroud)

/etc/skel不出所料,在 中的任何文件中也没有任何相关内容,也没有在任何文件中设置它/etc/cron*

$ grep PATH /etc/cron* /etc/cron*/*
grep: /etc/cron.d: Is a directory
grep: /etc/cron.daily: Is a directory
grep: /etc/cron.hourly: Is a directory
grep: /etc/cron.monthly: Is a directory
grep: /etc/cron.weekly: Is a directory
/etc/cron.d/0hourly:PATH=/sbin:/bin:/usr/sbin:/usr/bin
Run Code Online (Sandbox Code Playgroud)

那么,cron 为用户 crontabs 设置的默认 PATH 在哪里?它cron本身是硬编码的吗?它不会为此读取某种配置文件吗?

Ste*_*itt 55

它在源代码中进行硬编码(该链接指向当前的 Debian cron— 鉴于cron实现的多样性,很难选择一种,但其他实现可能类似):

#ifndef _PATH_DEFPATH
# define _PATH_DEFPATH "/usr/bin:/bin"
#endif
Run Code Online (Sandbox Code Playgroud)

cron不从配置文件中读取默认路径;我想其中的原因是它支持指定已PATH=在任何 cronjob 中使用的路径,因此无需在其他地方指定默认值。(如果作业条目中没有其他指定路径,则使用硬编码默认值。)


mur*_*uru 12

添加到 Stephen Kitt 的答案中,有一个配置文件PATH为 Ubuntu 上的 cron设置,并cron 忽略PATH以使用硬编码的默认值(或PATH在 crontabs 中设置的 s )。该文件是/etc/environment. Notecron的 PAM 配置:

$ cat /etc/pam.d/cron
...   
# Read environment variables from pam_env's default files, /etc/environment
# and /etc/security/pam_env.conf.
session       required   pam_env.so

# In addition, read system locale information
session       required   pam_env.so envfile=/etc/default/locale
...
Run Code Online (Sandbox Code Playgroud)

这很容易验证。将变量添加到/etc/environment,例如foo=barenv > /tmp/foo作为 cronjob运行并观察foo=bar输出中的显示。


但为什么?默认的系统范围路径在 /etc/profile 中设置,但包括其他目录:

$ grep PATH= /etc/profile
PATH="/usr/local/sbin:/usr/local/bin:/usr/bin"
Run Code Online (Sandbox Code Playgroud)

在 Arch Linux 中确实如此,但在 Ubuntu 中,基础PATH设置在/etc/environment. 文件附加到/etc/profile.d现有的PATH,您可以在~/.pam_environment. 我有一个关于 Arch 行为的错误

不幸的是,/etc/pam.d/cron不包括从~/.pam_environment. 奇怪的是,/etc/pam.d/atd 确实包含该文件:

$ cat /etc/pam.d/atd
#
# The PAM configuration file for the at daemon
#

@include common-auth
@include common-account
session    required   pam_loginuid.so
@include common-session-noninteractive
session    required   pam_limits.so
session    required   pam_env.so user_readenv=1
Run Code Online (Sandbox Code Playgroud)

...但是通过at显然继承创建at作业时可用的环境运行的命令(例如,env -i /usr/bin/at ...似乎在非常干净的环境中运行作业)。

修改/etc/pam.d/cronhaveuser_readenv=1似乎没有问题,并且变量 in~/.pam_environment开始显示正常(PATH当然,除了)。


总而言之,为 cron 设置环境变量似乎是一件麻烦事。最好的地方似乎是在作业规范本身,如果仅仅是因为您不知道 cron 可能决定忽略哪些继承的环境变量(不阅读源代码)。