home *** CD-ROM | disk | FTP | other *** search
/ Frozen Fish 1: Amiga / FrozenFish-Apr94.iso / bbs / alib / d8xx / d801 / cybercron.lha / CyberCron / RexxScripts / at.rexx next >
OS/2 REXX Batch file  |  1993-01-24  |  16KB  |  723 lines

  1. /*rx
  2.  *  at.rexx --- simulate UNIX at command.  For use with CyberCron.
  3.  *  By Loren J. Rittle (rittle@comm.mot.com) - Tue May  5 01:47:11 1992
  4.  *  Updated to work with any AmigaOS shell   - Wed May  6 03:32:00 1992
  5.  *
  6.  *  Copyright © 1992  Loren J. Rittle
  7.  *  Use as you will, just document your changes and keep my copyright
  8.  *  notice intact.  Feel free to mail enhancements to me.
  9.  *
  10.  *  Modifications © 1992 by Graham Walter (gwalter@gwalter.demon.co.uk)
  11.  *  Following added:
  12.  *    Ability to specify date, incremental time periods
  13.  *    Remove (-r option) jobs
  14.  *    List (-l option)
  15.  *      Schedule (-s option) - used to reschedule jobs if
  16.  *        CyberCron is restarted (due to eg reboot)
  17.  *
  18.  *  Usage:
  19.  *    at [-q[ac-z]] time [day [month [year]] [+ increment timeperiod]
  20.  *    <shell script>
  21.  *    <EOF>
  22.  *
  23.  *    at -qb
  24.  *    <shell script>
  25.  *    <EOF>
  26.  *
  27.  *    at -l [<task name as given at issue time, or from list>]
  28.  *      [for list current 'at' job(s)]
  29.  *
  30.  *    at -r <task name as given at issue time, or from list>
  31.  *      [remove an 'at' job as shown in list]
  32.  *
  33.  *    at -s
  34.  *      [schedule any unscheduled jobs]
  35.  */
  36.  
  37. /* USER MODIFIABLE DEFAULT, EDIT TO TASTE */
  38.  
  39. /* AT_FILES should be somewhere that only at writes to.
  40.    T: is not a suitable location, IMHO.  In the future, at.rexx
  41.    and some startup code magic might know how to requeue jobs
  42.    that should have been run while power was off or after a crash
  43.    occurred.  To prepare for this, AT_FILES should be on a real
  44.    file system. */
  45. AT_FILES = 's:at/'
  46. CRONLOG  = "T:CronLog"
  47.  
  48. /* NO GENERAL USER MODIFIABLE PARTS BELOW THIS COMMENT. */
  49.  
  50. /* Default queue to place jobs in.  Remember queue 'b' is special. */
  51. QUEUE = 'a'
  52.  
  53. HOUR = '00'
  54. MINUTE = '00'
  55.  
  56. options results
  57.  
  58. parse arg Parameters
  59. parse arg option line xline
  60.  
  61. if left( option, 2 ) = "-l"
  62. then do
  63.     call ListAt line
  64.     exit
  65. end
  66.  
  67. if left( option, 2 ) = "-r"
  68. then do
  69.     call RemoveAt line
  70.     exit
  71. end
  72.  
  73. if left( option, 2 ) = "-s"
  74. then do
  75.     call ReSchedule line
  76.     exit
  77. end
  78.  
  79. call ParseParameters Parameters
  80.  
  81. do 100 until ~ exists( AT_FILES || ID )
  82.     ID = GenerateId()
  83. end
  84.  
  85. if exists( AT_FILES || ID )
  86. then call ErrorExit 'at: can''t find unused ID'
  87.  
  88. scriptfile = AT_FILES || ID
  89.  
  90. if ~open('sfh', scriptfile, 'W')
  91. then call ErrorExit 'at: can''t open' scriptfile 'for output'
  92.  
  93. call writeln('sfh', '; ****** Start of Prologue *******')
  94.  
  95. call writeln('sfh', '; ****** At Job' ID '      *******')
  96.  
  97. call writeln('sfh', 'cd' '22'x||pragma('D')||'22'x)
  98. call writeln('sfh', 'stack' pragma('S', 4000))
  99.  
  100. if left(address(), 4) == 'WSH_'
  101. then do
  102.     /*
  103.      *  If we have a WShell under us, then try to propagate
  104.      *  local environment variables.  If you don't, I'm not
  105.      *  sorry for you, read comments below. :-)
  106.      */
  107.     'set | execio stem VARS.'
  108.     do i = 1 to VARS.0
  109.           if find( "PROCESS RC RESULT2", upper( word(VARS.i, 1) ) ) = 0
  110.     then call writeln('sfh', 'set' VARS.i)
  111.     end
  112. end
  113. else say 'warning: WShell not present under your ARexx, local environment variables can''t be propagated'
  114.  
  115. call writeln('sfh', '; ******** End of Prologue *******')
  116.  
  117. line = readln( stdin )
  118.  
  119. do while ~ eof( stdin )
  120.     call writeln('sfh', line)
  121.     line = readln(stdin)
  122. end
  123.  
  124. call writeln('sfh', '; ****** Start of Epilogue *******')
  125. call writeln('sfh', 'run <nil: >nil: delete' scriptfile)
  126. call close('sfh')
  127.  
  128. address command 'protect' scriptfile '+s'
  129.  
  130. USER = getenv('USER')
  131. if left(address(), 4) == 'WSH_' then
  132.   do
  133.     /*
  134.      *  If we have a WShell under us, then try to get a local
  135.      *  environment variable with the name USER to override
  136.      *  the global USER setting.
  137.      *  If you don't have a WShell under you, then why not? :-)
  138.      *  Basically, you are less than human if you don't have a WShell
  139.      *  under you.  If everyone had a WShell under their ARexx,
  140.      *  I wouldn't have to write such weird ARexx code.  I hate you
  141.      *  if you don't WShell under your ARexx, cause you make my life
  142.      *  Hell!  Does anyone read these comments, or I'm I wasting
  143.      *  my time here? :-)
  144.      */
  145.     /* AmigaDOG braindamage: */
  146.     'get USER >nil:'
  147.     if rc == 0 then
  148.       'get USER | execio var USERX'
  149.     else
  150.       USERX = ''
  151.     if USERX ~= '' then
  152.       USER = USERX
  153.   end
  154. else
  155.   say 'warning: WShell not present under your ARexx, local environment variable USER can''t be checked'
  156.  
  157. if USER == ''
  158. then do
  159.     say 'warning: USER environment variable not set, mail will be sent to root'
  160.     USER = 'root'
  161. end
  162.  
  163. address command "filenote" scriptfile '"'USER'.'QUEUE'.'execdate'-'HOUR||MINUTE'"'
  164.  
  165. call ScheduleJob ID User Queue execdate Hour||Minute
  166.  
  167. exit
  168.  
  169. /* ***************************************************************** */
  170. /*                             Functions                             */
  171. /* ***************************************************************** */
  172.  
  173. ParseParameters:    /* Parse the parameters specified to At */
  174.  
  175. if left(option, 2) == '-q'
  176. then do
  177.  
  178.     if length(option) ~= 3
  179.     then call ErrorExit 'at: invalid ''-q'' parameter'
  180.  
  181.     QUEUE = right(option, 1)
  182.  
  183.     if pos(QUEUE, xrange('a','z')) == 0
  184.     then call ErrorExit 'at: invalid queue name,' QUEUE
  185.  
  186.     parse arg option line
  187. end
  188. else parse arg line
  189.  
  190. IncrementPart = ""
  191. parse var line line '+' IncrementPart
  192. parse var line line xline
  193. parse var IncrementPart Increment Unit .
  194.  
  195. UnitNo = 1    /* default to minutes */
  196.  
  197. if Unit ~= ""
  198. then do
  199.     AbbrevUnit = upper( left( Unit, 3 ) )
  200.  
  201.     UnitNo = find( "MIN HOU DAY WEE MON YEA", AbbrevUnit )
  202.  
  203.     if UnitNo = 0
  204.     then call ErrorExit "at: Invalid increment unit:" Unit
  205. end
  206.  
  207. if Increment ~= ""
  208. then do
  209.     if ~ datatype( Increment, "N" )
  210.     then call ErrorExit "at: Invalid increment:" Increment
  211. end
  212.  
  213. if QUEUE == 'b'
  214. then do
  215.  
  216.     if line ~= ''
  217.     then call ErrorExit 'at: time can''t be given for use with b[atch] queue'
  218.  
  219.     HOUR = '*'
  220.     MINUTE = '*'
  221. end
  222. else do
  223.  
  224.     if line == ''
  225.     then do
  226.     say 'at: time must be given for use with' QUEUE 'queue'
  227.     exit 10
  228.     end
  229.  
  230.     nowdate = date( 's' )
  231.     now = time()
  232.     nowhour = left( now, 2 )
  233.     nowminute = substr( now, 4, 2 )
  234.  
  235.     if upper( line ) ~= "NOW"
  236.     then do
  237.  
  238.         if length(line) > 4 | ~datatype(line, 'N')
  239.         then call ErrorExit 'at: "'line'" is not a valid time, must be of form: H, HH, HMM, HHMM'
  240.  
  241.         if length(line) > 2
  242.         then do
  243.         if length( line ) < 4
  244.         then line = "0"line
  245.         MINUTE = right(line, 2)
  246.         HOUR = left(line, length(line) - 2)
  247.         end
  248.         else HOUR = line
  249.  
  250.         if MINUTE < 0 | MINUTE > 59 | HOUR < 0 | HOUR > 23
  251.         then call ErrorExit 'at: "'line'" is not in the range of a valid time'
  252.     end
  253.     else do
  254.         HOUR = nowhour
  255.         MINUTE = nowminute
  256.     end
  257.  
  258. /*trace ?i*/
  259.  
  260.     execdate = nowdate
  261.  
  262.     if xline ~= ""
  263.     then do
  264.     parse var xline d1 d2 d3 .
  265.  
  266.     if d2 ~= "" & ~ datatype( d1, "N" ) & datatype( d2, "N" )
  267.     then do
  268.         temp = d1
  269.         d1 = d2
  270.         d2 = temp
  271.     end
  272.  
  273.     if d3 ~= ""
  274.     then do
  275.         if ~ datatype( d3, "N" ) | length( d3 ) > 4
  276.         then call ErrorExit "at: Invalid year -" d1
  277.  
  278.         if d3 < 100 & d3 >= substr( nowdate, 3, 2 )
  279.         then execdate = "19"right( "00"d3, 2 ) || substr( execdate, 5 )
  280.         else execdate = overlay( d3, "2000", 5 - length( d3 ) ) || substr( execdate, 5 )
  281.     end
  282.  
  283.     if d2 ~= ""
  284.     then do
  285.         Month = find( "JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC", upper( d2 ) )
  286.  
  287.         if Month = 0
  288.         then call ErrorExit "at: Invalid month -" d2
  289.  
  290.         execdate = left( execdate, 4 ) || right( "00"Month, 2 ) || right( execdate, 2 )
  291.     end
  292.  
  293.     if d1 ~= ""
  294.     then do
  295.         if ~ datatype( d1, "N" )
  296.         then call ErrorExit "at: Invalid day of month -" d1
  297.  
  298.         execdate = left( execdate, 6 ) || right( "00"d1, 2 )
  299.     end
  300.     end
  301.  
  302.     if Increment ~= ""
  303.     then do
  304.  
  305. /*trace ?i*/
  306.         if UnitNo <= 4
  307.         then do
  308.             UDayno = date( 'i', execdate, 's' )
  309.  
  310.             if UnitNo = 1
  311.             then Minute = Minute + Increment
  312.             else if UnitNo = 2
  313.             then Hour = Hour + Increment
  314.             else if UnitNo = 3
  315.             then UDayno = UDayno + Increment
  316.             else if UnitNo = 4
  317.             then UDayno = UDayno + Increment * 7
  318.  
  319.                 Hour = right( "00" || Hour + Minute % 60, 2 )
  320.                 Minute = right( "00" || Minute // 60, 2 )
  321.             UDayno = Udayno + Hour % 24
  322.             execdate = date( 's', Udayno, 'i' )
  323.         end
  324.         else do
  325.             Year = left( execdate, 4 )
  326.  
  327.             if UnitNo = 5
  328.             then do
  329.                 Month = substr( execdate, 5, 2 ) + Increment
  330.                 Year = Year + ( Month - 1 ) % 12
  331.                 Month = right( "00" || ( Month - 1 ) // 12 + 1, 2 )
  332.                 execdate = Year || Month || substr( execdate, 7 )
  333.             end
  334.             else do
  335.                 execdate = Year + Increment || substr( execdate, 5 )
  336.             end
  337.         end
  338.     end
  339.  
  340.     if execdate'@'Hour':'Minute <= nowdate'@'nowhour':'nowminute
  341.     then do
  342.     if xline = ""
  343.     then execdate = date( 's', date('i') + 1 )
  344.     else if d2 = ""
  345.     then do
  346.         if substr( execdate, 5, 2 ) = 12
  347.         then execdate = left( execdate, 4 ) + 1 || "01" || right( execdate, 2 )
  348.         else execdate = left( execdate, 4 ) || right( "00"substr( execdate, 5, 2 ) + 1, 2 ) || right( execdate, 2 )
  349.     end
  350.     else if d3 = ""
  351.     then do
  352.         execdate = left( execdate, 4 ) + 1 || substr( execdate, 5 )
  353.     end
  354.     else call ErrorExit "at: Too late for" date("n", execdate, "s") Hour":"Minute
  355.     end
  356.  
  357.     Day = substr( execdate, 7, 2 )
  358.     Month = substr( execdate, 5, 2 )
  359.     Days = word( "31 28 31 30 31 30 31 31 30 31 30 31", Month )
  360.  
  361.     if Month = 2 & left( execdate, 4 ) // 4 = 0
  362.     then Days = 29
  363.  
  364.     if day > Days & UnitNo = 5
  365.     then do
  366.         day = Days
  367.         execdate = left( execdate, 6 ) || day
  368.     end
  369.  
  370.     if day < 1 | day > Days
  371.     then call ErrorExit "at: Invalid day of month -" d1
  372.  
  373. /*say execdate Hour":"Minute*/
  374. /*exit 20*/
  375.  
  376. end
  377.  
  378. return
  379.  
  380. /* ***************************************************************** */
  381.  
  382. ErrorExit:
  383.  
  384. parse arg message
  385.  
  386. say message
  387.  
  388. exit 10
  389.  
  390. /* ***************************************************************** */
  391.  
  392. GetCronEvents:
  393.  
  394. /*trace ?i*/
  395.  
  396. events = ""
  397.  
  398. if show( "ports", CYBERCRON )
  399. then do
  400.     address CYBERCRON 'LIST_EVENTS'
  401.     eventlist = result
  402.     numwords = words(eventlist)
  403.  
  404.     if (numwords > 0) & (word(eventlist,1) ~= "<None>")
  405.     then do
  406.  
  407.         do j = 1 to numwords
  408.             event = word( eventlist, j )
  409.             address CYBERCRON 'SHOW_EVENT' event
  410.             events = events || '0a'x || result
  411.         end
  412.     end
  413. end
  414.  
  415. if events ~= ""
  416. then events = substr( events, 2 )
  417.  
  418. return events
  419.  
  420. /* ***************************************************************** */
  421.  
  422. ListJob: procedure expose AT_FILES CRONLOG NJobs Events Header
  423.  
  424. parse arg Job
  425.  
  426. if ~ datatype( NJobs, "N" )
  427. then NJobs = 0
  428.  
  429. JobInfo = statef( AT_FILES || Job )
  430.  
  431. if JobInfo = ""
  432. then do
  433.     say "Job" Job "not found"
  434.     return
  435. end
  436.  
  437. parse var JobInfo . . . . fdays fmins fticks User '.' queue '.' edate '-' etime
  438.  
  439. ftime = right( "0000"etime, 4 )
  440. ftime = left( ftime, 2)":"right( ftime, 2 )
  441.  
  442. User = strip( User )
  443.  
  444. if index( Events, AT_FILES || Job ) ~= 0
  445. then do
  446.  
  447.     jsched = "Scheduled"
  448.     if Queue = 'b'
  449.     then do
  450.         if bMax = "BMAX"
  451.         then do
  452.             address CYBERCRON GET_QUEUE_MAX b
  453.             bMax = rc    /* Cybercron passes answer in rc ! */
  454.         end
  455.  
  456.         if bMax = 0
  457.         then jsched = "Held"
  458.         else jsched = "Waiting"
  459.     end
  460.  
  461.     jstat = ""
  462.     NJobs = NJobs + 1
  463. end
  464. else do
  465.     JobStartInfo = SearchCronLog( Job )
  466.  
  467.     if JobStartInfo = ""
  468.     then do
  469.         jsched = "NOT SCHEDULED"
  470.         jstat = "***"
  471.     end
  472.     else do
  473.         parse var JobStartInfo '(' JobStartDate ')' . JobNo jstart JobFileName
  474.         jsched = jstart JobStartdate
  475.     end
  476. end
  477.  
  478. if Header ~= ""
  479. then do
  480.     say "User             Job  Q Date        Time  Status"
  481.     Header = ""
  482. end
  483.  
  484. if Queue ~= 'b'
  485. then jexec = date("n", edate, "s") ftime
  486. else jexec = "                 "
  487.  
  488.  
  489. say left( User, 16 ) Job Queue jexec jsched
  490.  
  491. return
  492.  
  493. /* ***************************************************************** */
  494.  
  495. ListAt: procedure expose AT_FILES CRONLOG
  496.  
  497. parse arg ID
  498.  
  499. NJobs = 0
  500.  
  501. Events = GetCronEvents()
  502.  
  503. if ID ~= ""
  504. then do
  505.     Job = right( "0000"ID, 4 )
  506.     call ListJob( Job )
  507.     if open( fh, AT_FILES || Job, "r" )
  508.     then do
  509.         say "-------------------------------------------------"
  510.         do while ~eof( fh )
  511.             Line = readln( fh )
  512.             if Line = '; ******** End of Prologue *******'
  513.             then break
  514.         end
  515.  
  516.         do while ~eof( fh )
  517.             Line = readln( fh )
  518.             if Line = '; ****** Start of Epilogue *******'
  519.             then break
  520.  
  521.             say Line
  522.         end
  523.  
  524.         call close fh
  525.     end
  526.  
  527.     exit
  528. end
  529.  
  530. Jobs = showdir( AT_FILES )
  531.  
  532. /*trace ?i*/
  533.  
  534. do i = 1 to words( Jobs )
  535.     Job = word( Jobs, i )
  536.  
  537.     if upper( Job ) ~= "AT.SEQ"
  538.     then call ListJob Job
  539. end
  540.  
  541. say NJobs "jobs waiting"
  542.  
  543. return
  544.  
  545. /* ***************************************************************** */
  546.  
  547. ScheduleJob: procedure expose AT_FILES
  548.  
  549. parse arg Job User Queue edate etime .
  550.  
  551. if Queue ~= 'b'
  552. then do
  553.     Day    = right( edate, 2 )
  554.     Month  = substr( edate, 5 , 2 )
  555.     Hour   = left( etime, 2 )
  556.     Minute = right( etime, 2 )
  557. end
  558. else do
  559.     address CYBERCRON get_queue_max b
  560.     qbmax = rc            /* Cybercron passes answer in rc ! */
  561.     if qbmax = 0
  562.     then bStatus = "awaiting resources"
  563.     else bStatus = "will be scheduled soon"
  564.  
  565.     address CYBERCRON 'ADD_EVENT :MAILUSER' USER ':EXECONCE :OBEYQUEUE' QUEUE '* * * * *' AT_FILES || Job
  566.     if rc = 0
  567.     then say "Job" Job bStatus
  568.     else say "Job" Job "schedule failed"
  569.  
  570.     return
  571. end
  572.  
  573. nowdate = date( 's' )
  574. now = time()
  575. nowtime = left( now, 2 ) || substr( now, 4, 2 )
  576.  
  577. yearstime = left( nowdate, 4 ) + 1 || substr( nowdate, 5 )
  578.  
  579. if edate'@'etime <= nowdate'@'nowtime
  580. then do
  581.     say "at: Warning: job" Job "not scheduled - too late"
  582. end
  583. else if edate'@'etime < yearstime'@'now
  584. then do
  585.     address CYBERCRON 'ADD_EVENT :MAILUSER' USER ':EXECONCE :OBEYQUEUE' QUEUE MINUTE HOUR DAY MONTH '*' AT_FILES || Job
  586.     if rc = 0
  587.     then say "Job" Job "scheduled for" date( "n", edate, "s" ) Hour":"Minute
  588.     else say "Job" Job "schedule failed"
  589. end
  590. else do
  591.     say "at: Warning: job" Job "not scheduled - too far in the future"
  592. end
  593.  
  594. return
  595.  
  596. /* ***************************************************************** */
  597.  
  598. ReSchedule: procedure expose AT_FILES
  599.  
  600. parse arg ID
  601.  
  602. Jobs = showdir( AT_FILES )
  603.  
  604. Events = GetCronEvents()
  605.  
  606. do i = 1 to words( Jobs )
  607.     Job = word( Jobs, i )
  608.  
  609.     if upper( Job ) = "AT.SEQ"
  610.     then iterate
  611.  
  612.     if index( Events, AT_FILES || Job ) = 0
  613.     then do
  614.         JobInfo = statef( AT_FILES || Job )
  615.  
  616.         if JobInfo = ""
  617.         then do
  618.             say "Job" Job "not found"
  619.             iterate
  620.         end
  621.  
  622.         parse var JobInfo . . . . fdays fmins fticks User '.' queue '.' edate '-' etime
  623.  
  624.         call ScheduleJob Job User Queue edate etime
  625.     end
  626. end
  627.  
  628. return
  629.  
  630. /* ***************************************************************** */
  631.  
  632. RemoveAt: procedure expose AT_FILES
  633.  
  634. parse arg Job
  635.  
  636. Job = right( "0000"Job, 4 )
  637.  
  638. if ~ exists( AT_FILES || Job )
  639. then call ErrorExit "at: job" Job "not found."
  640.  
  641. Events = GetCronEvents()
  642.  
  643. do while Events ~= ""
  644.     parse var Events EventLine '0a'x Events
  645.  
  646.     if index( EventLine, AT_FILES || Job ) ~= 0
  647.     then break
  648.  
  649.     EventLine = ""
  650. end
  651.  
  652. if EventLine ~= ""
  653. then do
  654.     parse var EventLine EventID .
  655.     address cybercron delete_event EventID
  656.  
  657.     if rc ~= 0
  658.     then say "at: warning - couldn't remove CyberCRON event"
  659. end
  660.  
  661. call delete AT_FILES || Job
  662.  
  663. say "Job" Job "removed."
  664.  
  665. return
  666.  
  667. /* ***************************************************************** */
  668.  
  669. GenerateID: procedure expose AT_FILES
  670.  
  671. if open( fh, AT_FILES || "at.seq", "r" )
  672. then do
  673.     ID = readln( fh )
  674.     call close fh
  675. end
  676. else ID = 0
  677.  
  678. if ID > 9999
  679. then ID = 0
  680.  
  681. ID = ID + 1
  682.  
  683. if open( fh, AT_FILES || "at.seq", "w" )
  684. then do
  685.     call writeln( fh, id )
  686.     call close fh
  687. end
  688.  
  689. return right( "0000"ID, 4 )
  690.  
  691. /* ***************************************************************** */
  692.  
  693. SearchCronLog: procedure expose AT_FILES CRONLOG
  694.  
  695. parse arg ID
  696.  
  697. /*trace ?i*/
  698.  
  699. if ~ exists( CRONLOG )
  700. then return ""
  701.  
  702. /*"execio" read CRONLOG locate AT_FILES || ID var Startinfo*/
  703.  
  704. tfile = "T:at-info-" || ID
  705.  
  706.  
  707. "search >"tfile CRONLOG AT_FILES || ID
  708.  
  709. if rc = 0
  710. then do
  711.     if open( fht, tfile, "r" )
  712.     then do
  713.         Startinfo = readln( fht )
  714.         call close( fht )
  715.     end
  716.     else Startinfo = ""
  717. end
  718. else Startinfo = ""
  719.  
  720. call delete( tfile )
  721.  
  722. return Startinfo
  723.