tab.c

Go to the documentation of this file.
00001 /*      tab.c - process crontabs and create in-core crontab data
00002  *                                                      Author: Kees J. Bot
00003  *                                                              7 Dec 1996
00004  * Changes:
00005  * 17 Jul 2000 by Philip Homburg
00006  *      - Tab_reschedule() rewritten (and fixed).
00007  */
00008 #define nil ((void*)0)
00009 #include <sys/types.h>
00010 #include <assert.h>
00011 #include <stdio.h>
00012 #include <unistd.h>
00013 #include <fcntl.h>
00014 #include <stdlib.h>
00015 #include <string.h>
00016 #include <errno.h>
00017 #include <limits.h>
00018 #include <time.h>
00019 #include <dirent.h>
00020 #include <sys/stat.h>
00021 #include "misc.h"
00022 #include "tab.h"
00023 
00024 static int nextbit(bitmap_t map, int bit)
00025 /* Return the next bit set in 'map' from 'bit' onwards, cyclic. */
00026 {
00027         for (;;) {
00028                 bit= (bit+1) & 63;
00029                 if (bit_isset(map, bit)) break;
00030         }
00031         return bit;
00032 }
00033 
00034 void tab_reschedule(cronjob_t *job)
00035 /* Reschedule one job.  Compute the next time to run the job in job->rtime.
00036  */
00037 {
00038         struct tm prevtm, nexttm, tmptm;
00039         time_t nodst_rtime, dst_rtime;
00040 
00041         /* AT jobs are only run once. */
00042         if (job->atjob) { job->rtime= NEVER; return; }
00043 
00044         /* Was the job scheduled late last time? */
00045         if (job->late) job->rtime= now;
00046 
00047         prevtm= *localtime(&job->rtime);
00048         prevtm.tm_sec= 0;
00049 
00050         nexttm= prevtm;
00051         nexttm.tm_min++;        /* Minimal increment */
00052 
00053         for (;;)
00054         {
00055                 if (nexttm.tm_min > 59)
00056                 {
00057                         nexttm.tm_min= 0;
00058                         nexttm.tm_hour++;
00059                 }
00060                 if (nexttm.tm_hour > 23)
00061                 {
00062                         nexttm.tm_min= 0;
00063                         nexttm.tm_hour= 0;
00064                         nexttm.tm_mday++;
00065                 }
00066                 if (nexttm.tm_mday > 31)
00067                 {
00068                         nexttm.tm_hour= nexttm.tm_min= 0;
00069                         nexttm.tm_mday= 1;
00070                         nexttm.tm_mon++;
00071                 }
00072                 if (nexttm.tm_mon >= 12)
00073                 {
00074                         nexttm.tm_hour= nexttm.tm_min= 0;
00075                         nexttm.tm_mday= 1;
00076                         nexttm.tm_mon= 0;
00077                         nexttm.tm_year++;
00078                 }
00079 
00080                 /* Verify tm_year. A crontab entry cannot restrict tm_year
00081                  * directly. However, certain dates (such as Feb, 29th) do
00082                  * not occur every year. We limit the difference between
00083                  * nexttm.tm_year and prevtm.tm_year to detect impossible dates
00084                  * (e.g, Feb, 31st). In theory every date occurs within a
00085                  * period of 4 years. However, some years at the end of a 
00086                  * century are not leap years (e.g, the year 2100). An extra
00087                  * factor of 2 should be enough.
00088                  */
00089                 if (nexttm.tm_year-prevtm.tm_year > 2*4)
00090                 {
00091                         job->rtime= NEVER;
00092                         return;                 /* Impossible combination */
00093                 }
00094 
00095                 if (!job->do_wday)
00096                 {
00097                         /* Verify the mon and mday fields. If do_wday and
00098                          * do_mday are both true we have to merge both
00099                          * schedules. This is done after the call to mktime.
00100                          */
00101                         if (!bit_isset(job->mon, nexttm.tm_mon))
00102                         {
00103                                 /* Clear other fields */
00104                                 nexttm.tm_mday= 1;
00105                                 nexttm.tm_hour= nexttm.tm_min= 0;
00106 
00107                                 nexttm.tm_mon++;
00108                                 continue;
00109                         }
00110 
00111                         /* Verify mday */
00112                         if (!bit_isset(job->mday, nexttm.tm_mday))
00113                         {
00114                                 /* Clear other fields */
00115                                 nexttm.tm_hour= nexttm.tm_min= 0;
00116 
00117                                 nexttm.tm_mday++;
00118                                 continue;
00119                         }
00120                 }
00121 
00122                 /* Verify hour */
00123                 if (!bit_isset(job->hour, nexttm.tm_hour))
00124                 {
00125                         /* Clear tm_min field */
00126                         nexttm.tm_min= 0;
00127 
00128                         nexttm.tm_hour++;
00129                         continue;
00130                 }
00131 
00132                 /* Verify min */
00133                 if (!bit_isset(job->min, nexttm.tm_min))
00134                 {
00135                         nexttm.tm_min++;
00136                         continue;
00137                 }
00138 
00139                 /* Verify that we don't have any problem with DST. Try
00140                  * tm_isdst=0 first. */
00141                 tmptm= nexttm;
00142                 tmptm.tm_isdst= 0;
00143 #if 0
00144                 fprintf(stderr, 
00145         "tab_reschedule: trying %04d-%02d-%02d %02d:%02d:%02d isdst=0\n",
00146                                 1900+nexttm.tm_year, nexttm.tm_mon+1,
00147                                 nexttm.tm_mday, nexttm.tm_hour,
00148                                 nexttm.tm_min, nexttm.tm_sec);
00149 #endif
00150                 nodst_rtime= job->rtime= mktime(&tmptm);
00151                 if (job->rtime == -1) {
00152                         /* This should not happen. */
00153                         log(LOG_ERR,
00154                         "mktime failed for %04d-%02d-%02d %02d:%02d:%02d",
00155                                 1900+nexttm.tm_year, nexttm.tm_mon+1,
00156                                 nexttm.tm_mday, nexttm.tm_hour,
00157                                 nexttm.tm_min, nexttm.tm_sec);
00158                         job->rtime= NEVER;
00159                         return; 
00160                 }
00161                 tmptm= *localtime(&job->rtime);
00162                 if (tmptm.tm_hour != nexttm.tm_hour ||
00163                         tmptm.tm_min != nexttm.tm_min)
00164                 {
00165                         assert(tmptm.tm_isdst);
00166                         tmptm= nexttm;
00167                         tmptm.tm_isdst= 1;
00168 #if 0
00169                         fprintf(stderr, 
00170         "tab_reschedule: trying %04d-%02d-%02d %02d:%02d:%02d isdst=1\n",
00171                                 1900+nexttm.tm_year, nexttm.tm_mon+1,
00172                                 nexttm.tm_mday, nexttm.tm_hour,
00173                                 nexttm.tm_min, nexttm.tm_sec);
00174 #endif
00175                         dst_rtime= job->rtime= mktime(&tmptm);
00176                         if (job->rtime == -1) {
00177                                 /* This should not happen. */
00178                                 log(LOG_ERR,
00179                         "mktime failed for %04d-%02d-%02d %02d:%02d:%02d\n",
00180                                         1900+nexttm.tm_year, nexttm.tm_mon+1,
00181                                         nexttm.tm_mday, nexttm.tm_hour,
00182                                         nexttm.tm_min, nexttm.tm_sec);
00183                                 job->rtime= NEVER;
00184                                 return; 
00185                         }
00186                         tmptm= *localtime(&job->rtime);
00187                         if (tmptm.tm_hour != nexttm.tm_hour ||
00188                                 tmptm.tm_min != nexttm.tm_min)
00189                         {
00190                                 assert(!tmptm.tm_isdst);
00191                                 /* We have a problem. This time neither
00192                                  * exists with DST nor without DST.
00193                                  * Use the latest time, which should be
00194                                  * nodst_rtime.
00195                                  */
00196                                 assert(nodst_rtime > dst_rtime);
00197                                 job->rtime= nodst_rtime;
00198 #if 0
00199                                 fprintf(stderr,
00200                         "During DST trans. %04d-%02d-%02d %02d:%02d:%02d\n",
00201                                         1900+nexttm.tm_year, nexttm.tm_mon+1,
00202                                         nexttm.tm_mday, nexttm.tm_hour,
00203                                         nexttm.tm_min, nexttm.tm_sec);
00204 #endif
00205                         }
00206                 }
00207 
00208                 /* Verify this the combination (tm_year, tm_mon, tm_mday). */
00209                 if (tmptm.tm_mday != nexttm.tm_mday ||
00210                         tmptm.tm_mon != nexttm.tm_mon ||
00211                         tmptm.tm_year != nexttm.tm_year)
00212                 {
00213                         /* Wrong day */
00214 #if 0
00215                         fprintf(stderr, "Wrong day\n");
00216 #endif
00217                         nexttm.tm_hour= nexttm.tm_min= 0;
00218                         nexttm.tm_mday++;
00219                         continue;
00220                 }
00221 
00222                 /* Check tm_wday */
00223                 if (job->do_wday && bit_isset(job->wday, tmptm.tm_wday))
00224                 {
00225                         /* OK, wday matched */
00226                         break;
00227                 }
00228 
00229                 /* Check tm_mday */
00230                 if (job->do_mday && bit_isset(job->mon, tmptm.tm_mon) &&
00231                         bit_isset(job->mday, tmptm.tm_mday))
00232                 {
00233                         /* OK, mon and mday matched */
00234                         break;
00235                 }
00236 
00237                 if (!job->do_wday && !job->do_mday)
00238                 {
00239                         /* No need to match wday and mday */
00240                         break;
00241                 }
00242 
00243                 /* Wrong day */
00244 #if 0
00245                 fprintf(stderr, "Wrong mon+mday or wday\n");
00246 #endif
00247                 nexttm.tm_hour= nexttm.tm_min= 0;
00248                 nexttm.tm_mday++;
00249         }
00250 #if 0
00251         fprintf(stderr, "Using %04d-%02d-%02d %02d:%02d:%02d \n",
00252                 1900+nexttm.tm_year, nexttm.tm_mon+1, nexttm.tm_mday,
00253                 nexttm.tm_hour, nexttm.tm_min, nexttm.tm_sec);
00254         tmptm= *localtime(&job->rtime);
00255         fprintf(stderr, "Act. %04d-%02d-%02d %02d:%02d:%02d isdst=%d\n",
00256                 1900+tmptm.tm_year, tmptm.tm_mon+1, tmptm.tm_mday,
00257                 tmptm.tm_hour, tmptm.tm_min, tmptm.tm_sec,
00258                 tmptm.tm_isdst);
00259 #endif
00260 
00261 
00262         /* Is job issuing lagging behind with the progress of time? */
00263         job->late= (job->rtime < now);
00264 
00265         /* The result is in job->rtime. */
00266         if (job->rtime < next) next= job->rtime;
00267 }
00268 
00269 #define isdigit(c)      ((unsigned) ((c) - '0') < 10)
00270 
00271 static char *get_token(char **ptr)
00272 /* Return a pointer to the next token in a string.  Move *ptr to the end of
00273  * the token, and return a pointer to the start.  If *ptr == start of token
00274  * then we're stuck against a newline or end of string.
00275  */
00276 {
00277         char *start, *p;
00278 
00279         p= *ptr;
00280         while (*p == ' ' || *p == '\t') p++;
00281 
00282         start= p;
00283         while (*p != ' ' && *p != '\t' && *p != '\n' && *p != 0) p++;
00284         *ptr= p;
00285         return start;
00286 }
00287 
00288 static int range_parse(char *file, char *data, bitmap_t map,
00289         int min, int max, int *wildcard)
00290 /* Parse a comma separated series of 'n', 'n-m' or 'n:m' numbers.  'n'
00291  * includes number 'n' in the bit map, 'n-m' includes all numbers between
00292  * 'n' and 'm' inclusive, and 'n:m' includes 'n+k*m' for any k if in range.
00293  * Numbers must fall between 'min' and 'max'.  A '*' means all numbers.  A
00294  * '?' is allowed as a synonym for the current minute, which only makes sense
00295  * in the minute field, i.e. max must be 59.  Return true iff parsed ok.
00296  */
00297 {
00298         char *p;
00299         int end;
00300         int n, m;
00301 
00302         /* Clear all bits. */
00303         for (n= 0; n < 8; n++) map[n]= 0;
00304 
00305         p= data;
00306         while (*p != ' ' && *p != '\t' && *p != '\n' && *p != 0) p++;
00307         end= *p;
00308         *p= 0;
00309         p= data;
00310 
00311         if (*p == 0) {
00312                 log(LOG_ERR, "%s: not enough time fields\n", file);
00313                 return 0;
00314         }
00315 
00316         /* Is it a '*'? */
00317         if (p[0] == '*' && p[1] == 0) {
00318                 for (n= min; n <= max; n++) bit_set(map, n);
00319                 p[1]= end;
00320                 *wildcard= 1;
00321                 return 1;
00322         }
00323         *wildcard= 0;
00324 
00325         /* Parse a comma separated series of numbers or ranges. */
00326         for (;;) {
00327                 if (*p == '?' && max == 59 && p[1] != '-') {
00328                         n= localtime(&now)->tm_min;
00329                         p++;
00330                 } else {
00331                         if (!isdigit(*p)) goto syntax;
00332                         n= 0;
00333                         do {
00334                                 n= 10 * n + (*p++ - '0');
00335                                 if (n > max) goto range;
00336                         } while (isdigit(*p));
00337                 }
00338                 if (n < min) goto range;
00339 
00340                 if (*p == '-') {        /* A range of the form 'n-m'? */
00341                         p++;
00342                         if (!isdigit(*p)) goto syntax;
00343                         m= 0;
00344                         do {
00345                                 m= 10 * m + (*p++ - '0');
00346                                 if (m > max) goto range;
00347                         } while (isdigit(*p));
00348                         if (m < n) goto range;
00349                         do {
00350                                 bit_set(map, n);
00351                         } while (++n <= m);
00352                 } else
00353                 if (*p == ':') {        /* A repeat of the form 'n:m'? */
00354                         p++;
00355                         if (!isdigit(*p)) goto syntax;
00356                         m= 0;
00357                         do {
00358                                 m= 10 * m + (*p++ - '0');
00359                                 if (m > (max-min+1)) goto range;
00360                         } while (isdigit(*p));
00361                         if (m == 0) goto range;
00362                         while (n >= min) n-= m;
00363                         while ((n+= m) <= max) bit_set(map, n);
00364                 } else {
00365                                         /* Simply a number */
00366                         bit_set(map, n);
00367                 }
00368                 if (*p == 0) break;
00369                 if (*p++ != ',') goto syntax;
00370         }
00371         *p= end;
00372         return 1;
00373   syntax:
00374         log(LOG_ERR, "%s: field '%s': bad syntax for a %d-%d time field\n",
00375                 file, data, min, max);
00376         return 0;
00377   range:
00378         log(LOG_ERR, "%s: field '%s': values out of the %d-%d allowed range\n",
00379                 file, data, min, max);
00380         return 0;
00381 }
00382 
00383 void tab_parse(char *file, char *user)
00384 /* Parse a crontab file and add its data to the tables.  Handle errors by
00385  * yourself.  Table is owned by 'user' if non-null.
00386  */
00387 {
00388         crontab_t **atab, *tab;
00389         cronjob_t **ajob, *job;
00390         int fd;
00391         struct stat st;
00392         char *p, *q;
00393         size_t n;
00394         ssize_t r;
00395         int ok, wc;
00396 
00397         for (atab= &crontabs; (tab= *atab) != nil; atab= &tab->next) {
00398                 if (strcmp(file, tab->file) == 0) break;
00399         }
00400 
00401         /* Try to open the file. */
00402         if ((fd= open(file, O_RDONLY)) < 0 || fstat(fd, &st) < 0) {
00403                 if (errno != ENOENT) {
00404                         log(LOG_ERR, "%s: %s\n", file, strerror(errno));
00405                 }
00406                 if (fd != -1) close(fd);
00407                 return;
00408         }
00409 
00410         /* Forget it if the file is awfully big. */
00411         if (st.st_size > TAB_MAX) {
00412                 log(LOG_ERR, "%s: %lu bytes is bigger than my %lu limit\n",
00413                         file,
00414                         (unsigned long) st.st_size,
00415                         (unsigned long) TAB_MAX);
00416                 return;
00417         }
00418 
00419         /* If the file is the same as before then don't bother. */
00420         if (tab != nil && st.st_mtime == tab->mtime) {
00421                 close(fd);
00422                 tab->current= 1;
00423                 return;
00424         }
00425 
00426         /* Create a new table structure. */
00427         tab= allocate(sizeof(*tab));
00428         tab->file= allocate((strlen(file) + 1) * sizeof(tab->file[0]));
00429         strcpy(tab->file, file);
00430         tab->user= nil;
00431         if (user != nil) {
00432                 tab->user= allocate((strlen(user) + 1) * sizeof(tab->user[0]));
00433                 strcpy(tab->user, user);
00434         }
00435         tab->data= allocate((st.st_size + 1) * sizeof(tab->data[0]));
00436         tab->jobs= nil;
00437         tab->mtime= st.st_mtime;
00438         tab->current= 0;
00439         tab->next= *atab;
00440         *atab= tab;
00441 
00442         /* Pull a new table in core. */
00443         n= 0;
00444         while (n < st.st_size) {
00445                 if ((r = read(fd, tab->data + n, st.st_size - n)) < 0) {
00446                         log(LOG_CRIT, "%s: %s", file, strerror(errno));
00447                         close(fd);
00448                         return;
00449                 }
00450                 if (r == 0) break;
00451                 n+= r;
00452         }
00453         close(fd);
00454         tab->data[n]= 0;
00455         if (strlen(tab->data) < n) {
00456                 log(LOG_ERR, "%s contains a null character\n", file);
00457                 return;
00458         }
00459 
00460         /* Parse the file. */
00461         ajob= &tab->jobs;
00462         p= tab->data;
00463         ok= 1;
00464         while (ok && *p != 0) {
00465                 q= get_token(&p);
00466                 if (*q == '#' || q == p) {
00467                         /* Comment or empty. */
00468                         while (*p != 0 && *p++ != '\n') {}
00469                         continue;
00470                 }
00471 
00472                 /* One new job coming up. */
00473                 *ajob= job= allocate(sizeof(*job));
00474                 *(ajob= &job->next)= nil;
00475                 job->tab= tab;
00476 
00477                 if (!range_parse(file, q, job->min, 0, 59, &wc)) {
00478                         ok= 0;
00479                         break;
00480                 }
00481 
00482                 q= get_token(&p);
00483                 if (!range_parse(file, q, job->hour, 0, 23, &wc)) {
00484                         ok= 0;
00485                         break;
00486                 }
00487 
00488                 q= get_token(&p);
00489                 if (!range_parse(file, q, job->mday, 1, 31, &wc)) {
00490                         ok= 0;
00491                         break;
00492                 }
00493                 job->do_mday= !wc;
00494 
00495                 q= get_token(&p);
00496                 if (!range_parse(file, q, job->mon, 1, 12, &wc)) {
00497                         ok= 0;
00498                         break;
00499                 }
00500                 job->do_mday |= !wc;
00501 
00502                 q= get_token(&p);
00503                 if (!range_parse(file, q, job->wday, 0, 7, &wc)) {
00504                         ok= 0;
00505                         break;
00506                 }
00507                 job->do_wday= !wc;
00508 
00509                 /* 7 is Sunday, but 0 is a common mistake because it is in the
00510                  * tm_wday range.  We allow and even prefer it internally.
00511                  */
00512                 if (bit_isset(job->wday, 7)) {
00513                         bit_clr(job->wday, 7);
00514                         bit_set(job->wday, 0);
00515                 }
00516 
00517                 /* The month range is 1-12, but tm_mon likes 0-11. */
00518                 job->mon[0] >>= 1;
00519                 if (bit_isset(job->mon, 8)) bit_set(job->mon, 7);
00520                 job->mon[1] >>= 1;
00521 
00522                 /* Scan for options. */
00523                 job->user= nil;
00524                 while (q= get_token(&p), *q == '-') {
00525                         q++;
00526                         if (q[0] == '-' && q+1 == p) {
00527                                 /* -- */
00528                                 q= get_token(&p);
00529                                 break;
00530                         }
00531                         while (q < p) switch (*q++) {
00532                         case 'u':
00533                                 if (q == p) q= get_token(&p);
00534                                 if (q == p) goto usage;
00535                                 memmove(q-1, q, p-q);   /* gross... */
00536                                 p[-1]= 0;
00537                                 job->user= q-1;
00538                                 q= p;
00539                                 break;
00540                         default:
00541                         usage:
00542                                 log(LOG_ERR,
00543                         "%s: bad option -%c, good options are: -u username\n",
00544                                         file, q[-1]);
00545                                 ok= 0;
00546                                 goto endtab;
00547                         }
00548                 }
00549 
00550                 /* A crontab owned by a user can only do things as that user. */
00551                 if (tab->user != nil) job->user= tab->user;
00552 
00553                 /* Inspect the first character of the command. */
00554                 job->cmd= q;
00555                 if (q == p || *q == '#') {
00556                         /* Rest of the line is empty, i.e. the commands are on
00557                          * the following lines indented by one tab.
00558                          */
00559                         while (*p != 0 && *p++ != '\n') {}
00560                         if (*p++ != '\t') {
00561                                 log(LOG_ERR, "%s: contains an empty command\n",
00562                                         file);
00563                                 ok= 0;
00564                                 goto endtab;
00565                         }
00566                         while (*p != 0) {
00567                                 if ((*q = *p++) == '\n') {
00568                                         if (*p != '\t') break;
00569                                         p++;
00570                                 }
00571                                 q++;
00572                         }
00573                 } else {
00574                         /* The command is on this line.  Alas we must now be
00575                          * backwards compatible and change %'s to newlines.
00576                          */
00577                         p= q;
00578                         while (*p != 0) {
00579                                 if ((*q = *p++) == '\n') break;
00580                                 if (*q == '%') *q= '\n';
00581                                 q++;
00582                         }
00583                 }
00584                 *q= 0;
00585                 job->rtime= now;
00586                 job->late= 0;           /* It is on time. */
00587                 job->atjob= 0;          /* True cron job. */
00588                 job->pid= IDLE_PID;     /* Not running yet. */
00589                 tab_reschedule(job);    /* Compute next time to run. */
00590         }
00591   endtab:
00592 
00593         if (ok) tab->current= 1;
00594 }
00595 
00596 void tab_find_atjob(char *atdir)
00597 /* Find the first to be executed AT job and kludge up an crontab job for it.
00598  * We set tab->file to "atdir/jobname", tab->data to "atdir/past/jobname",
00599  * and job->cmd to "jobname".
00600  */
00601 {
00602         DIR *spool;
00603         struct dirent *entry;
00604         time_t t0, t1;
00605         struct tm tmnow, tmt1;
00606         static char template[] = "96.365.1546.00";
00607         char firstjob[sizeof(template)];
00608         int i;
00609         crontab_t *tab;
00610         cronjob_t *job;
00611 
00612         if ((spool= opendir(atdir)) == nil) return;
00613 
00614         tmnow= *localtime(&now);
00615         t0= NEVER;
00616 
00617         while ((entry= readdir(spool)) != nil) {
00618                 /* Check if the name fits the template. */
00619                 for (i= 0; template[i] != 0; i++) {
00620                         if (template[i] == '.') {
00621                                 if (entry->d_name[i] != '.') break;
00622                         } else {
00623                                 if (!isdigit(entry->d_name[i])) break;
00624                         }
00625                 }
00626                 if (template[i] != 0 || entry->d_name[i] != 0) continue;
00627 
00628                 /* Convert the name to a time.  Careful with the century. */
00629                 memset(&tmt1, 0, sizeof(tmt1));
00630                 tmt1.tm_year= atoi(entry->d_name+0);
00631                 while (tmt1.tm_year < tmnow.tm_year-10) tmt1.tm_year+= 100;
00632                 tmt1.tm_mday= 1+atoi(entry->d_name+3);
00633                 tmt1.tm_min= atoi(entry->d_name+7);
00634                 tmt1.tm_hour= tmt1.tm_min / 100;
00635                 tmt1.tm_min%= 100;
00636                 tmt1.tm_isdst= -1;
00637                 if ((t1= mktime(&tmt1)) == -1) {
00638                         /* Illegal time?  Try in winter time. */
00639                         tmt1.tm_isdst= 0;
00640                         if ((t1= mktime(&tmt1)) == -1) continue;
00641                 }
00642                 if (t1 < t0) {
00643                         t0= t1;
00644                         strcpy(firstjob, entry->d_name);
00645                 }
00646         }
00647         closedir(spool);
00648 
00649         if (t0 == NEVER) return;        /* AT job spool is empty. */
00650 
00651         /* Create new table and job structures. */
00652         tab= allocate(sizeof(*tab));
00653         tab->file= allocate((strlen(atdir) + 1 + sizeof(template))
00654                                                 * sizeof(tab->file[0]));
00655         strcpy(tab->file, atdir);
00656         strcat(tab->file, "/");
00657         strcat(tab->file, firstjob);
00658         tab->data= allocate((strlen(atdir) + 6 + sizeof(template))
00659                                                 * sizeof(tab->data[0]));
00660         strcpy(tab->data, atdir);
00661         strcat(tab->data, "/past/");
00662         strcat(tab->data, firstjob);
00663         tab->user= nil;
00664         tab->mtime= 0;
00665         tab->current= 1;
00666         tab->next= crontabs;
00667         crontabs= tab;
00668 
00669         tab->jobs= job= allocate(sizeof(*job));
00670         job->next= nil;
00671         job->tab= tab;
00672         job->user= nil;
00673         job->cmd= tab->data + strlen(atdir) + 6;
00674         job->rtime= t0;
00675         job->late= 0;
00676         job->atjob= 1;          /* One AT job disguised as a cron job. */
00677         job->pid= IDLE_PID;
00678 
00679         if (job->rtime < next) next= job->rtime;
00680 }
00681 
00682 void tab_purge(void)
00683 /* Remove table data that is no longer current.  E.g. a crontab got removed.
00684  */
00685 {
00686         crontab_t **atab, *tab;
00687         cronjob_t *job;
00688 
00689         atab= &crontabs;
00690         while ((tab= *atab) != nil) {
00691                 if (tab->current) {
00692                         /* Table is fine. */
00693                         tab->current= 0;
00694                         atab= &tab->next;
00695                 } else {
00696                         /* Table is not, or no longer valid; delete. */
00697                         *atab= tab->next;
00698                         while ((job= tab->jobs) != nil) {
00699                                 tab->jobs= job->next;
00700                                 deallocate(job);
00701                         }
00702                         deallocate(tab->data);
00703                         deallocate(tab->file);
00704                         deallocate(tab->user);
00705                         deallocate(tab);
00706                 }
00707         }
00708 }
00709 
00710 static cronjob_t *reap_or_find(pid_t pid)
00711 /* Find a finished job or search for the next one to run. */
00712 {
00713         crontab_t *tab;
00714         cronjob_t *job;
00715         cronjob_t *nextjob;
00716 
00717         nextjob= nil;
00718         next= NEVER;
00719         for (tab= crontabs; tab != nil; tab= tab->next) {
00720                 for (job= tab->jobs; job != nil; job= job->next) {
00721                         if (job->pid == pid) {
00722                                 job->pid= IDLE_PID;
00723                                 tab_reschedule(job);
00724                         }
00725                         if (job->pid != IDLE_PID) continue;
00726                         if (job->rtime < next) next= job->rtime;
00727                         if (job->rtime <= now) nextjob= job;
00728                 }
00729         }
00730         return nextjob;
00731 }
00732 
00733 void tab_reap_job(pid_t pid)
00734 /* A job has finished.  Try to find it among the crontab data and reschedule
00735  * it.  Recompute time next to run a job.
00736  */
00737 {
00738         (void) reap_or_find(pid);
00739 }
00740 
00741 cronjob_t *tab_nextjob(void)
00742 /* Find a job that should be run now.  If none are found return null.
00743  * Update 'next'.
00744  */
00745 {
00746         return reap_or_find(NO_PID);
00747 }
00748 
00749 static void pr_map(FILE *fp, bitmap_t map)
00750 {
00751         int last_bit= -1, bit;
00752         char *sep;
00753 
00754         sep= "";
00755         for (bit= 0; bit < 64; bit++) {
00756                 if (bit_isset(map, bit)) {
00757                         if (last_bit == -1) last_bit= bit;
00758                 } else {
00759                         if (last_bit != -1) {
00760                                 fprintf(fp, "%s%d", sep, last_bit);
00761                                 if (last_bit != bit-1) {
00762                                         fprintf(fp, "-%d", bit-1);
00763                                 }
00764                                 last_bit= -1;
00765                                 sep= ",";
00766                         }
00767                 }
00768         }
00769 }
00770 
00771 void tab_print(FILE *fp)
00772 /* Print out a stored crontab file for debugging purposes. */
00773 {
00774         crontab_t *tab;
00775         cronjob_t *job;
00776         char *p;
00777         struct tm tm;
00778 
00779         for (tab= crontabs; tab != nil; tab= tab->next) {
00780                 fprintf(fp, "tab->file = \"%s\"\n", tab->file);
00781                 fprintf(fp, "tab->user = \"%s\"\n",
00782                                 tab->user == nil ? "(root)" : tab->user);
00783                 fprintf(fp, "tab->mtime = %s", ctime(&tab->mtime));
00784 
00785                 for (job= tab->jobs; job != nil; job= job->next) {
00786                         if (job->atjob) {
00787                                 fprintf(fp, "AT job");
00788                         } else {
00789                                 pr_map(fp, job->min); fputc(' ', fp);
00790                                 pr_map(fp, job->hour); fputc(' ', fp);
00791                                 pr_map(fp, job->mday); fputc(' ', fp);
00792                                 pr_map(fp, job->mon); fputc(' ', fp);
00793                                 pr_map(fp, job->wday);
00794                         }
00795                         if (job->user != nil && job->user != tab->user) {
00796                                 fprintf(fp, " -u %s", job->user);
00797                         }
00798                         fprintf(fp, "\n\t");
00799                         for (p= job->cmd; *p != 0; p++) {
00800                                 fputc(*p, fp);
00801                                 if (*p == '\n') fputc('\t', fp);
00802                         }
00803                         fputc('\n', fp);
00804                         tm= *localtime(&job->rtime);
00805                         fprintf(fp, "    rtime = %.24s%s\n", asctime(&tm),
00806                                 tm.tm_isdst ? " (DST)" : "");
00807                         if (job->pid != IDLE_PID) {
00808                                 fprintf(fp, "    pid = %ld\n", (long) job->pid);
00809                         }
00810                 }
00811         }
00812 }
00813 
00814 /*
00815  * $PchId: tab.c,v 1.5 2000/07/25 22:07:51 philip Exp $
00816  */

Generated on Fri Apr 14 22:56:47 2006 for minix by  doxygen 1.4.6