Debian

cron:在未安裝 MTA 時將錯誤消息發送到文件

  • October 24, 2021

當我的 crontab 中出現錯誤時,我會收到此消息:

cron: No MTA installed, discarding output

我不想在我的系統上安裝 MTA,但我也不想錯過這些錯誤消息。

它在哪裡配置,cron 嘗試通過郵件發送這些?我可以更改它,以便將這些消息發送到文件嗎?(也許通過系統日誌)。

我不想記錄所有 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/

我相信我有一個解決方案,但它只測試了一半。不幸的是,我無法測試/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來表示應用程序。

大多數(或至少很多)發送電子郵件的程序本身並不實現通過套接字或網路連接直接與 MTA 通信所需的 SMTP 協議棧,並且也不包含 SMTP 庫;在安全方面犯錯誤將是致命的,而且這是沒有必要的,因為在大多數情況下,專門的應用程序會來救援。

其中一個專門的應用程序是/usr/bin/sendmailSMTP(以及更多),並且可以非常容易地使用。一個常見的模式是:

cat MyMailMessage | /usr/bin/sendmail [sendmail-options]
# OR, even shorter
/usr/bin/sendmail <MyMailMessage

也就是說,應用程序構造一個郵件消息(這很容易,因為它只需要幾個標頭加上實際的正文)並將其通過管道傳輸到sendmailSMTP 嚮導。

從歷史上看,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,或者是否先將其放入臨時文件中,然後從該文件重定向呼叫sendmailstdin或者是否執行其他操作。

但這在這裡並不重要:關鍵是在每種情況下,原始電子郵件消息都是由 構造的cron,並且cron將其放入sendmail’sstdin中(但是這可以完成)。

缺點、問題、改進

請注意,cat在腳本中使用可能不是效率方面的最佳選擇,但這取決於您的預期輸出。有無數文章解釋瞭如何最好地將腳本stdin放入文件中;我認為這不在您的問題範圍內。

我的解決方案的一個缺點很明顯:如果您需要“真實” /usr/sbin/sendmail,我們就有問題了。我可以想像一些微妙的事情:可能有一些應用程序會根據它們是否找到/usr/sbin/sendmail.

例如,如果應用程序找到執行檔,它可能會決定通過電子郵件發送錯誤,否則將錯誤寫入日誌文件。此應用程序現在將更改其行為。然後發生的事情在很大程度上取決於情況。首先,您可能在通常的地方找不到錯誤消息。其次,由於sendmailnow 是我們的簡單腳本,並且不能按應用程序預期的那樣工作,因此可能會發生奇怪的事情。

不過,有一個補救措施:您提到您將接受重新編譯cron。我相信(但現在無法驗證)在它的原始碼中config.h,有一個#define定義了它使用的 MSP 名稱。那麼補救措施就很清楚了:將我們的腳本重命名為abcd1234,並將其用作 的值#define,或者-可能更好-將腳本放入不在系統搜尋路徑中的另一個目錄中,並使用腳本的完整路徑作為值的#define

當您使用它時,您最終還應該更正命令行選項,它們位於單獨的#define; 它們不會傷害我們的腳本,因為它只是忽略了它們。可能您甚至可以轉儲腳本並將cron’ 的錯誤輸出直接發送到您想要的文件或設備。判斷這是否可能需要對原始碼進行進一步分析,而我沒有進行過。無論如何,我都會堅持使用腳本;看下一段有一個重要原因。

[ 2021-10-20 更新:

正如我在 2021 年 10 月 20 日對您的原始問題的評論中所概述的那樣,有一種解決問題的替代方法可以使您免於重新編譯:將“真實”移到sendmail其他地方並將腳本安裝在其位置。然後,每當執行腳本時,讓它弄清楚是誰呼叫了它。如果cron已呼叫它,則使其行為如上所示;如果沒有,讓它呼叫sendmail具有相同stdin和參數的“真實”,即“中繼”stdin和“真實”的參數sendmail

]

另一個問題是您不會只看到您感興趣的錯誤消息,而是每次都會看到cron將發送的整個原始電子郵件消息,包括標題。補救措施是在我們的腳本中添加一些程式碼,過濾掉您不感興趣的行,例如 usinggrepsed他們的朋友。但這也超出了這個問題的範圍。

您可以告訴 cron 使用 stdout/stderr 重定向器作為您的SHELL.

Cron(特別是 vixie-cron,如在 Debian Buster/10 中)在新的$SHELL. 此行為在crontab(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);

$SHELL -c "<command>"無論語法錯誤或執行時錯誤如何,發送到 MTA 的都是 stdout/stderr 中的任何內容。(這也在“do_command.c”中定義。)因此,通過SHELL=/path/to/stdout/stderr/redirector在您的 cron 文件中進行設置,您的所有四個問題都應該得到解決:

  1. >

我怎樣才能發送到日誌文件,如果安裝了它會發送到 MTA?

  1. >

無需為每個 li 指定

$$ n $$e 單獨

  1. >

如果我的 crontab 行中存在語法錯誤,則重定向不起作用。

  1. >

cron: No MTA installed, discarding output

我用輸出對它進行了測試>>/tmp/test,我認為你可以用>/dev/xconsole. 或者您可以編寫一個腳本來保存時間戳、詳細命令等,例如 syslog 的包裝器。

$ 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_SENDMAILconfig.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標籤

它在misc.c#L589中硬編碼,在void log_it(username, xpid, event, detail)

   syslog(LOG_INFO, "(%s) %s (%s)", username, event, detail);

似乎log_it可以使用一個更新檔來添加對日誌級別的支持。但是,日誌級別與 cron 中的命令輸出無關,因為它們要麼被發送到 MTA,要麼被完全丟棄。

引用自:https://unix.stackexchange.com/questions/672603