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