Message of the Day for Fedora Linux

Ubuntu’s Message of the Day

If you’re familiar with Ubuntu Server, you may have noticed the message of the day whenever you log in via SSH, something similar to this:

Welcome to Ubuntu 12.04.1 LTS (GNU/Linux 3.2.0-29-generic x86_64)

* Documentation: https://help.ubuntu.com/

System information as of Tue Sep 4 19:14:57 EDT 2012

System load: 0.0                Processes: 103
Usage of /: 5.8% of 38.23GB     Users logged in: 0
Memory usage: 34%               IP address for eth0: 192.168.1.150
Swap usage: 0%

Graph this data and manage this system at https://landscape.canonical.com/

0 packages can be updated.
0 updates are security updates.

Last login: Tue Sep 4 18:47:43 2012 from compy.workgroup.local

This sort of output displayed on login eliminates the need to check things like free memory or how much storage space remains on the server. This saves us a few steps if we ever need to see this information. However, this feature isn’t available on Fedora Linux by default. If we continue to follow the mantra that a lazy systems administrator is a good systems administrator, we should implement a solution to generate similar output when we access Fedora systems just the same as our Ubuntu servers.

Basic system monitoring tools

Before we create a Fedora-based solution for the message of the day, let’s take a look at a few system monitoring tools: uptimefreepswhoifconfig, and df.

uptime

uptime displays the system uptime (how long the system has been running uninterrupted) along with the number of users accessing the system and load averages, displayed as values corresponding to 1-, 5-, and 15-minute periods of time:

$ uptime
20:44:38 up 4 days, 21:36,  1 user,  load average: 0.00, 0.01, 0.05

In addition to uptime, we can also view the load averages by reading the file /proc/loadavg:

$ cat /proc/loadavg
0.00 0.01 0.05 1/131 5109

Load averages are handy values for determining the performance of the system, and I highly recommend reading this excellent blog post explaining what they mean. Linux Journal also does an excellent job explaining load averages just the same, albeit more complex.

free

free displays memory usage in kilobytes:

$ free
             total       used       free      shared    buffers     cached
Mem:       1019500     630080     389420           0     108828     367348
-/+ buffers/cache:     153904     865596
Swap:       524284          0     524284

We can make the output of free more readable by adding the -m switch, which will display the output in megabytes instead of kilobytes:

$ free -m
             total       used       free      shared    buffers     cached
Mem:           995        615        380           0        106        358
-/+ buffers/cache:        150        845
Swap:          511          0        511

The fields to take note of would be the first three columns of the first row: total, used, and free; these three give us a pretty good idea of how the system is running with physical memory.

ps

ps displays the processes running on the system:

$ ps
  PID TTY          TIME CMD
 4263 pts/0    00:00:00 bash
 4365 pts/0    00:00:00 ps

That’s fine, but it only displays two processes: the current bash shell you are running and ps. That doesn’t really tell us what else is running on the system and therefore fails to give us an accurate view of how many processes the system is handling at the moment. To see all processes, we use the switch -e to display every process (which I will not include here if only for brevity). Try it out on your system:

$ ps -e

who

who displays the current users logged into the system. The aforementioned command uptime does this just the same; however, uptime only provides us with the number of users logged in and doesn’t tell us who is accessing the system.

$ who
daniel   pts/0        2012-09-06 11:42 (testbox.business.local)

ifconfig

ifconfig displays network settings for the server, specifically the name of the connection, the type, the hardware/MAC address, IP address, subnet/netmask, etc. In single-NIC configurations, the first entry is the one that represents the network connection for the server. For example:

$ ifconfig
eth0      Link encap:Ethernet  HWaddr 00:00:00:00:00:00  
          inet addr:192.168.1.150  Bcast:192.168.2.255  Mask:255.255.0.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:386875 errors:0 dropped:0 overruns:0 frame:0
          TX packets:46620 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:71835819 (71.8 MB)  TX bytes:10015855 (10.0 MB)

lo        Link encap:Local Loopback  
          inet addr:192.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:16436  Metric:1
          RX packets:374 errors:0 dropped:0 overruns:0 frame:0
          TX packets:374 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:36528 (36.5 KB)  TX bytes:36528 (36.5 KB)

Knowing the name of the first entry allows us to filter for just that data when we pass ifconfig the name:

$ ifconfig eth0
eth0      Link encap:Ethernet  HWaddr 00:00:00:00:00:00  
          inet addr:192.168.1.150  Bcast:192.168.2.255  Mask:255.255.0.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:386875 errors:0 dropped:0 overruns:0 frame:0
          TX packets:46620 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:71835819 (71.8 MB)  TX bytes:10015855 (10.0 MB)

df

df identifies disk free space. You can specify a mount point to identify the free disk space on just that mount point to filter results from the whole file system. We use the switch -h to display output in human-readable format.

$ df -h
Filesystem                         Size  Used Avail Use% Mounted on
rootfs                              50G  2.5G   45G   6% /
devtmpfs                           1.4G     0  1.4G   0% /dev
tmpfs                              1.5G     0  1.5G   0% /dev/shm
tmpfs                              1.5G  356K  1.5G   1% /run
/dev/mapper/vg_compy-lv_root        50G  2.5G   45G   6% /
tmpfs                              1.5G     0  1.5G   0% /sys/fs/cgroup
tmpfs                              1.5G     0  1.5G   0% /media
/dev/sda2                          497M   88M  385M  19% /boot
/dev/mapper/vg_compy-lv_home       178G   42G  127G  25% /home

Let’s see what happens when we specify the root mount point:

$ df -h /
Filesystem                         Size  Used Avail Use% Mounted on
/dev/mapper/vg_compy-lv_root        50G  2.5G   45G   6% /

SysLogin

The script

Below is a bash script I wrote to use the aforementioned tools and harness their output for a simple informative display when we log in to our Fedora server, just the same as if you were accessing an Ubuntu server:

#!/bin/bash
# SysLogin

echo -ne "Welcome to" `awk '{print $1, $3}' /etc/redhat-release` "($OSTYPE" `uname -r`" $HOSTTYPE)\n\n"
echo -e " * Documentation: http://docs.fedoraproject.org/\n"
echo -ne "  System information as of" `date` "\n\n"
echo -ne "  System load:   " `awk '{print $1}' /proc/loadavg` "\n"
echo -ne "  Processes:     " `ps -e | wc -l` "\n"
echo -ne "  Usage of /:    " `df -h / | sed -n 2p | awk '{print $5}'` "of" `df -h / | sed -n 2p | awk '{print $2}'`"\n"
memtotal=`free | sed -n 2p | awk '{print $2}'`
memused=`free | sed -n 2p | awk '{print $3}'`
memperc=`echo "scale=4; ($memused / $memtotal) * 100" | bc | cut -d. -f 1`
echo -ne "  Memory usage:   $memperc%\n"
echo -ne "  Users:         " `who | wc -l` "\n"
echo -ne "  IP address for" `ifconfig | sed -n 1p | awk '{print $1}'` "is" `ifconfig | grep "inet addr:" | sed -n 1p | awk '{print $2}' | cut -d: -f2`
echo -e "\n"

Installation of the script

To install the script, you must first log in to the Fedora Linux server and create a new file named .syslogin making sure to add the . before the name of the script. This means the file will appear hidden to users who perform a simple directory listing with ls. Open the file with nano:

$ nano .syslogin

Copy and paste the code from the above section entitled “The script”, save the file, and close nano. Next, we need to make the script executable so it can be run at login. To do that, we change the file permissions with chmod:

$ chmod 700 .syslogin

Lastly, we need to add a line to the user’s bash profile to ensure that the file is run at login. On Fedora systems, this file is usually .bash_profile:

$ nano .bash_profile

At the end of the file add the following code:

$HOME/.syslogin

Save the file and close nano. We use $HOME instead of the file’s literal address because $HOME is a variable set by the system to reference the user’s home folder.

Install bc if not already present to allow the script to perform math calculations. bc is a calculator application included with most Linux distributions.

$ sudo yum install bc

When you log in again to your server you should be greeted with output similar to Ubuntu’s Message of the Day:

Welcome to Fedora 16 (linux-gnu 3.4.7-1.fc16.x86_64 x86_64)

 * Documentation: http://docs.fedoraproject.org/

  System information as of Wed Sep 5 09:39:50 EDT 2012 

  System load:    0.11 
  Processes:      95 
  Usage of /:     6% of 50G
  Memory usage:   95%
  Users:          1 
  IP address for eth0 is 192.168.1.150

How does it work?

Though there are some fantastic sources out there on bash programming and scripting, I feel it might be helpful to explain how this script works just in case someone is interested in modifying or improving the code.

Only one of the first two lines is necessary, and that’s the first line. The first line #!/bin/bash tells the system which shell to use when running this script. The second line is the name of the script/application, preceded by a hash to indicate that what follows is a comment and should not be read by bash as code to be executed.

#!/bin/bash
# syslogin

The next line of code parses a text-file and displays the values of two system variables. We use echo -ne to display the information from the commands on the same line and to acknowledge \n newline characters. The first thing is to parse the text-file /etc/redhat-release with awk, which prints only the first and third columns of the text-file. We finish by printing the system variables $OSTYPE and $HOSTTYPE along with running the command uname -r to print the OS type, the kernel version, and the host type (x86, x86_64).

echo -ne "Welcome to" `awk '{print $1, $3}' /etc/redhat-release` "($OSTYPE" `uname -r`" $HOSTTYPE)\n"

Now let’s add a link to the official documentation website for the Fedora Project using echo, which is the command we will consistently use to display output.

echo -e " * Documentation: http://docs.fedoraproject.org/\n"

Now we can start providing some system information to the user, more specifically, let’s add a timestamp to the output so if it’s ever copied and pasted, someone will have the corresponding date and time. We print a line and make a call to the command date:

echo -ne "  System information as of" `date` "\n\n"

Although we could use uptime to print the 1-minute load average, we could just as easily parse the text-file /proc/loadavg. Specifically, we parse the text-file with awk and print just the first column, which represents the 1-minute load average. If you wanted to display the 5- or 15-minute load average, you could substitute $1 for $2 or $3, the values representing the second and third columns of /proc/loadavg.

echo -ne "  System load:   " `awk '{print $1}' /proc/loadavg` "\n"

As we saw earlier, we can run ps -e to display all running processes; however, it doesn’t provide us with a count of how many processes are running. Therefore, we pipe the output of ps -e to wc -l, which is a command that performs word counts, or with the -l switch, line counts. We’re literally just counting the lines of output.

This will also count the first line of output, which is not an actual process but the heading for ps. Essentially our process count will be slightly off–specifically off by just one–although even that is not entirely accurate. Invoking ps is a process and also part of this count, which some may consider inaccurate just the same.

echo -ne "  Processes:     " `ps -e | wc -l` "\n"

Next, let’s parse the output of df -h / to print the percentage of disk usage in relation to the total size of the disk. Fortunately, these values already exist, so there is no need for maths just yet. If we want to make the output appear the same as how Ubuntu displays the information, we want to show disk usage as a percentage of the total. We pipe df -h / to sed to extract only the second row of output, then pipe that row to awk to print the fifth column, the percentage of disk usage. We perform the same task a second time to extract the total, only we select the second column of text instead of the fifth. Because we are calling df in human-readable format, we do not need to convert the total value from kilobytes.

echo -ne "  Usage of /:    " `df -h / | sed -n 2p | awk '{print $5}'` "of" `df -h / | sed -n 2p | awk '{print $2}'`"\n"

Now we create some variables and do some math. Because we would like to display memory usage as a percentage, we need to first extract values from free, then determine the percentage of used memory and print it. Using sed and awk we can extract the total memory available to the system and the used memory, stored as variables memtotal and memused. We then create another variable, memperc, which we derive by dividing memused by memtotal and multiplying that value by 100 so it doesn’t appear as a decimal when displayed.

The math here invokes bc and sets the scale to 4, which means calculations will retain 4 decimal places. This is done for accuracy when performing calculations, but when printing the variable memperc we don’t want to see 4 decimals after the percentage. To remove these spurious numerals, we pipe the output of bc to cut with the switch -d. to display output only up to the character preceding the decimal point. Lastly, we display that percentage.

memtotal=`free | sed -n 2p | awk '{print $2}'`
memused=`free | sed -n 2p | awk '{print $3}'`
memperc=`echo "scale=4; ($memused / $memtotal) * 100" | bc | cut -d. -f 1`
echo -ne "  Memory usage:   $memperc%\n"

Similar to how we displayed the number of processes, we will do the same for the command who to display the number of users accessing the system. Pipe the output of who to wc -l to count the lines of output and therefore provide us with the number of users logged in to the system. Unlike ps, there are no headings to worry about, so this will always be accurate as opposed to our previous attempts to count processes.

echo -ne "  Users:         " `who | wc -l` "\n"

Lastly, we finish with the script by parsing the output of ifconfig to display the IP address for the first network interface card on the system. The first thing we do is extract the first row and column of ifconfig to get the name of the NIC using sed and awk. Next, we extract the IP address by piping ifconfig to grep, which has been told to filter on the phrase "inet addr:" and extract the first row and second column, ignoring all characters after the IP address. Notice how cut -d: looks different than it did in a previous example. Before we selected the . as the delimiter, but in this command we used : as the delimiter.

echo -ne "  IP address for" `ifconfig | sed -n 1p | awk '{print $1}'` "is" `ifconfig | grep "inet addr:" | sed -n 1p | awk '{print $2}' | cut -d: -f2`
echo -e "\n"