Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

One Minute Cron

30 views
Skip to first unread message

Rob Young

unread,
Sep 2, 2002, 12:18:53 AM9/2/02
to
In article , dav...@alpha2.mdx.ac.uk writes:
>
> At the start of August Rob Young posted that he would be sending a version of
> CRON which was modified to run with a 1 minute interval to Hunter to put
> up on his freeware site.
>
> I've just looked at Hunter's freeware site and can't see it.
>
> Has it been put up anywhere ?
>

Apologize profusely, again. I had not tested certain aspects
and spotted these lines:

$ component_min = 1 !for days, months and dow
$ if element_number .eq. 0 then component_min = 59 !minutes

Realizing that was something I added and not correctly, I fixed
it:

$ component_min = 1 !for days and months
$ if element_number .eq. 0 then component_min = 0 !minutes
$ if element_number .eq. 0 then component_max = 59 !minutes

Here it is, again. Go through the DCL line by line it may have wrapped.
I cleaned up one minor wrap cutting and pasting this. Study
it and test it on a non-production box/cluster.

I last used this in 1998 so may have removed something
in cleaning it up and putting it out here.

One interesting feature is it will kick start itself.
@cron START and if it isn't running as a detached process, it will
start itself.

I make no claim as to the usefulness of the following procedure.
It is not intended for use other than as a study. If you have
problems or questions about it, drop me a line.

If you make improvements to it, drop me an email and I will
stick them in my version. If it is already sweet, maybe I send
it on. If not, you can google it, I killed off the other
google version.

One final note, I have one major concern. If there were 300 lines
in your crontab.dat file, would it be able to process it in less
than a minute? So it could probably use a re-write in C. But
why bother? When VMS becomes Linux compliant, perhaps someone
can grab it? Or better yet, when COE gets wrapped back into
base VMS, Cron is already there, right?

Rob

Men with walkie-talkie I'm home again to you babe
Men with flashlights waving You know it makes me wonder
Up upon the tower Sittin' in the quiet slipstream
The clock reads daylight savin' Rollin' in the thunder

-- Neil Young


$ veri = f$verify(0)
$ goto bypass_comments
$! CRON.COM
$!
$! A modified version of a Cron taken from an old VAX Pro. Note
$! that the original worked on an hourly basis. This one works
$! on 5 (1 on Alpha) minute increments. It will round to the nearest
$! 5 (or 1) minutes to determine whether to run it or not.
$! Commands will be SPAWNED unless SUBMIT is present in the command.
$!
$! The author of this procedure is Upcasing the input command.
$!
$! Rob Young May 14, 1993
$!
$! Added make_invincible February 11, 1994
$! Added initialization September 15, 1995
$! Initialization will allow us to support crons
$! running from several nodes as it uses the nodename
$! as part of the crontab filename (if found uses it,
$! otherwise defaults to crontab.dat)
$!
$! Changed day_of_week to reflect Unix behavior:
$!
$! Sunday = 0 .. Saturday = 6
$!
$! Modified wait_minute so that Cron now can do 1 minute increments
$! on Alpha's. February 17, 1998
$!
$!
$! Several cleanups. Minute wraps did not work (58-3 , 57-5, etc.),
$! day wrap was not adjusted to reflect Unix behavior, it too was broken.
$! Changed the Cron startup to be not as convoluted. You learn a few
$! tricks.
$! August 30, 2002
$!
$! A VMS version of the UNIX cron facility. Reads the file
$! crontab.dat every 5 (1 Minute on Alpha) minutes and performs the commands
$! according to the following rules:
$!
$! each line contains 6 fields separated by tabs or spaces
$! The fields are minutes (0-60), hours (0-23), days (1-31), months (1-12),
$! day of week (1-7) and command line. (unix has a minutes field too.)
$! so does this one now --^^^^^^^^^^^^^
$!
$! The number parameters can be one of these: (can mix and match types)
$! single number - matches exactly
$! number-number - matches inclusive range, note that
$! 1-3 matches 1,2, or 3 but 22-2 (for hours) matches
$! 22,23,0,1,2. Time goes around.
$! number,number... - matches any in list
$! * - matches any
$!
$! All four time fields must match for command to be executed.
$!
$! lines starting with # are comments.
$!
$! The command line goes to the end of line or to a %. If there is a % then
$! it is taken as a EOL for the command, further elements separated by % are
$! taken as input data lines to the command. The command is executed as
$! a subprocess from the account running cron.com, except if it is a submit
$! command in which case it is executed immediately. Since this is usually
$! the system account you should consider this facility mostly for system
$! functions. You can do something for another user by making the command
$! a submit/user=somebody of a command file or a run/uic=something.
$!
$! logging of activity is done with writes to sys$output. This can
$! be directed to a file during debugging or send to nl: when cron
$! is run as a detached process.
$!
$!
$!
$! Make this routine undeletable
$!
$ bypass_comments:
$! call make_invincible
$ call initialization
$ if p1 .eqs. "START"
$ then
$ call start_cron
$ exit
$ endif
$ if f$mode() .eqs. "INTERACTIVE"
$ then
$ write sys$output ""
$ write sys$output "P1 = START to start Cron . . ."
$ write sys$output ""
$ exit
$ endif
$!
$! define some constants
$ delim = " " ! field delimiter, change here if desired
$ dash = "-"
$ comma = ","
$ percent = "%"
$ asterix = "*"
$!
$! begin, first get the current time components
$ again:
$ ! show time
$ time = f$time()
$ current_0 = f$integer(f$cvtime(time,,"minute"))
$ current_1 = f$integer(f$cvtime(time,,"hour"))
$ current_2 = f$integer(f$cvtime(time,,"day"))
$ current_3 = f$integer(f$cvtime(time,,"month"))
$ day_of_week_name = f$cvtime(time,,"weekday")
$ if day_of_week_name .eqs. "Sunday" then current_4 = 0
$ if day_of_week_name .eqs. "Monday" then current_4 = 1
$ if day_of_week_name .eqs. "Tuesday" then current_4 = 2
$ if day_of_week_name .eqs. "Wednesday" then current_4 = 3
$ if day_of_week_name .eqs. "Thursday" then current_4 = 4
$ if day_of_week_name .eqs. "Friday" then current_4 = 5
$ if day_of_week_name .eqs. "Saturday" then current_4 = 6
$!
$! write sys$output -
$! "Starting cron run at: hour - ''current_0' day - ''current_1' month -
$!''current_2' dow - ''current_3'"
$!
$! get a line from the file
$ open/read/error=wait crontab 'the_crontab_file'
$! process each line in turn
$ line_number = 0 ! used if neccessary for unique file name
$ next_line:
$ line_number = line_number + 1
$ read/end_of_file=wait crontab line
$! write sys$output "Reading line:"
$ if f$extract(0,1,line) .eqs. "#" then goto next_line ! comment line
$ line = f$edit(line,"compress,trim,upcase") !condition input data
$!
$! parse out line parts, all parts must match to get command done
$! loop through first five elements of line calling check_element
$! with parameters current and element. Check $status on return. If
$! any element doesn't match then get next line.
$ element_number = 0
$ next_element:
$ element = f$element(element_number,delim,line)
$ current = current_'element_number'
$ gosub check_element
$ if $status .ne. 1 then goto next_line !no match
$ element_number = element_number + 1
$ if element_number .le. 4 then goto next_element
$!
$! here if elements 0-4 all match, time to do the command
$! find command in line.
$ command = f$extract(f$locate(f$element(5,delim,line),line),200,line)
$ write sys$output "At ''f$time()' executing line: ", command
$ if f$locate(percent,command) .ne. f$length(command) then goto data_lines
$ if f$extract(0,3,command) .eqs. "SUB" then goto immediate_submit
$ spawn = "spawn"
$ spawn/nowait 'command'
$ goto next_line
$!
$immediate_submit: ! here if command is submit so do immediately
$ submit = "submit"
$ set noon
$'command'
$ set on
$ goto next_line
$!
$data_lines: ! here if command has data lines following
$ data_line_number = 0
$ open/write temp cron_line'line_number'.com
$next_data_line:
$ write temp f$element(data_line_number,percent,command)
$ data_line_number = data_line_number + 1
$ if f$element(data_line_number,percent,command) .nes. percent then -
goto next_data_line
$ close temp
$ spawn/nowait @cron_line'line_number'.com
$ goto next_line
$!
$! wait for next go around, always synch to next even hour,
$! this code to be sure that the procedure restarts within
$! a few seconds after even hour.
$ ! ^^^^^--- That was then this is now. . .
$wait:
$ close crontab
$ if f$search("cron_line*.com") .nes. "" then spawn/nowait purge cron_line*.com
$ time = f$time()
$!
$! Will wait until next minute boundary, 5 on VAX, 0 on Alpha waiting
$! current_seconds to get to the next minute.
$!
$ current_minute = f$integer(f$cvtime(time,,"minute"))
$ if current_minute .eq. 0
$ then
$ wait_minute = 4
$ else
$! do some modulo
$ wait_minute = 4 - (current_minute - ((current_minute / 5) * 5))
$ endif
$!
$! Make sure you make it to the next minute boundary on speed machines
$!
$ current_second = f$integer(f$cvtime(time,,"second"))
$!
$! write sys$output "Waiting
$!00:''f$string(wait_minute)':''f$string(60-current_s
$!econd)'"
$!
$ if is_alpha then wait_minute = 0 ! Watch this . . .
$ wait 00:'f$string(wait_minute)':'f$string(60-current_second)'
$ wait 00:00:01 ! Wait 1 more second to make sure we are at the next min bound.
$ goto again
$!
$! subroutine to check for matches, current is the part of the real
$! current date and time and element is the chunk of the crontab file.
$! Need to check element and cut up into smaller pieces if neccesary
$! and then compare to current. Note that current is an integer and
$! element (and it's pieces) is a string.
$check_element:
$! parse out parts of element, if there are any
$! check simplest case first, i.e. *
$ if f$locate(asterix,element) .eq. f$length(element) then goto no_asterix
$ return 1 ! asterix found, immediate match
$!
$ no_asterix:
$ sub_element_number = -1
$!
$ find_sub_element: ! looking for comma separated list
$ sub_element_number = sub_element_number + 1
$ sub_element = f$element(sub_element_number,comma,element)
$ if sub_element .eqs. comma then goto out_of_sub_elements
$!
$ left_side_of_dash = f$element(0,dash,sub_element)
$ if left_side_of_dash .nes. sub_element then goto range_element
$ if current .eq. f$integer(sub_element) then return 1 !found a match
$ goto find_sub_element
$!
$ range_element: ! sub_element has a dash in it
$ right_side_of_dash = f$element(1,dash,sub_element)
$! if left is greater than right then things are not_straightforward
$ if f$integer(left_side_of_dash) .gt. f$integer(right_side_of_dash) -
then goto not_straightforward
$ if current .ge. f$integer(left_side_of_dash) .and. -
current .le. f$integer(right_side_of_dash) then return 1 ! in the range
$ goto find_sub_element
$!
$not_straightforward: !because time is circular at least in measurement
$! example: a range of days such as 27-3. this is taken to mean
$! any day from 27-31 or 1-3. Need to see if current falls into
$! 27-max_day_of_month or min_day_of_month-3. Note that each time
$! component has a different max and min. Set them now.
$ component_min = 1 !for days and months
$ if element_number .eq. 0 then component_min = 0 !minutes
$ if element_number .eq. 0 then component_max = 59 !minutes
$ if element_number .eq. 1 then component_min = 0 !hours
$ if element_number .eq. 1 then component_max = 23 !hours
$ if element_number .eq. 2 then component_max = 31 !days of month
$ if element_number .eq. 3 then component_max = 12 !months of year
$ if element_number .eq. 4 then component_min = 0 !day of week
$ if element_number .eq. 4 then component_max = 6 !day of week
$ if (current .ge. f$integer(left_side_of_dash) .and. -
current .le. component_max) .or. -
(current .ge. component_min .and. -
current .le. f$integer(right_side_of_dash)) then return 1 !whew!
$goto find_sub_element
$!
$out_of_sub_elements:
$return 3 ! ran through all sub_elements with any match
$! end of subroutine check_element !
$!
$ exit
$!
$! New subroutine to make CRON invincible
$!
$ make_invincible:
$ subroutine
$ !
$ ! This routine not supported in public version . . .
$ !
$ exit
$ path = f$trnlnm("the_base","the_table") - ".]"
$ if f$search("''path'.bin]undeletable.exe") .eqs. "" then -
exit
$ !
$ pid=f$getjpi("","pid")
$ open/write/error=closeup/end=closeup pidlog 'path'.log]_'pid'.log
$ write pidlog "$ undeletable :== $''path'.bin]undeletable.exe"
$ write pidlog "$ undeletable "
$ write pidlog "''pid'"
$ close pidlog
$ type 'path'.log]_'pid'.log
$ @'path'.log]_'pid'.log
$ delete/nolog/noconfirm 'path'.log]_'pid'.log;
$ endsubroutine


$ initialization:
$ subroutine
$ this_node == f$edit(f$getsyi("nodename"),"trim")
$ this_dir == "''f$element(0,"]",f$environment("PROCEDURE"))']"
$ set default 'this_dir'
$ if f$search("''this_node'_crontab.dat") .nes. ""
$ then
$ the_crontab_file :== 'this_node'_crontab.dat
$ else
$ the_crontab_file :== crontab.dat
$ endif
$ is_alpha == 'f$getsyi("hw_model")' .gt. 1024
$ !
$ endsubroutine


$ start_cron:
$ subroutine
$ ctx = ""
$ temp = F$CONTEXT("PROCESS", ctx, "MODE","OTHER","EQL")
$ loop:
$ pid = f$pid(ctx)
$ if pid .eqs. "" then goto start_cron
$ prcnam = f$getjpi(pid,"prcnam")
$ if prcnam .eqs. "''this_node' Cron"
$ then
$ write sys$output ""
$ write sys$output "''This_Node' Cron is already running . . ."
$ write sys$output ""
$ exit
$ endif
$ goto loop
$ !
$ START_CRON:
$ logdir = this_dir - "]" + ".logs]"
$ if f$search("''this_dir'logs.dir") .eqs. ""
$ then
$ create/direct [.logs]
$ endif
$ close/nolog wlog
$ open/write wlog 'logdir'cron_'this_node'_start.tmp
$ write wlog "$ run /uic = [1,4] - "
$ write wlog "/priority = 6 - "
$ write wlog "/input = ''this_dir'cron.com - "
$ write wlog "/output = ''logdir'''this_node'_cron.log - "
$ write wlog "/error = ''logdir'''this_node'_cron.err - "
$ write wlog "/process_name = ""''this_node' Cron"" - "
$ write wlog "sys$system:loginout "
$ close/nolog wlog
$ @'logdir'cron_'this_node'_start.tmp
$ purge/keep=2 'logdir'cron_'this_node'_start.tmp
$ endsubroutine


Sample Crontab.dat

# crontab.dat
# Use 5 minute increments on VAX, 1 minute increments on Alpha
# minute, hour, day, month, dow, command
#
# 58 minutes to the hour through 3 minutes after the hour, showtime
#
58-3 * * * * submit/queue=sys$batch/noprint/log disk1:[youngr]showtime.com
#
# At 11:18 p.m. on Friday August 30th , showtime
#
18 23 30 8 5 submit/queue=sys$batch/noprint/log disk1:[youngr]showtime.com
#
#
#0 5 * * 5,6 purge/nolog/keep=5 the_base:[log] ! Friday Saturday 5 a.m. purge
#
# end crontab.dat


To start Cron simply:

$ @device:[directory]cron START

0 new messages