1 #ifndef lint 2 static char *sccsid = "@(#)cron.c 4.16 (Berkeley) 06/29/90"; 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 daemon(0, 0); 83 (void) signal(SIGHUP, SIG_IGN); 84 (void) signal(SIGINT, SIG_IGN); 85 (void) signal(SIGQUIT, SIG_IGN); 86 (void) signal(SIGCHLD, reapchild); 87 (void) time(&itime); 88 itime -= localtime(&itime)->tm_sec; 89 90 for (;; itime+=60, slp()) { 91 struct stat cstat, lcstat; 92 int newcron, newloc; 93 94 newcron = 0; 95 if (stat(crontab, &cstat) < 0) 96 cstat.st_mtime = 1; 97 if (cstat.st_mtime != filetime) { 98 filetime = cstat.st_mtime; 99 newcron++; 100 } 101 102 newloc = 0; 103 if (stat(loc_crontab, &lcstat) < 0) 104 lcstat.st_mtime = 1; 105 if (lcstat.st_mtime != lfiletime) { 106 lfiletime = lcstat.st_mtime; 107 newloc++; 108 } 109 110 if (newcron || newloc) { 111 init(); 112 append(crontab); 113 append(loc_crontab); 114 *listend++ = EOS; 115 *listend++ = EOS; 116 } 117 118 loct = localtime(&itime); 119 loct->tm_mon++; /* 1-12 for month */ 120 if (loct->tm_wday == 0) 121 loct->tm_wday = 7; /* sunday is 7, not 0 */ 122 for(cp = list; *cp != EOS;) { 123 flag = 0; 124 cp = cmp(cp, loct->tm_min); 125 cp = cmp(cp, loct->tm_hour); 126 cp = cmp(cp, loct->tm_mday); 127 cp = cmp(cp, loct->tm_mon); 128 cp = cmp(cp, loct->tm_wday); 129 if(flag == 0) 130 ex(cp); 131 while(*cp++ != 0) 132 ; 133 } 134 } 135 } 136 137 char * 138 cmp(p, v) 139 char *p; 140 { 141 register char *cp; 142 143 cp = p; 144 switch(*cp++) { 145 146 case EXACT: 147 if (*cp++ != v) 148 flag++; 149 return(cp); 150 151 case ANY: 152 return(cp); 153 154 case LIST: 155 while(*cp != LIST) 156 if(*cp++ == v) { 157 while(*cp++ != LIST) 158 ; 159 return(cp); 160 } 161 flag++; 162 return(cp+1); 163 164 case RANGE: 165 if(*cp > v || cp[1] < v) 166 flag++; 167 return(cp+2); 168 } 169 if(cp[-1] != v) 170 flag++; 171 return(cp); 172 } 173 174 slp() 175 { 176 register i; 177 time_t t; 178 179 (void) time(&t); 180 i = itime - t; 181 if(i < -60 * 60 || i > 60 * 60) { 182 itime = t; 183 i = 60 - localtime(&itime)->tm_sec; 184 itime += i; 185 } 186 if(i > 0) 187 sleep((u_int)i); 188 } 189 190 ex(s) 191 char *s; 192 { 193 register struct passwd *pwd; 194 char user[BUFSIZ]; 195 char *c = user; 196 int pid; 197 198 switch (fork()) { 199 case 0: 200 break; 201 case -1: 202 syslog(LOG_ERR, "cannot fork: %m (running %.40s%s)", 203 s, strlen(s) > 40 ? "..." : ""); 204 /*FALLTHROUGH*/ 205 default: 206 return; 207 } 208 pid = getpid(); 209 while(*s != ' ' && *s != '\t') 210 *c++ = *s++; 211 *c = '\0'; 212 s++; 213 if ((pwd = getpwnam(user)) == NULL) { 214 syslog(LOG_ERR, "invalid user name \"%s\"", user); 215 dprintf(debug, "%d: cannot find %s\n", pid, user), 216 fflush(debug); 217 exit(1); 218 } 219 (void) setgid(pwd->pw_gid); 220 (void) initgroups(pwd->pw_name, pwd->pw_gid); 221 (void) setuid(pwd->pw_uid); 222 (void) freopen("/", "r", stdin); 223 closelog(); 224 dprintf(debug, "%d: executing %s", pid, s), fflush (debug); 225 execl(_PATH_BSHELL, "sh", "-c", s, 0); 226 syslog(LOG_ERR, "cannot exec %s: %m"); 227 dprintf(debug, "%d: cannot execute sh\n", pid), fflush (debug); 228 exit(0); 229 } 230 231 init() 232 { 233 /* 234 * Don't free in case was longer than LISTS. Trades off 235 * the rare case of crontab shrinking vs. the common case of 236 * extra realloc's needed in append() for a large crontab. 237 */ 238 if (list == 0) { 239 list = malloc(LISTS); 240 listsize = LISTS; 241 } 242 listend = list; 243 } 244 245 append(fn) 246 char *fn; 247 { 248 register i, c; 249 register char *cp; 250 register char *ocp; 251 register int n; 252 253 if (freopen(fn, "r", stdin) == NULL) 254 return; 255 cp = listend; 256 loop: 257 if(cp > list+listsize-MAXLIN) { 258 int length = cp - list; 259 260 listsize += LISTS; 261 list = realloc(list, listsize); 262 cp = list + length; 263 } 264 ocp = cp; 265 for(i=0;; i++) { 266 do 267 c = getchar(); 268 while(c == ' ' || c == '\t') 269 ; 270 if(c == EOF || c == '\n') 271 goto ignore; 272 if(i == 5) 273 break; 274 if(c == '*') { 275 *cp++ = ANY; 276 continue; 277 } 278 if ((n = number(c)) < 0) 279 goto ignore; 280 c = getchar(); 281 if(c == ',') 282 goto mlist; 283 if(c == '-') 284 goto mrange; 285 if(c != '\t' && c != ' ') 286 goto ignore; 287 *cp++ = EXACT; 288 *cp++ = n; 289 continue; 290 291 mlist: 292 *cp++ = LIST; 293 *cp++ = n; 294 do { 295 if ((n = number(getchar())) < 0) 296 goto ignore; 297 *cp++ = n; 298 c = getchar(); 299 } while (c==','); 300 if(c != '\t' && c != ' ') 301 goto ignore; 302 *cp++ = LIST; 303 continue; 304 305 mrange: 306 *cp++ = RANGE; 307 *cp++ = n; 308 if ((n = number(getchar())) < 0) 309 goto ignore; 310 c = getchar(); 311 if(c != '\t' && c != ' ') 312 goto ignore; 313 *cp++ = n; 314 } 315 while(c != '\n') { 316 if(c == EOF) 317 goto ignore; 318 if(c == '%') 319 c = '\n'; 320 *cp++ = c; 321 c = getchar(); 322 } 323 *cp++ = '\n'; 324 *cp++ = 0; 325 goto loop; 326 327 ignore: 328 cp = ocp; 329 while(c != '\n') { 330 if(c == EOF) { 331 (void) fclose(stdin); 332 listend = cp; 333 return; 334 } 335 c = getchar(); 336 } 337 goto loop; 338 } 339 340 number(c) 341 register c; 342 { 343 register n = 0; 344 345 while (isdigit(c)) { 346 n = n*10 + c - '0'; 347 c = getchar(); 348 } 349 (void) ungetc(c, stdin); 350 if (n>=100) 351 return(-1); 352 return(n); 353 } 354 355 reapchild() 356 { 357 union wait status; 358 int pid; 359 360 while ((pid = wait3(&status, WNOHANG, (struct rusage *)0)) > 0) 361 dprintf(debug, "%d: child exits with signal %d status %d\n", 362 pid, status.w_termsig, status.w_retcode), 363 fflush (debug); 364 } 365