1. Introduction
As one of the oldest still used forms of electronic message exchange, mails (emails, e-mails) emulate the real-world postal services, sometimes called snail mail. This is not much different from the internal Linux mailing system.
In this tutorial, we explore the Linux built-in mail exchange mechanism. First, we do a general overview of the internal Linux mail. Next, we continue with configuration. After that, we go through the mail directories, files, and formats. Then, we send some emails. Later, we understand how automatic mail checking works. Finally, we read the emails we sent.
We tested the code in this tutorial on Debian 12 (Bookworm) with GNU Bash 5.2.15. It should work in most POSIX-compliant environments unless otherwise specified.
2. Internal Linux Mail Overview
To begin with, the mailing mechanism is in place for many Linux distributions out of the box. This doesn’t mean every system is a global mail server. To have that, we require special packages and configuration, as well as the use of protocols like POP3 and IMAP.
Here, we only focus on mail processing within the system, not external network interaction.
2.1. Features
Similar to traditional electronic mail, Linux offers a message exchange system with many benefits:
- mail exchange
- authentication
- authorization
- separate inboxes
- forwarding
- notifications
Still, almost every Linux installation can internally route mails to users:
+-----------------------------+
| | Mail | Users |
| |------|-------|
| | +--->| user1 |
| | | |-------|
| System | +---<| user2 |
| | |-------|
| | | ... |
| | |-------|
| | | userN |
+-----------------------------+
In fact, this is roughly equivalent to sending mail from and to the same domain of a public email service.
2.2. Toolset
To facilitate the actual message exchange activities, Linux distributions can provide different commands and tools such as mutt and others.
Most often, we have the GNU mail command:
- check for mail
- compose mail
- send mail (including attachments)
- handle configuration
In general, this utility has evolved from the initial UNIX and BSD versions to the GNU implementation from the mailutils package that most installations use today.
In addition to mail, the ubiquitous mailutils provides several other tools:
- messages: get number of mails within a provided or system mailbox directory
- frm, from: get mail headers from a mailbox
- movemail: transfer mail to local file
- readmsg: extract (filtered) messages from a directory
- decodemail: decode multipart messages
Hence, we use the package for this article. Now, let’s go over some typical scenarios.
3. Linux Mail Configuration (Optional)
The main global configuration file for Linux mail as implemented by mailutils is typically /etc/mailutils.conf:
$ cat /etc/mailutils.conf
address {
email-domain gerganov.com;
};
Here, we only specify what automatic email address domain name we’d like.
In addition, we have the /etc/mail.rc RC file with commands.
On the other hand, we can also have a local user configuration in $HOME/.mail and user commands in $HOME/.mailrc.
In these files, we can control many mail aspects:
- access control via an acl
- general mail server configuration
- [auth]entication options and pam
- encryption via tls
- mailbox settings
- virtdomain virtual domain options
- logging verbosity
- program-specific settings
The configuration can employ variables and has a fairly simple syntax with a comprehensive set of options.
4. Linux Mail Directories and Format
By default, there are two main directories responsible for keeping and preserving emails:
- /var/spool/mail/
: classic location for the mail inbox (file) spool of user USERNAME - /var/mail/
: modern location for the mail inbox (file) spool of user USERNAME - $HOME/mbox: saved messages mailbox
Usually, /var/spool/mail is a symbolic link to /var/mail. We can check this via realpath:
$ realpath /var/spool/mail
/var/mail
In any case, all messages are in the MBOX format:
$ cat $HOME/mbox
From SENDER@domain Wed Nov 22 10:06:56 2023
Return-path: <SENDER@domain>
Envelope-to: RECIPIENT@domain
Delivery-date: Wed, 22 Nov 2023 10:06:56
Received: from USERNAME by DOMAIN with local (Exim 4.94.2)
(envelope-from <SENDER@domain>)
id 1r7uR1-000Jqc-EU; Wed, 22 Nov 2023 10:06:56
Subject: Mail Subject
To: <RECIPIENT@domain>
X-Mailer: mail (GNU Mailutils 3.10)
Message-Id: <E1r6661-000Jqc-DD@domain>
From: SENDER@domain
Date: Wed, 22 Nov 2023 10:06:56
Mail Body.
In short, this is a basic text format with mail fields such as From, Subject:, To:, and similar, as defined in RFC 2074 – Common Internet Message Headers.
5. Send Linux Mail
Of course, we can send emails via the mail command:
$ whoami
user2
$ mail -s 'Mail Subject' user1 <<EOI
Mail body.
EOI
First, we use whoami to verify the current user is user2. After that, *we invoke mail and set the [-s]ubject as Mail Subject and the intended recipient* as user1. For the main body, we use a here-string with the EOI terminator.
Alternatively, we can compose our message interactively:
$ mail user1
Cc: [Return]
Subject: Interactive Message[Return]
Write message body here and press Ctrl+D (EOF) to submit.[Return]
[Return]
This way, messages can be on multiple lines.[Return]
[Ctrl+D]
Notably, we start by entering any Cc: recipients and the mail Subject:. Then, we continue with the mail body until Ctrl+D (EOF) is pressed.
At this point, we’ve sent two emails from user2 to user1.
Let’s send a message to several recipients simultaneously:
$ mail -s 'Multimail' user1,user3 <<EOI
Body.
EOI
Now, user1 should have three messages in their mailbox.
6. Check Linux Mail
In most environments, the login and shell-spawn events trigger a mail check. This is usually done via configuration files.
However, modern shells often facilitate this via a feature called mailcheck, responsible for polling a preconfigured path of mail files at certain times.
6.1. Automatic Bash Mail Check
When it comes to Bash, mail checks are automated and controlled by several variables:
- MAIL (default /var/mail/$(whoami)): path to current user mail inbox file
- MAILPATH (default /var/mail/$(whoami)): path to current user mail inbox file
- MAILCHECK (default 60): interval (seconds) between mail checks
Mail checks are done just before each primary prompt refresh. To disable interval mail checking, we can assign $MAILCHECK a non-integer non-zero value or just unset it.
Now, let’s see where the mail variables and other triggers are potentially set.
6.2. PAM Modules
In addition to the check at regular intervals, the pam_mail Pluggable Authentication Module (PAM) ensures we see notifications at certain login events.
In particular, three files often hold lines that configure mail initialization:
- /etc/pam.d/login: standard (imply empty), meaning set $MAIL, show a You have… format message, and don’t show the spool
- /etc/pam.d/sshd: standard, but with noenv, i.e., without setting $MAIL
- /etc/pam.d/su: nopen sets $MAIL without any notifications
So, we can check the regular login setting:
$ cat /etc/pam.d/login
# Prints the status of the user's mailbox upon successful login
# (Replaces the `MAIL_CHECK_ENAB' option from login.defs).
#
# This also defines the MAIL environment variable
# However, userdel also needs MAIL_DIR and MAIL_FILE variables
# in /etc/login.defs to make sure that removing a user
# also removes the user's mail spool file.
# See comments in /etc/login.defs
session optional pam_mail.so standard
Notably, the $MAIL variable isn’t set for SSH sessions:
$ cat /etc/pam.d/sshd
# Print the status of the user's mailbox upon successful login.
session optional pam_mail.so standard noenv # [1]
Finally, using su doesn’t produce any notifications:
$ cat /etc/pam.d/su
# Defines the MAIL environment variable
# However, userdel also needs MAIL_DIR and MAIL_FILE variables
# in /etc/login.defs to make sure that removing a user
# also removes the user's mail spool file.
# See comments in /etc/login.defs
#
# "nopen" stands to avoid reporting new mail when su'ing to another user
session optional pam_mail.so nopen
To disable any of these behaviors, we can comment out the respective line.
6.3. Mail Check Behavior
First, we can check the login notification:
xost login: user1
Password:
[...]
You have mail.
$ echo $MAIL
/var/mail/user1
Next, we see the example notifications after su:
$ whoami
user2
$ mail -s 'Mail 1' user1 <<< 'Mail body.'
$ su user1
$ echo $MAIL
/var/mail/user1
$ [wait 60 seconds] [Return]
You have new mail in /var/mail/user1
$
Also, let’s try an SSH session:
$ ssh user1@xost
[...]
You have new mail.
Last login: Wed Nov 22 11:10:00 2023
$ echo $MAIL
$
Finally, we can read these emails.
7. Read Linux Mail
While the rudimentary messages, readmsg, from (frm), and similar utilities are convenient for automation, mail is usually the preferred choice when it comes to user mail interaction:
$ mail
"/var/mail/user1": 3 messages 3 new
>N 1 user2@xost Wed Nov 22 11:11 15/430 Mail Subject
N 2 user2@xost Wed Nov 22 11:11 17/530 Interactive Message
N 3 user2@xost Wed Nov 22 11:11 14/419 Multimail
?
Here, we see our user mail inbox at /var/mail/user1 and the three emails from earlier in several columns:
- selection (>)
- status ([N]ew, [P]reserved, [U]nread, [***] saved or written)
- number
- sender
- date and time (received)
- size (lines/characters, including headers)
- subject
Now, let’s read each message by supplying its number at the ? prompt:
? 1
Return-path: <user2@xost>
Envelope-to: user1@xost
Delivery-date: Wed, 22 Nov 2023 11:11:11 -0600
Received: from user2 by xost with local (Exim 4.94.2)
(envelope-from <user2@xost>)
id 1r80gN-00666y-Ke
for user1@xost; Wed, 22 Nov 2023 11:11:11 -0600
Subject: Mail Subject
To: <user1@xost>
X-Mailer: mail (GNU Mailutils 3.10)
Message-Id: <E1r80gN-00666y-Ke@xost>
From: user2@xost
Date: Wed, 22 Nov 2023 11:11:11 -0600
Mail body.
? 2
[...]
Subject: Interactive Message
[...]
Write message body here and press Ctrl+D (EOF) to submit.
This way, messages can be on multiple lines.
? 3
[...]
Subject: Multimail
[...]
Body.
?
At this point, we can q[uit], so our messages automatically get saved in our home folder mbox file:
? quit
Saved 3 messages in /home/user1/mbox
Held 0 messages in /var/mail/user1
Thus, we can use mail –file with /home/user1/mbox and browse saved messages.
Should we use e[xit] or x[it] instead, no messages are moved to the user mbox. However, since spools are meant to be temporary, it’s usually a good idea to move messages to a more permanent location.
Of course, mail has many other features and a useful help system.
8. Summary
In this article, we talked about the mail system of Linux.
In conclusion, even though there are alternatives to mail, mailutils is usually preinstalled on many Linux distributions.