« replay-log - resubmit syslog data back to syslog | Main | \command »

Guarded Commands

Much code, even if bug free, will leave a system in an inconsistent state should the unexpected occur. The resulting mess often requires extensive time and effort fix. Once running again, the code is no more than a house of cards waiting the next bump to fly apart. Writing fault tolerant code should be considered in addition to bug and security flaw free code.

Technorati Tags:

Consider a single line of a longer installation script. This line creates a Unix symbolic link file to a configuration file that varies depending on the class of the system being installed on.

#!/bin/sh … ln -s $class_specific_configuration $config_link

The code works, and will set the link up properly the first time. However, on subsequent installs, undesired behavior may result, especially when the $class_specific_configuration target changes. In this case, the symbolic link target will remain pointed at the old configuration file. Depending on the installation script, the error may be unlogged or very difficult to find. Should the service fail, linking the failure to an untraceable script event will not be obvious.

In this case, forcing the link creation usually is the best option: no race condition that a rm $config_link; ln -s … imposes, and a desired link update should the destination change:

#!/bin/sh … ln -s -f $class_specific_configuration $config_link

Going beyond the forced link update, a better system would use a guarded command, where a boolean expression first checks whether an action is needed, and only if required applies a change, then checks that the required change has been applied.

#!/usr/bin/perl … if (!-e $config_link or ( -l $config_link and readlink( $config_link ) ne $class_specific_configuration ) ) { symlink $class_specific_configuration $config_link; # TODO check if symlink has proper target… }

This method has the advantage of doing nothing if nothing need be done, and checking that the desired result took place. If similar logic is used throughout the script, the script may be run multiple times without adding duplicate lines to a configuration file or mounting a filesystem twice.

Downside: hard to write all the error handling and proper boolean expressions in advance. The Commands::Guarded Perl module greatly helps writing code in this style, and includes excellent discussion in the rationale section of the documentation. Other options include CFEngine, which follows a similar methodology when creating a link that is not exposed in the policy definition:

#!/var/cfengine/bin/cfagent -qKf … links: any:: ${config_link} ->! ${class_specific_configuration}

Additional background material:

However, I do use goto on occasion. :)