当我的 crontab 中出现错误时,我收到此消息:
cron: No MTA installed, discarding output
我不想在我的系统上安装 MTA,但我也不想错过这些错误消息。
cron 尝试通过邮件发送这些信息是在哪里配置的?我可以更改它,以便将这些消息发送到文件吗? (也许通过 sysylog)。
我不想记录所有 cron 消息,只想记录错误。
我的rsyslog.conf
:
cron.=info stop
*.* |/dev/xconsole
不幸的是,似乎即使错误消息也有.info
标签
我怎样才能只记录 cron 错误?或者,换句话说:我如何发送到日志文件,如果安装了 MTA,否则会发送到什么内容?
我的系统是 Debian 10,我用于rsyslog
日志记录(无 systemd)
更新:
正如 @basin 所建议的那样,对每一行单独使用重定向是我到目前为止使用的解决方案,它几乎没有问题:
首先,正如我所说,我想要一个解决方案,将通常默认发送到 MTA 的内容重定向到其他位置,即|/dev/xconsole
,无需单独指定每个谎言。
其次,如果我的 crontab 行中存在语法错误,则重定向将不起作用。 Cron 仍然尝试通过 MTA 发送错误,并且我No MTA installed
在日志中收到错误。
是否有某种方法可以重定向通过 MTA 发送的内容,以便将其发送(直接或通过 sysylog)到/dev/xconsole
?
附加问题:
当使用建议的解决方案时@Binarus
,编写我自己的自定义sendmail
脚本:
我可以不使用默认值/usr/sbin/sendmail
,而是为自定义脚本指定其他位置,例如/usr/local/sbin/sendmail
?中的cron
信息在哪里?这是硬编码的,还是可以在 cron 的配置文件之一中进行配置?sendmail
/usr/sbin/
答案1
我相信我有一个解决方案,但只测试了一半。不幸的是,我无法测试/dev/xconsole
,因为我的系统上没有该设备,而且我承认我什至不知道它是什么并且我没有时间研究它。
不过,有两个好处:第一,下面的方法是通用的;也就是说,您几乎肯定可以使用/dev/xconsole
而不是我使用的文件名。其次,我有只是在 Debian Buster 上对其进行了测试,因此它确实应该可以开箱即用。
我将首先展示(非常简单)的解决方案,然后解释它是如何工作的,然后展示一些可能的问题以及如何规避它们。
解决方案
作为预防措施,请先检查您是否拥有/usr/sbin/sendmail
。这应该不是是这样的,因为这个程序通常属于MTA,但你说你没有安装MTA。如果存在,请卸载它所在的包。
/usr/sbin/sendmail
现在,创建一个包含以下内容的脚本:
#!/bin/bash
cat >>/root/result
相应地设置权限:
chmod a+rx,u+w,og-w /usr/sbin/sendmail
重新开始cron
:
systemctl restart cron
就是这样。cron
通常通过电子邮件发送的所有错误消息现在都将进入/root/result
.
当然,您必须以 root 用户身份执行上述步骤。
在你的情况下,你可能想要替换/root/result
为/dev/xconsole
(但请记住,我还没有测试过这个,如上所述:-)),并且你最终应该替换>>
为>
(但由于我对 完全一无所知/dev/xconsole
,这可能是错误的) 。
它是如何工作的?
定义: 在下文中,我将使用 SENDMAIL 来表示 SENDMAIL 软件包,并使用 来sendmail
表示应用程序。
大多数(或至少是许多)发送电子邮件消息的程序本身并不实现 SMTP 协议栈,该协议栈需要通过套接字或网络连接直接与 MTA 通信,并且也不包含 SMTP 库;从安全角度来看,犯错误将是致命的,而且这是没有必要的,因为在大多数情况下,专门的应用程序会来救援。
这些专门的应用程序之一是/usr/bin/sendmail
,它具有 SMTP(以及更多功能),并且使用起来非常容易。一个常见的模式是:
cat MyMailMessage | /usr/bin/sendmail [sendmail-options]
# OR, even shorter
/usr/bin/sendmail <MyMailMessage
也就是说,应用程序构造一条邮件消息(这非常简单,因为它只需要几个标头加上实际的正文)并将其通过管道传输到sendmail
,而后者又执行 SMTP 魔法。
从历史上看,SENDMAIL 曾一度是主要的 MTA。 SENDMAIL不仅包含MTA,还包含MSP(消息提交程序)。 MSP部分是在/usr/sbin/sendmail
.很长一段时间以来,没有人能够摆脱 SENDMAIL,因此许多应用程序实际上仍然依赖于/usr/sbin/sendmail
或至少sendmail
可以用作邮件提交程序。这就是为什么即使是 SENDMAIL 的竞争对手(例如 POSTFIX)也仍然提供该程序作为兼容性包装器。
cron
在这方面,其行为与其他应用程序类似。它不支持 SMTP。相反,它依赖于sendmail
实际发送消息;它只是构造原始消息,包括标头和正文,并将它们通过管道传输到sendmail
,而后者实际发送它们。
所以我只需替换/usr/sbin/sendmail
为上面显示的脚本(因为我安装了 MTA,这只是为了测试)。当它尝试发送邮件时cron
调用/usr/sbin/sendmail
,这就是我们的脚本。该脚本仅获取其标准输入并将其重定向到文件。
作为旁注,我实际上不知道是否cron
将原始电子邮件消息直接通过管道传输到sendmail
,或者是否先将其放入临时文件中,然后从该文件进行重定向调用sendmail
,stdin
或者是否执行其他操作。
但这在这里并不重要:关键点是在每种情况下原始电子邮件消息都是由 , 构建的cron
,并将cron
其放入sendmail
's stdin
(但是这可以完成)。
缺点、问题、改进
请注意,cat
在脚本中使用可能不是效率方面的最佳选择,但这取决于您的预期输出。有无数的文章解释了如何最好地将脚本放入stdin
文件中;我认为这不在你的问题范围内。
我的解决方案的一个缺点是显而易见的:如果您需要“真实” /usr/sbin/sendmail
,我们就会遇到问题。我可以想象一些微妙的事情:可能有一些应用程序会根据它们是否找到/usr/sbin/sendmail
.
例如,如果应用程序发现该可执行文件,则可能决定通过电子邮件发送错误,否则将错误写入日志文件。该应用程序现在将改变其行为。接下来会发生什么很大程度上取决于具体情况。首先,您可能无法在通常的位置找到错误消息。其次,由于sendmail
现在是我们的简单脚本,并且不能按应用程序预期的方式工作,因此可能会发生奇怪的事情。
不过,有一个补救措施:您提到您会接受重新编译cron
。我相信(但现在无法验证)在其源代码中,config.h
有一个#define
定义了它使用的 MSP 名称。那么补救措施就很明确了:将我们的脚本重命名为abcd1234
,并将其用作 的值#define
,或者 - 可能更好 - 将脚本放入另一个目录中不是在系统的搜索路径中,并使用脚本的完整路径作为#define
.
当您这样做时,您最终还应该更正命令行选项,这些选项位于单独的#define
;中。不过,它们不会损害我们的脚本,因为它只是忽略它们。也许您甚至可以将脚本和 directcron
的错误输出直接转储到您想要的文件或设备。判断这是否可行需要对源代码进行进一步分析,但我尚未进行过分析。无论如何我都会坚持剧本;请参阅下一段,了解一个重要原因。
[ 2021 年 10 月 20 日更新:
正如我在 2021 年 10 月 20 日对您的原始问题的评论中所述,有一种解决该问题的替代方法可以使您免于重新编译:将“真实”移动sendmail
到其他地方并在其位置安装脚本。然后,每当执行脚本时,让它找出谁调用了它。如果cron
已调用它,则使其行为如上所示;如果没有,则使其sendmail
以相同的stdin
参数调用“real”,即“relay”stdin
和“real”的参数sendmail
。
]
另一个问题是,您不仅看到您感兴趣的错误消息,而且每次看到cron
都会发送整个原始电子邮件,包括标题。补救措施是在我们的脚本中添加一些代码,过滤掉您不感兴趣的行,例如使用grep
和sed
他们的朋友。但这也不属于这个问题的范围。
答案2
您可以告诉 cron 使用 stdout/stderr 重定向器作为您的SHELL
.
Cron(特别是 vixie-cron,如 Debian Buster/10 中)在新的$SHELL
.此行为在中指定定时任务(5),并且定义在do_command.c,
该行的整个命令部分(直到换行符或 % 字符)将由 /bin/sh 或 crontab 文件的 SHELL 变量中指定的 shell 执行。
320 | char *shell = env_get("SHELL", jobenv);
... |
348 | execle(shell, shell, "-c", e->cmd, (char *)0, jobenv);
将发送到 MTA 的是 stdout/stderr 中的任何内容$SHELL -c "<command>"
,无论语法错误或运行时错误如何。 (这也在“do_command.c”中定义。)因此,通过SHELL=/path/to/stdout/stderr/redirector
在 cron 文件中进行设置,所有四个问题都应该得到解决:
-
我怎样才能发送到日志文件,如果安装了MTA,什么会发送到它?
-
无需单独指定每个 li[n]e
-
如果我的 crontab 行中有语法错误,则重定向不起作用。
-
cron: No MTA installed, discarding output
我用输出测试了它>>/tmp/test
,我认为你可以用 替换它>/dev/xconsole
。或者您可以编写一个脚本来保留时间戳、详细命令等,例如系统日志的包装器。
$ cat /tmp/sh-out
#!/usr/bin/sh
1>>/tmp/test 2>>/tmp/test /usr/bin/sh "$@"
$ crontab -l
SHELL=/tmp/sh-out
* * * * * echo output
* * * * * wrong-command
$ tail -f /tmp/test
output
/usr/bin/sh: 1: wrong-command: not found
为了回答您的其他问题,
cron 从哪里获取 sendmail 所在的信息
/usr/sbin/
?
Debian 10 和 11 使用经过大量修补的 vixie-cron。被MAILCMD
定义为_PATH_SENDMAIL
在配置.h#L24作为/usr/bin/sendmail
。
是否有某种方法可以重定向通过 MTA 发送的内容,以便将其发送(直接或通过 sysylog)到
/dev/xconsole
?
不,vixie-cron 不支持这一点,尽管您可以通过更改命令或 MTA 来解决它。
克罗尼可以做到。
-s This option will direct Cron to send the job output to the
system log using syslog(3). This is useful if your system
does not have sendmail(8), installed or if mail is
disabled.
然而,cronie 仅在 Debian Experimental 中,并且计划从 vixie-cron 切换到 cronie此后就没有消息了2019-11-05。
不幸的是,似乎即使错误消息也有
.info
标签
它被硬编码在杂项.c#L589,在体内void log_it(username, xpid, event, detail)
syslog(LOG_INFO, "(%s) %s (%s)", username, event, detail);
似乎log_it
可以使用一个补丁来添加对日志级别的支持。但是,日志级别与 cron 中的命令输出无关,因为它们要么发送到 MTA,要么完全丢弃。
答案3
最简单的方法是在 crontab 中的所有任务中进行重定向:
* * * * * user-name command to be executed >/dev/null 2>>/tmp/some.log
或者,cronie
有这个选项:
-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.
您可以创建一个 shell 脚本,将cat
其内部输入写入文件。
我在 中没有找到类似的选项anacron
,但也许/usr/bin/sendmail
用自定义脚本替换会起作用。
答案4
如果您的操作系统sendmail
默认使用类似于邮件系统,您也许可以使用自定义mailer.conf
文件,该文件又调用自定义 shell 脚本,该脚本cron
根据您的需要处理作业输出。
此设置需要sendmail
类似的环境,但sendmail
应该禁用守护进程本身,即不是跑步。
首先创建一个简单的mailer.conf
文件,该文件将定义一个替代 shell 脚本作为sendmail
“发送”邮件的组件,并创建 shell 脚本本身:
printf 'sendmail\t/etc/mail/mail-script.sh\n' > /etc/mail/mailer.conf
cat << 'EOF' > /etc/mail/mail-script.sh
#!/usr/bin/env bash
{
date
printf -- '--- BEGIN\n'
cat
printf -- '--- END\n'
} >> /var/log/cron-jobs.log
EOF
chmod 755 /etc/mail/mail-script.sh
这就完成了设置的“电子邮件”部分。现在让我们创建一个可以测试的示例 cron 作业:
cat << 'EOF' > /root/testjob.sh
#!/usr/bin/env bash
printf 'This is a test cron job run at %s\n' "$(date)"
EOF
chmod 755 /root/testjob.sh
最后,我们将创建一个每分钟/etc/crontab
运行一次的条目:/root/testjob.sh
cat << EOF >> /etc/crontab
MAILTO="foobar"
* * * * * root /root/testjob.sh
EOF
该MAILTO
条目必须存在(否则不会生成电子邮件),但可以是任意的。如果您愿意,您可以MAILTO
为不同的目的定义某些名称,并cron
根据作业引用的MAILTO
标签以不同的方式处理作业输出cron
。
完成所有这些后,我们发现以下输出累积在/var/log/cron-jobs.log
:
# tail -f /var/log/cron-jobs.log
Tue Oct 12 14:11:00 PDT 2021
--- BEGIN
From: Cron Daemon <[email protected]>
To: foobar
Subject: Cron <root@test> /root/testjob.sh
X-Cron-Env: <SHELL=/bin/sh>
X-Cron-Env: <PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin>
X-Cron-Env: <MAILTO=foobar>
X-Cron-Env: <LOGNAME=root>
X-Cron-Env: <USER=root>
This is a test cron job run at Tue Oct 12 14:11:00 PDT 2021
--- END
Tue Oct 12 14:12:00 PDT 2021
--- BEGIN
From: Cron Daemon <[email protected]>
To: foobar
Subject: Cron <root@test> /root/testjob.sh
X-Cron-Env: <SHELL=/bin/sh>
X-Cron-Env: <PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin>
X-Cron-Env: <MAILTO=foobar>
X-Cron-Env: <LOGNAME=root>
X-Cron-Env: <USER=root>
This is a test cron job run at Tue Oct 12 14:12:00 PDT 2021
--- END
Tue Oct 12 14:13:00 PDT 2021
--- BEGIN
From: Cron Daemon <[email protected]>
To: foobar
Subject: Cron <root@test> /root/testjob.sh
X-Cron-Env: <SHELL=/bin/sh>
X-Cron-Env: <PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin>
X-Cron-Env: <MAILTO=foobar>
X-Cron-Env: <LOGNAME=root>
X-Cron-Env: <USER=root>
This is a test cron job run at Tue Oct 12 14:13:00 PDT 2021
--- END
Tue Oct 12 14:14:00 PDT 2021
--- BEGIN
From: Cron Daemon <[email protected]>
To: foobar
Subject: Cron <root@test> /root/testjob.sh
X-Cron-Env: <SHELL=/bin/sh>
X-Cron-Env: <PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin>
X-Cron-Env: <MAILTO=foobar>
X-Cron-Env: <LOGNAME=root>
X-Cron-Env: <USER=root>
This is a test cron job run at Tue Oct 12 14:14:00 PDT 2021
--- END
Tue Oct 12 14:15:00 PDT 2021
--- BEGIN
From: Cron Daemon <[email protected]>
To: foobar
Subject: Cron <root@test> /root/testjob.sh
X-Cron-Env: <SHELL=/bin/sh>
X-Cron-Env: <PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin>
X-Cron-Env: <MAILTO=foobar>
X-Cron-Env: <LOGNAME=root>
X-Cron-Env: <USER=root>
This is a test cron job run at Tue Oct 12 14:15:00 PDT 2021
--- END