home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: SysTools / SysTools.zip / pmcron03.zip / tables.c < prev    next >
C/C++ Source or Header  |  1996-05-09  |  68KB  |  1,276 lines

  1. /* Copyright (c) 1995 Florian Große-Coosmann, RCS section at the eof         */
  2. /* This module includes all functions necessary for the cron mechanism. You  */
  3. /* may be interested but you'll be disappointed. This is not a good looking  */
  4. /* code.                                                                     */
  5. #define INCL_NOPM
  6. #define INCL_NOCOMMON
  7. #define INCL_DOSSEMAPHORES
  8. #define INCL_DOSFILEMGR
  9. #define INCL_DOSPROCESS
  10. #define INCL_DOSERRORS
  11. #include <os2.h>
  12. #include <stdio.h>
  13. #include <stdlib.h>
  14. #include <string.h>
  15. #include <time.h>
  16. #include <ctype.h>
  17. #include <signal.h>
  18. #include <stddef.h>
  19. #include <setjmp.h>
  20. #include <errno.h>
  21. #include <io.h>
  22. #include <sys/ioctl.h>
  23. #include "server.h"
  24.  
  25. #define DAY_LOOKAHEAD 1                 /* distance within to look for the   */
  26.                                         /* next starting time                */
  27. time_t AutoDisConnect = (time_t) -1l;   /* close the communication pipe after*/
  28.                                         /* this time                         */
  29. time_t AutoCloseSocket = (time_t) -1l;  /* close the communication socket    */
  30.                                         /* after this time                   */
  31. ULONG EditEntryNumber = (ULONG) -1;     /* No. of job which is edited or -1  */
  32. HMTX ThreadSem = (HMTX) 0;              /* semaphore allowing exclusiv usage */
  33.                                         /* of the job list                   */
  34. LIST_ENTRY ListHead = {NULL,NULL,};     /* the currently used job list       */
  35. static int AutoReWrite = 0;             /* flag: do we need (another try of) */
  36.                                         /* a write of the Crontabs file?     */
  37. static time_t currtime;                 /* current time, don't compute on    */
  38.                                         /* every line.                       */
  39. static struct tm currtime_tm;            /* current time in tm format         */
  40. static int CurrTimeIsOK = 0;            /* Every function which needs the    */
  41.                                         /* currtime should use the time      */
  42.                                         /* function to get the value.        */
  43. static jmp_buf Continue;                /* in case of an unexpected signal   */
  44. static int PrintSleepingTime = 0;       /* debugging flag                    */
  45. static int PrintEachStartingTime = 0;   /* debugging flag                    */
  46.  
  47. /*****************************************************************************/
  48. /*  function name : BlockProcess                                             */
  49. /*                                                                           */
  50. /*  description   : the calling thread gets the exclusive access to the      */
  51. /*                  job list. This function must be called before any        */
  52. /*                  access to the ListHead.                                  */
  53. /*                  In case of an unallocated semaphore or a global stop     */
  54. /*                  this function works fine.                                */
  55. /*****************************************************************************/
  56. void BlockProcess(void)
  57. {
  58.    ULONG err;
  59.    if (ThreadSem == (HMTX) 0)
  60.       return;
  61.    do {
  62.       err = DosRequestMutexSem(ThreadSem,SEM_INDEFINITE_WAIT);
  63.    } while (err && !GlobalStop);
  64. }
  65.  
  66. /*****************************************************************************/
  67. /*  function name : UnBlockProcess                                           */
  68. /*                                                                           */
  69. /*  description   : releases the exclusive access to the job list.           */
  70. /*                                                                           */
  71. /*  note          : should only called by the thread that has called         */
  72. /*                  BlockProcess.                                            */
  73. /*****************************************************************************/
  74. void UnBlockProcess(void)
  75. {
  76.    DosReleaseMutexSem(ThreadSem);
  77. }
  78.  
  79. /*****************************************************************************/
  80. /*  function name : LocalStop                                                */
  81. /*                                                                           */
  82. /*  arguments     : signal number generated by the runtime system            */
  83. /*                                                                           */
  84. /*  description   : This is a signal routine. This routine is called by a    */
  85. /*                  SIGTERM. This should never happen, since this the        */
  86. /*                  corresponding threads are not the main thread.           */
  87. /*                                                                           */
  88. /*  note          : don't call directly                                      */
  89. /*****************************************************************************/
  90. static void LocalStop(int code)
  91. {
  92.    GlobalStop++;
  93.    if (pipehandle != -1)
  94.       close(pipehandle);
  95.    pipehandle = -1;
  96.    signal(code,SIG_ACK);
  97.    longjmp(Continue,1);                 /* points to the atexit execution of */
  98. }                                       /* the job list, see end of this file*/
  99.  
  100. /*****************************************************************************/
  101. /*  function name : LookupEntryNum                                           */
  102. /*                                                                           */
  103. /*  arguments     : number of the entry (1-based!), flag if it should        */
  104. /*                  count comment lines too                                  */
  105. /*                                                                           */
  106. /*  return value  : the entry or NULL in case of an error                    */
  107. /*                                                                           */
  108. /*  description   : looks for the requested entry. If the flag is set, the   */
  109. /*                  comment lines of the Crontabs file are returned, too.    */
  110. /*                  Call with the flag set only if you want to rewrite the   */
  111. /*                  Crontabs file.                                           */
  112. /*                                                                           */
  113. /*  note          : don't forget to block the list                           */
  114. /*****************************************************************************/
  115. LIST_ENTRY *LookupEntryNum(ULONG num,int AllowComment)
  116. {
  117.    LIST_ENTRY *run = &ListHead;
  118.    if (num == 0)                        /* can't return &ListHead            */
  119.       return(NULL);
  120.    while (num) {
  121.       run = run->next;
  122.       if (run == NULL)                  /* no more entries                   */
  123.          return(NULL);
  124.       if (!AllowComment)
  125.          if (run->IsComment)
  126.             continue;                   /* ignore comment lines              */
  127.       num--;
  128.    }
  129.    return(run);
  130. }
  131.  
  132. /*****************************************************************************/
  133. /*  function name : IsBitSet                                                 */
  134. /*                                                                           */
  135. /*  arguments     : bitset, number of the bit (0-based)                      */
  136. /*                                                                           */
  137. /*  return value  : 1 if the specified bit is set, 0 otherwise               */
  138. /*                                                                           */
  139. /*  description   : checks wether the specified bit in the bitset is set.    */
  140. /*****************************************************************************/
  141. static int IsBitSet(unsigned long long val,unsigned bitno)
  142. {
  143.    if (val & (1ull << bitno))
  144.       return(1);
  145.    return(0);
  146. }
  147.  
  148. /*****************************************************************************/
  149. /*  function name : ComputeNextStart                                         */
  150. /*                                                                           */
  151. /*  arguments     : job list entry, start time of the day of computing,      */
  152. /*                  day count of the look ahead                              */
  153. /*                                                                           */
  154. /*  return value  : the next start time of the job entry or (time_t) -1,     */
  155. /*                  if this job should never executed or should not been     */
  156. /*                  executed within the DayCount                             */
  157. /*                                                                           */
  158. /*  description   : computes the next start time of the job entry. This      */
  159. /*                  may be "never" in case of a comment or an entry of       */
  160. /*                  "atstartup" or "atexit" execution.                       */
  161. /*                  if DayStart >= 0 the computing starts from the           */
  162. /*                  "current time" placed in reference until the end of      */
  163. /*                  the day. The following DayCount days were checked for    */
  164. /*                  a possible start of the job, too.                        */
  165. /*                                                                           */
  166. /*  note          : This function is called very frequently. I think we      */
  167. /*                  spend the most time in the called function localtime.    */
  168. /*                  Be careful if you want to change things.                 */
  169. /*****************************************************************************/
  170. static time_t ComputeNextStart(LIST_ENTRY *e,time_t reference,int DayCount)
  171. {
  172.    int i,j;
  173.    struct tm tm;
  174.  
  175.    if (DayCount < 0)                    /* nothing more to do?               */
  176.       return(e->NextStartTime);
  177.    e->NextStartTime = (time_t) -1;      /* default: never                    */
  178.    if (e->AtStartup || e->AtExit || e->IsComment || e->Daily)  /* don't      */
  179.       return(e->NextStartTime);         /* execute this jobs automatically   */
  180.    if ((i = (int) (reference % 60)) != 0) /* go to the next start of a minute*/
  181.       reference += 60 - i;
  182.    tm = *localtime(&reference);         /* compute current day, weekday, etc.*/
  183.  
  184.    if ((!IsBitSet(e->Month,tm.tm_mon + 1)) ||   /* Either month, day or      */
  185.        (!IsBitSet(e->Day,tm.tm_mday)) ||  /* weekday are inacceptable        */
  186.        (!IsBitSet(e->WeekDay,tm.tm_wday))) {
  187.                                         /* compute the job start time with a */
  188.                                         /* reference of "tomorrow at day     */
  189.                                         /* start"                            */
  190.       reference -= tm.tm_hour * 60 * 60 + tm.tm_min * 60;   /* back to the   */
  191.                                         /* start of the current day          */
  192.       reference += 24 * 60 * 60;        /* add one day                       */
  193.       return(ComputeNextStart(e,reference,DayCount - 1));
  194.    }
  195.                                         /* check the next hour and minute we */
  196.                                         /* have to start the job             */
  197.    if (IsBitSet(e->Hour,tm.tm_hour)) {  /* within the current hour?          */
  198.       for (j = tm.tm_min;j < 60;j++)
  199.          if (IsBitSet(e->Minute,j)) {   /* found! start at this minute       */
  200.             tm.tm_min = j;
  201.             return(e->NextStartTime = mktime(&tm));
  202.          }
  203.    }
  204.                                         /* not within the current hour, check*/
  205.                                         /* the rest of the day               */
  206.    for (i = tm.tm_hour + 1;i < 24;i++) {
  207.       if (!IsBitSet(e->Hour,i))         /* not within this hour              */
  208.          continue;
  209.       for (j = 0;j < 60;j++)            /* hour correct, check the minutes   */
  210.          if (IsBitSet(e->Minute,j)) {   /* found! start at this minute       */
  211.             tm.tm_hour = i;
  212.             tm.tm_min = j;
  213.             return(e->NextStartTime = mktime(&tm));
  214.          }
  215.    }
  216.                                         /* don't start this job today,       */
  217.                                         /* check wether to start tomorrow    */
  218.    reference -= tm.tm_hour * 60 * 60 + tm.tm_min * 60;
  219.    reference += 24 * 60 * 60;
  220.    return(ComputeNextStart(e,reference,DayCount - 1));
  221. }
  222.  
  223. /*****************************************************************************/
  224. /*  function name : NextWord                                                 */
  225. /*                                                                           */
  226. /*  arguments     : start of the 0-terminated string, buffer to hold the     */
  227. /*                  length of the word                                       */
  228. /*                                                                           */
  229. /*  return value  : the beginning of the next whitespace terminated word     */
  230. /*                  within the string or "" if there is no such word.        */
  231. /*                  The return value is NULL in case of an error.            */
  232. /*                                                                           */
  233. /*  description   : looks for the start of the next word. return the         */
  234. /*                  beginning of the word and places the length into the     */
  235. /*                  supplied buffer. After the fist call you may give a      */
  236. /*                  NULL as the string argument. In this case the search     */
  237. /*                  is continued after the last returned word.               */
  238. /*                  This function is pretty similar to strtok, but we        */
  239. /*                  don't destroy any char within the string and we return   */
  240. /*                  an empty string in case of no more words.                */
  241. /*****************************************************************************/
  242. static const char *NextWord(const char *start,ULONG *len)
  243. {
  244.    static const char *oldstart = NULL;  /* not thread save!                  */
  245.    const char *retval,*ptr;
  246.    *len = 0;
  247.    if ((start == NULL) && (oldstart == NULL))   /* never started or at end   */
  248.       return(NULL);                     /* of line?                          */
  249.    retval = (start == NULL) ? oldstart : start; /* continue with the last    */
  250.                                         /* processed line?                   */
  251.    while (isspace(*retval))             /* jump over leading whitespace      */
  252.       retval++;
  253.    ptr = retval;
  254.    while (*ptr != 0) {                  /* until string not empty            */
  255.       (*len)++;
  256.       if (isspace(ptr[1])) {            /* next char is a terminator (white)?*/
  257.          oldstart = ptr + 1;            /* save pointer to allow the use of  */
  258.          return(retval);                /* NULL for the string argument      */
  259.       }
  260.       ptr++;
  261.    }
  262.    oldstart = ptr;                      /* save "" to allow the use of NULL  */
  263.    return(retval);                      /* for the string argument           */
  264. }
  265.  
  266. /*****************************************************************************/
  267. /*  function name : NextNumber                                               */
  268. /*                                                                           */
  269. /*  arguments     : string, buffer to hold the length of the converted       */
  270. /*                  characters, maximum length of string                     */
  271. /*                                                                           */
  272. /*  return value  : 0xFFFF in case of an error, the converted number         */
  273. /*                  otherwise.                                               */
  274. /*                                                                           */
  275. /*  description   : returns the next number within the string. Leading       */
  276. /*                  whitespaces are not accepted. The length buffer is       */
  277. /*                  filled with the converted characters. The length of      */
  278. /*                  the string must be supplied, therefore the string may    */
  279. /*                  not be terminated.                                       */
  280. /*                                                                           */
  281. /*  note          : we accept only numbers in the range (0 - 65530)!         */
  282. /*****************************************************************************/
  283. static unsigned NextNumber(const char *start,ULONG *len,ULONG maxlen)
  284. {
  285.    unsigned retval = 0;
  286.    *len = 0;
  287.    while ((isdigit(*start)) && (*len < maxlen)) {
  288.       if (retval > 6552)
  289.          return(0xFFFF);
  290.       retval *= 10;
  291.       retval += (unsigned) (*start - '0');
  292.       start++;
  293.       (*len)++;
  294.    }
  295.    if (*len == 0)                       /* nothing done?                     */
  296.       return(0xFFFF);
  297.    return(retval);
  298. }
  299.  
  300. /*****************************************************************************/
  301. /*  function name : ResolvWord                                               */
  302. /*                                                                           */
  303. /*  arguments     : bitset to hold the values, minimal acceptable value,     */
  304. /*                  maximal acceptable value, string with values (not        */
  305. /*                  0-terminated) and its length                             */
  306. /*                                                                           */
  307. /*  return value  : 1 on success, 0 otherwise                                */
  308. /*                                                                           */
  309. /*  description   : this function converts the string to a bitset. The       */
  310. /*                  string consists of a comma separated list of numbers.    */
  311. /*                  The special string "*" is accepted as an abbreviation    */
  312. /*                  of "all valid numbers". Intervals are supported.         */
  313. /*                  Each number is check to fall into the given interval     */
  314. /*                  [min,max] inclusively.                                   */
  315. /*                  The bitset is zeroed at the start of the function.       */
  316. /*                  Any character except digits, commas and a minus sign     */
  317. /*                  leads to a failure.                                      */
  318. /*                                                                           */
  319. /*  note          : an empty string is accepted.                             */
  320. /*****************************************************************************/
  321. static int ResolvWord(unsigned long long *bits,int min,int max,const char *s,
  322.                                                                      ULONG len)
  323. {
  324.    unsigned val,valend;
  325.    ULONG toklen;
  326.    *bits = 0ull;
  327.    if (len == 0)
  328.       return(0);
  329.    if ((len == 1) && (*s == '*')) {     /* all valid numbers?                */
  330.       for (val = min;val <= max;val++)
  331.          *bits |= (1ull << val);
  332.       return(1);
  333.    }
  334.    for (;;) {
  335.       val = NextNumber(s,&toklen,len);
  336.       if (((int) val < min) || ((int) val > max))  /* invalid value?         */
  337.          return(0);
  338.       *bits |= (1ull << val);
  339.       s += toklen;                      /* eat the number                    */
  340.       len -= toklen;
  341.       if (len == 0)                     /* end of the string?                */
  342.          return(1);
  343.       switch (*s) {
  344.          case ',':                      /* the list must be comma separated  */
  345.             s++;                        /* eat the comma                     */
  346.             len--;
  347.             break;
  348.          case '-':                      /* the entry is an interval          */
  349.             s++;                        /* eat the minus sign                */
  350.             len--;
  351.             valend = NextNumber(s,&toklen,len);
  352.             if (((int) val < min) || ((int) val > max))  /* invalid value?   */
  353.                return(0);
  354.             if (valend < val)           /* Illegal interval                  */
  355.                return(0);
  356.             s += toklen;                /* eat the number                    */
  357.             len -= toklen;
  358.             while (val <= valend) {
  359.                *bits |= (1ull << val);
  360.                val++;
  361.             }
  362.             if (len == 0)               /* this was the last entry           */
  363.                return(1);
  364.             if (*s != ',')              /* only commas are expected          */
  365.                return(0);
  366.             s++;                        /* eat the comma                     */
  367.             len--;
  368.             break;
  369.          default:                       /* other characters are not expected */
  370.             return(0);
  371.       }
  372.    }
  373. }
  374.  
  375. /*****************************************************************************/
  376. /*  function name : PrepareListEntry                                         */
  377. /*                                                                           */
  378. /*  arguments     : job list entry                                           */
  379. /*                                                                           */
  380. /*  return value  : 0 on success, EINVAL otherwise                           */
  381. /*                                                                           */
  382. /*  description   : parses the line new->s and sets all computable values    */
  383. /*                  of the job.                                              */
  384. /*                  Allowed starting time are:                               */
  385. /*                  "CronStart"                                              */
  386. /*                  "CronStop"                                               */
  387. /*                  "Once" "CronStart"                                       */
  388. /*                  "Once" "CronStop"                                        */
  389. /*                  standard time list                                       */
  390. /*                  "Once" standard time list                                */
  391. /*                                                                           */
  392. /*  note          : don't call this function if the entry has already been   */
  393. /*                  placed into the job list. currtime must been set.        */
  394. /*****************************************************************************/
  395. static int PrepareListEntry(LIST_ENTRY *new)
  396. {
  397.    const char *s,*word;
  398.    ULONG len;
  399.    unsigned long long ull;
  400.    s = new->s;
  401.    word = NextWord(s,&len);
  402.    if ((strnicmp(word,"Once",(size_t) len) == 0) && (len == 4)) {
  403.       new->Once = 1;                    /* the "Once" statement is allowed   */
  404.       word = NextWord(NULL,&len);       /* on every time string              */
  405.    }
  406.    if ((strnicmp(word,"CronStart",(size_t) len) == 0) && (len == 9)) {
  407.       new->AtStartup = 1;
  408.       word = NextWord(NULL,&len);
  409.       if (len == 0)                     /* empty command string?             */
  410.          return(EINVAL);
  411.       new->StartCmd = word;
  412.       return(0);
  413.    }
  414.    if ((strnicmp(word,"CronStop",(size_t) len) == 0) && (len == 8)) {
  415.       new->AtExit = 1;
  416.       word = NextWord(NULL,&len);
  417.       if (len == 0)
  418.          return(EINVAL);
  419.       new->StartCmd = word;
  420.       return(0);
  421.    }
  422.    if ((strnicmp(word,"Daily",(size_t) len) == 0) && (len == 5)) {
  423.       new->Daily = 1;
  424.       word = NextWord(NULL,&len);
  425.       if (len == 0)
  426.          return(EINVAL);
  427.       new->StartCmd = word;
  428.       return(0);
  429.    }
  430.                                         /* now we have to check for the      */
  431.                                         /* standard time list (minutes hours */
  432.                                         /* days months weekdays)             */
  433.    if (!ResolvWord(&(new->Minute),0,59,word,len))  /* resolve the minute list*/
  434.       return(EINVAL);
  435.    s = word + len;                      /* eat the list                      */
  436.    word = NextWord(NULL,&len);
  437.    if (!ResolvWord(&ull,0,23,word,len)) /* process the hour list             */
  438.       return(EINVAL);
  439.    new->Hour = (unsigned) ull;
  440.    word = NextWord(NULL,&len);
  441.    if (!ResolvWord(&ull,1,31,word,len)) /* process the day list              */
  442.       return(EINVAL);
  443.    new->Day = (unsigned) ull;
  444.    word = NextWord(NULL,&len);
  445.    if (!ResolvWord(&ull,1,12,word,len)) /* process the month list            */
  446.       return(EINVAL);
  447.    new->Month = (unsigned) ull;
  448.    word = NextWord(NULL,&len);
  449.    if (!ResolvWord(&ull,0,6,word,len))  /* process the weekday list          */
  450.       return(EINVAL);
  451.    new->WeekDay = (unsigned) ull;
  452.    word = NextWord(NULL,&len);
  453.    if (len == 0)                        /* empty command line?               */
  454.       return(EINVAL);
  455.    new->StartCmd = word;                /* assign the command line           */
  456.    ComputeNextStart(new,currtime,DAY_LOOKAHEAD);
  457.    return(0);
  458. }
  459.  
  460. /*****************************************************************************/
  461. /*  function name : CleanListEntry                                           */
  462. /*                                                                           */
  463. /*  arguments     : job list entry                                           */
  464. /*                                                                           */
  465. /*  description   : frees the entry and probably its command line            */
  466. /*****************************************************************************/
  467. static void CleanListEntry(LIST_ENTRY *entry)
  468. {
  469.    if (entry == NULL)
  470.       return;
  471.    if (entry->s != NULL)
  472.       free(entry->s);
  473.    free(entry);
  474. }
  475.  
  476. /*****************************************************************************/
  477. /*  function name : ExtractListEntry                                         */
  478. /*                                                                           */
  479. /*  arguments     : job list entry to delete, head of the list of the job    */
  480. /*                  entries where to search for the extraction candidat.     */
  481. /*                                                                           */
  482. /*  return value  : NULL on error, the job list entry otherwise              */
  483. /*                                                                           */
  484. /*  description   : looks for the given entry in the list, cuts it off but   */
  485. /*                  doesn't delete it. This function is made for the         */
  486. /*                  exclusive use by DeleteListEntry and RunJobs (see        */
  487. /*                  below).                                                  */
  488. /*                                                                           */
  489. /*  note          : there should only be one thread which has the            */
  490. /*                  exclusive access to the list.                            */
  491. /*                  The EditEntryNumber is maintained to flag the PM a       */
  492. /*                  changed number of the job which is currently edited.     */
  493. /*****************************************************************************/
  494. static LIST_ENTRY *ExtractListEntry(LIST_ENTRY *e,LIST_ENTRY *head)
  495. {
  496.    LIST_ENTRY *father;
  497.    ULONG number = 0;                    /* number of the entry which should  */
  498.                                         /* be deleted                        */
  499.    if (e == NULL)
  500.       return(NULL);
  501.                                         /* now look for the predecessor of   */
  502.                                         /* the entry, it may be a comment    */
  503.                                         /* line.                             */
  504.    father = head;
  505.    while ((father != NULL) && (father->next != e)) {
  506.       father = father->next;
  507.       number++;
  508.    }
  509.    if (father == NULL)                  /* what's that? orphaned son?        */
  510.       return(NULL);
  511.    father->next = e->next;
  512.    e->next = NULL;
  513.    if (EditEntryNumber != (ULONG) -1) { /* There is a job in editing mode?   */
  514.       if (EditEntryNumber > number)     /* job after the deleted one         */
  515.          EditEntryNumber--;
  516.       else if (EditEntryNumber == number) /* job IS the deleted one          */
  517.          EditEntryNumber = (ULONG) -1;
  518.    }
  519.    return(e);
  520. }
  521.  
  522. /*****************************************************************************/
  523. /*  function name : DeleteListEntry                                          */
  524. /*                                                                           */
  525. /*  arguments     : job list entry to delete, head of the list of the job    */
  526. /*                  entries where to search for the deletion candidat.       */
  527. /*                                                                           */
  528. /*  return value  : 0 on success, errno otherwise                            */
  529. /*                                                                           */
  530. /*  description   : looks for the given entry in the list, cuts it off and   */
  531. /*                  deletes the entry.                                       */
  532. /*                                                                           */
  533. /*  note          : there should only be one thread which has the            */
  534. /*                  exclusive access to the list.                            */
  535. /*****************************************************************************/
  536. int DeleteListEntry(LIST_ENTRY *entry,LIST_ENTRY *head)
  537. {
  538.    if ((entry = ExtractListEntry(entry,head)) == NULL)   /* can't extract?   */
  539.       return(EACCES);
  540.    CleanListEntry(entry);
  541.    return(0);
  542. }
  543.  
  544. /*****************************************************************************/
  545. /*  function name : InsertListEntry                                          */
  546. /*                                                                           */
  547. /*  arguments     : string with a new Crontabs statement, its length, flag   */
  548. /*                  to allow comment lines, head of the list of the job      */
  549. /*                  entries where to append the statement                    */
  550. /*                                                                           */
  551. /*  return value  : 0 on success, errno otherwise                            */
  552. /*                                                                           */
  553. /*  description   : duplicates and parses the string and appends it to the   */
  554. /*                  list. This function checks the validity and sets all     */
  555. /*                  necessary entry values, too.                             */
  556. /*                  If the flag is set comment lines were accepted.          */
  557. /*                                                                           */
  558. /*  note          : there should only be one thread which has the            */
  559. /*                  exclusive access to the list. currtime must been set.    */
  560. /*****************************************************************************/
  561. static int InsertListEntry(const char *s,size_t len,int AllowComment,
  562.                                                               LIST_ENTRY *head)
  563. {
  564.    LIST_ENTRY *run = head,*new;
  565.    int err;
  566.    while (isspace(*s) && (len > 0)) {   /* cut off leading and trailing      */
  567.       s++;                              /* whitespaces                       */
  568.       len--;
  569.    }
  570.    while (len > 0) {
  571.       if (isspace(s[len - 1]))
  572.          len--;
  573.       else
  574.          break;
  575.    }
  576.    if (len == 0) {                      /* empty line?                       */
  577.       if (AllowComment)                 /* ignore the line if allowed        */
  578.          return(0);
  579.       return(EINVAL);
  580.    }
  581.    while (run->next != NULL)            /* look for the end of the list      */
  582.       run = run->next;
  583.    if ((new = malloc(sizeof(LIST_ENTRY))) == NULL) /* create a new entry     */
  584.       return(ENOMEM);
  585.    memset(new,0,sizeof(LIST_ENTRY));
  586.    if ((new->s = malloc(len + 1)) == NULL) { /* allocate buffer for the      */
  587.       CleanListEntry(new);              /* copy of the Crontabs statement    */
  588.       return(ENOMEM);
  589.    }
  590.    memcpy(new->s,s,len);                /* the statement isn't 0-terminated  */
  591.    new->s[len] = '\0';
  592.    if (AllowComment && ((new->s[0] == ';') || (new->s[0] == '#'))) {
  593.       new->NextStartTime = (time_t) -1;
  594.       new->IsComment = 1;
  595.    } else if ((err = PrepareListEntry(new)) != 0) {   /* error in the stmt?  */
  596.       CleanListEntry(new);
  597.       return(err);
  598.    }
  599.    run->next = new;                     /* OK, append to the list            */
  600.    return(0);
  601. }
  602.  
  603. /*****************************************************************************/
  604. /*  function name : CreateList                                               */
  605. /*                                                                           */
  606. /*  arguments     : flag: which output should be generated                   */
  607. /*                                                                           */
  608. /*  return value  : newly allocated buffer holding a long string with all    */
  609. /*                  the table entry lines. The string is terminated by a     */
  610. /*                  '\0' und each line is separated by a CR LF pair.         */
  611. /*                  NULL in case on an error.                                */
  612. /*                                                                           */
  613. /*  description   : The flag may has one of the following states:            */
  614. /*                  CL_FILE:                                                 */
  615. /*                     The output is generated for the Crontabs file. All    */
  616. /*                     comments and executable entries are included.         */
  617. /*                  CL_USER:                                                 */
  618. /*                     The output is generated for the user communication    */
  619. /*                     pipe. No comments are included but each line is       */
  620. /*                     preceeded by a line number.                           */
  621. /*                     A notice is generated in case of an empty list.       */
  622. /*                  CL_PM:                                                   */
  623. /*                     The output is generated for the PM interface. Only    */
  624. /*                     executable entries are included. No other lines       */
  625. /*                     were generated.                                       */
  626. /*                                                                           */
  627. /*  note          : there should only be one thread which has the            */
  628. /*                  exclusive access to the list.                            */
  629. /*****************************************************************************/
  630. char *CreateList(int flag)
  631. {
  632.    ULONG i;
  633.    size_t buflen,len,pos;
  634.    LIST_ENTRY *e;
  635.    char *buf = NULL,*new;
  636.    if (flag == CL_USER) {               /* check out an empty table          */
  637.       if ((buf = malloc(200)) == NULL)
  638.          return(NULL);
  639.       sprintf(buf,Get(IDS_ThisIsTheJobList),
  640.                   GetTimeString());
  641.       if (LookupEntryNum(1,0) == NULL) {  /* empty list?                     */
  642.          strcat(buf,Get(IDS_NoJobList));
  643.          return(buf);
  644.       }
  645.       buflen = strlen(buf) + 1;         /* 1 = termination 0                 */
  646.    } else
  647.       buflen = 1;                       /* 1 = termination 0                 */
  648.    if ((e = LookupEntryNum(1,(flag == CL_FILE) ? 1 : 0)) == NULL) {
  649.       if (buf != NULL)                  /* should never happen...            */
  650.          free(buf);
  651.       return(NULL);
  652.    }
  653.    i = 1;
  654.    while (e != NULL) {
  655.       len = strlen(e->s);
  656.       pos = buflen - 1;                 /* appendance position               */
  657.       buflen += len + 2;                /* data + "\r\n"                     */
  658.       if (flag == CL_USER)
  659.          buflen += 12;                    /* 12 for numbering                */
  660.       if ((new = realloc(buf,buflen)) == NULL) {
  661.          free(buf);
  662.          return(NULL);
  663.       }
  664.       buf = new;
  665.       if (flag == CL_USER)
  666.          sprintf(buf + pos,"%10lu: %s\r\n",i,e->s);
  667.       else
  668.          sprintf(buf + pos,"%s\r\n",e->s);
  669.  
  670.       e = LookupEntryNum(++i,(flag == CL_FILE) ? 1 : 0); /* next entry       */
  671.    }
  672.    return(buf);
  673. }
  674.  
  675. /*****************************************************************************/
  676. /*  function name : ReWrite                                                  */
  677. /*                                                                           */
  678. /*  return value  : 0 on success, errno otherwise                            */
  679. /*                                                                           */
  680. /*  description   : rewrites the complete job table including the comments   */
  681. /*                  to the Crontabs file. A special flag AutoReWrite is      */
  682. /*                  maintained in case of an error. This flag can be used    */
  683. /*                  to write the table after a time. Thus, the possibility   */
  684. /*                  of loosing the complete table is minimized.              */
  685. /*                                                                           */
  686. /*  note          : there should only be one thread which has the            */
  687. /*                  exclusive access to the list.                            */
  688. /*                  The AutoReWrite flag has not been exported. In case      */
  689. /*                  of changing the Crontabs file the old file may been      */
  690. /*                  left in an unexpected state.                             */
  691. /*****************************************************************************/
  692. static int ReWrite(void)
  693. {
  694.    size_t len;
  695.    char *buf;
  696.    int htype;
  697.  
  698.    ioctl(cronhandle,FGETHTYPE,&htype);
  699.    if (htype == HT_DEV_NUL) {           /* There is no need to rewrite the   */
  700.       AutoReWrite = 0;                  /* "nul" device                      */
  701.       return(0);
  702.    }
  703.    buf = CreateList(CL_FILE);
  704.    AutoReWrite = 1;                     /* good assumption                   */
  705.    lseek(cronhandle,0l,SEEK_SET);
  706.    if (buf == NULL) {                   /* no buffer?                        */
  707.       if (ListHead.next != NULL)        /* but any data?                     */
  708.          return(ENOMEM);                /* this is an error                  */
  709.                                         /* else: empty file                  */
  710.       if (ftruncate(cronhandle,0l) == -1)
  711.          return(errno);
  712.       fsync(cronhandle);                /* does ftruncate make a SYNC ?      */
  713.       DosResetBuffer(cronhandle);       /* let OS/2 update the directory     */
  714.       return(0);
  715.    }
  716.  
  717.    len = strlen(buf);                   /* some data available               */
  718.    if (ftruncate(cronhandle,(long) len) == -1)  /* first, try to truncate    */
  719.       return(errno);                    /* the file size                     */
  720.    errno = 0;                           /* assume no error                   */
  721.    if (write(cronhandle,buf,len) != len) {   /* shit! Maybe, the disk is full*/
  722.       if (errno == 0)                   /* but some data of the Crontabs     */
  723.          errno = ENOSPC;                /* may have been overwritten. This   */
  724.       free(buf);                        /* is a critical error.              */
  725.       return(errno);
  726.    }
  727.  
  728.    DosResetBuffer(cronhandle);          /* let OS/2 update the directory     */
  729.    free(buf);
  730.    AutoReWrite = 0;                     /* no need to rewrite                */
  731.    return(0);
  732. }
  733.  
  734. /*****************************************************************************/
  735. /*  function name : DeleteEntryNum                                           */
  736. /*                                                                           */
  737. /*  arguments     : number of the entry, buffer to hold the answer in case   */
  738. /*                  of success (approx. 1K is sufficent)                     */
  739. /*                                                                           */
  740. /*  return value  : 0 on success (buffer filled), errno otherwise (buffer    */
  741. /*                  not filled)                                              */
  742. /*                                                                           */
  743. /*  description   : deletes the job entry with the given number from the     */
  744. /*                  current job list. The Crontabs file is rewritten.        */
  745. /*                                                                           */
  746. /*  note          : there should only be one thread which has the            */
  747. /*                  exclusive access to the list.                            */
  748. /*****************************************************************************/
  749. int DeleteEntryNum(ULONG num,char *replybuf)
  750. {
  751.    int err;
  752.    if ((err = DeleteListEntry(LookupEntryNum(num,0),&ListHead)) != 0)
  753.       return(err);
  754.    if (ReWrite() != 0)
  755.       strcpy(replybuf,Get(IDS_JobDeletedNotWritten));
  756.    else
  757.       strcpy(replybuf,Get(IDS_JobDeleted));
  758.    return(0);
  759. }
  760.  
  761. /*****************************************************************************/
  762. /*  function name : NewEntry                                                 */
  763. /*                                                                           */
  764. /*  arguments     : string holding a new Crontabs statement, buffer to       */
  765. /*                  hold the answer in case of success (approx. 1K is        */
  766. /*                  sufficent)                                               */
  767. /*                                                                           */
  768. /*  return value  : 0 on success (buffer filled), errno otherwise (buffer    */
  769. /*                  not filled)                                              */
  770. /*                                                                           */
  771. /*  description   : parses the given string and checks its correctness. In   */
  772. /*                  this case the string is appended to the current job      */
  773. /*                  list and the Crontabs file is rewritten.                 */
  774. /*                                                                           */
  775. /*  note          : there should only be one thread which has the            */
  776. /*                  exclusive access to the list.                            */
  777. /*****************************************************************************/
  778. int NewEntry(char *line,size_t size,char *replybuf)
  779. {
  780.    int err;
  781.    if (!CurrTimeIsOK) {                 /* Don't take the current time stamp */
  782.       time(&currtime);                  /* if the daemon thread is running   */
  783.       currtime_tm = *localtime(&currtime);   /* in the inner loop, we must   */
  784.    }                                    /* prevent a new setting while it    */
  785.                                         /* believes in the unchangable of the*/
  786.                                         /* time stamp!                       */
  787.    if ((err = InsertListEntry(line,size,0,&ListHead)) != 0)
  788.       return(err);
  789.    if (ReWrite() != 0)
  790.       strcpy(replybuf,Get(IDS_JobSavedNotWritten));
  791.    else
  792.       strcpy(replybuf,Get(IDS_JobSaved));
  793.    return(0);
  794. }
  795.  
  796. /*****************************************************************************/
  797. /*  function name : ParseLine                                                */
  798. /*                                                                           */
  799. /*  arguments     : buffer, its size, position where to continue reading     */
  800. /*                  the next line (will be updated), buffer to hold the      */
  801. /*                  length of the parsed line                                */
  802. /*                                                                           */
  803. /*  return value  : NULL if no (more) lines are available else the pointer   */
  804. /*                  to the first character within the next line.             */
  805. /*                                                                           */
  806. /*  description   : This function implements a special "fgets" on a          */
  807. /*                  buffer. The buffer represents a complete file. The       */
  808. /*                  function has been written to allow a complete control    */
  809. /*                  and error detection on files. Another reason to write    */
  810. /*                  this function was the speed of this function.            */
  811. /*                  The function starts working at *size (set to 0 on the    */
  812. /*                  first call!) reading until a newline, a ^Z or the end    */
  813. /*                  of the file (buffer) is reached, whatever comes first.   */
  814. /*                  This start of the search is returned which is the        */
  815. /*                  start of the current line. Its length is put into the    */
  816. /*                  length buffer and start is updated allowing a            */
  817. /*                  consecutively calling to this function.                  */
  818. /*                                                                           */
  819. /*  note          : The buffer must been 0-terminated!                       */
  820. /*****************************************************************************/
  821. const char *ParseLine(const char *buf,size_t bufsize,size_t *start,size_t *len)
  822. {
  823.    size_t l = 0;
  824.    const char *ptr;
  825.    const char *retval = buf + *start;
  826.    if (*start >= bufsize)               /* nothing more to do?               */
  827.       return(NULL);
  828.    if ((ptr = strchr(retval,'\n')) == NULL) { /* no more newlines in the buf?*/
  829.       l = bufsize - *start;             /* assume some characters left       */
  830.       *start = bufsize;
  831.       while (isspace(*retval) && (l > 0)) {  /* eat leading whitespace of the*/
  832.          l--;                           /* current line                      */
  833.          retval++;
  834.       }
  835.       if ((l == 0) ||                   /* no characters                     */
  836.           ((l == 1) && (*retval == '\x1A'))) /* ^Z is the only char on the   */
  837.          return(NULL);                  /* line, that's OK                   */
  838.       *len = l;
  839.       return(retval);
  840.    }
  841.                                         /* lineend exists                    */
  842.    l = (ULONG) ptr - (ULONG) retval + 1;  /* length including '\n'           */
  843.    *start += l;
  844.    *len = l;
  845.    return(retval);
  846. }
  847.  
  848. /*****************************************************************************/
  849. /*  function name : ReadInFile                                               */
  850. /*                                                                           */
  851. /*  arguments     : handle of an open file, head of the list where to        */
  852. /*                  append all valid lines.                                  */
  853. /*                                                                           */
  854. /*  return value  : 0 on success, errno otherwise                            */
  855. /*                                                                           */
  856. /*  description   : reads a Crontabs file completely and builds a job        */
  857. /*                  list. The file must be opened in O_BINARY mode.          */
  858. /*                                                                           */
  859. /*  note          : there should only be one thread which has the            */
  860. /*                  exclusive access to the list. The handle remains open.   */
  861. /*****************************************************************************/
  862. int ReadInFile(int handle,LIST_ENTRY *head)
  863. {
  864.    char *buf;
  865.    const char *ptr;
  866.    long flen;
  867.    size_t pos,len;
  868.    int err,htype;
  869.  
  870.    ioctl(handle,FGETHTYPE,&htype);
  871.    if (htype == HT_DEV_NUL)             /* we don't have to do anything      */
  872.       return(0);                        /* while reading the "nul" device    */
  873.    time(&currtime);                     /* needed to recompute the next      */
  874.    currtime_tm = *localtime(&currtime); /* starting times of the jobs        */
  875.    if ((flen = filelength(handle)) == -1l)      /* device?                   */
  876.       return(errno);
  877.    if ((lseek(handle,0l,SEEK_SET)) == -1l)      /* device?                   */
  878.       return(errno);
  879.    if (flen == 0)                       /* empty file?                       */
  880.       return(0);
  881.    if (flen >= MAX_CRONFILESIZE)        /* potential mistake of the user?    */
  882.       return(EACCES);
  883.    if ((buf = malloc((size_t) flen + 1)) == NULL)  /* read the complete file */
  884.       return(ENOMEM);                   /* in one operation                  */
  885.    errno = 0;
  886.    if ((long) read(handle,buf,(size_t) flen) != flen) {  /* do the operation */
  887.       if (errno == 0)
  888.          errno = EIO;
  889.       free(buf);
  890.       return(errno);
  891.    }
  892.    buf[(size_t) flen] = 0;              /* terminate the buffer              */
  893.                                         /* now we can use our fast function  */
  894.    pos = 0;
  895.    while ((ptr = ParseLine(buf,(size_t) flen,&pos,&len)) != NULL) {
  896.       if ((err = InsertListEntry(ptr,len,1,head)) != 0) {
  897.          free(buf);                     /* error? free up the buffer and     */
  898.          while (DeleteListEntry(head->next,head) == 0)   /* delete all       */
  899.             ;                           /* already inserted jobs             */
  900.          return(err);
  901.       }
  902.    }
  903.    free(buf);
  904.    return(0);
  905. }
  906.  
  907. /*****************************************************************************/
  908. /*  function name : ComputeNextStartingTimes                                 */
  909. /*                                                                           */
  910. /*  return value  : time of the job starting time that has to start next     */
  911. /*                                                                           */
  912. /*  description   : computes the starting time for all periodically          */
  913. /*                  starting jobs and returns the time of the job which      */
  914. /*                  has to be started next.                                  */
  915. /*                                                                           */
  916. /*  note          : there should only be one thread which has the            */
  917. /*                  exclusive access to the list. currtime must been set.    */
  918. /*****************************************************************************/
  919. static time_t ComputeNextStartingTimes(void)
  920. {
  921.    LIST_ENTRY *run = ListHead.next;
  922.    time_t retval = (time_t) -1,next,TomorrowMorning = (time_t) -1;
  923.    char timebuf[40];
  924.    while (run != NULL) {
  925.       if (!run->AtStartup && !run->AtExit && !run->Deletable &&
  926.                                                              !run->IsComment) {
  927.                                         /* ignore all not periodically       */
  928.                                         /* starting jobs                     */
  929.          if (run->Daily) {
  930.             if (TomorrowMorning == (time_t) -1) {
  931.                TomorrowMorning = currtime;
  932.                TomorrowMorning -= currtime_tm.tm_hour * 60 * 60 +
  933.                                   currtime_tm.tm_min * 60 +
  934.                                   currtime_tm.tm_sec;
  935.                                         /* back to the start of the current  */
  936.                                         /* day                               */
  937.                TomorrowMorning += 24 * 60 * 60; /* add one day               */
  938.             }
  939.             next = TomorrowMorning;
  940.          } else
  941.             next = ComputeNextStart(run,currtime,DAY_LOOKAHEAD);
  942.          if (PrintEachStartingTime) {
  943.             if (next == (time_t) -1)
  944.                strcpy(timebuf,"(unknown)");
  945.             else
  946.                GetTime(timebuf,sizeof(timebuf),next);
  947.             Message("DEBUG: start of \"%s\" at %s\n",
  948.                     run->StartCmd,timebuf);
  949.          }
  950.          if (next != (time_t) -1) {     /* valid time computed?              */
  951.             if (retval == (time_t) -1)
  952.                retval = next;
  953.             else if (next < retval)
  954.                retval = next;
  955.          }
  956.       }
  957.       run = run->next;
  958.    }
  959.    return(retval);
  960. }
  961.  
  962. /*****************************************************************************/
  963. /*  function name : RunJobs                                                  */
  964. /*                                                                           */
  965. /*  arguments     : flags: run the AtStartup jobs, run the AtExit jobs,      */
  966. /*                         run Daily jobs                                    */
  967. /*                                                                           */
  968. /*  description   : runs the jobs with the given flag. If no flag is set     */
  969. /*                  the periodical jobs were checked and run if outdated.    */
  970. /*                  Jobs with the "Once" flag where deleted before           */
  971. /*                  startting the execution.                                 */
  972. /*                                                                           */
  973. /*  note          : there should only be one thread which has the            */
  974. /*                  exclusive access to the ListHead.                        */
  975. /*                  currtime must been set.                                  */
  976. /*                  You should call ComputeNextStartingTimes after calling   */
  977. /*                  this function.                                           */
  978. /*****************************************************************************/
  979. static void RunJobs(unsigned AtStartup,unsigned AtExit,unsigned Daily)
  980. {
  981.    int start;
  982.    LIST_ENTRY *run = ListHead.next;
  983.    LIST_ENTRY *specialjob,*specialnext;
  984.    while (run != NULL) {
  985.       start = 0;
  986.                                         /* determine wether to start the job */
  987.       if (AtStartup && run->AtStartup)
  988.          start = 1;
  989.       else if (AtExit && run->AtExit)
  990.          start = 1;
  991.       else if (Daily && run->Daily)
  992.          start = 1;
  993.       else if (!AtStartup && !AtExit)   /* periodically starting jobs        */
  994.          if (!run->AtStartup && !run->AtExit && !run->Daily)   /* right job  */
  995.                                         /* type?                             */
  996.             if (run->NextStartTime <= currtime) /* job outdated?             */
  997.                start = 1;
  998.       if (!start) {
  999.          run = run->next;
  1000.          continue;
  1001.       }
  1002.                                         /* Well, we won't run into race      */
  1003.                                         /* conditions, look at this example: */
  1004.                                         /* Some jobs:                        */
  1005.                                         /* "Once Cronstart setboot /B"       */
  1006.                                         /* (other jobs that will be started  */
  1007.                                         /* too, loading costs some time)     */
  1008.                                         /* The first job will be started and */
  1009.                                         /* the system shuts down before any  */
  1010.                                         /* writing will occur. On the next   */
  1011.                                         /* system start this will happen too.*/
  1012.                                         /* Thus, we run into an endless      */
  1013.                                         /* booting loop.                     */
  1014.  
  1015.       if (!run->Once) {                 /* no need to rewrite, simple        */
  1016.          StartProcess(run);
  1017.          run = run->next;
  1018.          continue;
  1019.       }
  1020.       specialnext = run->next;          /* save the next entry to proceed    */
  1021.       if ((specialjob = ExtractListEntry(run,&ListHead)) == NULL) {  /* ???? */
  1022.          run = run->next;
  1023.          continue;
  1024.       }
  1025.       ReWrite();                        /* rewrite the new job list first!!  */
  1026.       JobsModified();                   /* tell PM the changes               */
  1027.       run = specialnext;                /* set new running variable          */
  1028.       StartProcess(specialjob);         /* do the work                       */
  1029.       CleanListEntry(specialjob);       /* delete the extracted entry        */
  1030.    }
  1031. }
  1032.  
  1033. /*****************************************************************************/
  1034. /*  function name : SetDebugFlags                                            */
  1035. /*                                                                           */
  1036. /*  arguments     : contents of the environment variable called              */
  1037. /*                  CROND_DEBUG, buffer to hold the default maximum          */
  1038. /*                  sleeping time of this thread                             */
  1039. /*                                                                           */
  1040. /*  description   : evaluates the contents of the environment variable and   */
  1041. /*                  sets the appropriate variables.                          */
  1042. /*                  Within the string we determine the following options:    */
  1043. /*                  -s    print time, this thread will sleep                 */
  1044. /*                  -t    print each starting time of a job                  */
  1045. /*                  -w t  set maximum sleeping time to t (in seconds)        */
  1046. /*                  The options must be separated by spaces.                 */
  1047. /*                  The last value is copied to the argument buffer.         */
  1048. /*****************************************************************************/
  1049. static void SetDebugFlags(const char *flags,time_t *DefaultWakeup)
  1050. {
  1051.    const char *s;
  1052.    char *ptr;
  1053.    ULONG len,val = 0ul;
  1054.    if (flags == NULL)
  1055.       return;
  1056.    BlockProcess();                      /* NextWord uses static buffers!     */
  1057.    s = NextWord(flags,&len);
  1058.    while (len > 0) {
  1059.       if (strncmp("-s",s,(size_t) len) == 0) {
  1060.          PrintSleepingTime = 1;
  1061.          Message("DEBUG: the sleeping time will be displayed each going into "
  1062.                                                                     "sleep\n");
  1063.       }
  1064.       else if (strncmp("-t",s,(size_t) len) == 0) {
  1065.          PrintEachStartingTime = 1;
  1066.          Message("DEBUG: each computed starting time will be displayed\n");
  1067.       }
  1068.       else if (strncmp("-w",s,2) == 0) {
  1069.          if (len > 2)                   /* time follows -w?                  */
  1070.             val = strtoul(s + 2,&ptr,10);
  1071.          else {
  1072.             s = NextWord(NULL,&len);
  1073.             if (len == 0)
  1074.                ptr = "x";               /* set ptr to "not space"            */
  1075.             else
  1076.                val = strtoul(s,&ptr,10);
  1077.          }
  1078.          if ((val < 24ul * 3600ul) && ((*ptr == '\0') || (isspace(*ptr)))) {
  1079.             *DefaultWakeup = (time_t) val;
  1080.             Message("DEBUG: max. sleeping time is set to %lu seconds\n",val);
  1081.          }
  1082.       }
  1083.       s = NextWord(NULL,&len);
  1084.    }
  1085.    UnBlockProcess();
  1086. }
  1087.  
  1088. /*****************************************************************************/
  1089. /*  function name : CheckNewDay                                              */
  1090. /*                                                                           */
  1091. /*  return value  : bitcoded: 1 = run daily jobs                             */
  1092. /*                            2 = problems writing data to disk, try this    */
  1093. /*                                function again at a later time             */
  1094. /*                                                                           */
  1095. /*  description   : figures out if a new day has started since the last      */
  1096. /*                  change. The value is saved in the profile data. If       */
  1097. /*                  an error occurs, a 2 is or'ed into the return value.     */
  1098. /*****************************************************************************/
  1099. static unsigned CheckNewDay(void)
  1100. {
  1101.    ULONG NewDate = ((ULONG) (currtime_tm.tm_year + 1900) << 16) |
  1102.                    ((ULONG) (currtime_tm.tm_mon) << 8) |
  1103.                    (ULONG) currtime_tm.tm_mday;
  1104.    if (NewDate != LastDayStarted) {
  1105.       LastDayStarted = NewDate;
  1106.       ReWritePrfFlags |= REWRITE_DATE;
  1107.       if (!ReWritePrf())
  1108.          return(3);                     /* new day start and problems writing*/
  1109.                                         /* profile data                      */
  1110.       return(1);                        /* new day                           */
  1111.    }
  1112.    if (!ReWritePrf())                   /* problems writing profile to disk? */
  1113.       return(2);                        /* return problem error              */
  1114.    return(0);
  1115. }
  1116.  
  1117. /*****************************************************************************/
  1118. /*  function name : CronThread                                               */
  1119. /*                                                                           */
  1120. /*  arguments     : not needed, must be void *                               */
  1121. /*                                                                           */
  1122. /*  description   : this is a thread function. It makes all the work that    */
  1123. /*                  needs to be done periodically.                           */
  1124. /*                  Stops running if GlobalStop is set. The function         */
  1125. /*                  automatically disconnects the pipe or closes the         */
  1126. /*                  socket if a timeout is set. Post the CronSem to awake    */
  1127. /*                  this function.                                           */
  1128. /*                  The maximum sleeping time is taken from WAKEUP or from   */
  1129. /*                  a environment variable CROND_DEBUG.                      */
  1130. /*                                                                           */
  1131. /*  note          : should be called via _beginthread                        */
  1132. /*****************************************************************************/
  1133. void CronDaemon(void *dummy)
  1134. {
  1135.    time_t nextrestart,DefaultWakeup = WAKEUP;
  1136.    ULONG posts,
  1137.          maxpostcnt;                    /* expected posts to our semaphore   */
  1138.    int hlp;
  1139.    unsigned dailycheck;
  1140.    SetDebugFlags(getenv("CROND_DEBUG"),&DefaultWakeup);
  1141.    signal(SIGCHLD,ChildDies);           /* all signals should never occur    */
  1142.    signal(SIGINT,SIG_IGN);              /* but it's not wrong to set handlers*/
  1143.    signal(SIGBREAK,SIG_IGN);            /* We won't be killed by a           */
  1144.    if (!setjmp(Continue)) {             /* misprogrammed runtime system thus,*/
  1145.                                         /* we set them.                      */
  1146.       signal(SIGTERM,LocalStop);
  1147.       time(&currtime);
  1148.       currtime_tm = *localtime(&currtime);
  1149.       DosSleep(1000);                   /* wait for the main thread to       */
  1150.                                         /* become ready                      */
  1151.       if (ProgramFlags & PRG_RUNATSTARTUP) {
  1152.          NewProgramStatus(IDS_StatusAtStartup);
  1153.          dailycheck = CheckNewDay();
  1154.          BlockProcess();
  1155.          RunJobs(1,0,dailycheck & 1);
  1156.          UnBlockProcess();
  1157.       }
  1158.       NewProgramStatus(IDS_StatusNormal);
  1159.       maxpostcnt = 0;                   /* no posts expected                 */
  1160.       while (!GlobalStop) {
  1161.          time(&currtime);
  1162.          currtime_tm = *localtime(&currtime);
  1163.          CurrTimeIsOK = 1;              /* Don't allow NewEntry to get its   */
  1164.                                         /* own time                          */
  1165.          dailycheck = CheckNewDay();
  1166.          if (currtime > AutoDisConnect) { /* timeout of the pipe?            */
  1167.             Message(Get(IDS_AutoCloseingPipe));
  1168.             AutoDisConnect = (time_t) -1l;
  1169.             DosDisConnectNPipe(pipehandle);
  1170.          }
  1171.          if (currtime > AutoCloseSocket) {   /* timeout of the socket?       */
  1172.             Message(Get(IDS_AutoCloseingSocket));
  1173.             AutoCloseSocket = (time_t) -1l;
  1174.             hlp = CommSock;
  1175.             CommSock = -1;
  1176.             close(hlp);
  1177.          }
  1178.          ReapChildren();
  1179.          BlockProcess();
  1180.          RunJobs(0,0,dailycheck & 1);   /* some jobs out of date?            */
  1181.          nextrestart = ComputeNextStartingTimes();
  1182.          NewStartTime(nextrestart);
  1183.          if (AutoReWrite)
  1184.             ReWrite();
  1185.          if ((AutoDisConnect != (time_t) -1) && (AutoDisConnect < nextrestart))
  1186.             nextrestart = AutoDisConnect;
  1187.          if ((AutoCloseSocket != (time_t) -1) && (AutoCloseSocket <
  1188.                                                                   nextrestart))
  1189.             nextrestart = AutoCloseSocket;
  1190.          time(&currtime);
  1191.          currtime_tm = *localtime(&currtime);
  1192.                                         /* do we have problems with the      */
  1193.                                         /* writing of the profile? Try again */
  1194.                                         /* after 10 seconds.                 */
  1195.          if ((dailycheck & 2) && (nextrestart > currtime + 10))
  1196.             nextrestart = currtime + 10;
  1197.          CurrTimeIsOK = 0;              /* Allow NewEntry to get its own time*/
  1198.          posts = 0;
  1199.          DosResetEventSem(CronSem,&posts);
  1200.          UnBlockProcess();
  1201.          if (GlobalStop)
  1202.             break;
  1203.          if ((nextrestart < currtime) || (posts > maxpostcnt)) {
  1204.                                         /* Are there intermediate posts to   */
  1205.                                         /* our semaphore or do we spend too  */
  1206.                                         /* much time in the routine above?   */
  1207.             DosSleep(1);                /* give up current time slice        */
  1208.             maxpostcnt = 0;             /* no posts expected                 */
  1209.             continue;                   /* and restart                       */
  1210.          }
  1211.          maxpostcnt = 1;                /* we'll expect one post in case of  */
  1212.                                         /* a successfull WaitEventSem        */
  1213.          if (currtime + DefaultWakeup < nextrestart)  /* sleep no longer than*/
  1214.             nextrestart = currtime + DefaultWakeup;   /* DefaultWakeup       */
  1215.                                         /* add one second. The timer may     */
  1216.                                         /* count incorrectly by some ticks.  */
  1217.          if (PrintSleepingTime)
  1218.             Message("DEBUG: sleeping %lu seconds\n",
  1219.                     (unsigned long) (nextrestart - currtime + 1));
  1220.          if (DosWaitEventSem(CronSem,
  1221.                              (ULONG) (nextrestart - currtime + 1) * 1000) != 0)
  1222.             maxpostcnt = 0;             /* don't expect posts in case of     */
  1223.       }                                 /* timeouts or interrupts            */
  1224.    }
  1225.    if (!setjmp(Continue))
  1226.       if ((GlobalStop <= 1) && (ProgramFlags & PRG_RUNATSTARTUP)) {
  1227.          NewProgramStatus(IDS_StatusAtExit);
  1228.          ReapChildren();
  1229.          BlockProcess();
  1230.          RunJobs(0,1,0);                /* no daily jobs!                    */
  1231.          UnBlockProcess();
  1232.       }
  1233.  
  1234.    if (!setjmp(Continue))
  1235.       if (AutoReWrite)
  1236.          ReWrite();
  1237.    if (GlobalStop <= 1) {
  1238.       time(&nextrestart);
  1239.       do {
  1240.          DosSleep(500);
  1241.       } while (ReapChildren() && (time(NULL) + 5 < nextrestart));
  1242.    }
  1243.    ShowStillRunnings();
  1244.    StopPM();
  1245. }
  1246.  
  1247. /* RCS depending informations
  1248.  *
  1249.  * $Name: Version121 $
  1250.  *
  1251.  * $Log: tables.c $
  1252.  * Revision 1.5  1995/10/18 09:46:10  Florian
  1253.  * Some cosmetic changes.
  1254.  * Editing of an entry is supported now.
  1255.  * Daily and intervals introduced.
  1256.  * Race conditions while reaping children and receiving signals eleminated.
  1257.  *
  1258.  * Revision 1.4  1995/03/30 16:50:20  Florian
  1259.  * Debug code added.
  1260.  * An ugly time computing error fixed.
  1261.  *
  1262.  * Revision 1.3  1995/03/15 09:07:34  Florian
  1263.  * Some minor bugs fixed.
  1264.  * TCP/IP support added.
  1265.  *
  1266.  * Revision 1.2  1995/02/20 12:53:23  Florian
  1267.  * All dialogs are placed into a notebook.
  1268.  * Some bugs fixed.
  1269.  *
  1270.  * Revision 1.1  1995/02/03 10:42:51  Florian
  1271.  * Initial revision
  1272.  *
  1273.  *
  1274.  */
  1275. static char rcsid[] = "@(#)$Id: tables.c 1.5 1995/10/18 09:46:10 Florian Rel $";
  1276.