Thursday, 30 June 2016

Scripting Mail with Shell and Perl

This is a tutorial aimed at systems and middleware administrators who are new to mailing techniques but know how to use VI, VIM or any other editor. This article will also interest experts and others interested in an easier way of mailing by using a basic system and without having to install extra packages or modules.
Shell and Perl have both been used for varying purposes in the field of systems administration. However, one cannot completely replace the other. So let’s look at how to send emails using Shell and Perl on UNIX/GNU Linux platforms. This is very useful while automating alerting or monitoring systems. If you know emailing in Shell, you will find it very easy to translate it in Perl. Both have almost the same lines of code in this article. What we achieve here using Shell and Perl should be equally possible with Ruby, Python or any scripting language that can make use of the existing mailing facilities in UNIX/GNU Linux.
For Perl, we will not use any mailing modules, though that is the most preferred method. This is just to make users aware that Perl can work well even without modules.

mailer
Figure 1: Mail without attachment
Prerequisites
Prior to exploring Shell and Perl, let’s just go through the following checklist:
i) For UNIX or GNU Linux systems, you need Fedora or Ubuntu. I am using Fedora for our examples. So my install pattern will prefer yum to dpkg/apt.
ii) You need the Internet to reach the software repository to install packages. Though I said we wouldn’t be installing packages, this is just for our test environment. We cannot practice on a production environment, so it is necessary that we have a small server environment installed to see the mailing in action.
iii) Regarding the mail server, most companies that have Red Hat or Fedora Linux should have Sendmail configured, by default. Some might even have Postfix but the sendmail command is very common. The local sendmail command will take care of passing the mail onto the local MTA or mail transfer agent (either sendmail or postfix).
a. To keep things really simple, I am using:
  • Sendmail as an MTA
  • Local UNIX services as an MDA (mail delivery agent)
  • The local mbox as the mail delivery location
  • The mail command as mail client
  • System users for basic authentication and sending/receiving mail
b. You need to install Sendmail, as follows:
yum install sendmail-cf
service sendmail start
c. VIM or VI Improved is a package that might make your work easier because of the benefit of syntax highlighting. It is very easy to get this package on GNU Linux. On UNIX platforms, we might have to use just the VI editor. (Please check the reference at the end of this article for VIM installations.)
iv) The following default mail clients can be used:
mailx/mail
mutt
OR sendmail -t
The above three are the common, basic command line parameters used in UNIX or GNU Linux to send mails. We will be using the last one, sendmail, by piping commands to it. This is just to keep things simple and standardised, as we are looking at both UNIX and GNU Linux environments as well as very basic arguments, since all parameters might not be available in all the environments.
The modules normally used in Perl for SMTP are:
Net::SMTP
Mail::Sendmail
MIME::Lite (Most widely and commonly used)

mailer-attach
Figure 2: Mail with attachment
The procedure
We will avoid importing any Perl modules for actual mailing. Going through these methods, we should be able to learn how a mail is sent out. These are the four types of mails:
1. Clear text email
2. Clear text email with attachment
3. HTML email
4. HTML email with attachment (This is a combination of 2 and 3. Since it is a repetition, we will not actually do this as an example.)
In the examples that follow, do notice the portions that create new lines, in both Shell and Perl. If we miss the right number of new lines, the mail might not produce the desired results.
In Shell, by default, echo adds a new line. But in some cases, we need to give one more new line apart from the one Shell already outputs. Perl, by default, does not add a new line; so we have to specifically add the required number of new lines.
In the first case of a clear text email without attachment we might not notice much of a difference. The difference becomes apparent when we have to make more than one MIME addition. Adding files as attachments is one example of going in for MIME additions.
Clear text email without an attachment
On Shell, use the following code:
#!/bin/bash
subject="Shell Mail without attachment";
from="osfyuser"; #can have complete mailing email id
to="osfyedit"; # These are local mail users
bodyFile="mailbody.txt";
cat > $bodyFile <<EOF
This is a text for all mailers
Welcome to the world of mailing... Buhahaha!!
EOF
{
echo "From: $from"
echo "To: $to"
echo "Subject: $subject"
echo -e "Content-Type: text/plain; charset=us-ascii\n"
cat $bodyFile
echo
} | /usr/sbin/sendmail –t
i) As root, create two users, as shown below:
a. osfyuser
useradd osfyuser
b. osfyedit
useradd osfyedit
ii) Log in as osfyuser. Use the editor of your choice to save the above code as a file. Let us call it mailer.sh and make it executable (we’re using VIM), as follows:
a. chmod u+x mailer.sh
iii) Run the executable file, as follows, so that it sends a text mail to the user osfyedit:
a. ./mailer.sh
iv) A file gets created so as to represent the body
of the mail:
[osfyuser@osfymail ~]$ ls -l mailbody.txt
-rw-rw-r-- 1 osfyuser osfyuser 76 Aug 31 19:05 mailbody.txt
Open another terminal, log in as osfyedit user and type ‘mail’ to see if there are any mails.
On Perl, after saving the following code as a file in osfyuser, with the filename mailer.pl, repeat the procedures from iii) to v) given above, and compare the results. They should be exactly the same.
#!/usr/local/bin/perl
my ($subject, $from, $to, $fh, $content);
$subject = “Perl Mail without attachment”;
$from = “osfyuser”;
$to = “osfyedit”;
$fh=”mailbody.txt”;
open(FILE, “>”, “$fh”) or die “Cannot open $fh: $!”;
print FILE “This is a text for all mailers\nWelcome to the world of mailing... Buhahaha!!”;
close(FILE);
open(MAIL, “|/usr/sbin/sendmail -t”);
print MAIL “FROM: $from\n”;
print MAIL “TO: $to\n”;
print MAIL “Subject: $subject\n”;
print MAIL “Content-Type: text/plain; charset=us-ascii\n\n”;
open(FILE, “<”, “$fh”) or die “Cannot open $fh: $!”;
print MAIL <FILE>;
close(FILE);
print MAIL “\n\n”;
close(MAIL);

mailer_browser-view1
Figure 3: Mail listing with browser mail client and IMAP mail service
Clear text email with an attachment
Normally, we use uuencode, the universal method of converting a file into understandable UNIX code that is decoded at the server end.
For Shell, use the following code:
#!/bin/bash
subject="Shell Attachment";
from="osfyuser";
to="osfyedit";
attachment="/home/osfyuser/test.txt"
bodyFile="mailbody.txt";
cat > $bodyFile <<EOF
This is a text for all mailers
Welcome to the world of mailing... Buhahaha!!
EOF
{
echo "From: $from"
echo "To: $to"
echo "Subject: $subject"
echo "Content-Type: multipart/mixed; boundary=\"frontier\"";
echo "--frontier"
echo -e "Content-Type: text/plain; charset=us-ascii\n"
cat $bodyFile
echo
echo "--frontier"
echo -e "Content-Disposition: attachment; filename=`basename $attachment`"
echo -e "Content-Type: text/plain; name=$attachment\n";
cat $attachment
echo
} | /usr/sbin/sendmail –t
This time, we will have an extra line while checking for mail. The attachment, which is in plain text, will turn out as readable text in the destination mail box.
This is a squirrelmail client tool which is a browser based view of the mails sent from osfyuser on the command line to user osfyedit. I used Dovecot IMAP/POP to make the browser connect to a mailbox (see Figure 4).
For Perl, use the following code:
#!/bin/perl
my ($subject, $mach, $from, $to, $attachment, $fh, $content);
$subject = “Test Mail”;
$from = “osfyuser”;
$to = “osfyedit”;
$attachment=”/home/osfyuser/test.txt”;
$fh=”mailbody.txt”;
open(FILE, “>”, “$fh”) or die “Cannot open $fh: $!”;
print FILE “This is a text for all mailers\nWelcome to the world of mailing... Buhahaha!!”;
close(FILE);
open(MAIL, “|/usr/sbin/sendmail -t”);
print MAIL “FROM: $from\n”;
print MAIL “TO: $to\n”;
print MAIL “Subject: $subject\n”;
print MAIL “Content-Type: multipart/mixed; boundary=frontier\n”;
print MAIL “--frontier\n”;
print MAIL “Content-Type: text/plain; charset=us-ascii\n\n”;
open(FILE, “<”, “$fh”) or die “Cannot open $fh: $!”;
print MAIL <FILE>;
close(FILE);
print MAIL “\n\n”;
print MAIL “--frontier\n”;
chomp(my $basename=`basename $attachment`);
print MAIL “Content-Disposition: attachment; filename=$basename\n”;
print MAIL “Content-Type: text/plain; name=$attachment\n\n”;
open(FILE, “<”, “$attachment”) or die “Cannot open $attachment: $!”;
print MAIL <FILE>;
print MAIL “\n”;
close(FILE);
close(MAIL);
For sending .pdf, .zip or .doc or .xlsx files, we need to use:
i) uuencode or base64 for Shell
ii) base64 and the IO::File module for Perl
The following are only small snippets we can adjust/add to the code provided earlier.
For Shell, the snippet is:
echo “Content-Transfer-Encoding: uuencode”
#echo “Content-Transfer-Encoding: base64”
echo -e “Content-Disposition: attachment; filename=`basename $attachment`”
echo -e “Content-Type: application/octet-stream; name=$attachment\n”;
uuencode $attachment `basename $attachment`
#base64 $attachment
For Perl, the snippet is:
#print MAIL “Content-Transfer-Encoding: base64\n”;
#print MAIL “Content-Transfer-Encoding: uuencode\n”;
print MAIL “Content-Disposition: attachment; filename=$basename\n”;
print MAIL “Content-Type: application/octet-stream; name=$attachment\n\n”;
# print MAIL read_file($attachment); # Can be also used for text/plain attachments
print MAIL encode_base64( read_file($attachment) ); # CTE must be 64; this must be accompanied by the MIME::Base64 module which provides this function for encoding. It comes default on all Perl installs.
#print MAIL `base64 $attachment`; # CTE must be 64; this procedure is incase you do not want to use the above module. Using the Shell command ticks.
#print MAIL `uuencode $attachment $basename`; # CTE must be uuencode; this also uses the Shell command ticks to use uuencode instead of base64
The following function is required for Perl to read the file properly, only if we use the encode_base64 function from the MIME::Base64 module. We could also use this function for plain text as in a commented portion in the code earlier.
sub read_file
{
my $filename = shift @_;
my $attach = new IO::File;
$attach->open(“< $filename”)
or die “Error opening $filename for reading - $!\n”;
$attach->binmode;
local $/;
<$attach>
}
Ensure that you use the right command for the right content transfer encoding. Whichever line comes before theuuencode or base64 command, it must have two new lines before it, to process the mail properly. In Shell, by default,echo comes with a new line. Adding ‘-e’ option and ‘\n’ would make that two new lines.
The Content Type can be figured out using the file command in Linux. But in other UNIX platforms, the file command doesn’t have that feature. Keeping it as ‘octet-stream’ handles any kind of file. By default, when we attach any file, the normal mail command takes the default as octet-stream, except for plain text files.

mailer_browser-view2
Figure 4: Mail without attachment read with browser mail client


mailer_browser-view3
Figure 5: Mail with attachment seen with browser mail client
HTML email
We have so far handled only plain text mails. And if we receive text mails in Outlook or any other mail client, when we try to reply, it (by default) sticks to the non-HTML environment in which it was initially received. Only in HTML format can we highlight or beautify the text if we need to forward a text mail to someone. To do that, we have to manually change the property of the mail from text to HTML. Instead, we can also make the machine send an HTML mail rather than a text mail.
We can also add different kinds of HTML entries, like tables, URLs or any form of coloured mail, even using style sheets. The presentation always matters.
The following code will send HTML mails. I’m not covering HTML with attachments. If you want to add attachments, the earlier attachment methods could be used (plain text or application/octet-stream and encoding standards to be base64 or normal 7-bit).
For Shell, the code is:
#!/bin/bash
subject=”Bash HTML”;
from=”osfyuser”;
to=”osfyedit”;
url=’http://localhost/css/mail.css’;
#css=’location/to/css/file’ # If we are using a local stylesheet,then `cat $css` into the body
css=’mail.css’ # If we are using a local file, then cat $css into the body
`wget -qO- $url > $css`;
bodyFile=”mailbody.html”;
cat > $bodyFile <<EOF
<!doctype html public “-//w3c//dtd html 4.0 transitional//en”>
<html>
<head><style>
`cat $css`
</style></head>
<p>this is test mail<br>
<table id=’mail’><tr><th>Month</th><th>Savings</th></tr><tr>
<td>January</td><td>\$100</td></tr>
<tr class=’alt’><td>February</td><td>\$500</td></tr></table>
</html>
EOF
{
echo “From: $from”
echo “To: $to”
echo “Subject: $subject”
echo -e “Content-Type: text/html; charset=us-ascii\n”;
cat $bodyFile
echo
} | /usr/sbin/sendmail -t
For Perl, the code is:
#!/usr/local/bin/perl
use LWP::Simple qw(get);
my ($url, @css, $subject, $from, $to, $fh, $content);
$subject = “Perl HTML”;
$from=”osfyuser”;
$to=”osfyedit”;
$fh=”mailbody.html”;
$url = ‘http://localhost/css/mail.css’; @css = get $url;
#@css=`cat $css`;
my $message=<<”EOF”;
<!doctype html public “-//w3c//dtd html 4.0 transitional//en”>
\n<html>\n
\n<head>\n<style>\n
@css
</style></head>\n\n
<p>this is test mail\n\n
<table id=’mail’>\n<tr>\n<th>Month</th>\n<th>Savings</th></tr>\n<tr>\n
<td>January</td>\n<td>\$100</td></tr>\n<tr class=’alt’>\n
<td>February</td>\n<td>\$500</td></tr></table>\n
</html>\n
EOF
open(FILE, “>”, “$fh”) or die “Cannot open $fh: $!”;
print FILE $message;
close(FILE);
open(FILE, “<”, “$fh”) or die “Cannot open $fh: $!”;
open(MAIL, “|/usr/sbin/sendmail -t”);
print MAIL “FROM: $from\n”;
print MAIL “TO: $to\n”;
print MAIL “Subject: $subject\n”;
print MAIL “Content-Type: text/html; charset=us-ascii\n”;
print MAIL <FILE>;
close(FILE);
close(MAIL);

mailer_browser-view4
Figure 6: Attachment opened from the browser using Zip utility
Emailing via PHP is well explained in the following URL for all the above three cases, though it does not include style sheets: http://webcheatsheet.com/php/send_email_text_html_attachment.php

mailer-html
Figure 7: Mail with HTML contrary to normal text
Note: There are two major differences between PHP and Shell/Perl when it comes to effective emailing:
i) PHP is itself embedded HTML, while in Perl we have to separately embed to make it HTML.
ii) PHP has a built-in mail function, which does a major chunk of the mailing. We could very well write a Perl module or Shell function file that we could reuse or call in programs. I prefer a Perl module because of the level of intricacies that can be included to make it a perfect function; and then we can use it within a Shell program too.

References
[1] https://en.wikipedia.org/wiki/MIME
[2] http://www.perlmonks.org/?node_id=675595 à Net::SMTP
[3] http://perldoc.perl.org/MIME/Base64.html
[4] http://search.cpan.org/~mivkovic/Mail-Sendmail-0.79/Sendmail.pm
[5] http://search.cpan.org/~rjbs/MIME-Lite-3.030/lib/MIME/Lite.pm
[6] http://www.if-not-true-then-false.com/2012/vi-vim-syntax-highlighting-on-fedora-centos-red-hat-rhel/
[7] http://www.vim.org/download.php
[8] http://perl.plover.com/local.html

Tuesday, 28 June 2016

Device Drivers, Part 1: Linux Device Drivers for Your Girl Friend

This series on Linux device drivers aims to present the usually technical topic in a way that is more interesting to a wider cross-section of readers.
“After a week of hard work, we finally got our driver working,” were Pugs’ first words when he met his girlfriend, Shweta.
“Why? What was your driver up to? Was he sick? And what hard work did you do?” asked Shweta. Confused, Pugs responded, “What are you talking about?”
Now it was Shweta’s turn to look puzzled, as she replied, “Why ask me? You tell me — which of your drivers are you talking about?”
When understanding dawned on him, Pugs groaned, “Ah c’mon! Not my car drivers — I am talking about a device driver on my computer.”
“I know about car and bus drivers, pilots, and even screwdrivers; but what is this ‘device driver’?” queried Shweta, puzzled.
That was all it took to launch Pugs into a passionate explanation of device drivers for the newbie — in particular, Linux device drivers, which he had been working on for many years.

Of drivers and buses

A driver drives, manages, controls, directs and monitors the entity under its command. What a bus driver does with a bus, a device driver does with a computer device (any piece of hardware connected to a computer) like a mouse, keyboard, monitor, hard disk, Web-camera, clock, and more.
Further, a “pilot” could be a person or even an automatic system monitored by a person (an auto-pilot system in airliners, for example). Similarly, a specific piece of hardware could be controlled by a piece of software (a device driver), or could be controlled by another hardware device, which in turn could be managed by a software device driver. In the latter case, such a controlling device is commonly called a device controller. This, being a device itself, often also needs a driver, which is commonly referred to as a bus driver.
General examples of device controllers include hard disk controllers, display controllers, and audio controllers that in turn manage devices connected to them. More technical examples would be an IDE controller, PCI controller, USB controller, SPI controller, I2C controller, etc. Pictorially, this whole concept can be depicted as in Figure 1.

Device and driver interaction
Figure 1: Device and driver interaction

Device controllers are typically connected to the CPU through their respectively named buses (collection of physical lines) — for example, the PCI bus, the IDE bus, etc. In today’s embedded world, we encounter more micro-controllers than CPUs; these are the CPU plus various device controllers built onto a single chip. This effective embedding of device controllers primarily reduces cost and space, making it suitable for embedded systems. In such cases, the buses are integrated into the chip itself. Does this change anything for the drivers, or more generically, on the software front?
The answer is, not much — except that the bus drivers corresponding to the embedded device controllers are now developed under the architecture-specific umbrella.

Drivers have two parts

Bus drivers provide hardware-specific interfaces for the corresponding hardware protocols, and are the bottom-most horizontal software layers of an operating system (OS). Over these sit the actual device drivers. These operate on the underlying devices using the horizontal layer interfaces, and hence are device-specific. However, the whole idea of writing these drivers is to provide an abstraction to the user, and so, at the other “end”, these do provide an interface (which varies from OS to OS). In short, a device driver has two parts, which are: a) device-specific, and b) OS-specific. Refer to Figure 2.

Linux device driver partition
Figure 2: Linux device driver partition

The device-specific portion of a device driver remains the same across all operating systems, and is more about understanding and decoding the device data sheets than software programming. A data sheet for a device is a document with technical details of the device, including its operation, performance, programming, etc. — in short a device user manual.
Later, I shall show some examples of decoding data sheets as well. However, the OS-specific portion is the one that is tightly coupled with the OS mechanisms of user interfaces, and thus differentiates a Linux device driver from a Windows device driver and from a MacOS device driver.

Verticals

In Linux, a device driver provides a “system call” interface to the user; this is the boundary line between the so-called kernel space and user-space of Linux, as shown in Figure 2. Figure 3 provides further classification.

Linux kernel overview
Figure 3: Linux kernel overview

Based on the OS-specific interface of a driver, in Linux, a driver is broadly classified into three verticals:
  • Packet-oriented or the network vertical
  • Block-oriented or the storage vertical
  • Byte-oriented or the character vertical
The CPU vertical and memory vertical, taken together with the other three verticals, give the complete overview of the Linux kernel, like any textbook definition of an OS: “An OS performs 5 management functions: CPU/process, memory, network, storage, device I/O.” Though these two verticals could be classified as device drivers, where CPU and memory are the respective devices, they are treated differently, for many reasons.
These are the core functionalities of any OS, be it a micro-kernel or a monolithic kernel. More often than not, adding code in these areas is mainly a Linux porting effort, which is typically done for a new CPU or architecture. Moreover, the code in these two verticals cannot be loaded or unloaded on the fly, unlike the other three verticals. Henceforth, when we talk about Linux device drivers, we mean to talk only about the latter three verticals in Figure 3.
Let’s get a little deeper into these three verticals. The network vertical consists of two parts: a) the network protocol stack, and b)the network interface card (NIC) device drivers, or simply network device drivers, which could be for Ethernet, Wi-Fi, or any other network horizontals. Storage, again, consists of two parts: a) File-system drivers, to decode the various formats on different partitions, and b) Block device drivers for various storage (hardware) protocols, i.e., horizontals like IDE, SCSI, MTD, etc.
With this, you may wonder if that is the only set of devices for which you need drivers (or for which Linux has drivers). Hold on a moment; you certainly need drivers for the whole lot of devices that interface with the system, and Linux does have drivers for them. However, their byte-oriented cessibility puts all of them under the character vertical — this is, in reality, the majority bucket. In fact, because of the vast number of drivers in this vertical, character drivers have been further sub-classified — so you have tty drivers, input drivers, console drivers, frame-buffer drivers, sound drivers, etc. The typical horizontals here would be RS232, PS/2, VGA, I2C, I2S, SPI, etc.

Multiple-vertical drivers

One final note on the complete picture (placement of all the drivers in the Linux driver ecosystem): the horizontals like USB, PCI, etc, span below multiple verticals. Why is that?
Simple — you already know that you can have a USB Wi-Fi dongle, a USB pen drive, and a USB-to-serial converter — all are USB, but come under three different verticals!
In Linux, bus drivers or the horizontals, are often split into two parts, or even two drivers: a) device controller-specific, and b) an abstraction layer over that for the verticals to interface, commonly called cores. A classic example would be the USB controller drivers ohci, ehci, etc., and the USB abstraction, usbcore.

Summing up

So, to conclude, a device driver is a piece of software that drives a device, though there are so many classifications. In case it drives only another piece of software, we call it just a driver. Examples are file-system drivers, usbcore, etc. Hence, all device drivers are drivers, but all drivers are not device drivers.
“Hey, Pugs, hold on; we’re getting late for class, and you know what kind of trouble we can get into. Let’s continue from here, later,” exclaimed Shweta.
Jumping up, Pugs finished his explanation: “Okay. This is the basic theory about device drivers. If you’re interested, later, I can show you the code, and all that we have been doing for the various kinds of drivers.” And they hurried towards their classroom.