Skip to content

Schedule a script to check remote disk usage and send an alert email using Mac OS X

5 February 2012
mail

 

 

Goal: I need to schedule a bash script that runs each day on my mac and checks the available disk space on a remote computer. In case of low space it has to automatically send a usage report via email to alert the users.

 

 

 

This task involves:

  1. writing the script that
    • checks the remote machine for disk space [using df]
    • creates the disk usage report for each user [using du]
    • sends an alert email in case of low space to everyone [using mail]
  2. setting up the script to run automatically each day on OS X [using launchd]

I assume you have an administrator account in both machines (to be able to sudo commands) and you are able to login into the remote machine (golan) from the local one (macbob).

 

Solution:

First of all, you will need to set up your ssh private/public keys so that you won’t be prompted for a password each time you try to login to the remote machine. If you do not know how to do it,  it is very simple and involves using ssh-keygen. There are many how-to you can find on the internet, for example this one.

The script will then use the ssh ability to remotely execute commands as in

ssh bob@golan command

First, it will check the disk usage with df, then, if below the given threshold, it will execute a one-liner script on the remote machine to create the user usage report using the du command. Finally, it will nicely format and redirect the outputs to a text file and email it to a list of users using the mail command.

Everything is straightforward aside from a small tricky detail.  You will need to use your administrator super powers to create the usage report using something like sudo du to enter the users homes! The problem is that sudo is usually set up to prompt for your login password so that your script has to pass it somehow. Of course you do not want to “echo-pipe” the password written in clear in your script. The solution is either you change the sudo settings editing /etc/sudoers to avoid the password request, or you devise a way to obfuscate and pass your password to sudo on the remote machine.

I adopted the latter method even though it is not particularly safe but at least the password does not appear verbatim in any script. To do this you can use the -base64 option of openssl to encode/decode your password as in

bob@macbob:~ >echo "mypass" | openssl enc -base64
bXlwYXNzCg==
bob@macbob:~ >echo "bXlwYXNzCg==" | openssl enc -base64 -d
mypass

Finally, the mail command is simply great and does just what you would expect :)

Enough said, here is the local script (on macbob computer):

#!/bin/bash
# macBob:/var/root/mycronjobs/check-disk.sh

email="/tmp/email-disk.txt"
lim=100000000

# check if free space is below threshold
full=`ssh bob@golan "df | tail -n 1 "|awk -v l=$lim '{if ($4<l) print 1;else print 0}'`

if [ $full -eq 1 ]; then
   # disk space is low, prepare an email
   echo "This is an automatically generated email. Please do not reply." > $email
   echo "golan free space below $lim KB." >> $email
   echo >> $email
   echo "CURRENT USAGE PER USER:" >> $email
   echo >> $email
   
   # retrieve the disk usage per user, passing an encrypted password
   ssh bob@golan "echo bXlwYXNzd29yZAo= | /home/bob/script/disk-space.sh" >> $email

   # send the email to the users
   cat $email | mail -s "Disk usage limit exceeded" bob@mail.com alice@mail.com fred@mail.com

   echo "golan disk space exceeded, email sent."
   rm $email
else
   echo "golan is ok, nothing to be done."
fi

and here is the remote one-liner script that creates the user usage report and print it to stdout:

# golan:/home/bob/script/disk-space.sh
# expects an encrypted password from stdin, decrypt it and pipes it to sudo
# in order to be able to do a du (disk-usage) command on all users directories
openssl enc -base64 -d|sudo -S du /workspace/ --max-depth=1 -h|sort -n -g -k 1 -r

As an example, this is how the email would look like:

To: <bob@mail.com>, <alice@mail.com>, <fred@mail.com>
Subject: Disk usage limit exceeded
Date: Sun,  5 Feb 2012 07:03:05
From: "System Administrator" <root@bobmac.local>

This is an automatically generated email, do not reply.
golan free space below 100000000 KB.

CURRENT USAGE PER USER:

531G	/workspace/fred
512G	/workspace/alice
415G	/workspace/bob
286G	/workspace/john
176G	/workspace/boss
5.0G	/workspace/mark

Now we have to schedule check-disk.sh script to run each morning at, say, 7 a.m.. To do so you will need a launch manager, called a property list file (plist), located in a specific directory so that launchd daemons can take care of it. You can find the syntax reference of such files in the Mac OS X launchd plist documentation here.

Here is the com.bob.golan-check.plist file I created:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key>
  <string>com.bob.golan-check</string>

  <key>ProgramArguments</key>
  <array>
    <string>/var/root/mycronjobs/check-disk.sh</string>
  </array>

  <key>Nice</key>
  <integer>1</integer>

  <key>AbandonProcessGroup</key>
  <true/>

  <key>StartCalendarInterval</key>
  <dict>
    <key>Hour</key>
    <integer>7</integer>
    <key>Minute</key>
    <integer>0</integer>
  </dict>

  <key>RunAtLoad</key>
  <false/>

  <key>StandardErrorPath</key>
  <string>/tmp/golan-diskusage.err</string>

  <key>StandardOutPath</key>
  <string>/tmp/golan-diskusage.out</string>

</dict>
</plist>

You can put your plist file in any of the following directories:

/Library/LaunchDaemons/
/Library/LaunchAgents/
~/Library/LaunchAgents/

The first is for scripts that have to be executed even when no users are logged in, the second for users logged in (but run by root) and for the last one the script is executed only when the user is logged in.

I am using the first one, so the script will be run by root and for this reason I put it in a directory accessible only by root (I do not want a script run by root to be readable/writeable by any user).

Once you have your plist file you need to make launchd daemon aware of it. This is done using launchctl as follows:

load the manager:

bob@bobmac:~ >sudo launchctl load /Library/LaunchDaemons/com.bob.golan-diskusage.plist

check if it is loaded:

bob@bobmac:~ >sudo launchctl list |grep bob
-	0	com.bob.golan-diskusage

execute the script right away (to give it a try and test if everything is working):

bob@bobmac:~ >sudo launchctl start com.bob.golan-diskusage

if you need to change the plist file, unload it first:

bob@bobmac:~ >sudo launchctl unload /Library/LaunchDaemons/com.bob.golan-diskusage.plist

In these examples, I use sudo since the script is executed by root. If you are using the ~/Library/LaunchAgents/ directory and your script is executed by the user you do not need to.

That’s all, I hope it is clear otherwise play around or drop a line ;)

L
 

One final important note: if you want your scheduled script to be able to send emails you have to set the AbandonProcessGroup key to true in your plist file. This allows any spawned process by your script to finish before being killed by launchd. This is necessary since the mail command takes some time to execute and launchd would kill it otherwise (as reported here).

Further reading: I found this blog very useful and clear to describe how scheduled jobs work on Mac OS X. Give it a try if interested: Mac crontab

Advertisements

From → programming

Leave a Comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: