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").
|