home *** CD-ROM | disk | FTP | other *** search
/ Club Amiga de Montreal - CAM / CAM_CD_1.iso / files / 201.lha / DCron_v1.05 / dcron.c < prev    next >
C/C++ Source or Header  |  1988-12-27  |  15KB  |  625 lines

  1.  
  2. /*
  3.  *  DCRON.C  V1.05
  4.  *
  5.  *  Limitations:    The only RUN commands allowed are of the form:
  6.  *
  7.  *            RUN >nil: <nil: <command>
  8.  *
  9.  *  -Wakeup once every minute & check the modify date for S:CRONTAB
  10.  *   and to check if the DateStamp() has been re-set.
  11.  *
  12.  *  -Attempt to be smart about checking s:crontab (scan the file
  13.  *   as little as possible).
  14.  *
  15.  *  -Handles massive Date changes and crontab modifications
  16.  *
  17.  *  DCRON [-d] logfile
  18.  */
  19.  
  20. #include <stdio.h>
  21. #include <local/typedefs.h>
  22.  
  23. #define CRONTAB "s:crontab"
  24. #define SIGS    (SIGBREAKF_CTRL_C|SIGBREAKF_CTRL_D|SIGBREAKF_CTRL_E|SIGBREAKF_CTRL_F)
  25.  
  26. #define ACTION_READWRITE    1004
  27. #define ACTION_FIND_INPUT   1005        /* various ACTION's supported   */
  28. #define ACTION_FIND_OUTPUT  1006
  29. #define ACTION_END        1007
  30. #define ACTION_EXAMINE        23
  31. #define ACTION_EXAMINENEXT  24
  32. #define ACTION_LOCATE        8
  33. #define ACTION_FREELOCK     15
  34. #define ACTION_COPYDIR        19
  35.  
  36. #define DOS_FALSE    0
  37. #define DOS_TRUE     -1
  38.  
  39. #define BTOC(bptr)  ((void *)((long)(bptr) << 2))
  40.  
  41. typedef struct FileHandle FH;
  42.  
  43. typedef struct {
  44.     short min;
  45.     short hour;
  46.     short day;
  47.     short month;
  48.     short dow;
  49. } MHDMW;
  50.  
  51. typedef struct {
  52.     short entry[5];
  53. } MHDMWARY;
  54.  
  55. typedef struct {
  56.     MHDMW   Beg;
  57.     MHDMW   End;
  58. } CDATE;
  59.  
  60. typedef struct {
  61.     char min[60];
  62.     char hour[24];
  63.     char day[32];
  64.     char month[13];
  65.     char dow[7];
  66. } CARRY;
  67.  
  68. extern    int    Enable_Abort;
  69. extern    APTR    DeviceProc();
  70.  
  71. DATESTAMP   BegDate;        /*    Start and end range for     */
  72. DATESTAMP   EndDate;        /*     file compare            */
  73. DATESTAMP   ModDate;        /*    Check if date modified        */
  74. DATESTAMP   TabDate;        /*    Check if crontab modified   */
  75.  
  76. IOT    Iot;            /*    Best guess at next timeout        */
  77. IOT    Iotmin;         /*    1 minute T.O. (date/file modified test  */
  78. PORT    *TPort;         /*    Collector plate for IO requests     */
  79. short    FatalError;        /*    Uh oh, can't recover                    */
  80. short    NextTimeout;        /*    # minutes till next timeout        */
  81. char    *LogFile;
  82. char    XDebug;
  83. long    NilFH;
  84. short    CronFileExists = 1; /*    Does the crontab file exist?        */
  85.  
  86. void    logmessage();
  87.  
  88. void
  89. main(ac, av)
  90. short ac;
  91. char *av[];
  92. {
  93.     PROC *proc = FindTask(NULL);
  94.  
  95.     Enable_Abort = 0;
  96.     {
  97.     register short i;
  98.     short j = 0;
  99.  
  100.     for (i = 1; i < ac; ++i) {
  101.         register char *ptr = av[i];
  102.         if (*ptr != '-') {
  103.         switch(j++) {
  104.         case 0:
  105.             LogFile = ptr;
  106.             break;
  107.         }
  108.         continue;
  109.         }
  110.         while (*++ptr) {
  111.         switch(*ptr) {
  112.         case 'd':
  113.             ++XDebug;
  114.             break;
  115.         default:
  116.             WriteStr(Output(), "bad option\n");
  117.             goto fail;
  118.         }
  119.         }
  120.     }
  121.     }
  122.     if (!LogFile) {
  123. fail:
  124.     WriteStr(Output(), "DCron [-d] Logfile\n");
  125.     WriteStr(Output(), "DCron, V1.05\n");
  126.     exit(1);
  127.     }
  128.     if (OpenDevice("timer.device", 0, &Iot, UNIT_VBLANK)) {
  129.     logmessage("Unable to open timer.device\n");
  130.     exit(1);
  131.     }
  132.     if (!DeviceProc("NULL:")) {
  133.     logmessage("NULL: device required for dcron to run\n");
  134.     WriteStr(Output(), "NULL: device required to run\n");
  135.     exit(1);
  136.     }
  137.     proc->pr_ConsoleTask = DeviceProc("NULL:");
  138.     fclose(stderr);
  139.     logmessage("Startup: V1.05 (dist: 22 December 1988)\n");
  140.  
  141.     NilFH = Open("null:", 1006);
  142.     DateStamp(&EndDate);
  143.     DateStamp(&ModDate);
  144.     TPort = CreatePort(NULL,0);
  145.     Iot.tr_time.tv_secs = 1;
  146.     Iot.tr_time.tv_micro= 0;
  147.     Iot.tr_node.io_Message.mn_ReplyPort = TPort;
  148.     Iot.tr_node.io_Command = TR_ADDREQUEST;
  149.     Iotmin = Iot;
  150.     SendIO(&Iot);
  151.     SendIO(&Iotmin);
  152.     for (;;) {
  153.     long mask;
  154.     mask = Wait(SIGS | (1 << TPort->mp_SigBit));
  155.     if (mask & (SIGBREAKF_CTRL_C|SIGBREAKF_CTRL_D)) {
  156.         logmessage("DCRON: Break\n");
  157.         break;
  158.     }
  159.     if (mask & (SIGBREAKF_CTRL_E|SIGBREAKF_CTRL_F)) {
  160.         logmessage("^E/F force scan\n");
  161.         AbortIO(&Iot);          /*  force execution                     */
  162.     }
  163.     if (FatalError)
  164.         break;
  165.     if (CheckIO(&Iotmin)) {     /*  if file/date modified, force exec.  */
  166.         WaitIO(&Iotmin);
  167.         if (datemod() + filemod())
  168.         AbortIO(&Iot);
  169.         Iotmin.tr_time.tv_secs = 60;
  170.         Iotmin.tr_time.tv_micro= 0;
  171.         SendIO(&Iotmin);
  172.     }
  173.     if (CheckIO(&Iot)) {
  174.         DATESTAMP Ds;
  175.         short secmin;
  176.  
  177.         WaitIO(&Iot);
  178.         dostuff();
  179.         if (NextTimeout <= 0)
  180.         NextTimeout = 1;
  181.         if (XDebug) {
  182.         logmessage(" Next Timeout in %02ld:%02ld\n",
  183.             NextTimeout / 60,
  184.             NextTimeout % 60
  185.         );
  186.         }
  187.         DateStamp(&Ds);
  188.         secmin = 61 - (Ds.ds_Tick / 50);    /*  1+Secs till next min. */
  189.         Iot.tr_time.tv_secs = secmin + (NextTimeout - 1) * 60;
  190.         Iot.tr_time.tv_micro= 0;
  191.         SendIO(&Iot);
  192.     }
  193.     }
  194.     AbortIO(&Iot);
  195.     AbortIO(&Iotmin);
  196.     WaitIO(&Iot);
  197.     WaitIO(&Iotmin);
  198.     CloseDevice(&Iot);
  199.     DeletePort(TPort);
  200.     Close(NilFH);
  201. }
  202.  
  203. /*
  204.  *  If the current date is less than the previous date
  205.  *  If the current date is more than +5 minutes the previous date
  206.  */
  207.  
  208. datemod()
  209. {
  210.     DATESTAMP Date;
  211.     long xold, xnew;
  212.  
  213.     DateStamp(&Date);
  214.     xold = ModDate.ds_Days * 1440 + ModDate.ds_Minute;
  215.     xnew = Date.ds_Days * 1440 + Date.ds_Minute;
  216.     ModDate = Date;
  217.     if (xnew < xold || xnew - 5 > xold) {
  218.     DateStamp(&EndDate);
  219.     logmessage("Date change noted\n");
  220.     return(1);
  221.     }
  222.     return(0);
  223. }
  224.  
  225. /*
  226.  *  If the file modification time is different than when
  227.  *  we last checked, we want to recalculate our timeout
  228.  *  and rescan it.
  229.  */
  230.  
  231. filemod()
  232. {
  233.     char buf[sizeof(FIB)+4];
  234.     register FIB *fib = (FIB *)(((long)buf+3)&~3);
  235.     long lock;
  236.     long result = 0;
  237.  
  238.     if (lock = Lock(CRONTAB, SHARED_LOCK)) {
  239.     if (Examine(lock, fib)) {
  240.         if (fib->fib_Date.ds_Tick   != TabDate.ds_Tick ||
  241.         fib->fib_Date.ds_Minute != TabDate.ds_Minute ||
  242.         fib->fib_Date.ds_Days    != TabDate.ds_Days) {
  243.  
  244.         if (TabDate.ds_Days) {
  245.             logmessage("crontab modification noted\n");
  246.             result = 1;
  247.         }
  248.         TabDate = fib->fib_Date;
  249.         }
  250.     }
  251.     UnLock(lock);
  252.     }
  253.     return(result);
  254. }
  255.  
  256. /*
  257.  *  DOSTUFF()
  258.  *
  259.  *  Scan the CRONTAB and execute any entries that fall between
  260.  *  BegDate and EndDate.  Specifically, >= BegDate and < EndDate.
  261.  *
  262.  *  This is done as little as possible.  While scanning, we calculate the
  263.  *  timeout period till the next entry and will attempt to sleep for that
  264.  *  period of time before comming back to this routine.  Checks are still
  265.  *  done every 60 seconds for radical date changes and crontab modification
  266.  *  (routines above).
  267.  *
  268.  *  The calculation of the timeout period is not entirely accurate.
  269.  *  Specifically, in cases similar to:
  270.  *
  271.  *    "30 16 * * *"
  272.  *
  273.  *  If the current time is 16:xx, the timeout period calculated for the hours
  274.  *  will 0 instead of 24 (if the minutes had been '*', this would be
  275.  *  correct.  But if it is 16:31 the system will wait 29 minutes instead of
  276.  *  23 hours and 30 minutes.  Of course, it will re-scan at +29 minutes and
  277.  *  not find anything to do, but you get the point.
  278.  */
  279.  
  280. static char dim[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
  281. static CARRY CArry;
  282. static short CRanStart[] = {  0,  0,  1  ,  1, 0 };
  283. static short CRanEnd[]     = { 59, 23, 31  , 12, 6 };
  284. static long  CWeight[]     = {  1, 60, 1440, 28*1440, 1440 };
  285. static char *CBase[]     = { CArry.min, CArry.hour, CArry.day, CArry.month, CArry.dow };
  286.  
  287. dostuff()
  288. {
  289.     CDATE cdate;
  290.     long fh;
  291.     char buf[256];
  292.     int ie;
  293.  
  294.     NextTimeout = -1;
  295.  
  296.     BegDate = EndDate;
  297.     DateStamp(&EndDate);
  298.     breakup(&EndDate, &cdate.End);          /*  Breakup the time            */
  299.     breakup(&BegDate, &cdate.Beg);          /*  note: dim[FEB] is modified  */
  300.     bzero(&CArry, sizeof(CArry));           /*  range array                 */
  301.  
  302.     /*
  303.      *    Generate the range for the delta time that has occured since the
  304.      *    last reading.  (A) This is used to ensure we don't miss anything
  305.      *               (B) Ensure we don't repeat anything
  306.      */
  307.  
  308.     ie = makerange(CArry.min  , 0, 59, cdate.Beg.min  , cdate.End.min  ,  1);
  309.     ie = makerange(CArry.hour , 0, 23, cdate.Beg.hour , cdate.End.hour , ie);
  310.     ie = makerange(CArry.day  , 1, dim[cdate.Beg.month-1],
  311.                        cdate.Beg.day  , cdate.End.day  , ie);
  312.     ie = makerange(CArry.month, 1, 12, cdate.Beg.month, cdate.End.month, ie);
  313.     ie = makerange(CArry.dow  , 0,  6, cdate.Beg.dow  , cdate.End.dow  , ie);
  314.  
  315.     /*
  316.      *    Note:    if ie is set after this then dostuff() was called
  317.      *        within the same minute segment and would yield a
  318.      *        repeation of commands.
  319.      */
  320.  
  321.     if (ie == 0) {
  322.     ReadLn(NULL, NULL, 0);
  323.     if (fh = Open(CRONTAB, 1005)) {
  324.         long whennext;                /*  in minutes, estimate    */
  325.  
  326.         if (CronFileExists == 0) {
  327.         CronFileExists = 1;
  328.         logmessage("DCron File %s exists\n", CRONTAB);
  329.         }
  330.         while (ReadLn(fh, buf, 256)) {
  331.         if (buf[0] == 0 || buf[0] == '#')
  332.             continue;
  333.         whennext = handleline(buf, &cdate.End); /*  take smallest           */
  334.         if (XDebug > 1)
  335.             logmessage("whennext == %ld\n", whennext);
  336.         if (NextTimeout < 0 || whennext < NextTimeout)
  337.             NextTimeout = whennext;
  338.         }
  339.         Close(fh);
  340.     } else {
  341.         if (CronFileExists == 1) {
  342.         CronFileExists = 0;
  343.         logmessage("Unable to open %s\n", CRONTAB);
  344.         }
  345.     }
  346.     }
  347.  
  348.     /*
  349.      *    You can't trust the Amiga's clock vs the timer.device ... this is to
  350.      *    ensure that they don't get too far off... no more than an hour timeout
  351.      *    is allowed.  Frankly, anything above 30 minutes will yield unnoticeable
  352.      *    results.
  353.      */
  354.  
  355.     if (--NextTimeout > 60)
  356.     NextTimeout = 60;
  357. }
  358.  
  359. breakup(date, mhd)
  360. DATESTAMP *date;
  361. MHDMW *mhd;
  362. {
  363.     long days;
  364.     long years;
  365.     char leap;
  366.     short month;
  367.  
  368.     days = date->ds_Days + 731;         /*    1976        */
  369.     years = days / (365*3+366);             /*  #quad yrs   */
  370.     days -= years * (365*3+366);
  371.     leap = (days < 366);                    /*  is a leap yr*/
  372.     years = 1976 + 4 * years;
  373.     dim[1] = 29;
  374.     if (!leap) {
  375.     dim[1] = 28;
  376.     days -= 366;
  377.     ++years;
  378.     }
  379.     years += days / 365;
  380.     days -= (days / 365) * 365;
  381.     for (month = 0; (month==1) ? (days >= 28 + leap) : (days >= dim[month]); ++month)
  382.     days -= (month==1) ? (28 + leap) : dim[month];
  383.     mhd->min    = date->ds_Minute % 60;
  384.     mhd->hour    = date->ds_Minute / 60;
  385.     mhd->day    = days + 1;
  386.     mhd->month    = month + 1;
  387.     mhd->dow    = date->ds_Days % 7;    /*  0 = sunday     */
  388. }
  389.  
  390. /*
  391.  *  S < RANGE <= E    (if previous entries were equal)
  392.  *        note: if S == E, E is still included
  393.  *  S <= RANGE <= E    (if previous entries were not equal)
  394.  */
  395.  
  396. makerange(ptr, cs, ce, s, e, isequal)
  397. register char *ptr;
  398. {
  399.     long error = 5000;
  400.     short oldisequal = isequal;
  401.  
  402.     isequal = (isequal && s == e);
  403.     if (oldisequal && s != e) {
  404.     if (++s > ce)
  405.         s = cs;
  406.     }
  407.     while (--error && s != e) {
  408.     ptr[s++] = 1;
  409.     if (s > ce)
  410.         s = cs;
  411.     }
  412.     ptr[e] = 1;
  413.     if (error == 0) {
  414.     logmessage("DCRON,makerange(): software error\n");
  415.     FatalError = 1;
  416.     }
  417.     return(isequal);
  418. }
  419.  
  420. /*
  421.  *  This is the core of the routine.  The fields are interpreted, times
  422.  *  estimated, ready entries run, etc..
  423.  *
  424.  *  order:  minutes hours days months dayofweek
  425.  *
  426.  *  Each field may be a * indicating all-times, and a combination of a
  427.  *  range and comma delimited numbers:    0-3,45,46-49        (MUST BE SORTED)
  428.  *
  429.  *  Each field is separated by one or spaces+tabs
  430.  */
  431.  
  432. handleline(buf, endt)
  433. char *buf;
  434. MHDMWARY *endt;
  435. {
  436.     register char *ptr = buf;
  437.     register short i;
  438.     char *gnum();
  439.     short sumok = 0;
  440.     long howsoon = 0;            /*    how soon until next entry */
  441.  
  442.     for (i = 0; i < 5; ++i) {       /*  5 arguments     */
  443.     char ok = 0;
  444.     short nextunit = -1;        /*    count, in units */
  445.  
  446.     while (*ptr == ' ' || *ptr == 9)
  447.         ++ptr;
  448.     while (*ptr && *ptr != 9 && *ptr != ' ') {
  449.         short start;
  450.         short finish;
  451.         if (*ptr == '*') {
  452.         ++ptr;
  453.         ok = 1;
  454.         break;
  455.         }
  456.         ptr = gnum(ptr, &start);
  457.         finish = start;
  458.         if (*ptr == '-')
  459.         ptr = gnum(ptr+1, &finish);
  460.         if (*ptr == ',')
  461.         ++ptr;
  462.  
  463.         /*
  464.          *    Determine if current date is within time range.  nextunit
  465.          *    is the number of time units for this index till the next
  466.          *    entry will be executed.
  467.          */
  468.  
  469.         if (endt->entry[i] < start) {
  470.         register short newnextunit = start - endt->entry[i];
  471.         if (nextunit < 0 || newnextunit < nextunit)
  472.             nextunit = newnextunit;
  473.         } else
  474.         if (endt->entry[i] > finish) {
  475.         register short newnextunit = 1 + CRanEnd[i] - endt->entry[i] + start - CRanStart[i];
  476.         if (nextunit < 0 || newnextunit < nextunit)
  477.             nextunit = newnextunit;
  478.         } else
  479.         nextunit = 0;  /*  Inside time range   */
  480.  
  481.         if (start < CRanStart[i] || finish < CRanStart[i] || start > CRanEnd[i] || finish > CRanEnd[i]) {
  482.         logmessage("Illegal Bounds in: %s\n", buf);
  483.         logmessage(" Value %ld & %ld bounds %ld-%ld ", start, finish, CRanStart[i], CRanEnd[i]);
  484.         logmessage("  AT: '%s'\n", ptr);
  485.         continue;
  486.         } else {
  487.         do {
  488.             if (start > CRanEnd[i])
  489.             start = CRanStart[i];
  490.             if (CBase[i][start]) {
  491.             ok = 1;
  492.             break;
  493.             }
  494.             ++start;
  495.         } while (start != finish + 1);
  496.         }
  497.     }
  498.     if (ok)
  499.         ++sumok;
  500.     if (nextunit > 0)
  501.         howsoon += nextunit * CWeight[i];
  502.     }
  503.  
  504.     /*
  505.      *    This is tricky.  Since this program also handles the dummy-null
  506.      *    device, it CANNOT block on the execute.  The only way to do that
  507.      *    is to run <nil: >nil: so run itself does not attempt to access
  508.      *    "*".  It is ok if the program run runs accesses "*" as we will not
  509.      *    be frozen then.
  510.      */
  511.  
  512.     if (sumok == i) {
  513.     register char *p2 = malloc(strlen(ptr)+32);
  514.  
  515.     logmessage("%s\n", buf);
  516.     while (*ptr == ' ' || *ptr == 9)
  517.         ++ptr;
  518.     /*      strcpy(p2, "run <nil: >nil: ");   */
  519.     strcpy(p2, "run ");
  520.     strcat(p2, ptr);
  521.     Execute(p2, NilFH, NilFH);
  522.     }
  523.     if (XDebug > 2) {
  524.     logmessage("FOR %-20s   In %ld %02ld:%02ld\n",
  525.         buf, howsoon / 1440, howsoon / 60 % 24, howsoon % 60
  526.     );
  527.     }
  528.     return(howsoon);
  529. }
  530.  
  531. /*
  532.  *  Poor man's log.  Note that the log file is not left open ... this allows
  533.  *  one to read or tail it at any time.
  534.  */
  535.  
  536. void
  537. logmessage(ptr, a, b, c, d, e)
  538. char *ptr;
  539. {
  540.     char *buf = malloc(512);
  541.     DATESTAMP date;
  542.     MHDMW   cb;
  543.     long    fh;
  544.     static char *Dow[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
  545.     static char *Miy[] = { "---", "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  546.                "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
  547.  
  548.     if (!buf)
  549.     return;
  550.  
  551.     DateStamp(&date);
  552.     breakup(&date, &cb);
  553.  
  554.     sprintf(buf, "dcron: %s %2ld %s %02ld:%02ld   ",
  555.     Dow[cb.dow], cb.day, Miy[cb.month], cb.hour, cb.min
  556.     );
  557.     sprintf(buf+strlen(buf), ptr, a, b, c, d, e);
  558.     if ((fh = Open(LogFile, 1005)) == NULL)
  559.     fh = Open(LogFile, 1006);
  560.     if (fh) {
  561.     Seek(fh, 0L, 1);
  562.     WriteStr(fh, buf);
  563.     Close(fh);
  564.     }
  565.     free(buf);
  566. }
  567.  
  568. /*
  569.  *  Scan a string for a value, return the new pointer position and set the
  570.  *  specified short pointer to the value.
  571.  */
  572.  
  573. char *
  574. gnum(ptr, pval)
  575. register char *ptr;
  576. register short *pval;
  577. {
  578.     short val = 0;
  579.     if (*ptr < '0' || *ptr > '9')
  580.     logmessage("Not a number: %s\n", ptr);
  581.     while (*ptr >= '0' && *ptr <= '9') {
  582.     val = val * 10 + *ptr - '0';
  583.     ++ptr;
  584.     }
  585.     *pval = val;
  586.     return(ptr);
  587. }
  588.  
  589. WriteStr(fh, buf)
  590. long fh;
  591. char *buf;
  592. {
  593.     Write(fh, buf, strlen(buf));
  594. }
  595.  
  596. ReadLn(fh, buf, max)
  597. long fh;
  598. char *buf;
  599. short max;
  600. {
  601.     static char Data[1024];
  602.     static short RIdx, RLen;
  603.     register short i;
  604.  
  605.     if (fh == NULL) {
  606.     RIdx = RLen = 0;
  607.     return(0);
  608.     }
  609.     for (--max, i = 0; i < max; ++i) {
  610.     if (RIdx == RLen) {
  611.         RLen = Read(fh, Data, 1024);
  612.         RIdx = 0;
  613.         if (RLen <= 0) {
  614.         buf[i] = 0;
  615.         return(0);
  616.         }
  617.     }
  618.     if ((buf[i] = Data[RIdx++]) == '\n')
  619.         break;
  620.     }
  621.     buf[i] = 0;
  622.     return(1);
  623. }
  624.  
  625.