Yet another background
I’ve really got to stop with these things. I think this might be my favorite so far though: It displays excerpts from e-books (in plain text format). I downloaded a bunch of classics from Project Gutenberg for the text source.
Sample generated wallpaper. Click for larger image.
The source code can be downloaded here. It requires ImageMagick (which seems to behave differently on different platforms, so some tweaking may be in order). The config file has lines of three entries separated by whitespace: the file name, the first line that can be used, and the last line that should be used. My config file looks like this:
/home/ben/.book_wallpaper/romeo_and_juliet.txt 259 3871 /home/ben/.book_wallpaper/macbeth.txt 336 4413 /home/ben/.book_wallpaper/sonnets.txt 289 2891 /home/ben/.book_wallpaper/mariner.txt 1443 2392 /home/ben/.book_wallpaper/mariner.txt 3177 3226
And as always, it needs to be added to your crontab. It might be a fun challenge to port this to Windows, but it will have to change significantly to do so.
Load-aware utility
This utility will run a specified command only if the system load is below a certain threshold. The motivation was my installing the animated background from my previous post on my somewhat underpowerd laptop. While the script itself isn’t very intensive, my laptop doesn’t have a lot of memory, and when I have several applications running simultaneously, performance degrades noticiably. I want the animated desktop, but I don’t want to bog down my computer with it. So this utility will refuse to run if the system is on battery (and I have better things to do with the power) or if the system is in use (and I can’t spare the processor or memory).
#! /bin/ksh # Note: ksh93 required. Uses floating point comparison if (( $# != 3 )); then print "Usage: $0 N <load> <command>" print " Executes <command> if the N min load is less than <load>" print " N may be 1, 5, or 15. If output is redirected, it applies" print " to the command as well. Will not run on battery." exit 1 fi case $1 in 1) AVG=1 ;; 5) AVG=2 ;; 15) AVG=3 ;; *) print "Error: N must be 1, 5, or 15." exit 1 ;; esac MAXLOAD=$2 CMD=$3 # Battery check grep 'discharging' /proc/acpi/battery/BAT*/state > /dev/null (( $? == 0 )) && exit 0 LOAD=$( cut -d' ' -f$AVG /proc/loadavg ) (( $LOAD < $MAXLOAD )) && exec $CMD
So now my crontab looks like:
BIN=/home/ben/bin */5 * * * * $BIN/loadtrip 1 0.3 $BIN/cell_background.py
Meaning that the background will only be updated if the load over the past minute is less than 0.3 and I’m not on battery.
Another animated background
I appear to be on a bit of an animated wallpaper kick. Here’s another one. It slowly animates the results of John Conway’s Game of Life, with a bit of visual tweaking to make it pretty to look at. You can also easily modify the rules to generate any of its other variations. The thumbnails below don’t look do it justice. It looks much better full size.
I have it automatically updating with the following line in my crontab:
*/10 * * * * /home/ben/bin/cell_background.py
Webcam Wallpaper for Linux
This is a Python script I wrote to use as an example for my scripting class. It takes webcam images and turns them into a wallpaper. Set it to run automatically through cron and you have a animated desktop. This lecture is going to touch on string parsing, functions, and basic list manipulation, so if they way I’m doing things seem a round-about, that’s why. This script requires ImageMagick to be installed.
#! /usr/bin/env python # Copyright 2009 by Benjamin Fogle # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. import sys import os import os.path import urllib2 import tempfile import math # Constants. Change these to customize the script # In ksh: CONFIG_FILE=$HOME/lectures/examples/LEC-05/webcam_config CONFIG_FILE=os.getenv('HOME') + '/.webcam_config' TARGET=os.getenv('HOME') + '/Pictures/Wallpapers/webcam.jpg' SCREEN_X = 1024 SCREEN_Y = 768 def GetTiles(num_images): """Returns a tuple (N,M) representing the tile grid""" # This algorithm could be much improved tile = math.ceil(math.sqrt(N)) return (tile, tile) def GetImageSize(N, M): """Returns a the max size of each image based on the screen size and the tile grid""" global SCREEN_X global SCREEN_Y ImgX = SCREEN_X / N ImgY = SCREEN_Y / M return (ImgX, ImgY) def CopyFile(src_fp, dest_fp): """Copy a file intelligently""" data = src_fp.read(49152) # Copy 48k at a time while data: dest_fp.write(data) data = src_fp.read(49152) # Note: shutils.copyfileobj does this too. if len(sys.argv) != 1: print "Usage: %s" % (sys.argv[0]) print print "Config file is located in %s" % CONFIG_FILE sys.exit(1) # Make sure the config file is readable and that it is a regular file # There is a better way to do this that we will learn later. if not os.access(CONFIG_FILE, os.R_OK) or not os.path.isfile(CONFIG_FILE): print "Error: %s could not be opened!" % CONFIG_FILE sys.exit(2) urls = [] # Url to download from. Read from config file captions = [] # Captions for each image. Read from config file filenames = [] # Temporary file names config_fp = open(CONFIG_FILE, 'r') line = config_fp.readline() N=0 while line: params = line.split(":::") urls.append(params[0].strip()) captions.append(params[1].strip()) line = config_fp.readline() N+=1 config_fp.close() # Download each url. This is how to iterate over mutliple lists at once for url, caption in zip(urls, captions): url_fp = urllib2.urlopen(url) # The following two lines are the preffered way to open a temporary # file. fd, name = tempfile.mkstemp() img_fp = os.fdopen(fd, 'w') filenames.append(name) CopyFile(url_fp, img_fp) url_fp.close() img_fp.close() # The data won't appear in img_fp until it's closed # Use the ImageMagick suite from the shell to add a caption os.system("convert '%s' -set comment '%s' '%s'" % \ (name, caption, name)) # Figure out the parameters tilex, tiley = GetTiles(N) imgx, imgy = GetImageSize(tilex, tiley) # Use ImageMagick again to assemble the wallpaper cmd = "montage -geometry %dx%d \ -tile %dx%d \ -set caption '%%c' \ -pointsize 32 \ -size %dx%d \ -texture plasma: \ +polaroid \ -background black " % (imgx, imgy, tilex, tiley, SCREEN_X, SCREEN_Y) # Add each image file to the command string for filename in filenames: cmd += '"%s" ' % filename # Add the output file to the command string and execute cmd += TARGET os.system(cmd) # Cleanup for filename in filenames: os.unlink(filename) # In ksh: rm $filename
Contents of .webcam_config:
http://live-fuji.jp/hi-fuji/now.jpg ::: Mt. Fuji http://www.nature.nps.gov/air/WebCams/parks/grcacam/grca.jpg ::: Grand Canyon http://www.nature.nps.gov/air/WebCams/parks/moracam/mora.jpg ::: Mount Rainier http://www.parislive.net/eiffelcam3.jpg ::: Eiffel Tower
To install, you should probably run this script once after you’ve modified it to your configuration in order to create the file. Set that file as your desktop wallpaper. Finally, you should add the script to your crontab:
echo '*/20 * * * * /home/ben/bin/webcam_background' | crontab -

As of this writing, the sun is just coming up in Japan, it’s late afternoon at the Grand Canyon, it’s late at night in Paris, and it appears to be snowing in Washington.
Sending email from the command line
A lot of the scripts I write are long-running, and I like to have them email me when they are done. Typically my script run on Purdue’s servers, so sending email from a shell script is all set up and ready to go. The same is not true of my laptop running Ubuntu 8.10, but I would like to be able to send email from the command line there as well. (At the moment I don’t really have a need, but it might be useful in the future.) Luckily, on Ubuntu it only takes a few minutes.
Setting up
First I need to set up a mail transport agent (MTA). An MTA might be described in simple terms as an email server, but they come in different flavors. What I want is basically a proxy: it will take email messages from my laptop and forward (relay) them through Purdue’s outgoing mail server. There’s no reason I couldn’t set it up to send mail directly, but since my laptop changes IP addresses frequently as it travels, often connecting from a dynamic IP address at home, any mail coming out would likely get flagged as spam. It’s going to be far more reliable to send messages via Purdue’s servers, and simpler to set up and maintain. The IT guys at Purdue are going to do a much better job running email servers than I will anyway.
The MTA I will be using is called exim. Exim is a large and complicated program, but for what I need it’s a breeze to set up. First it needs to be installed. In a terminal, run the following:
$ sudo apt-get install exim4 exim4-config mailutils
This should install all the software I need. You might get a configuration screen while installing, in which case skip the next command. If not, execute the following:
$ sudo dpkg-reconfigure exim4-configYou should see a series of screens like the following:

This first screen asks how mail generally works on this machine. I am passing along mail to another server for sending (a smarthost), but I still want local mail delivery (i.e., I can still send mail to ben@localhost for local notifications). Don’t confuse local mail messages with sending message to the Internet at large; they are set up separately here. A local message is never going to leave my laptop.

This screen helps set up the local messages. My machine is named gulliver, so if I send mail to “ben” it will get sent to ben@gulliver, a synonym for ben@localhost” There is no real reason to change this from the default value.

This screen specifies what computers on the network can send messages using my relay. This should only be the local machine (127.0.0.1). Setting this to another value risks allowing the machine to be used as an open relay by spammers, so don’t touch.

Here I specify that anything sent to @gulliver should not be sent out into the Internet, but should be delivered to a local mailbox on my laptop. Again, this is largely for notifications.

We are not relaying anyone else’s mail, so leave this blank.

My smarthost is Purdue’s SMTP server, which used port 587 to authenticate via TLS. The standard port 25 is only accepted on campus, which would make this whole scheme a bit useless. Check your IP’s requirements. I will configure the authentication later. Note that it will connect using TLS if it can; I don’t need to explicitly enable it.
You will get a screen asking about hiding the local domain name. Select ‘Yes’. As a spam fighting measure, Purdue won’t accept email from any domain that doesn’t actually resolve. The domain ‘gulliver’ won’t ever resolve as a valid Internet address, so Purdue will reject my mail unless I forge the domain.

It doesn’t matter what I put really, as long as it’s a real site. I’m using nihiladrem.com because ben@nihiladrem.com (as it will appear) is a real email address that I control. That way, if the recipient replies, I will actually get it. My Purdue login is not ‘ben’, so using purdue.edu would just make the email appear to come from someone else.
You will get a screen asking about minimal DNS queries. Unless you have a really funky connection to the Internet, you don’t need this.

When delivering local messages, I want them to be in mbox format (i.e., the messages are all just concatenated into one big file). Since just about every email client ever made can read mbox files, it’s a pretty good choice.
Then it asks you about splitting the configuration file. I don’t intend to alter the config file by hand, so splitting it provides no benefit. (Also, it warns about split config files being less stable, so might as well go with the more stable option.)
There is one more thing to do: enable it to authenticate against Purdue’s server. Edit the password file for exim4 with the following command:
$ sudo gedit /etc/exim4/passwd.client
Add the following line to the end, adding your information where appropriate.
your.smtp.server:your_login:your_password
This file is by default readable by only root and exim4. Do not change the permissions, as it contains your clear-text password.
Finally, restart the MTA for the changes to take effect:
$ sudo /etc/init.d/exim4 restart
Trying it out
We can use the mail command to send a basic message:
$ mail -s"This is a test" ben@nihiladrem.com <<EOF Hello, Internet! EOF
If all worked well, this message should be delivered to the recipient specified.
Another trick I’m a bit proud of is sending an attachment via the command line. I don’t mean the hacks you usually see, but a real attachment that will appear as such in an email client like Thunderbird. The following example will compress the directory $DIRNAME into a tarball and send it as an attachment.
$ mail -s "This is a test" \ -a "MIME-Version: 1.0" \ -a 'Content-Type: multipart/mixed; boundary="1234567890"' \ ben@nihiladrem.com <<EOF --1234567890 Content-Type: text/plain This is the body text of the email. --1234567890 Content-Type: application/x-gzip; name="$DIRNAME.tar.gz" Content-Transfer-Encoding: base64 $(tar cz $DIRNAME | base64) --1234567890-- EOF
That’s all there is to it. On one final note, procmail, the reason I want local messages to work, is already set up correctly. You just need to install it if it isn’t already.
Click for animated demo.