| 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.
bgrun.sh 
(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 bgrun.sh, 
outfile receives the standard error stream intermixed
with 
standard output. If the command is run as bgrun2.sh
(by having 
a link to bgrun.sh 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, bgrun.sh and bgrun2.sh work identically,
and all the comments that follow about bgrun.sh apply
equally 
to bgrun2.sh. 
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,
bgrun.sh 
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 bgrun.sh,
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 bgrun.sh. For overnight execution, a job name based
on the 
AppId identifier is constructed and the script is fed
to spoolonite.sh 
(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 onitego.sh 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 
onitego.sh 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 leor@rdpub.com ("...!uunet!bdsoft!rdpub!leor"). 
 
 
 |