1 /* Copyright 1988,1990,1993,1994 by Paul Vixie 2 * All rights reserved 3 * 4 * Distribute freely, except: don't remove my name from the source or 5 * documentation (don't take credit for my work), mark your changes (don't 6 * get me blamed for your possible bugs), don't alter or remove this 7 * notice. May be sold if buildable source is provided to buyer. No 8 * warrantee of any kind, express or implied, is included with this 9 * software; use at your own risk, responsibility for damages (if any) to 10 * anyone resulting from the use of this software rests entirely with the 11 * user. 12 * 13 * Send bug reports, bug fixes, enhancements, requests, flames, etc., and 14 * I'll try to keep a version up to date. I can be reached as follows: 15 * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul 16 * 17 * $FreeBSD: src/usr.sbin/cron/lib/misc.c,v 1.8.2.2 2002/04/28 22:45:53 dwmalone Exp $ 18 * $DragonFly: src/usr.sbin/cron/lib/misc.c,v 1.2 2003/06/17 04:29:53 dillon Exp $ 19 */ 20 21 /* vix 26jan87 [RCS has the rest of the log] 22 * vix 30dec86 [written] 23 */ 24 25 26 #include "cron.h" 27 #if SYS_TIME_H 28 # include <sys/time.h> 29 #else 30 # include <time.h> 31 #endif 32 #include <sys/file.h> 33 #include <sys/stat.h> 34 #include <err.h> 35 #include <errno.h> 36 #include <string.h> 37 #include <fcntl.h> 38 #if defined(SYSLOG) 39 # include <syslog.h> 40 #endif 41 42 43 #if defined(LOG_DAEMON) && !defined(LOG_CRON) 44 #define LOG_CRON LOG_DAEMON 45 #endif 46 47 48 static int LogFD = ERR; 49 50 51 int 52 strcmp_until(left, right, until) 53 char *left; 54 char *right; 55 int until; 56 { 57 register int diff; 58 59 while (*left && *left != until && *left == *right) { 60 left++; 61 right++; 62 } 63 64 if ((*left=='\0' || *left == until) && 65 (*right=='\0' || *right == until)) { 66 diff = 0; 67 } else { 68 diff = *left - *right; 69 } 70 71 return diff; 72 } 73 74 75 /* strdtb(s) - delete trailing blanks in string 's' and return new length 76 */ 77 int 78 strdtb(s) 79 char *s; 80 { 81 char *x = s; 82 83 /* scan forward to the null 84 */ 85 while (*x) 86 x++; 87 88 /* scan backward to either the first character before the string, 89 * or the last non-blank in the string, whichever comes first. 90 */ 91 do {x--;} 92 while (x >= s && isspace(*x)); 93 94 /* one character beyond where we stopped above is where the null 95 * goes. 96 */ 97 *++x = '\0'; 98 99 /* the difference between the position of the null character and 100 * the position of the first character of the string is the length. 101 */ 102 return x - s; 103 } 104 105 106 int 107 set_debug_flags(flags) 108 char *flags; 109 { 110 /* debug flags are of the form flag[,flag ...] 111 * 112 * if an error occurs, print a message to stdout and return FALSE. 113 * otherwise return TRUE after setting ERROR_FLAGS. 114 */ 115 116 #if !DEBUGGING 117 118 printf("this program was compiled without debugging enabled\n"); 119 return FALSE; 120 121 #else /* DEBUGGING */ 122 123 char *pc = flags; 124 125 DebugFlags = 0; 126 127 while (*pc) { 128 char **test; 129 int mask; 130 131 /* try to find debug flag name in our list. 132 */ 133 for ( test = DebugFlagNames, mask = 1; 134 *test && strcmp_until(*test, pc, ','); 135 test++, mask <<= 1 136 ) 137 ; 138 139 if (!*test) { 140 fprintf(stderr, 141 "unrecognized debug flag <%s> <%s>\n", 142 flags, pc); 143 return FALSE; 144 } 145 146 DebugFlags |= mask; 147 148 /* skip to the next flag 149 */ 150 while (*pc && *pc != ',') 151 pc++; 152 if (*pc == ',') 153 pc++; 154 } 155 156 if (DebugFlags) { 157 int flag; 158 159 fprintf(stderr, "debug flags enabled:"); 160 161 for (flag = 0; DebugFlagNames[flag]; flag++) 162 if (DebugFlags & (1 << flag)) 163 fprintf(stderr, " %s", DebugFlagNames[flag]); 164 fprintf(stderr, "\n"); 165 } 166 167 return TRUE; 168 169 #endif /* DEBUGGING */ 170 } 171 172 173 void 174 set_cron_uid() 175 { 176 #if defined(BSD) || defined(POSIX) 177 if (seteuid(ROOT_UID) < OK) 178 err(ERROR_EXIT, "seteuid"); 179 #else 180 if (setuid(ROOT_UID) < OK) 181 err(ERROR_EXIT, "setuid"); 182 #endif 183 } 184 185 186 void 187 set_cron_cwd() 188 { 189 struct stat sb; 190 191 /* first check for CRONDIR ("/var/cron" or some such) 192 */ 193 if (stat(CRONDIR, &sb) < OK && errno == ENOENT) { 194 warn("%s", CRONDIR); 195 if (OK == mkdir(CRONDIR, 0700)) { 196 warnx("%s: created", CRONDIR); 197 stat(CRONDIR, &sb); 198 } else { 199 err(ERROR_EXIT, "%s: mkdir", CRONDIR); 200 } 201 } 202 if (!(sb.st_mode & S_IFDIR)) 203 err(ERROR_EXIT, "'%s' is not a directory, bailing out", CRONDIR); 204 if (chdir(CRONDIR) < OK) 205 err(ERROR_EXIT, "cannot chdir(%s), bailing out", CRONDIR); 206 207 /* CRONDIR okay (now==CWD), now look at SPOOL_DIR ("tabs" or some such) 208 */ 209 if (stat(SPOOL_DIR, &sb) < OK && errno == ENOENT) { 210 warn("%s", SPOOL_DIR); 211 if (OK == mkdir(SPOOL_DIR, 0700)) { 212 warnx("%s: created", SPOOL_DIR); 213 stat(SPOOL_DIR, &sb); 214 } else { 215 err(ERROR_EXIT, "%s: mkdir", SPOOL_DIR); 216 } 217 } 218 if (!(sb.st_mode & S_IFDIR)) 219 err(ERROR_EXIT, "'%s' is not a directory, bailing out", SPOOL_DIR); 220 } 221 222 223 /* acquire_daemonlock() - write our PID into /etc/cron.pid, unless 224 * another daemon is already running, which we detect here. 225 * 226 * note: main() calls us twice; once before forking, once after. 227 * we maintain static storage of the file pointer so that we 228 * can rewrite our PID into the PIDFILE after the fork. 229 * 230 * it would be great if fflush() disassociated the file buffer. 231 */ 232 void 233 acquire_daemonlock(closeflag) 234 int closeflag; 235 { 236 static FILE *fp = NULL; 237 238 if (closeflag && fp) { 239 fclose(fp); 240 fp = NULL; 241 return; 242 } 243 244 if (!fp) { 245 char pidfile[MAX_FNAME]; 246 char buf[MAX_TEMPSTR]; 247 int fd, otherpid; 248 249 (void) sprintf(pidfile, PIDFILE, PIDDIR); 250 if ((-1 == (fd = open(pidfile, O_RDWR|O_CREAT, 0644))) 251 || (NULL == (fp = fdopen(fd, "r+"))) 252 ) { 253 sprintf(buf, "can't open or create %s: %s", 254 pidfile, strerror(errno)); 255 log_it("CRON", getpid(), "DEATH", buf); 256 errx(ERROR_EXIT, "%s", buf); 257 } 258 259 if (flock(fd, LOCK_EX|LOCK_NB) < OK) { 260 int save_errno = errno; 261 262 fscanf(fp, "%d", &otherpid); 263 sprintf(buf, "can't lock %s, otherpid may be %d: %s", 264 pidfile, otherpid, strerror(save_errno)); 265 log_it("CRON", getpid(), "DEATH", buf); 266 errx(ERROR_EXIT, "%s", buf); 267 } 268 269 (void) fcntl(fd, F_SETFD, 1); 270 } 271 272 rewind(fp); 273 fprintf(fp, "%d\n", getpid()); 274 fflush(fp); 275 (void) ftruncate(fileno(fp), ftell(fp)); 276 277 /* abandon fd and fp even though the file is open. we need to 278 * keep it open and locked, but we don't need the handles elsewhere. 279 */ 280 } 281 282 /* get_char(file) : like getc() but increment LineNumber on newlines 283 */ 284 int 285 get_char(file) 286 FILE *file; 287 { 288 int ch; 289 290 ch = getc(file); 291 if (ch == '\n') 292 Set_LineNum(LineNumber + 1) 293 return ch; 294 } 295 296 297 /* unget_char(ch, file) : like ungetc but do LineNumber processing 298 */ 299 void 300 unget_char(ch, file) 301 int ch; 302 FILE *file; 303 { 304 ungetc(ch, file); 305 if (ch == '\n') 306 Set_LineNum(LineNumber - 1) 307 } 308 309 310 /* get_string(str, max, file, termstr) : like fgets() but 311 * (1) has terminator string which should include \n 312 * (2) will always leave room for the null 313 * (3) uses get_char() so LineNumber will be accurate 314 * (4) returns EOF or terminating character, whichever 315 */ 316 int 317 get_string(string, size, file, terms) 318 char *string; 319 int size; 320 FILE *file; 321 char *terms; 322 { 323 int ch; 324 325 while (EOF != (ch = get_char(file)) && !strchr(terms, ch)) { 326 if (size > 1) { 327 *string++ = (char) ch; 328 size--; 329 } 330 } 331 332 if (size > 0) 333 *string = '\0'; 334 335 return ch; 336 } 337 338 339 /* skip_comments(file) : read past comment (if any) 340 */ 341 void 342 skip_comments(file) 343 FILE *file; 344 { 345 int ch; 346 347 while (EOF != (ch = get_char(file))) { 348 /* ch is now the first character of a line. 349 */ 350 351 while (ch == ' ' || ch == '\t') 352 ch = get_char(file); 353 354 if (ch == EOF) 355 break; 356 357 /* ch is now the first non-blank character of a line. 358 */ 359 360 if (ch != '\n' && ch != '#') 361 break; 362 363 /* ch must be a newline or comment as first non-blank 364 * character on a line. 365 */ 366 367 while (ch != '\n' && ch != EOF) 368 ch = get_char(file); 369 370 /* ch is now the newline of a line which we're going to 371 * ignore. 372 */ 373 } 374 if (ch != EOF) 375 unget_char(ch, file); 376 } 377 378 379 /* int in_file(char *string, FILE *file) 380 * return TRUE if one of the lines in file matches string exactly, 381 * FALSE otherwise. 382 */ 383 static int 384 in_file(string, file) 385 char *string; 386 FILE *file; 387 { 388 char line[MAX_TEMPSTR]; 389 390 rewind(file); 391 while (fgets(line, MAX_TEMPSTR, file)) { 392 if (line[0] != '\0') 393 if (line[strlen(line)-1] == '\n') 394 line[strlen(line)-1] = '\0'; 395 if (0 == strcmp(line, string)) 396 return TRUE; 397 } 398 return FALSE; 399 } 400 401 402 /* int allowed(char *username) 403 * returns TRUE if (ALLOW_FILE exists and user is listed) 404 * or (DENY_FILE exists and user is NOT listed) 405 * or (neither file exists but user=="root" so it's okay) 406 */ 407 int 408 allowed(username) 409 char *username; 410 { 411 static int init = FALSE; 412 static FILE *allow, *deny; 413 414 if (!init) { 415 init = TRUE; 416 #if defined(ALLOW_FILE) && defined(DENY_FILE) 417 allow = fopen(ALLOW_FILE, "r"); 418 deny = fopen(DENY_FILE, "r"); 419 Debug(DMISC, ("allow/deny enabled, %d/%d\n", !!allow, !!deny)) 420 #else 421 allow = NULL; 422 deny = NULL; 423 #endif 424 } 425 426 if (allow) 427 return (in_file(username, allow)); 428 if (deny) 429 return (!in_file(username, deny)); 430 431 #if defined(ALLOW_ONLY_ROOT) 432 return (strcmp(username, ROOT_USER) == 0); 433 #else 434 return TRUE; 435 #endif 436 } 437 438 439 void 440 log_it(username, xpid, event, detail) 441 char *username; 442 int xpid; 443 char *event; 444 char *detail; 445 { 446 PID_T pid = xpid; 447 #if defined(LOG_FILE) 448 char *msg; 449 TIME_T now = time((TIME_T) 0); 450 register struct tm *t = localtime(&now); 451 #endif /*LOG_FILE*/ 452 453 #if defined(SYSLOG) 454 static int syslog_open = 0; 455 #endif 456 457 #if defined(LOG_FILE) 458 /* we assume that MAX_TEMPSTR will hold the date, time, &punctuation. 459 */ 460 msg = malloc(strlen(username) 461 + strlen(event) 462 + strlen(detail) 463 + MAX_TEMPSTR); 464 465 if (msg == NULL) 466 warnx("failed to allocate memory for log message"); 467 else { 468 if (LogFD < OK) { 469 LogFD = open(LOG_FILE, O_WRONLY|O_APPEND|O_CREAT, 0600); 470 if (LogFD < OK) { 471 warn("can't open log file %s", LOG_FILE); 472 } else { 473 (void) fcntl(LogFD, F_SETFD, 1); 474 } 475 } 476 477 /* we have to sprintf() it because fprintf() doesn't always 478 * write everything out in one chunk and this has to be 479 * atomically appended to the log file. 480 */ 481 sprintf(msg, "%s (%02d/%02d-%02d:%02d:%02d-%d) %s (%s)\n", 482 username, 483 t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min, 484 t->tm_sec, pid, event, detail); 485 486 /* we have to run strlen() because sprintf() returns (char*) 487 * on old BSD. 488 */ 489 if (LogFD < OK || write(LogFD, msg, strlen(msg)) < OK) { 490 if (LogFD >= OK) 491 warn("%s", LOG_FILE); 492 warnx("can't write to log file"); 493 write(STDERR, msg, strlen(msg)); 494 } 495 496 free(msg); 497 } 498 #endif /*LOG_FILE*/ 499 500 #if defined(SYSLOG) 501 if (!syslog_open) { 502 /* we don't use LOG_PID since the pid passed to us by 503 * our client may not be our own. therefore we want to 504 * print the pid ourselves. 505 */ 506 # ifdef LOG_DAEMON 507 openlog(ProgramName, LOG_PID, LOG_CRON); 508 # else 509 openlog(ProgramName, LOG_PID); 510 # endif 511 syslog_open = TRUE; /* assume openlog success */ 512 } 513 514 syslog(LOG_INFO, "(%s) %s (%s)\n", username, event, detail); 515 516 #endif /*SYSLOG*/ 517 518 #if DEBUGGING 519 if (DebugFlags) { 520 fprintf(stderr, "log_it: (%s %d) %s (%s)\n", 521 username, pid, event, detail); 522 } 523 #endif 524 } 525 526 527 void 528 log_close() { 529 if (LogFD != ERR) { 530 close(LogFD); 531 LogFD = ERR; 532 } 533 } 534 535 536 /* two warnings: 537 * (1) this routine is fairly slow 538 * (2) it returns a pointer to static storage 539 */ 540 char * 541 first_word(s, t) 542 register char *s; /* string we want the first word of */ 543 register char *t; /* terminators, implicitly including \0 */ 544 { 545 static char retbuf[2][MAX_TEMPSTR + 1]; /* sure wish C had GC */ 546 static int retsel = 0; 547 register char *rb, *rp; 548 549 /* select a return buffer */ 550 retsel = 1-retsel; 551 rb = &retbuf[retsel][0]; 552 rp = rb; 553 554 /* skip any leading terminators */ 555 while (*s && (NULL != strchr(t, *s))) { 556 s++; 557 } 558 559 /* copy until next terminator or full buffer */ 560 while (*s && (NULL == strchr(t, *s)) && (rp < &rb[MAX_TEMPSTR])) { 561 *rp++ = *s++; 562 } 563 564 /* finish the return-string and return it */ 565 *rp = '\0'; 566 return rb; 567 } 568 569 570 /* warning: 571 * heavily ascii-dependent. 572 */ 573 void 574 mkprint(dst, src, len) 575 register char *dst; 576 register unsigned char *src; 577 register int len; 578 { 579 while (len-- > 0) 580 { 581 register unsigned char ch = *src++; 582 583 if (ch < ' ') { /* control character */ 584 *dst++ = '^'; 585 *dst++ = ch + '@'; 586 } else if (ch < 0177) { /* printable */ 587 *dst++ = ch; 588 } else if (ch == 0177) { /* delete/rubout */ 589 *dst++ = '^'; 590 *dst++ = '?'; 591 } else { /* parity character */ 592 sprintf(dst, "\\%03o", ch); 593 dst += 4; 594 } 595 } 596 *dst = '\0'; 597 } 598 599 600 /* warning: 601 * returns a pointer to malloc'd storage, you must call free yourself. 602 */ 603 char * 604 mkprints(src, len) 605 register unsigned char *src; 606 register unsigned int len; 607 { 608 register char *dst = malloc(len*4 + 1); 609 610 if (dst != NULL) 611 mkprint(dst, src, len); 612 613 return dst; 614 } 615 616 617 #ifdef MAIL_DATE 618 /* Sat, 27 Feb 93 11:44:51 CST 619 * 123456789012345678901234567 620 */ 621 char * 622 arpadate(clock) 623 time_t *clock; 624 { 625 time_t t = clock ?*clock :time(0L); 626 struct tm *tm = localtime(&t); 627 static char ret[32]; /* zone name might be >3 chars */ 628 629 if (tm->tm_year >= 100) 630 tm->tm_year += 1900; 631 632 (void) snprintf(ret, sizeof(ret), "%s, %2d %s %d %02d:%02d:%02d %s", 633 DowNames[tm->tm_wday], 634 tm->tm_mday, 635 MonthNames[tm->tm_mon], 636 tm->tm_year, 637 tm->tm_hour, 638 tm->tm_min, 639 tm->tm_sec, 640 TZONE(*tm)); 641 return ret; 642 } 643 #endif /*MAIL_DATE*/ 644 645 646 #ifdef HAVE_SAVED_UIDS 647 static int save_euid; 648 int swap_uids() { save_euid = geteuid(); return seteuid(getuid()); } 649 int swap_uids_back() { return seteuid(save_euid); } 650 #else /*HAVE_SAVED_UIDS*/ 651 int swap_uids() { return setreuid(geteuid(), getuid()); } 652 int swap_uids_back() { return swap_uids(); } 653 #endif /*HAVE_SAVED_UIDS*/ 654