Using XMPP to Find Out When a Long-Running Process is Done

Do you often find yourself running a process that you know is going to take a while? Do you also find yourself checking the shell it's running in every five minutes to see if it's done? I do this fairly often, so what I used to do is something like this:

my-long-running-process; notify-send Complete "Your long-running process is complete"

This pops up a nice GUI notification letting me know my process is done. However, it has a few disadvantages. One is that it requires me to be at the machine I'm running the job on; sometimes I set up a job to run and leave. Another disadvantage is that I may miss the GUI notification if I get up from my desk to grab a cup of coffee or something.

I decided the best alternative would be to write a simple script that would notify me over XMPP that my job had completed. That way, my IM program would let me know I had a message when I got back to my laptop, and my phone would receive the message too. So now what I do is this:

my-long-running-process; notify-rob.pl "Your long-running process is complete"

That's nice, but if I'm running a process while I'm out and about and I'm interested in a summary of the data it outputs, I'd like that included in the message. So I added support for using standard input as the message. Let's say I want to know how long my process took:

(time my-long-running-process) 2>&1 | notify-rob.pl -i

Now when my-long-running-process completes, it sends a message with the duration of the job to my phone, as well as any chat clients I have running at the time.

Here's my notify-rob.pl script, with the Rob-specific bits removed. If you'd like to use it, you'll need the AnyEvent-XMPP distribution installed for Perl, and perl 5.10 or better.

notify-rob.pl
#!/usr/bin/env perl

use strict;
use warnings;
use feature 'say';

use AnyEvent::XMPP::IM::Connection;
use AnyEvent::XMPP::IM::Message;
use Getopt::Long;

my $from_stdin = 0;
my $from_file  = 0;

GetOptions(
    input    => \$from_stdin,
    'file=s' => \$from_file,
);

my $body;

if($from_stdin || $from_file) {
    my $fh;

    if($from_stdin) {
        $fh = \*STDIN;
    } else {
        open $fh, '<', $from_file or die "Unable to open $from_file: $!\n";
    }

    $body = do {
        local $/;

        <$fh>;
    };

    close $fh;
} elsif(@ARGV) {
    $body = join(' ', @ARGV);
} else {
    die "usage: $0 [-i] [-f file] [message...]\n";
}

my $cond = AnyEvent->condvar;
my $conn = AnyEvent::XMPP::IM::Connection->new(
    jid              => 'your source JID here',
    password         => 'your password here',
    domain           => 'gmail.com', # I use Google Talk for this; you can
				     # remove the domain, host, port, and
				     # old_style_ssl options if you use
				     # a "regular" XMPP server
    host             => 'talk.google.com',
    port             => 5223,
    old_style_ssl    => 1,
    initial_presence => undef,
);

my $timer;
$conn->reg_cb(session_ready => sub {
    my $msg = AnyEvent::XMPP::IM::Message->new(
        to   => 'your destination JID here',
        type => 'chat',
        body => $body,
    );
    $msg->send($conn);
    $timer = AnyEvent->timer(
        after => 3,
        cb    => sub { $cond->send },
    );
});

$conn->reg_cb(error => sub {
    my ( undef, $error ) = @_;

    say $error->string;
    $cond->send;
});

$conn->connect;
$cond->recv;
Published on 2011-10-27