############################################################################### # cron -- perl implementation of cron for NT/95 ############################################################################### # # by Scott McMahan # version 1.0 beta prerelease # ############################################################################### ############################################################################### # Usage: cron # # Where is a valid crontab file in the usual UNIX format. # Defaults to file named "crontab" in current directory ############################################################################### # There are some configuration options after the comments. # They're probably okay as-is, but may be worth looking at. ############################################################################### ############################################################################### # Author's Notes: # Perl is obviously a prerequisite for using this program! # I chose perl because it was trivial to implement the file parser. # Just fire this up when you log in, and it'll keep running. # Warning: this is not a complete implementation of cron! # It's just the features I needed for my own task automation. It's about # 86% authentic, when you discount the differences in starting it and # security between UNIX and NT. # I do not support: # - ranges in entries (3,4,5,6 is okay, 3-6 is not) # - Daylight savings time (schedule stuff some other time besides # the overlap hour, or implement the logic and send me a copy!) # - embedded newlines in commands (run multiple commands from # a script and run the script from cron) # - usage is different (on the command line, a crontab file is # specified or the default "crontab" in the current directory # is opened) # - no security or allow/deny stuff (this cron assumes you're # running from a single logged-on NT account) # # Wish list: # [ ] Support for ranges would be nice # [ ] DST logic # [ ] In-crontab "pragmas" or command line switches for log file # and logging level # [ ] Leading 0 logic for date/times -- I wrote this once and will # have to find it ############################################################################### ############################################################################### # If you're not familiar with crontab files, they have lines # with the following: # # min hour monthday month weekday command # # - Lines beginning with '#' are comments and are ignored. # - The numeric entries can be separated by commas -- eg 1,2,3 # - Ranges for each are as follows: # minute (0-59), # hour (0-23), # day of the month (1-31), # month of the year (1-12), # day of the week (0-6 with 0=Sunday). ############################################################################### ############################################################################### # configuration section ############################################################################### # Note there are two levels of logging: normal logging, controlled by # the logfile variable, logs all commands executed and when they're # executed. The message facility prints messages about everything that # the program does, and is sent to the screen unless the msgfile # variable is set. Use the msgfile only in emergencies, as voluminous # output is generated (so much so that leaving msgfile active all night # could exhaust available disk space on all but the most capacious # systems) -- its primary purpose is for emergency debugging. # Due to the peculiar construction of the log functions, the log and # msg files can be the same file. This may or may not be good. $logfile = "cronlog.txt"; $msgfile = ""; # assign this only in emergency # end of configuration ############################################################################### # in_csl searches for an element in a comma separated list ############################################################################### sub in_csl { ($what, $csl) = @_; MSG("Processing CSL"); @a = split(/,/, $csl); for $x (@a) { MSG("is $what equal to item $x?"); if ($what eq $x) { return 1; } } return 0; } ############################################################################### # main program ############################################################################### if (defined $ARGV[0]) { ACT("using $ARGV[0] as crontab file\n"); $crontab = $ARGV[0]; } else { ACT("using default file crontab\n"); $crontab = "crontab"; } while (1) { open(F, "$crontab") or die "Can't open crontab; file $crontab: $!\n"; $line = 0; while () { $line++; if (/^$/) { MSG("blank line $line"); next; } if (/^#/) { MSG("comment on line $line"); next; } # mon = 0..11 and wday = 0..6 ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(time); $mon++; # to get it to agree with the cron syntax ($tmin, $thour, $tmday, $tmon, $twday, $tcommand) = split(/ +/, $_, 6); MSG("it is now $hour:$min:$sec on $mon/$mday/$year wday=$wday"); MSG("should we do $thour:$tmin on $tmon/$tmday/--, wday=$twday?"); $do_it = 0; # assume don't do it until proven otherwise # do it -- this month? if ( ($tmon eq "*") || ($mon == $tmon) || &in_csl($mon, $tmon)) { $do_it = 1; MSG("the month is valid"); } else { $do_it = 0; MSG("cron: the month is invalid"); } # do it -- this day of the month? if ( $do_it && ( ($tmday eq "*") || ($mday == $tmday) || &in_csl($mday, $tmday)) ) { $do_it = 1; MSG("the day of month is valid"); } else { $do_it = 0; MSG("the day of month is invalid"); } # do it -- this day of the week? if ( $do_it && ( ($twday eq "*") || ($wday == $twday) || &in_csl($wday, $twday)) ) { $do_it = 1; MSG("the day of week is valid"); } else { $do_it = 0; MSG("the day of week is invalid"); } # do it -- this hour? if ( $do_it && ( ($thour eq "*") || ($hour == $thour)|| &in_csl($hour, $thour) ) ) { $do_it = 1; MSG("the hour is valid"); } else { $do_it = 0; MSG("the hour is invalid"); } # do it -- this minute? if ( $do_it && ( ($tmin eq "*") || ($min == $tmin) || &in_csl($min, $tmin) ) ) { $do_it = 1; MSG("the min is valid"); } else { $do_it = 0; MSG("the minute is invalid"); } if ($do_it) { chop $tcommand; ACT("executing command <$tcommand>"); system ("start $tcommand"); } } close(F); MSG("***-----***"); sleep(60); } exit; ############################################################################### # Log activity ############################################################################### sub ACT { # mon = 0..11 and wday = 0..6 ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(time); $mon++; # to get it to agree with the cron syntax print "cron [$mon/$mday/$year $hour:$min]: @_\n"; # since we're appending the log, always open it only as little # as necessary, so if we crash the info will be there open(LOGFILE, ">>$logfile") or return; print LOGFILE "cron [$mon/$mday/$year $hour:$min]: @_\n"; close(LOGFILE); } ############################################################################### # routine to log messages # logs to screen unless $msgfile is set to a filename ############################################################################### sub MSG { # mon = 0..11 and wday = 0..6 ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(time); $mon++; # to get it to agree with the cron syntax print "cron*[$mon/$mday/$year $hour:$min]: @_\n"; return unless $msgfile; # since we're appending the log, always open it only as little # as necessary, so if we crash the info will be there open(LOGFILE, ">>$logfile") or return; print LOGFILE "cron*[$mon/$mday/$year $hour:$min]: @_\n"; close(LOGFILE); }