Annotation of src/usr.bin/sudo/logging.c, Revision 1.17
1.1 millert 1: /*
1.17 ! millert 2: * Copyright (c) 1994-1996,1998-2007 Todd C. Miller <Todd.Miller@courtesan.com>
1.1 millert 3: *
1.15 millert 4: * Permission to use, copy, modify, and distribute this software for any
5: * purpose with or without fee is hereby granted, provided that the above
6: * copyright notice and this permission notice appear in all copies.
1.1 millert 7: *
1.15 millert 8: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1.13 millert 15: *
16: * Sponsored in part by the Defense Advanced Research Projects
17: * Agency (DARPA) and Air Force Research Laboratory, Air Force
18: * Materiel Command, USAF, under agreement number F39502-99-1-0512.
1.1 millert 19: */
20:
1.15 millert 21: #ifdef __TANDEM
22: # include <floss.h>
23: #endif
24:
1.17 ! millert 25: #include <config.h>
1.1 millert 26:
1.5 millert 27: #include <sys/types.h>
28: #include <sys/param.h>
29: #include <sys/stat.h>
30: #include <sys/wait.h>
1.1 millert 31: #include <stdio.h>
32: #ifdef STDC_HEADERS
1.5 millert 33: # include <stdlib.h>
34: # include <stddef.h>
35: #else
36: # ifdef HAVE_STDLIB_H
37: # include <stdlib.h>
38: # endif
1.1 millert 39: #endif /* STDC_HEADERS */
1.5 millert 40: #ifdef HAVE_STRING_H
41: # include <string.h>
42: #else
43: # ifdef HAVE_STRINGS_H
44: # include <strings.h>
45: # endif
46: #endif /* HAVE_STRING_H */
1.1 millert 47: #ifdef HAVE_UNISTD_H
1.5 millert 48: # include <unistd.h>
1.1 millert 49: #endif /* HAVE_UNISTD_H */
1.12 millert 50: #ifdef HAVE_ERR_H
51: # include <err.h>
52: #else
53: # include "emul/err.h"
54: #endif /* HAVE_ERR_H */
1.1 millert 55: #include <pwd.h>
56: #include <signal.h>
57: #include <time.h>
58: #include <errno.h>
59:
60: #include "sudo.h"
61:
62: #ifndef lint
1.17 ! millert 63: __unused static const char rcsid[] = "$Sudo: logging.c,v 1.168.2.11 2007/07/24 15:52:37 millert Exp $";
1.1 millert 64: #endif /* lint */
65:
66: static void do_syslog __P((int, char *));
67: static void do_logfile __P((char *));
68: static void send_mail __P((char *));
69: static void mail_auth __P((int, char *));
70: static char *get_timestr __P((void));
1.5 millert 71: static void mysyslog __P((int, const char *, ...));
1.1 millert 72:
1.5 millert 73: #define MAXSYSLOGTRIES 16 /* num of retries for broken syslogs */
1.1 millert 74:
75: /*
1.5 millert 76: * We do an openlog(3)/closelog(3) for each message because some
77: * authentication methods (notably PAM) use syslog(3) for their
78: * own nefarious purposes and may call openlog(3) and closelog(3).
79: * Note that because we don't want to assume that all systems have
80: * vsyslog(3) (HP-UX doesn't) "%m" will not be expanded.
81: * Sadly this is a maze of #ifdefs.
1.1 millert 82: */
83: static void
1.5 millert 84: #ifdef __STDC__
85: mysyslog(int pri, const char *fmt, ...)
86: #else
87: mysyslog(pri, fmt, va_alist)
1.1 millert 88: int pri;
89: const char *fmt;
1.5 millert 90: va_dcl
91: #endif
1.1 millert 92: {
1.5 millert 93: #ifdef BROKEN_SYSLOG
1.1 millert 94: int i;
1.5 millert 95: #endif
96: char buf[MAXSYSLOGLEN+1];
97: va_list ap;
1.1 millert 98:
1.5 millert 99: #ifdef __STDC__
100: va_start(ap, fmt);
101: #else
102: va_start(ap);
103: #endif
104: #ifdef LOG_NFACILITIES
1.15 millert 105: openlog("sudo", 0, def_syslog);
1.5 millert 106: #else
1.12 millert 107: openlog("sudo", 0);
1.5 millert 108: #endif
109: vsnprintf(buf, sizeof(buf), fmt, ap);
110: #ifdef BROKEN_SYSLOG
111: /*
112: * Some versions of syslog(3) don't guarantee success and return
113: * an int (notably HP-UX < 10.0). So, if at first we don't succeed,
114: * try, try again...
115: */
1.1 millert 116: for (i = 0; i < MAXSYSLOGTRIES; i++)
1.5 millert 117: if (syslog(pri, "%s", buf) == 0)
1.1 millert 118: break;
119: #else
1.5 millert 120: syslog(pri, "%s", buf);
1.1 millert 121: #endif /* BROKEN_SYSLOG */
1.5 millert 122: va_end(ap);
123: closelog();
124: }
1.1 millert 125:
126: /*
127: * Log a message to syslog, pre-pending the username and splitting the
128: * message into parts if it is longer than MAXSYSLOGLEN.
129: */
130: static void
131: do_syslog(pri, msg)
132: int pri;
133: char *msg;
134: {
1.17 ! millert 135: size_t len, maxlen;
! 136: char *p, *tmp, save;
! 137: const char *fmt;
! 138: const char fmt_first[] = "%8s : %s";
! 139: const char fmt_contd[] = "%8s : (command continued) %s";
1.1 millert 140:
141: /*
142: * Log the full line, breaking into multiple syslog(3) calls if necessary
143: */
1.17 ! millert 144: fmt = fmt_first;
! 145: maxlen = MAXSYSLOGLEN - (sizeof(fmt_first) - 6 + strlen(user_name));
! 146: for (p = msg; *p != '\0'; ) {
! 147: len = strlen(p);
! 148: if (len > maxlen) {
1.1 millert 149: /*
150: * Break up the line into what will fit on one syslog(3) line
1.17 ! millert 151: * Try to avoid breaking words into several lines if possible.
1.1 millert 152: */
1.17 ! millert 153: tmp = memrchr(p, ' ', maxlen);
! 154: if (tmp == NULL)
! 155: tmp = p + maxlen;
1.1 millert 156:
157: /* NULL terminate line, but save the char to restore later */
158: save = *tmp;
159: *tmp = '\0';
160:
1.17 ! millert 161: mysyslog(pri, fmt, user_name, p);
1.1 millert 162:
163: *tmp = save; /* restore saved character */
164:
1.17 ! millert 165: /* Advance p and eliminate leading whitespace */
! 166: for (p = tmp; *p == ' '; p++)
1.1 millert 167: ;
168: } else {
1.17 ! millert 169: mysyslog(pri, fmt, user_name, p);
! 170: p += len;
1.1 millert 171: }
1.17 ! millert 172: fmt = fmt_contd;
! 173: maxlen = MAXSYSLOGLEN - (sizeof(fmt_contd) - 6 + strlen(user_name));
1.1 millert 174: }
175: }
176:
177: static void
178: do_logfile(msg)
179: char *msg;
180: {
181: char *full_line;
182: char *beg, *oldend, *end;
183: FILE *fp;
184: mode_t oldmask;
1.9 millert 185: size_t maxlen;
1.1 millert 186:
187: oldmask = umask(077);
1.15 millert 188: maxlen = def_loglinelen > 0 ? def_loglinelen : 0;
189: fp = fopen(def_logfile, "a");
1.1 millert 190: (void) umask(oldmask);
191: if (fp == NULL) {
192: easprintf(&full_line, "Can't open log file: %s: %s",
1.15 millert 193: def_logfile, strerror(errno));
1.1 millert 194: send_mail(full_line);
1.17 ! millert 195: efree(full_line);
1.1 millert 196: } else if (!lock_file(fileno(fp), SUDO_LOCK)) {
197: easprintf(&full_line, "Can't lock log file: %s: %s",
1.15 millert 198: def_logfile, strerror(errno));
1.1 millert 199: send_mail(full_line);
1.17 ! millert 200: efree(full_line);
1.1 millert 201: } else {
1.15 millert 202: if (def_loglinelen == 0) {
1.1 millert 203: /* Don't pretty-print long log file lines (hard to grep) */
1.15 millert 204: if (def_log_host)
1.1 millert 205: (void) fprintf(fp, "%s : %s : HOST=%s : %s\n", get_timestr(),
206: user_name, user_shost, msg);
207: else
208: (void) fprintf(fp, "%s : %s : %s\n", get_timestr(),
209: user_name, msg);
210: } else {
1.15 millert 211: if (def_log_host)
1.1 millert 212: easprintf(&full_line, "%s : %s : HOST=%s : %s", get_timestr(),
213: user_name, user_shost, msg);
214: else
215: easprintf(&full_line, "%s : %s : %s", get_timestr(),
216: user_name, msg);
217:
218: /*
219: * Print out full_line with word wrap
220: */
221: beg = end = full_line;
222: while (beg) {
223: oldend = end;
224: end = strchr(oldend, ' ');
225:
226: if (maxlen > 0 && end) {
227: *end = '\0';
228: if (strlen(beg) > maxlen) {
229: /* too far, need to back up & print the line */
230:
231: if (beg == (char *)full_line)
232: maxlen -= 4; /* don't indent first line */
233:
234: *end = ' ';
235: if (oldend != beg) {
236: /* rewind & print */
237: end = oldend-1;
238: while (*end == ' ')
239: --end;
240: *(++end) = '\0';
241: (void) fprintf(fp, "%s\n ", beg);
242: *end = ' ';
243: } else {
244: (void) fprintf(fp, "%s\n ", beg);
245: }
246:
247: /* reset beg to point to the start of the new substr */
248: beg = end;
249: while (*beg == ' ')
250: ++beg;
251: } else {
252: /* we still have room */
253: *end = ' ';
254: }
255:
256: /* remove leading whitespace */
257: while (*end == ' ')
258: ++end;
259: } else {
260: /* final line */
261: (void) fprintf(fp, "%s\n", beg);
262: beg = NULL; /* exit condition */
263: }
264: }
1.17 ! millert 265: efree(full_line);
1.1 millert 266: }
267: (void) fflush(fp);
268: (void) lock_file(fileno(fp), SUDO_UNLOCK);
269: (void) fclose(fp);
270: }
271: }
272:
273: /*
274: * Two main functions, log_error() to log errors and log_auth() to
275: * log allow/deny messages.
276: */
277: void
278: log_auth(status, inform_user)
279: int status;
280: int inform_user;
281: {
1.17 ! millert 282: char *evstr = NULL;
1.1 millert 283: char *message;
284: char *logline;
285: int pri;
286:
1.15 millert 287: if (ISSET(status, VALIDATE_OK))
288: pri = def_syslog_goodpri;
1.1 millert 289: else
1.15 millert 290: pri = def_syslog_badpri;
1.1 millert 291:
292: /* Set error message, if any. */
1.15 millert 293: if (ISSET(status, VALIDATE_OK))
1.1 millert 294: message = "";
1.15 millert 295: else if (ISSET(status, FLAG_NO_USER))
1.1 millert 296: message = "user NOT in sudoers ; ";
1.15 millert 297: else if (ISSET(status, FLAG_NO_HOST))
1.1 millert 298: message = "user NOT authorized on host ; ";
1.15 millert 299: else if (ISSET(status, VALIDATE_NOT_OK))
1.1 millert 300: message = "command not allowed ; ";
301: else
302: message = "unknown error ; ";
303:
1.17 ! millert 304: if (sudo_user.env_vars != NULL) {
! 305: size_t len = 7; /* " ; ENV=" */
! 306: struct list_member *cur;
! 307: for (cur = sudo_user.env_vars; cur != NULL; cur = cur->next)
! 308: len += strlen(cur->value) + 1;
! 309: evstr = emalloc(len);
! 310: strlcpy(evstr, " ; ENV=", len);
! 311: for (cur = sudo_user.env_vars; cur != NULL; cur = cur->next) {
! 312: strlcat(evstr, cur->value, len);
! 313: strlcat(evstr, " ", len); /* NOTE: last one will fail */
! 314: }
! 315: }
! 316: easprintf(&logline, "%sTTY=%s ; PWD=%s ; USER=%s%s ; COMMAND=%s%s%s",
! 317: message, user_tty, user_cwd, *user_runas, evstr ? evstr : "",
! 318: user_cmnd, user_args ? " " : "", user_args ? user_args : "");
1.1 millert 319:
320: mail_auth(status, logline); /* send mail based on status */
321:
322: /* Inform the user if they failed to authenticate. */
1.15 millert 323: if (inform_user && ISSET(status, VALIDATE_NOT_OK)) {
324: if (ISSET(status, FLAG_NO_USER))
1.1 millert 325: (void) fprintf(stderr, "%s is not in the sudoers file. %s",
326: user_name, "This incident will be reported.\n");
1.15 millert 327: else if (ISSET(status, FLAG_NO_HOST))
1.1 millert 328: (void) fprintf(stderr, "%s is not allowed to run sudo on %s. %s",
329: user_name, user_shost, "This incident will be reported.\n");
1.15 millert 330: else if (ISSET(status, FLAG_NO_CHECK))
1.1 millert 331: (void) fprintf(stderr, "Sorry, user %s may not run sudo on %s.\n",
332: user_name, user_shost);
333: else
334: (void) fprintf(stderr,
335: "Sorry, user %s is not allowed to execute '%s%s%s' as %s on %s.\n",
336: user_name, user_cmnd, user_args ? " " : "",
337: user_args ? user_args : "", *user_runas, user_host);
338: }
339:
340: /*
341: * Log via syslog and/or a file.
342: */
1.15 millert 343: if (def_syslog)
1.1 millert 344: do_syslog(pri, logline);
1.15 millert 345: if (def_logfile)
1.1 millert 346: do_logfile(logline);
347:
1.17 ! millert 348: efree(evstr);
! 349: efree(logline);
1.1 millert 350: }
351:
352: void
353: #ifdef __STDC__
354: log_error(int flags, const char *fmt, ...)
355: #else
1.17 ! millert 356: log_error(flags, fmt, va_alist)
! 357: int flags;
! 358: const char *fmt;
1.1 millert 359: va_dcl
360: #endif
361: {
362: int serrno = errno;
363: char *message;
364: char *logline;
1.17 ! millert 365: char *evstr = NULL;
1.1 millert 366: va_list ap;
367: #ifdef __STDC__
368: va_start(ap, fmt);
369: #else
370: va_start(ap);
371: #endif
372:
373: /* Become root if we are not already to avoid user control */
374: if (geteuid() != 0)
1.9 millert 375: set_perms(PERM_ROOT);
1.1 millert 376:
377: /* Expand printf-style format + args. */
378: evasprintf(&message, fmt, ap);
379: va_end(ap);
380:
1.17 ! millert 381: if (sudo_user.env_vars != NULL) {
! 382: size_t len = 7; /* " ; ENV=" */
! 383: struct list_member *cur;
! 384: for (cur = sudo_user.env_vars; cur != NULL; cur = cur->next)
! 385: len += strlen(cur->value) + 1;
! 386: evstr = emalloc(len);
! 387: strlcpy(evstr, " ; ENV=", len);
! 388: for (cur = sudo_user.env_vars; cur != NULL; cur = cur->next) {
! 389: strlcat(evstr, cur->value, len);
! 390: strlcat(evstr, " ", len); /* NOTE: last one will fail */
! 391: }
! 392: }
! 393:
! 394: if (ISSET(flags, MSG_ONLY))
1.1 millert 395: logline = message;
1.17 ! millert 396: else if (ISSET(flags, USE_ERRNO)) {
1.1 millert 397: if (user_args) {
398: easprintf(&logline,
1.17 ! millert 399: "%s: %s ; TTY=%s ; PWD=%s ; USER=%s%s ; COMMAND=%s %s",
1.1 millert 400: message, strerror(serrno), user_tty, user_cwd, *user_runas,
1.17 ! millert 401: evstr ? evstr : "", user_cmnd, user_args);
1.1 millert 402: } else {
403: easprintf(&logline,
1.17 ! millert 404: "%s: %s ; TTY=%s ; PWD=%s ; USER=%s%s ; COMMAND=%s", message,
! 405: strerror(serrno), user_tty, user_cwd, *user_runas,
! 406: evstr ? evstr : "", user_cmnd);
1.1 millert 407: }
408: } else {
409: if (user_args) {
410: easprintf(&logline,
1.17 ! millert 411: "%s ; TTY=%s ; PWD=%s ; USER=%s%s ; COMMAND=%s %s", message,
! 412: user_tty, user_cwd, *user_runas, evstr ? evstr : "",
! 413: user_cmnd, user_args);
1.1 millert 414: } else {
415: easprintf(&logline,
1.17 ! millert 416: "%s ; TTY=%s ; PWD=%s ; USER=%s%s ; COMMAND=%s", message,
! 417: user_tty, user_cwd, *user_runas, evstr ? evstr : "", user_cmnd);
1.1 millert 418: }
419: }
420:
421: /*
422: * Tell the user.
423: */
1.17 ! millert 424: if (ISSET(flags, USE_ERRNO))
1.12 millert 425: warn("%s", message);
426: else
427: warnx("%s", message);
1.1 millert 428:
429: /*
430: * Send a copy of the error via mail.
431: */
1.17 ! millert 432: if (!ISSET(flags, NO_MAIL))
1.1 millert 433: send_mail(logline);
434:
435: /*
436: * Log to syslog and/or a file.
437: */
1.15 millert 438: if (def_syslog)
439: do_syslog(def_syslog_badpri, logline);
440: if (def_logfile)
1.1 millert 441: do_logfile(logline);
442:
1.17 ! millert 443: efree(message);
1.5 millert 444: if (logline != message)
1.17 ! millert 445: efree(logline);
1.1 millert 446:
1.17 ! millert 447: if (!ISSET(flags, NO_EXIT))
1.1 millert 448: exit(1);
449: }
450:
451: #define MAX_MAILFLAGS 63
452:
453: /*
454: * Send a message to MAILTO user
455: */
456: static void
457: send_mail(line)
458: char *line;
459: {
460: FILE *mail;
461: char *p;
1.11 millert 462: int pfd[2];
1.8 mpech 463: pid_t pid;
1.2 millert 464: sigset_t set, oset;
1.7 millert 465: #ifndef NO_ROOT_MAILER
466: static char *root_envp[] = {
467: "HOME=/",
468: "PATH=/usr/bin:/bin",
469: "LOGNAME=root",
1.17 ! millert 470: "USERNAME=root",
1.7 millert 471: "USER=root",
472: NULL
473: };
474: #endif
1.1 millert 475:
476: /* Just return if mailer is disabled. */
1.15 millert 477: if (!def_mailerpath || !def_mailto)
1.1 millert 478: return;
479:
1.2 millert 480: (void) sigemptyset(&set);
481: (void) sigaddset(&set, SIGCHLD);
482: (void) sigprocmask(SIG_BLOCK, &set, &oset);
1.1 millert 483:
1.12 millert 484: if (pipe(pfd) == -1)
485: err(1, "cannot open pipe");
1.1 millert 486:
1.2 millert 487: switch (pid = fork()) {
488: case -1:
489: /* Error. */
1.12 millert 490: err(1, "cannot fork");
1.2 millert 491: break;
492: case 0:
493: {
494: char *argv[MAX_MAILFLAGS + 1];
495: char *mpath, *mflags;
496: int i;
497:
1.5 millert 498: /* Child, set stdin to output side of the pipe */
499: if (pfd[0] != STDIN_FILENO) {
500: (void) dup2(pfd[0], STDIN_FILENO);
501: (void) close(pfd[0]);
502: }
1.2 millert 503: (void) close(pfd[1]);
504:
505: /* Build up an argv based the mailer path and flags */
1.15 millert 506: mflags = estrdup(def_mailerflags);
507: mpath = estrdup(def_mailerpath);
1.2 millert 508: if ((argv[0] = strrchr(mpath, ' ')))
509: argv[0]++;
510: else
511: argv[0] = mpath;
512:
513: i = 1;
514: if ((p = strtok(mflags, " \t"))) {
515: do {
516: argv[i] = p;
517: } while (++i < MAX_MAILFLAGS && (p = strtok(NULL, " \t")));
518: }
519: argv[i] = NULL;
1.1 millert 520:
1.5 millert 521: /* Close password file so we don't leak the fd. */
522: endpwent();
523:
1.7 millert 524: /*
525: * Depending on the config, either run the mailer as root
526: * (so user cannot kill it) or as the user (for the paranoid).
527: */
528: #ifndef NO_ROOT_MAILER
1.17 ! millert 529: set_perms(PERM_ROOT);
1.7 millert 530: execve(mpath, argv, root_envp);
531: #else
1.9 millert 532: set_perms(PERM_FULL_USER);
1.2 millert 533: execv(mpath, argv);
1.7 millert 534: #endif /* NO_ROOT_MAILER */
1.2 millert 535: _exit(127);
536: }
537: break;
538: }
1.1 millert 539:
1.5 millert 540: (void) close(pfd[0]);
1.2 millert 541: mail = fdopen(pfd[1], "w");
1.1 millert 542:
1.2 millert 543: /* Pipes are all setup, send message via sendmail. */
1.17 ! millert 544: (void) fprintf(mail, "To: %s\nFrom: %s\nAuto-Submitted: %s\nSubject: ",
! 545: def_mailto, user_name, "auto-generated");
1.15 millert 546: for (p = def_mailsub; *p; p++) {
1.2 millert 547: /* Expand escapes in the subject */
548: if (*p == '%' && *(p+1) != '%') {
549: switch (*(++p)) {
550: case 'h':
551: (void) fputs(user_host, mail);
552: break;
553: case 'u':
554: (void) fputs(user_name, mail);
555: break;
556: default:
557: p--;
558: break;
559: }
560: } else
561: (void) fputc(*p, mail);
562: }
563: (void) fprintf(mail, "\n\n%s : %s : %s : %s\n\n", user_host,
564: get_timestr(), user_name, line);
565: fclose(mail);
1.1 millert 566:
1.17 ! millert 567: (void) sigprocmask(SIG_SETMASK, &oset, NULL);
1.10 millert 568: /* If mailer is done, wait for it now. If not, we'll get it later. */
569: reapchild(SIGCHLD);
1.1 millert 570: }
571:
572: /*
573: * Send mail based on the value of "status" and compile-time options.
574: */
575: static void
576: mail_auth(status, line)
577: int status;
578: char *line;
579: {
580: int mail_mask;
581:
582: /* If any of these bits are set in status, we send mail. */
1.15 millert 583: if (def_mail_always)
1.1 millert 584: mail_mask =
585: VALIDATE_ERROR|VALIDATE_OK|FLAG_NO_USER|FLAG_NO_HOST|VALIDATE_NOT_OK;
586: else {
587: mail_mask = VALIDATE_ERROR;
1.15 millert 588: if (def_mail_no_user)
589: SET(mail_mask, FLAG_NO_USER);
590: if (def_mail_no_host)
591: SET(mail_mask, FLAG_NO_HOST);
592: if (def_mail_no_perms)
593: SET(mail_mask, VALIDATE_NOT_OK);
1.1 millert 594: }
595:
596: if ((status & mail_mask) != 0)
597: send_mail(line);
598: }
599:
600: /*
601: * SIGCHLD sig handler--wait for children as they die.
602: */
603: RETSIGTYPE
604: reapchild(sig)
605: int sig;
606: {
607: int status, serrno = errno;
1.14 millert 608: #ifdef sudo_waitpid
609: pid_t pid;
1.1 millert 610:
1.14 millert 611: do {
612: pid = sudo_waitpid(-1, &status, WNOHANG);
613: } while (pid != 0 && (pid != -1 || errno == EINTR));
1.1 millert 614: #else
615: (void) wait(&status);
616: #endif
617: errno = serrno;
618: }
619:
620: /*
621: * Return an ascii string with the current date + time
622: * Uses strftime() if available, else falls back to ctime().
623: */
624: static char *
625: get_timestr()
626: {
627: char *s;
628: time_t now = time((time_t) 0);
629: #ifdef HAVE_STRFTIME
630: static char buf[128];
631: struct tm *timeptr;
632:
633: timeptr = localtime(&now);
1.15 millert 634: if (def_log_year)
1.1 millert 635: s = "%h %e %T %Y";
636: else
637: s = "%h %e %T";
638:
639: /* strftime() does not guarantee to NUL-terminate so we must check. */
640: buf[sizeof(buf) - 1] = '\0';
641: if (strftime(buf, sizeof(buf), s, timeptr) && buf[sizeof(buf) - 1] == '\0')
642: return(buf);
643:
644: #endif /* HAVE_STRFTIME */
645:
646: s = ctime(&now) + 4; /* skip day of the week */
1.15 millert 647: if (def_log_year)
1.1 millert 648: s[20] = '\0'; /* avoid the newline */
649: else
650: s[15] = '\0'; /* don't care about year */
651:
652: return(s);
653: }