1 #ifndef lint 2 static char *sccsid = "@(#)cron.c 4.13 (Berkeley) 09/30/87"; 3 #endif 4 5 #include <sys/types.h> 6 #include <stdio.h> 7 #include <ctype.h> 8 #include <signal.h> 9 #include <sys/time.h> 10 #include <sys/stat.h> 11 #include <sys/wait.h> 12 #include <sys/ioctl.h> 13 #include <sys/file.h> 14 #include <sys/resource.h> 15 #include <pwd.h> 16 #include <fcntl.h> 17 #include <syslog.h> 18 19 #define LISTS (2*BUFSIZ) 20 #define MAXLIN BUFSIZ 21 22 #ifndef CRONTAB 23 #define CRONTAB "/usr/lib/crontab" 24 #endif 25 26 #ifndef CRONTABLOC 27 #define CRONTABLOC "/usr/lib/crontab.local" 28 #endif 29 30 #define EXACT 100 31 #define ANY 101 32 #define LIST 102 33 #define RANGE 103 34 #define EOS 104 35 36 char crontab[] = CRONTAB; 37 char loc_crontab[] = CRONTABLOC; 38 time_t itime, time(); 39 struct tm *loct; 40 struct tm *localtime(); 41 char *malloc(); 42 char *realloc(); 43 int reapchild(); 44 int flag; 45 char *list; 46 char *listend; 47 unsigned listsize; 48 49 FILE *debug; 50 #define dprintf if (debug) fprintf 51 52 main(argc, argv) 53 int argc; 54 char **argv; 55 { 56 register char *cp; 57 char *cmp(); 58 time_t filetime = 0; 59 time_t lfiletime = 0; 60 char c; 61 extern char *optarg; 62 63 openlog("cron", LOG_PID | LOG_CONS | LOG_NOWAIT, LOG_DAEMON); 64 switch (fork()) { 65 66 case -1: 67 syslog(LOG_ERR, "fork: %m"); 68 exit(1); 69 /* NOTREACHED */ 70 case 0: 71 break; 72 default: 73 exit(0); 74 /* NOTREACHED */ 75 } 76 77 c = getopt(argc, argv, "d:"); 78 if (c == 'd') { 79 debug = fopen(optarg, "w"); 80 if (debug == NULL) 81 exit(1); 82 (void) fcntl(fileno(debug), F_SETFL, FAPPEND); 83 } 84 (void) chdir("/"); 85 (void) freopen("/", "r", stdout); 86 (void) freopen("/", "r", stderr); 87 untty(); 88 (void) signal(SIGHUP, SIG_IGN); 89 (void) signal(SIGINT, SIG_IGN); 90 (void) signal(SIGQUIT, SIG_IGN); 91 (void) signal(SIGCHLD, reapchild); 92 (void) time(&itime); 93 itime -= localtime(&itime)->tm_sec; 94 95 for (;; itime+=60, slp()) { 96 struct stat cstat, lcstat; 97 int newcron, newloc; 98 99 newcron = 0; 100 if (stat(crontab, &cstat) < 0) 101 cstat.st_mtime = 1; 102 if (cstat.st_mtime != filetime) { 103 filetime = cstat.st_mtime; 104 newcron++; 105 } 106 107 newloc = 0; 108 if (stat(loc_crontab, &lcstat) < 0) 109 lcstat.st_mtime = 1; 110 if (lcstat.st_mtime != lfiletime) { 111 lfiletime = lcstat.st_mtime; 112 newloc++; 113 } 114 115 if (newcron || newloc) { 116 init(); 117 append(crontab); 118 append(loc_crontab); 119 *listend++ = EOS; 120 *listend++ = EOS; 121 } 122 123 loct = localtime(&itime); 124 loct->tm_mon++; /* 1-12 for month */ 125 if (loct->tm_wday == 0) 126 loct->tm_wday = 7; /* sunday is 7, not 0 */ 127 for(cp = list; *cp != EOS;) { 128 flag = 0; 129 cp = cmp(cp, loct->tm_min); 130 cp = cmp(cp, loct->tm_hour); 131 cp = cmp(cp, loct->tm_mday); 132 cp = cmp(cp, loct->tm_mon); 133 cp = cmp(cp, loct->tm_wday); 134 if(flag == 0) 135 ex(cp); 136 while(*cp++ != 0) 137 ; 138 } 139 } 140 } 141 142 char * 143 cmp(p, v) 144 char *p; 145 { 146 register char *cp; 147 148 cp = p; 149 switch(*cp++) { 150 151 case EXACT: 152 if (*cp++ != v) 153 flag++; 154 return(cp); 155 156 case ANY: 157 return(cp); 158 159 case LIST: 160 while(*cp != LIST) 161 if(*cp++ == v) { 162 while(*cp++ != LIST) 163 ; 164 return(cp); 165 } 166 flag++; 167 return(cp+1); 168 169 case RANGE: 170 if(*cp > v || cp[1] < v) 171 flag++; 172 return(cp+2); 173 } 174 if(cp[-1] != v) 175 flag++; 176 return(cp); 177 } 178 179 slp() 180 { 181 register i; 182 time_t t; 183 184 (void) time(&t); 185 i = itime - t; 186 if(i < -60 * 60 || i > 60 * 60) { 187 itime = t; 188 i = 60 - localtime(&itime)->tm_sec; 189 itime += i; 190 } 191 if(i > 0) 192 sleep((u_int)i); 193 } 194 195 ex(s) 196 char *s; 197 { 198 register struct passwd *pwd; 199 char user[BUFSIZ]; 200 char *c = user; 201 int pid; 202 203 switch (fork()) { 204 case 0: 205 break; 206 case -1: 207 syslog(LOG_ERR, "cannot fork: %m (running %.40s%s)", 208 s, strlen(s) > 40 ? "..." : ""); 209 /*FALLTHROUGH*/ 210 default: 211 return; 212 } 213 pid = getpid(); 214 while(*s != ' ' && *s != '\t') 215 *c++ = *s++; 216 *c = '\0'; 217 s++; 218 if ((pwd = getpwnam(user)) == NULL) { 219 syslog(LOG_ERR, "invalid user name \"%s\"", user); 220 dprintf(debug, "%d: cannot find %s\n", pid, user), 221 fflush(debug); 222 exit(1); 223 } 224 (void) setgid(pwd->pw_gid); 225 (void) initgroups(pwd->pw_name, pwd->pw_gid); 226 (void) setuid(pwd->pw_uid); 227 (void) freopen("/", "r", stdin); 228 closelog(); 229 dprintf(debug, "%d: executing %s", pid, s), fflush (debug); 230 execl("/bin/sh", "sh", "-c", s, 0); 231 syslog(LOG_ERR, "cannot exec /bin/sh: %m"); 232 dprintf(debug, "%d: cannot execute sh\n", pid), fflush (debug); 233 exit(0); 234 } 235 236 init() 237 { 238 /* 239 * Don't free in case was longer than LISTS. Trades off 240 * the rare case of crontab shrinking vs. the common case of 241 * extra realloc's needed in append() for a large crontab. 242 */ 243 if (list == 0) { 244 list = malloc(LISTS); 245 listsize = LISTS; 246 } 247 listend = list; 248 } 249 250 append(fn) 251 char *fn; 252 { 253 register i, c; 254 register char *cp; 255 register char *ocp; 256 register int n; 257 258 if (freopen(fn, "r", stdin) == NULL) 259 return; 260 cp = listend; 261 loop: 262 if(cp > list+listsize-MAXLIN) { 263 int length = cp - list; 264 265 listsize += LISTS; 266 list = realloc(list, listsize); 267 cp = list + length; 268 } 269 ocp = cp; 270 for(i=0;; i++) { 271 do 272 c = getchar(); 273 while(c == ' ' || c == '\t') 274 ; 275 if(c == EOF || c == '\n') 276 goto ignore; 277 if(i == 5) 278 break; 279 if(c == '*') { 280 *cp++ = ANY; 281 continue; 282 } 283 if ((n = number(c)) < 0) 284 goto ignore; 285 c = getchar(); 286 if(c == ',') 287 goto mlist; 288 if(c == '-') 289 goto mrange; 290 if(c != '\t' && c != ' ') 291 goto ignore; 292 *cp++ = EXACT; 293 *cp++ = n; 294 continue; 295 296 mlist: 297 *cp++ = LIST; 298 *cp++ = n; 299 do { 300 if ((n = number(getchar())) < 0) 301 goto ignore; 302 *cp++ = n; 303 c = getchar(); 304 } while (c==','); 305 if(c != '\t' && c != ' ') 306 goto ignore; 307 *cp++ = LIST; 308 continue; 309 310 mrange: 311 *cp++ = RANGE; 312 *cp++ = n; 313 if ((n = number(getchar())) < 0) 314 goto ignore; 315 c = getchar(); 316 if(c != '\t' && c != ' ') 317 goto ignore; 318 *cp++ = n; 319 } 320 while(c != '\n') { 321 if(c == EOF) 322 goto ignore; 323 if(c == '%') 324 c = '\n'; 325 *cp++ = c; 326 c = getchar(); 327 } 328 *cp++ = '\n'; 329 *cp++ = 0; 330 goto loop; 331 332 ignore: 333 cp = ocp; 334 while(c != '\n') { 335 if(c == EOF) { 336 (void) fclose(stdin); 337 listend = cp; 338 return; 339 } 340 c = getchar(); 341 } 342 goto loop; 343 } 344 345 number(c) 346 register c; 347 { 348 register n = 0; 349 350 while (isdigit(c)) { 351 n = n*10 + c - '0'; 352 c = getchar(); 353 } 354 (void) ungetc(c, stdin); 355 if (n>=100) 356 return(-1); 357 return(n); 358 } 359 360 reapchild() 361 { 362 union wait status; 363 int pid; 364 365 while ((pid = wait3(&status, WNOHANG, (struct rusage *)0)) > 0) 366 dprintf(debug, "%d: child exits with signal %d status %d\n", 367 pid, status.w_termsig, status.w_retcode), 368 fflush (debug); 369 } 370 371 untty() 372 { 373 int i; 374 375 i = open("/dev/tty", O_RDWR); 376 if (i >= 0) { 377 (void) ioctl(i, TIOCNOTTY, (char *)0); 378 (void) close(i); 379 } 380 } 381