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