The Wizard's Lab

Running Portsnap From Cron

| Comments

Today I wanted to polish up my backup scripts by automatically updating the installed ports and sending me a mail in case one of the installed packages is older than its port (FYI: this is what pkg_version -v is for). My first approach was to have something like this in a single python script:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import os, sys, subprocess

def check_version (output):
    packages = output.split("\n")
    # get rid of the suspicious additional last element (a single "\n")
    for p in packages[:-1]:
        info = p.split()
        if info[1] == "<":
            # cron will take care of sending the mail from here
            print p

null = open(os.devnull)
try:
    subprocess.check_call(["portsnap", "fetch"], stdout=null)
    subprocess.check_call(["portsnap", "update"], stdout=null)
    check_version(subprocess.check_output(["pkg_version", "-v"]))
except subprocess.CalledProcessError as e:
    print "*** ERROR ***"
    print e.output
null.close()

Which always returned none although I was sure there was an outdated package installed on the system, plus the script worked perfectly when run interactively (not from cron). After fiddling around a lot with environment and path settings in crontab, I decided to get the actual output of the 2 portsnap commands. This eventually provided the clue … portsnap fetch is not supossed to be run from cron and actually checks for that! A quick look in the according man page revealed the secret:

1
$ portsnap cron

which waits between 1 and 60 minutes before actually fetching the updates. So the final solution is now broken down in two parts (providing even basic privilege separation).

A crontab entry (root):

1
0 1 * * * /usr/sbin/portsnap cron > /dev/null && /usr/sbin/portsnap update > /dev/null

and a checker script (user):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import subprocess

def check_version (output):
    """
    checks the output of 'pkg_version -v' for outdated packages
    """
    packages = output.split("\n")
    # get rid of the suspicious additional last element (a single "\n")
    for p in packages[:-1]:
        info = p.split()
        if info[1] == "<":
            # cron will take care of sending the mail from here
            print p

try:
    check_version(subprocess.check_output(["pkg_version", "-v"]))
except subprocess.CalledProcessError as e:
    print "*** ERROR ***"
    print e.output

In my opinion a classical case of RTFM … and yes, I know, all of the above can be condensed and my exception handler is exceptionally bad.

Comments