I believe I have a solution, but it is only tested halfway. Unfortunately, I couldn't test /dev/xconsole
, because I don't have that device on my systems, and I admit that I even don't know what it is and that I don't have the time to research it.
However, there are two good things: First, the following method is generic; that is, you nearly sure can use /dev/xconsole
instead of the file name I have used. Second, I have just tested it on Debian Buster, so it really should work out-of-the-box for you.
I'll first show the (surprisingly simple) solution, then explain how it works, and then show some possible problems and how to circumvent them.
Solution
As a precaution, first check whether you have /usr/sbin/sendmail
. This should not be the case, because this program usually belongs to an MTA, but you said you didn't have installed an MTA. If it exists, uninstall the package where it is from.
Now, create a script /usr/sbin/sendmail
with the following content:
#!/bin/bash
cat >>/root/result
Set the permissions accordingly:
chmod a+rx,u+w,og-w /usr/sbin/sendmail
Restart cron
:
systemctl restart cron
That was it. All error messages cron
would normally send via email are now going into /root/result
.
Of course, you must perform the steps above as root user.
In your case, you probably want to replace /root/result
by /dev/xconsole
(but please remember that I haven't tested this, as outlined above :-)), and you eventually should replace >>
by >
(but since I know exactly nothing about /dev/xconsole
, this might be wrong).
How does it work?
Definition: In the following text, I'll use SENDMAIL to denote the SENDMAIL software package, and I'll use sendmail
to denote an application.
Most (or at least, many) programs which send email messages do not themselves implement an SMTP protocol stack which would be needed to talk to an MTA directly via a socket or a network connection, and also don't incorporate SMTP libraries; making mistakes there would be fatal security-wise, and it's just not necessary because in most cases specialized applications come to rescue.
One of those specialized applications is /usr/bin/sendmail
, which is capable of SMTP (and much more), and can be used very easily. A common pattern is:
cat MyMailMessage | /usr/bin/sendmail [sendmail-options]
# OR, even shorter
/usr/bin/sendmail <MyMailMessage
That is, the application constructs a mail message (which is very easy since it only needs a few headers plus the actual body) and pipes it to sendmail
, which in turn does the SMTP wizardry.
Historically, SENDMAIL was the predominant MTA for a while. SENDMAIL contains not only an MTA, but also an MSP (message submission program). The MSP part is implemented in /usr/sbin/sendmail
. For a long time, nobody got away without SENDMAIL, and therefore many applications still literally rely on /usr/sbin/sendmail
or at least sendmail
to be usable as mail submission program. This is the reason that even SENDMAIL's competitors like POSTFIX still also provide that program as a compatibility wrapper.
cron
behaves like other applications in this respect. It is not capable of SMTP. Instead, it relies on sendmail
to actually send messages; it just constructs the raw messages, including headers and body, and pipes them to sendmail
, which in turn actually sends them.
So I just had to replace /usr/sbin/sendmail
by the script shown above (since I have an MTA installed, this was just for testing). cron
calls /usr/sbin/sendmail
when it tries to send mail, and this now is our script. The script just takes its standard input and redirects it to the file.
As a side note, I actually don't know whether cron
pipes the raw email message directly to sendmail
, or whether it puts it into a temporary file first and then calls sendmail
with stdin
redirected from that file, or whether it does something else.
But that's not important here: The key point is that the raw email message in every case is constructed by cron
, and that cron
puts it into sendmail
's stdin
(however this may be accomplished).
Disadvantages, Problems, Improvements
Please note that using cat
in the script might not be the best choice efficiency-wise, but this depends on your expected output. There are myriads of articles out there which explain how you best can put a script's stdin
into a file; I don't think that this in your question's scope.
One disadvantage of my solution is obvious: Should you ever need the "real" /usr/sbin/sendmail
, we have a problem. I could imagine something subtle: There might be applications which change their behavior (especially, error and status reporting) depending on whether they find /usr/sbin/sendmail
.
For example, an application may decide to email errors if it finds that executable, but write errors to log files otherwise. This application will now change its behavior. What then happens heavily depends on circumstances. First, you might not find error messages at the usual place. Second, since sendmail
now is our simple script and does not work as the application expects, weird things might happen.
There is a remedy, though: You mentioned that you would accept recompiling cron
. I believe (but can't verify right now) that in its source code, in config.h
, there is a #define
which defines the MSP's name it uses. Then the remedy is clear: Rename our script to abcd1234
, and use that as the value of the #define
, or -probably better- put the script into another directory which is not in the system's search path, and use the complete path to the script as value of the #define
.
And while you're at it, you eventually should correct the command line options as well, which are in a separate #define
; they don't hurt our script because it just ignores them, though. Possibly you even could dump the script and direct cron
's error output directly to the file or device you want. Telling whether that would be possible would require further analysis of the source code, which I haven't conducted. I would stick with the script anyway; see the next paragraph for an important reason.
[ Update per 2021-10-20:
As outlined in my comment to your original question from 2021-10-20, there is an alternative way around the problem which saves you from recompiling: Move the "real" sendmail
elsewhere and install the scripts in its place. Then, whenever the script is executed, make it figure out who called it. If cron
has called it, make it behave as shown above; if not, make it call the "real" sendmail
with the same stdin
and parameters, i.e. "relay" stdin
and the parameters to the "real" sendmail
.
]
Another problem is that you don't see only the error messages you're interested in, but each time see the whole raw email message cron
would send, including the headers. The remedy would be to add some code to our script which filters out the lines you are not interested in, e.g. using grep
, sed
and their friends. But this also is outside the scope of this question.
strings /usr/sbin/cron | grep sendmail
. That's usually a symbolic link to the actual mta.logger
or whatever. There probably already are ready-made debian packages which do just that.config.h
(cron sources) you have#define MAILARG _PATH_SENDMAIL
. Inpathnames.h
(cron sources), you have a#define
which sets_PATH_SENDMAIL
, but this is not relevant because_PATH_SENDMAIL
already is defined here by the inclusion of<paths.h>
(glibc sources), and there you finally have#define _PATH_SENDMAIL "/usr/sbin/sendmail"
. To change the script's location, you could write#define MAILARG "/your/own/path/to/script"
inconfig.h
and recompile cron./usr/sbin/sendmail
a symbolic link pointing to the custom script? Then you'll have the same problem as my solution: What if the "real" program expected there is needed? I fear that there is no other way than recompiling to solve this problem .../usr/sbin/sendmail
, and make it figure out who has called it when it is executed. If cron has called it, make it behave as described in my solution; if not, let it just call the "real" sendmail binary (wherever you have moved it to) with the same arguments and stdin (that is, just let the script "relay" the arguments and stdin to sendmail).