Category Archives: admin

Interactive, Full Screen Aptitude

Debian’s aptitude command is, according to their documentation, the preferred program for package management. I use Ubuntu for my work machine and servers and have found aptitude to be easy to understand and use, as well as reliable.

see also: Aptitude vs Apt-get Comparison

Unlike apt-get, aptitude offers a full-screen (or full-window more likely) mode that can be used to interactively manage packages on a Debian or Ubuntu system. As is often the case, a combination of command line operation and an interactive utility prove to be very attractive and useful.

You might use command line mode most often, to install, remove and upgrade packages, and sometimes use the interactive mode to check the status of your system and mark packages to manage how particular packages are dealt with.

To start in interactive mode, simply enter aptitide, with no subcommands:

aptitude


note: You do not need to sudo aptitude because, in interactive mode, it will prompt you and run sudo itself, if needed. Oh, the convenience!

Right away, you will see the interactive mode user interface:

Drop-down menus

The interactive interface includes drop-down menus at the top of the display. These are accessed with control-r or F10. Many of the of the commands available have a shortcut command key available, listed on the right side of the menu choice.

A full list of menu commands can be found here: http://algebraicthunk.net/~dburrows/projects/aptitude/doc/en/ch02s01s02.html

Package categories

Packages are shown in groups, organized by the following categories:

category explanation
New packages Packages that have been added since the list of new packages has been cleared (with the f command).
Installed packages Packages that are installed on the computer.
Not installed packages Packages that are not installed on the computer.
Obsolete and locally created packages Packages that are installed on the computer, but not available from an apt source.
Virtual packages Pseudonymous for other packages for compatibility or convenience.
Tasks Groups of packages that offer an easy means to install groups of packages for some purpose.

While the user interface is pretty well designed, it can be tricky to use because a variety of single letter codes are displayed and commands are entered via arcane single letter commands.

Navigation command keys

key action
F10 or control-t Show the drop-down menu
? Show help
Arrow keys Navigation
Enter key Select
+/- keys Mark a package to be installed, updated or removed
g key* Go forward: preview/confirm actions
q key* Go back: quit

* The q command quits the current operation and goes back to the previous mode. When at the top level, the q command quits the program. In general, the g goes forward and the q goes back.

Example workflow

  • press u to update local list of available packages
  • press U to mark upgradable packages
  • press g to review pending actions (modify if desired*)
  • press g (again) to start the process

Press g twice? Yes, the first time results in a list of packages that will be processed and the second time completes the action. This gives you a chance to review the changes before proceeding.

If you’ve erred, you can select ‘Cancel pending actions’ in the ‘Actions’ menu.

*When reviewing pending actions:

  • a explicitly accepts an action (press it again to un-accept).
  • r rejects a pending action.
  • g again goes ahead with pending actions.

Common actions

Use control-t for the drop-down menu to see all available actions. All of these commands are found in the Actions drop-down menu.

shortcut key item in Action menu action
g Install/remove packages Show preview, or if preview visiable, perform actions
u Update package list Update local package list from Internet sources
U Mark Upgradable Flag upgradable packages
f Forget new packages Clear “new” packages list
Q Quit Quit aptitude

Common package commands

These commands are found in the Package drop-down menu.

shortcut key item in Action menu action
+ Install Marks package for installation
- Remove Marks package for removal
i Cycle Package Information Changes information displayed for selected package

Current state flags

These flags appear in the first column of a table of packages when you’ve drilled down in one of the categories.

flag meaning
i Package is installed and all its dependencies are satisfied.
c Package was removed, but its configuration files are still present.
p Package and all its configuration files were removed, or the package was never installed.
v Package is virtual.
B Package has broken dependencies.
u Package has been unpacked but not configured.
C Half-configured: the package’s configuration was interrupted.
H Half-installed: the package’s installation was interrupted.

Position of the Current State and Action flags in the Package lists:

Action flags

These flags appear right after the current state flag in column one of displayed packages when you’ve given a command to make an alteration. One or more action flags will appear.

flag meaning
i Package will be installed.
u Package will be upgraded.
d Package will be deleted: it will be removed, but its configuration files will remain on the system.
p Package will be purged: it and its configuration files will be removed.
h Package will be held back: it will be kept at its current version, even if a newer version becomes available, until the hold is cancelled.
F An upgrade of the package has been forbidden.
r Package will be reinstalled.
B Package is broken: some of its dependencies will not be satisfied. aptitude will not allow you to install, remove, or upgrade anything while you have broken packages.

Sources

http://wiki.debian.org/Aptitude

http://algebraicthunk.net/~dburrows/projects/aptitude/doc/en/

Aptitude vs Apt-get Comparison

see also: Interactive, Full Screen Aptitude

One of the many attractive features of Ubuntu and Debian Linux is the package management system. Coming from other operating systems and other distributions makes the discovery of the Advanced Packaging Tool, APT, a source of pleasure and delight. Here is a system that solves dependency hell, makes keeping soft up to date easy and facilitates simple installation and removal of software packages.

The mainstay of this system has been apt-get, an extremely useful and versatile program that has been the heart of the Debian APT system. A great and useful program, but not perfect, the newer program aptitude is the result of an effort to improve on apt-get. In addition to a cleaner command line interface, aptitude offers a fullscreen character-based UI and more complete tracking of what has been installed and interdependencies.

aptitude is a newer and improved replacement for apt-get

These two programs provides higher level capabilities compared to dpkg, the Debian low-level package management utility. They offer an interface to package repositories and provide relief from dependency hell.

feature apt-get command aptitude command
fullscreen interface N/A aptitude
install package apt-get install ‘pkgname’ aptitude install ‘pkgname’
remove package apt-get remove ‘pkgname’ aptitude remove ‘pkgname’
purge package (removes package
and installation files)
apt-get –purge remove ‘pkgname’ aptitude purge ‘pkgname’
upgrade installed packages apt-get upgrade aptitude upgrade
upgrade installed packages
even if other packages
must be removed
apt-get dist-upgrade aptitude dist-upgrade
show package details (apt-cache show ‘pkgname’) aptitude show ‘pkgname’
search for packages (apt-file ‘searchpattern’) aptitude search ‘searchpattern’
delete installation files apt-get clean aptitude clean
delete obsolete installation files apt-get autoclean aptitude autoclean
update local cache of
available packages
apt-get update aptitude update
Show package details apt-get show ‘pkgname’ aptitude show ‘pkgname’
Retain the current version
of a package going forward
N/A aptitude hold pkgname
Clear the hold on a
package from
‘aptitude hold pkgname’ command
N/A aptitude unhold pkgname
List reverse dependencies apt-cache rdepends packagename aptitude -D packagename
super cow powers apt-get moo aptitude -v[v[v[v[v]]]] moo

As you can see, in addition to a more complete dependency tracking solution, aptitude also provides a (well designed) full-screen interface and cleaned-up command line syntax. Also, contrary to reports elsewhere, aptitude offers the ever important super cow functionality, with sarcasm added (Easter egg).

When installing a package, aptitude will show which other packages, though not required, are recommended or suggested, so you can decide whether or not to also install those.

Because aptitude more completely track dependencies, if you use it exclusively your will not need to use deborphan or debfoster.

Apt-get does not remove packages it installed as dependencies when the package you specified to be installed is removed. Aptitude does remove unneeded dependencies. This basic difference can cause problems when switching from apt-get to aptitude.

Don’t use both apt-get and aptitude interchangeably

Use one or the other. I’ve switched completely from using apt-get to using aptitude. If you do use apt-get, afterward run aptitude and fix any problems detected by first pressing g, which will show broken dependencies and packages that aptitude would remove. If you want to retain the packages that would be removed, arrow down to the header of that category and press the + key. Pressing g again will update your system with your indicated changes.

There is a difference in how aptitude keeps track of installed packages and their dependencies compared to apt-get. Aptitude is more precise is tracking these dependencies. Consequently, when you switch from using apt-get to aptitude, the first time you use aptitude you can run into problems because it can remove needed packages because it does not know that they are needed. This can be resolved through aptitudes’s full-screen interface or by entering the following command:

sudo aptitude keep-all

This will cause aptitude to retain all current packages going forward.

Aptitude’s full-screen interface

As displayed in the comparison table, aptitude offers a character-based full-screen interactive user interface if aptitude is entered as a command with no options.

The interface is very well designed and implemented, logical and easy to use after a few minutes of examination and trial. It has a drop-down menu system, which I found to work well using a mouse. The help system can be access via the drop-down menu or by pressing the ‘?’ key. A plethora of options are presented, most of which will make sense from what you’ve seen in the command line operation of the program. Pressing ‘q’ exits help…indeed pressing ‘q’ is the way to exit other modes in the program and exiting the program itself.

Other thoughts

I’ve found aptitude to be so well done, that I use it for all my package management needs. I use Synaptic to help search for software sometimes, whether for my local machine or a server. Then, I install it with aptitude…in any case Synaptic would be pretty useless over SSH to a server.

Some pages I found helpful: http://pthree.org/2007/08/12/aptitude-vs-apt-get/ and http://www.garfieldtech.com/blog/your-debian-aptitude.

Mix in some CLI fun on your server

This post is directed at Ubuntu and Debian server admins. As all work and no play makes Jack a dull boy, it is imperative that you immediately make your server more fun. If you do not get a little smile when you log into your server via SSH, then something is terribly wrong! Avoid dullness by all means.

Here I will show how to add and use figlet, fortune, cowsay and xmlstarlet to have big banners, random quotes’n’quips, talking cows and word of the day appear when using SSH to access your server.

FIGlet is a program for making large letters out of ordinary text.

FIGlet project: http://www.figlet.org/

Fortune is a simple program that displays a random message from a database of quotations.

Cowsay is a filter that takes text and displays a cow saying it.

Cowsay project page: http://www.nog.net/~tony/warez/cowsay.shtml

Cowsay article: http://linuxgazette.net/issue67/orr.html

XMLStarlet is a set of command line utilities (tools) which can be used to transform, query, validate, and edit XML documents and files using simple set of shell commands in similar way it is done for plain text files using UNIX grep, sed, awk, diff, patch, join, etc commands.

XMLSartlet project page: http://xmlstar.sourceforge.net/overview.php

Adding Universe and Multiverse Repository in Ubuntu

These packages are in the Universe and Multiverse repositories. If you need to add these repositories, just un-remark the pertinent lines in /etc/apt/sources.list and run aptitude update:

First, make a backup of the original /etc/apt/sources.list file.

sudo cp /etc/apt/sources.list /etc/apt/sources.list.original

Edit /etc/apt/sources.list:

sudo vi /etc/apt/sources.list

Un-remark the universe and multiverse lines (remove the leading # character) so the lines look something like this:

deb http://us.archive.ubuntu.com/ubuntu/ lucid universe
deb-src http://us.archive.ubuntu.com/ubuntu/ lucid universe
deb http://us.archive.ubuntu.com/ubuntu/ lucid-updates universe
deb-src http://us.archive.ubuntu.com/ubuntu/ lucid-updates universe
deb http://us.archive.ubuntu.com/ubuntu/ lucid multiverse
deb-src http://us.archive.ubuntu.com/ubuntu/ lucid multiverse
deb http://us.archive.ubuntu.com/ubuntu/ lucid-updates multiverse
deb-src http://us.archive.ubuntu.com/ubuntu/ lucid-updates multiverse

Finish by retrieving the updated package lists to your system with:

sudo aptitude update

By the way, to search for packages in the Ubuntu packages repositories, visit:


http://packages.ubuntu.com/

Figlet

Figlet creates character graphic block letter banners. Thus:

media2

becomes…

                       ___      ___ 
   ____ ___  ___  ____/ (_)___ |__ \
  / __ `__ \/ _ \/ __  / / __ `/_/ /
 / / / / / /  __/ /_/ / / /_/ / __/ 
/_/ /_/ /_/\___/\__,_/_/\__,_/____/ 

Install Figlet

On an Unbuntu/Debian system, install figlet like this:

sudo aptitude install figlet

The following will create the media2 banner, as above:

figlet -f slant media2

Modifying Message of the Day

Once you are satisfied with the output of figlet, you can have your character graphics banner appear whenever a user uses SSH to access the server, just modify the /etc/motd.tail file with:

sudo figlet -f slant media2 >>/etc/motd.tail

Bingo, whenever a user logs into the media2 server then will see your nifty banner! A little dullness have been bannished.

Installing xmlstarlet, cowsay and fortune

These are the commands we’ll be using to offer some fresh content on every login.

sudo aptitude install xmlstarlet cowsay fortune

Putting it all together

Modify the /etc/bash.bashrc file. This affects all users that use bash as their default shell.

fortune -a | cowsay -f $(ls /usr/share/cowsay/cows/ | shuf | head -n1)

echo -n "word of the day: "
/usr/bin/xmlstarlet sel --net -t -m "/rss/channel/item/description" -v "." "http://dictionary.reference.com/wordoftheday/wotd.rss"

The fortune command pops out a random quip, piped into cowsay, which is configured here with -f to use a random character graphic image. Then, word of the day is sourced from an RSS feed with xmlstarlet.

With everything, here is an example of what will appear when your SSH into your server:

Linux ijuki 2.6.32-x86_64-somewhere #1 SMP Sat Dec 5 16:55:26 UTC 2009 x86_64

                       ___      ___ 
   ____ ___  ___  ____/ (_)___ |__ \
  / __ `__ \/ _ \/ __  / / __ `/_/ /
 / / / / / /  __/ /_/ / / /_/ / __/ 
/_/ /_/ /_/\___/\__,_/_/\__,_/____/ 

Last login: Sun May  2 18:39:14 2010 from 00.000.000.000
 ________________________________________
/ Everything that you know is wrong, but \
\ you can be straightened out.           /
 ----------------------------------------
       \    ____
        \  /    \
          | ^__^ |
          | (oo) |______
          | (__) |      )\/\
           \____/|----w |
                ||     ||

	         Moofasa
word of the day: sesquipedalianism: given to using long words.

Making and installing Subversion on a 1and1 shared host

For historical reasons, I have a 1and1 shared host account with a few sites hosted there.

For more projects, I use Slicehost, which I’ve had great experiences with. Ideally, I’d like to move these 1and1 hosted sites to a Slice, but, my experience with 1and1 has been pretty flawless and two of the sites generate enough income that I am loathe to mess with them. (Other than changing Copyright dates, the code has not been changed in a couple of years. Not broke.)

So…I have this shared hosting. I like to avoid unnecessary expenses, so paying for a Slicehost without a clear money-making purpose is to be avoided. The problem is I’d like a Trac and Subversion setup for my personal stuff. I’ve got a few hosts that I use for various projects, but none are mine alone, so it would be wrong to host my personal stuff one one of them. Thus, I would like to set up Trac and Subversion on 1and1. Tricky. I do not have sudo rights there and can’t configure Apache, for instance.

Here, we will make and install Subversion. Later, I’ll install Python and Trac. Then, integrate them together.

First, download source for Subversion. I went to http://subversion.apache.org/source-code.html, found the source release area link and got the tarball HTTP links for the two tarballs I needed. We’ll create a directory called dev in our home directory and build Subversion there.

You’ll need to do the same so you get the current, stable version of Subversion (1.6.9 when I did this).

mkdir ~/dev
cd ~/dev
wget http://subversion.tigris.org/downloads/subversion-1.6.9.tar.gz
wget http://subversion.tigris.org/downloads/subversion-deps-1.6.9.tar.gz

Esplode da tarballs into a working source tree. The following commands correctly melt the two tarballs into an appropriately named directory.

tar -xzvf subversion-1.6.9.tar.gz
tar -xzvf subversion-deps-1.6.9.tar.gz

Build Subversion and install it to a logical place, (~/opt/).

cd subversion-1.6.9
mkdir ~/opt
./configure --prefix=$HOME/opt
make
make install

Modify your ~/.bash_profile file so you can run Subverison commands without typing the full path.

echo 'export PATH=$HOME/opt/bin:$PATH' >> ~/.bash_profile

Use the source command to reset your environment from your newly modified ~/.bash_profile file.

source ~/.bash_profile

Test the result.

svn --version

End of procedure.

My remote Subversion dump/tar/rotating file Perl script

This is the script I use to SSH remotely dump Subversion repositories on various servers for which I am responsible.

Before you can use this script, you need to set up SSH so your local cron can access the remote servers without a password.

One thing to note about this script is that it automatically rotates the archived dump files; keeping a fie for the 1st of the week on a month, 1st of the month and 1st of the year.

see: Using Public/Private Key Pairs with SSH

Then, just modify the script for your database/servers (the block @ about line 22).

This will create a series of files over time with daily/weekly/monthly Subversion dump backup tar files. The point is not so much to have every state of every repository, but to grab the daily changes without clobbering the last know good one. More is better, no?

#!/usr/bin/perl -w
#
# rtar_svn.pl
#
# by Andrew Ault www.andrewault.net
#
# No arguments. The program is to be modified to include each Subversion repository to be archived.
#
# Saves a tar of a remote Subversion dump in a rotating file.
#
# Of course you have to have SSH authentication already set up.
#
# This get cron'd daily on my local workstation.
#
use strict;
use warnings;

use DateTime;

my $fileError;
my $jobError  = 0;
my $jobErrors = "";
my $result;

# Specify a data block for each remote repository to be archived.
my %dumpJobs = (
				 'servername-repositoryname' => {
							'remoteServer' => 'servername',
							'repository'     => 'repositoryname',
							'dumpFilename' => 'servername-repositoryname.dump.svn',
							'svnDumpCmd' => '/usr/bin/svnadmin dump', # find svnadmin on your server
							'tarCmd'       => '/bin/tar', # find tar on your server
				 },
				 'servername-repositoryname2' => {
							'remoteServer' => 'servername',
							'repository'     => 'repositoryname2',
							'dumpFilename' => 'servername-repositoryname2.dump.svn',
							'svnDumpCmd' => '/usr/bin/svnadmin dump',
							'tarCmd'       => '/bin/tar',
				 },
);

# Process each specified repository dump/archive job.
for my $dumpJob ( sort keys %dumpJobs ) {
	$fileError = 0;
	my $tarballFilename = "$dumpJobs{$dumpJob}{'dumpFilename'}-" . tarDateSegment() . ".tgz";
	my $svnDumpCmd    = $dumpJobs{$dumpJob}{'svnDumpCmd'};
	my $tarCmd          = $dumpJobs{$dumpJob}{'tarCmd'};
	print "$dumpJob\n";

	my $dumpCommand = "ssh $dumpJobs{$dumpJob}{'remoteServer'} '$svnDumpCmd ";
	$dumpCommand .= "/var/lib/svn/$dumpJobs{$dumpJob}{'repository'} > $dumpJobs{$dumpJob}{'dumpFilename'}'";
	print $dumpCommand . "\n";
	$result = system($dumpCommand );
	if ($result) { $fileError = 1; }

	if ( !$fileError ) {
		my $remoteMakeTarball = "ssh $dumpJobs{$dumpJob}{'remoteServer'} '$tarCmd ";
		$remoteMakeTarball .= "cvfz $tarballFilename $dumpJobs{$dumpJob}{'dumpFilename'}'";
		print $remoteMakeTarball . "\n";
		$result = system($remoteMakeTarball );
		if ($result) { $fileError = 1; }
	}

	if ( !$fileError ) {
		my $downloadCommand = "scp $dumpJobs{$dumpJob}{'remoteServer'}:$tarballFilename .";
		print $downloadCommand . "\n";
		$result = system($downloadCommand );
		if ($result) { $fileError = 1; }
	}

	if ($fileError) {
		$jobError = 1;
		$jobErrors .= "$dumpJob ";
	}
}
if ($jobError) {
	warn "Errors were encountered: $jobErrors\n";
	exit(1);
}

sub tarDateSegment {
	my $dt = DateTime->now();

	my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) = localtime(time);
	$year += 1900;
	my $dateTime = sprintf "%4d-%02d-%02d %02d:%02d:%02d", $year, $mon + 1, $mday, $hour, $min, $sec;
	my $date     = sprintf "%4d-%02d-%02d",                $year, $mon + 1, $mday;
	my @weekdays = qw( sun mon tue wed thu fri sat );
	my $weekday  = $weekdays[$wday];
	my @months   = qw( jan feb mar apr may jun jul aug sep oct nov dec );
	my $month    = $months[$mon];

	my $weekOfMonth = $dt->week_of_month;

	my $dateTar = "";

	# if the first day of the year, set $dateTar like: 2009-1st
	if ( $yday == 1 ) {
		$dateTar = "$year-1st";
	}

	# if the first day of the month, set $dateTar like: feb-1st
	elsif ( $mday == 1 ) {
		$dateTar = "$month-1st";
	}

	# if the first day of the week, set $dateTar like: mon-1
	# where the number is the week of the month number
	elsif ( $wday == 1 ) {
		$dateTar = "$weekday-$weekOfMonth";
	}

	# otherwise, set the $dateTar like: mon
	else {
		$dateTar = "$weekday";
	}

	# $sec      seconds          54
	# $min      monutes          37
	# $hour     hour             11
	# $mon      month            4
	# $year     year             2009
	# $wday     weekday          3
	# $yday     day of the year  146
	# $isdst    is DST           1
	# $weekday  day of the week  wed
	# $month    month            may
	# $dateTime date and time    2009-05-27 11:37:54
	# $date     date             2009-05-27
	return $dateTar;
}

=head1 NAME

rtar_svn.pl - Andrew's remote Subversion repository archive program.

=head1 SYNOPSIS

    use: rtar_svn.pl

=head1 DESCRIPTION

This is a program I wrote to SSH/dump/tar/download/rotate archives of Subversion repositories.

=over

=back

=head1 LICENSE

Use this as you will.

=head1 AUTHOR

Andrew Ault 

=cut

Installing Net::Amazon::S3 Perl module on an Ubuntu server

The following is the same on recent Ubuntu releases, including Karmic, Lucid and Maverick.

What will not work

There seems to be a problem if you install Net::Amazon::S3 from CPAN. This will not work:

sudo cpan Net::Amazon::S3

Just about every dependency in the world installs, but fails in the home stretch when XML::LibXML::XPathContext and XML::LibXML fail to install.

What will work

sudo aptitude install libnet-amazon-s3-perl
sudo cpan Net::Amazon::S3::Client

Test your install with this

After throwing some data into S3 with S3Fox, test your installation. You will need to set values for aws_access_key_id and aws_secret_access_key, of course.

#!/usr/bin/perl
use warnings;
use strict;
use Net::Amazon::S3;
use Net::Amazon::S3::Client;

my %s3_hash = (
				aws_access_key_id     => "XXXXXXXXXXXXXXXXX",
				aws_secret_access_key => "YYYYYYYYYYYYYYYYYYYYYYYYYY",
				retry                 => 1,
);

my $s3 = Net::Amazon::S3->new( \%s3_hash );
my $client = Net::Amazon::S3::Client->new( s3 => $s3 );

my @buckets = $client->buckets;
foreach my $bucket (@buckets) {
	print $bucket->name . "\n";
}

XAMMP erroneous error message

While setting up a test system for a new MVC PHP web project, I ran into a hiccup when I restarted Apache under XAMPP on my Mac (OSX).

Googling this error turns out not to be very helpful. It is shown as an error, but no solutions.

This is a hard-to-track-down XAMPP error because the error issued has nothing to do with the problem.

Here is the text from the error dialog, so Google et al can find it: “/Applications/XAMPP/xamppfiles/bin/apachectl: line 70: ulimit: open files: cannot modify limit: Invalid argument”.

I had simply created a typo in the CustomLog line in the httpd-vhosts.conf file.

This was in the httpd-vhosts.conf file in the code block sort like this:


    ServerName projectname.dev
    ServerAlias www.projectname.dev
    DocumentRoot "/Users/andrewault/www/my.projectname.com/public"
    ErrorLog "/Users/andrewault/www/my.projectname.com/logs/error.log"
    CustomLog "/Users/andrewault/www/my.projectname.com/logs/combined.log" common

The directory part of the CustomLog line was wrong, causing the error.

My remote MySQL backup script in Perl – rtar_mysql.pl

Before you can use this script, you need to set up SSH so your local cron can access the remote servers without a password.

One thing to note about this script is that it automatically rotates the archived dump files; keeping a fie for the 1st of the week on a month, 1st of the month and 1st of the year.

see: Using Public/Private Key Pairs with SSH

Then, just modify the script for your database/servers (the block @ line 22).

This will create a series of files over time with daily/weekly/monthly MySQL dump backups.

#!/usr/bin/perl -w
# rtar_mysql.pl
#
# No arguments. The program is to be modified to include each database to be archived.
#
# Saves a tar of a remote mysql dump in a rotating file.
#
# This is used on Andrew's workstation to automatically grab a sql dump tar of each database daily.
#
use strict;
use warnings;

use DateTime;

my $fileError;
my $jobError  = 0;
my $jobErrors = "";
my $result;

# Specify a data block for each remote database to be archived.
my %dumpJobs = (
				 'db1' => {
							'remoteServer' => 'server_1',
							'database'     => 'database_name_1',
							'dbUser'       => 'database_username_1',
							'dbPassword'   => 'database_password_1',
							'dumpFilename' => 'server_1-database_name_1.dump.sql',
							'mysqlDumpCmd' => '/usr/bin/mysqldump',
							'tarCmd'       => '/bin/tar',
				 },
				 'db2' => {
							'remoteServer' => 'server_2',
							'database'     => 'database_name_2',
							'dbUser'       => 'database_username_1',
							'dbPassword'   => 'database_password_2',
							'dumpFilename' => 'server_2-database_name_2.dump.sql',
							'mysqlDumpCmd' => '/usr/bin/mysqldump',
							'tarCmd'       => '/bin/tar',
				 },
);

# Process each specified database dump/archive job.
for my $dumpJob ( sort keys %dumpJobs ) {
	$fileError = 0;
	my $tarballFilename = "$dumpJobs{$dumpJob}{'dumpFilename'}-" . tarDateSegment() . ".tgz";
	my $mysqlDumpCmd    = $dumpJobs{$dumpJob}{'mysqlDumpCmd'};
	my $tarCmd          = $dumpJobs{$dumpJob}{'tarCmd'};
	print "$dumpJob\n";

	my $dumpCommand = "ssh $dumpJobs{$dumpJob}{'remoteServer'} '$mysqlDumpCmd ";
	$dumpCommand .= "--user=$dumpJobs{$dumpJob}{'dbUser'} --password=$dumpJobs{$dumpJob}{'dbPassword'} ";
	$dumpCommand .= "$dumpJobs{$dumpJob}{'database'} > $dumpJobs{$dumpJob}{'dumpFilename'}'";
	print $dumpCommand . "\n";
	$result = system($dumpCommand );
	if ($result) { $fileError = 1; }

	if ( !$fileError ) {
		my $remoteMakeTarball = "ssh $dumpJobs{$dumpJob}{'remoteServer'} '$tarCmd ";
		$remoteMakeTarball .= "cvfz $tarballFilename $dumpJobs{$dumpJob}{'dumpFilename'}'";
		print $remoteMakeTarball . "\n";
		$result = system($remoteMakeTarball );
		if ($result) { $fileError = 1; }
	}

	if ( !$fileError ) {

		# using a more flexible naming scheme now
		my $downloadCommand = "scp $dumpJobs{$dumpJob}{'remoteServer'}:$tarballFilename .";
		print $downloadCommand . "\n";
		$result = system($downloadCommand );
		if ($result) { $fileError = 1; }
	}

	if ($fileError) {
		$jobError = 1;
		$jobErrors .= "$dumpJob ";
	}
}
if ($jobError) {
	warn "Errors were encountered: $jobErrors\n";
	exit(1);
}


sub tarDateSegment {
	my $dt = DateTime->now();

	my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) = localtime(time);
	$year += 1900;
	my $dateTime = sprintf "%4d-%02d-%02d %02d:%02d:%02d", $year, $mon + 1, $mday, $hour, $min, $sec;
	my $date     = sprintf "%4d-%02d-%02d",                $year, $mon + 1, $mday;
	my @weekdays = qw( sun mon tue wed thu fri sat );
	my $weekday  = $weekdays[$wday];
	my @months   = qw( jan feb mar apr may jun jul aug sep oct nov dec );
	my $month    = $months[$mon];

	my $weekOfMonth = $dt->week_of_month;

	my $dateTar = "";

	# if the first day of the year, set $dateTar like: 2009-1st
	if ( $yday == 1 ) {
		$dateTar = "$year-1st";
	}

	# if the first day of the month, set $dateTar like: feb-1st
	elsif ( $mday == 1 ) {
		$dateTar = "$month-1st";
	}

	# if the first day of the week, set $dateTar like: mon-1
	# where the number is the week of the month number
	elsif ( $wday == 1 ) {
		$dateTar = "$weekday-$weekOfMonth";
	}

	# otherwise, set the $dateTar like: mon
	else {
		$dateTar = "$weekday";
	}

	# $sec      seconds          54
	# $min      monutes          37
	# $hour     hour             11
	# $mon      month            4
	# $year     year             2009
	# $wday     weekday          3
	# $yday     day of the year  146
	# $isdst    is DST           1
	# $weekday  day of the week  wed
	# $month    month            may
	# $dateTime date and time    2009-05-27 11:37:54
	# $date     date             2009-05-27
	return $dateTar;
}

=head1 NAME

rtar_mysql.pl - Andrew's remote MySQL archive program.

=head1 SYNOPSIS

    use: rtar_mysql.pl

=head1 DESCRIPTION

This is a program I wrote to SSH/dump/tar/download/rotate archives of MySQL databases.

=over

=back

=head1 LICENSE

None.

=head1 AUTHOR

Andrew Ault 

=cut