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