# 2.11BSD#431 - /usr/src/usr.sbin/cron/misc.c

##### Functions

    acquire_daemonlockX
    allowed           X
    arpadate          X
    first_word        X
    get_char          X
    get_string        X
    log_close         X
    log_it            X
    mkprint           X
    mkprints          X
    set_cron_cwd      X
    set_cron_uid      X
    set_debug_flags   X
    skip_comments     X
    strcmp_until      X
    strdtb            X
    swap_uids         X
    swap_uids_back    X
    unget_char        X

##### Macros

    LOG_CRON          X

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

#### Defined functions

acquire_daemonlock defined in line 236; never used
allowed defined in line 412; never used
arpadate defined in line 618; never used
first_word defined in line 538; never used
get_char defined in line 290; used 4 times

- in line 331, 353-358(2), 374

get_string defined in line 322; never used
in_file defined in line 389; used 2 times

- in line 432-434(2)

log_close defined in line 525; never used
log_it defined in line 444; used 2 times

- in line 260, 271

mkprint defined in line 571; used 1 times

- in line 608

mkprints defined in line 601; never used
set_cron_cwd defined in line 177; never used
set_cron_uid defined in line 167; never used
set_debug_flags defined in line 100; never used
skip_comments defined in line 347; never used
strcmp_until defined in line 45; used 1 times

- in line 128

strdtb defined in line 71; never used
swap_uids defined in line 640; never used
swap_uids_back defined in line 646; used 1 times

- in /usr/src/usr.sbin/cron/crontab.c line 211

unget_char defined in line 305; used 1 times

- in line 381

#### Defined variables

LogFD defined in line 43; used 10 times

- in line 470-477(4), 492-493(3), 527-529(3)

rcsid defined in line 19; never used
save_euid defined in line 639; used 2 times

- in line 642, 648

#### Defined macros

LOG_CRON defined in line 40; used 2 times

- in line 39, 508

Last modified: 1999-06-19 Generated: 2016-12-26 | Generated by src2html V0.67 page hit count: 2131 |