Cover V01, I02
Listing 1
Listing 2
Listing 3
Listing 4


A Background Job Launcher

Leor Zolman

In the Premiere issue of SysAdmin, I presented a set of shell scripts that manage overnight processing of batch jobs. Those scripts, which I collectively named the "Onite" package, reduce daytime CPU load by offering users the opportunity to queue resource-intensive programs for execution during late-night hours.

Some jobs, however, are important enough that they need to be run immediately; and some programs may require immediate execution in some cases but be suitable for the overnight queue in others. A framework flexible enough to support such variations in priority would give users the option, for any particular job, of either running that job as an immediate background task or queuing it for overnight processing.

The shell scripts described are the pieces necessary to "upgrade" the Onite system to provide such a flexible framework. (Listing 1) is a general-purpose background task launcher. Lines 7-9 show the usage syntax. The only required parameter, outfile, specifies the name of a file to which the standard output stream of the background script is written. If the command is run as, outfile receives the standard error stream intermixed with standard output. If the command is run as (by having a link to so named), then the standard error stream is not automatically redirected to outfile; instead, the invoking script is allowed to explicitly direct the standard error stream to some other file. Except for this difference in the treatment of the standard error stream, and work identically, and all the comments that follow about apply equally to

Setting up background command scripts is left fully to the system administrator. If the administrator wishes to prevent multiple instances of any particular background application from executing concurrently, then he/she must manage the creation and deletion of lock files within the context of the driver script. Typically, this involves checking for the absence of a lock file before allowing the job to be initiated, creating the lock file, and inserting an instruction at the end of the job text itself to delete the lock file when processing has been completed.

To deal correctly with the case where an executing background script having an associated lock file is abnormally terminated, accepts an optional command-line parameter (after the outfile specification) to name the lock file. If such a lock file is specified, then a trap command is inserted into the background job script to delete the lock file if the job should happen to get interrupted (see Listing 1, line 52).

Launching a Simple Background Task

A template for a background-only driver script is shown in Listing 2. Lines 5-9 of the template define some characteristics of the job: if debug is Y, then job output is written to the current directory instead of to the directory specified by the Ltmp variable. The AppId variable should be set to a short string describing the nature of the job. This text is used in the naming of the job output file. Finally, the UseLock variable tells whether or not a lock file is to be used for the background job.

If the job uses a lock file, then lines 17-20 check to see if the lock file exists and, if it does, prevent the job from being run until the lock file disappears. If no lock file exists, then line 24 creates one, and a trap command is issued to make sure the lock file is deleted if the user interrupts the driver script before the job is actually launched.

Lines 27-32 define the output log file name. The log is always placed in the current directory during debugging.

Lines 34-37 show how to launch the background task by placing the text in-line using the shell construct <<. This method is convenient when the background script text is always structured the same way, perhaps varying only in the values of interpolated shell variables.

The alternative method for constructing the job script is to write the text into a temporary file and send it to, as shown in line 40. This method must be used when the job text has to be constructed dynamically based on user input. Listing 4 illustrates such a case.

Choosing between Background and Overnight Execution

Listing 3 is a template driver script that offers users a choice between running a job in the background or running the same job through the overnight spooler. The only real difference in setting up a job for overnight versus background execution is that there are no lock file issues involved in overnight execution; the overnight spooler runs all queued jobs sequentially. While it is possible for a user to decide to run a lockfile-related job in the background while the overnight spooler is running a similar job, such a scenario does not seem to occur frequently enough in practice to warrant any special consideration -- at least not from the efficiency point-of-view. If, however, the reason for the lockfile is more serious than just to limit background system load (e.g., a job requires exclusive access to a particular file or table), then your custom driver script must handle such locking issues explicitly.

In the last section of Listing 3, the job text is submitted to the appropriate utility script for either overnight spooling or immediate background execution. In this listing, I've used the in-line approach of specifying the job script. In an actual production script, lines 46 and 53 would be the places where the application-specific commands would go.

Listing 4 is a complete, functional implementation of a driver script based on the template script in Listing 3. The application generates a sequence of reports from our magazine subscription database. The Informix "Ace" report generator program, named swkpay, accepts two command-line parameters: a publication code (mag) and a source code value (sc). The driver script prompts the user for as many publication/source code pairs as desired, spews a report program invocation corresponding to each pair into a cumulative script file, and finally submits that script file for execution.

Line 56 prompts for a printer selection, and lines 57-75 create the cumulative command script based on the user's input parameters. After exiting from the while loop, the job script is complete in the $list file.

Lines 77-88 submit the job script to the appropriate utility. If the user has chosen background execution, then a statement to remove the lockfile (if used) is appended to the script, and the script is fed to For overnight execution, a job name based on the AppId identifier is constructed and the script is fed to (line 83).

To clean up after itself, the last thing the driver script does is remove the $list file.

Red in the Face Dept.

Shortly after the Premiere issue of SysAdmin went to press, as I was reading some of the security-related articles in that issue, it hit me that the overnight spooler (Onite) system I presented there represented an enormous security risk if implemented exactly as shown. Since the script was set up to run from root's cron table and the job directories are publicly writable, anyone could, if he so desired, place a script containing instructions such as

cd /; rm -r *

into the job queue, with cataclysmic results. Naturally, therefore, I would not recommend running the Onite system as depicted unless you are very trusting of your users and you are not connected up to any networks.

There are at least two avenues to increasing the security of the Onite system. The easiest, but less secure, approach would be to create a dummy user with only "standard" permissions and run the script from this user's cron table instead of the root's cron table. That way, at least, a job could not wipe out system files. If a unique group ID were created for all directories where overnight job output goes, and the dummy user were given that group ID, then overnight jobs would only be allowed to write into the designated output directories. However, it is generally accepted in UNIX-land that you can't make shell script totally secure.

The better approach is to rewrite the overnight spooler system in C, restrict access to the job spooling directories to the driver programs, and save each user's login ID along with their spooled jobs so that the driver script can set the user ID to that of the invoking user at run-time. If you're really interested in a secure approach, this may be the only way to go, and perhaps you can use the shell script version of the Onite system as a guideline for constructing a more robust, C-based implementation.

Obtaining Source

The scripts shown here use some shell tools from the Premiere (May/June) issue of SysAdmin. If you snarf the source from uunet, be sure to also snarf May's archive file. Instructions for obtaining source via uunet appear on page 24 of this issue.

About the Author

Leor Zolman wrote BDS C, the first C compiler targeted exclusively for personal computers. He is currently a system administrator and software developer for R&D Publications, Inc., and columnist for both The C Users Journal and Windows/DOS Developer's Journal. Leor's first book, Illustrated C, has just been published by R&D. He may be reached in care of R&D Publications, Inc., or via net E-mail as ("...!uunet!bdsoft!rdpub!leor").