Index: usr.sbin/syslogd/syslogd.8 =================================================================== --- usr.sbin/syslogd/syslogd.8 +++ usr.sbin/syslogd/syslogd.8 @@ -36,7 +36,7 @@ .Nd log systems messages .Sh SYNOPSIS .Nm -.Op Fl 468ACcdFHkNnosTuv +.Op Fl 468ACcdFHkNnosTuvZ .Op Fl a Ar allowed_peer .Op Fl b Ar bind_address .Op Fl f Ar config_file @@ -333,6 +333,10 @@ If specified more than once, the names of the facility and priority are logged with each locally-written message. +.It Fl Z +Generate timestamps in ISO 8601 format. +This includes the year and the timezone, and all logging is done +in UTC. .El .Pp The Index: usr.sbin/syslogd/syslogd.c =================================================================== --- usr.sbin/syslogd/syslogd.c +++ usr.sbin/syslogd/syslogd.c @@ -71,7 +71,7 @@ */ /* Maximum number of characters in time of last occurrence */ -#define MAXDATELEN 16 +#define MAXDATELEN 33 #define MAXLINE 1024 /* maximum line length */ #define MAXSVLINE MAXLINE /* maximum saved line length */ #define DEFUPRI (LOG_USER|LOG_NOTICE) @@ -309,6 +309,7 @@ static int MarkSeq; /* mark sequence number */ static int NoBind; /* don't bind() as suggested by RFC 3164 */ static int SecureMode; /* when true, receive only unix domain socks */ +static int ZuluTime = 0; /* display date and time in UTC ISO format */ #ifdef INET6 static int family = PF_UNSPEC; /* protocol family (IPv4, IPv6 or both) */ #else @@ -352,7 +353,7 @@ static void fprintlog(struct filed *, int, const char *); static void init(int); static void logerror(const char *); -static void logmsg(int, const char *, const char *, const char *, int); +static void logmsg(int, char [], const char *, const char *, int); static void log_deadchild(pid_t, int, const char *); static void markit(void); static int socksetup(struct peer *); @@ -460,7 +461,7 @@ if (madvise(NULL, 0, MADV_PROTECT) != 0) dprintf("madvise() failed: %s\n", strerror(errno)); - while ((ch = getopt(argc, argv, "468Aa:b:cCdf:FHkl:m:nNop:P:sS:Tuv")) + while ((ch = getopt(argc, argv, "468Aa:b:cCdf:FHkl:m:nNop:P:sS:TuvZ")) != -1) switch (ch) { #ifdef INET @@ -601,6 +602,9 @@ case 'v': /* log facility and priority */ LogFacPri++; break; + case 'Z': /* time stamps in UTC ISO format */ + ZuluTime = 1; + break; default: usage(); } @@ -843,7 +847,7 @@ { fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n", - "usage: syslogd [-468ACcdFHknosTuv] [-a allowed_peer]", + "usage: syslogd [-468ACcdFHknosTuvZ] [-a allowed_peer]", " [-b bind_address] [-f config_file]", " [-l [mode:]path] [-m mark_interval]", " [-P pid_file] [-p log_socket]", @@ -858,7 +862,7 @@ static void parsemsg(const char *from, char *msg) { - const char *timestamp; + char timestamp[MAXDATELEN]; char *q; long n; int i, c, pri, msglen; @@ -901,26 +905,87 @@ pri = LOG_MAKEPRI(LOG_USER, LOG_PRI(pri)); /* - * The TIMESTAMP field is the local time and is in the format of + * If the "-Z" option was not used when invoking syslogd, + * the TIMESTAMP field is the local time and is in the format of * "Mmm dd hh:mm:ss" (without the quote marks). - * A single space character MUST follow the TIMESTAMP field. + * If the "-Z" option was given when invoking syslogd, + * the TIMESTAMP field is in UTC and is in the ISO 8601 format of + * "YYYY-MM-DDThh:mm:ss[.mmm]Z" (without the quote marks). + * A single space character MUST follow the TIMESTAMP field. * * XXXGL: the check can be improved. */ + timestamp[0] = '\0'; msg += i + 1; msglen = strlen(msg); - if (msglen < MAXDATELEN || msg[3] != ' ' || msg[6] != ' ' || - msg[9] != ':' || msg[12] != ':' || msg[15] != ' ') { + if (msglen >= 16 && msg[3] == ' ' && msg[6] == ' ' && + msg[9] == ':' && msg[12] == ':' && msg[15] == ' ') { + /* BSD syslog TIMESTAMP, RFC 3164 */ + strlcpy(timestamp, msg, 16); + msg += 16; + msglen -= 16; + if (ZuluTime) + RemoteAddDate = 1; + } else if (msglen >= 20 && + isdigit(msg[0]) && isdigit(msg[1]) && isdigit(msg[2]) && + isdigit(msg[3]) && msg[4] == '-' && + isdigit(msg[5]) && isdigit(msg[6]) && msg[7] == '-' && + isdigit(msg[8]) && isdigit(msg[9]) && msg[10] == 'T' && + isdigit(msg[11]) && isdigit(msg[12]) && msg[13] == ':' && + isdigit(msg[14]) && isdigit(msg[15]) && msg[16] == ':' && + isdigit(msg[17]) && isdigit(msg[18]) && (msg[19] == '.' || + msg[19] == 'Z' || msg[19] == '+' || msg[19] == '-')) { + /* FULL-DATE "T" FULL-TIME, RFC 5424 */ + strlcpy(timestamp, msg, sizeof(timestamp)); + msg += 19; + msglen -= 19; + i = 0; + if (msglen >= 3 && msg[0] == '.' && isdigit(msg[1])) { + /* TIME-SECFRAC */ + msg += 2; + msglen -= 2; + i += 2; + while(i < 7 && msglen >= 1 && isdigit(msg[0])) { + msg++; + msglen--; + i++; + } + } + if (msglen >= 2 && msg[0] == 'Z' && msg[1] == ' ') { + /* "Z" */ + timestamp[20+i] = '\0'; + msg += 2; + msglen -= 2; + } else if (msglen >= 7 && + (msg[0] == '+' || msg[0] == '-') && + isdigit(msg[1]) && isdigit(msg[2]) && + msg[3] == ':' && + isdigit(msg[4]) && isdigit(msg[5]) && + msg[6] == ' ') { + /* TIME-NUMOFFSET */ + timestamp[25+i] = '\0'; + msg += 7; + msglen -= 7; + } else { + /* invalid time format, roll back */ + msg -= 19 + i; + msglen += 19 + i; + dprintf("Invalid TIMESTAMP from %s: %s\n", from, msg); + RemoteAddDate = 1; + } + } else if (msglen >= 2 && msg[0] == '-' && msg[1] == ' ') { + /* NILVALUE, RFC 5424 */ + msg += 2; + msglen -= 2; + dprintf("No TIMESTAMP included from %s: %s\n", from, msg); + RemoteAddDate = 1; + } else { dprintf("Invalid TIMESTAMP from %s: %s\n", from, msg); - return; + RemoteAddDate = 1; } - if (!RemoteAddDate) - timestamp = msg; - else - timestamp = NULL; - msg += MAXDATELEN; - msglen -= MAXDATELEN; + if (RemoteAddDate) + *timestamp = '\0'; /* * A single space character MUST also follow the HOSTNAME field. @@ -1046,7 +1111,7 @@ logmsg(pri, NULL, p, LocalHostName, flags); } -static time_t now; +static struct timeval now; /* * Match a program or host name against a specification. @@ -1098,21 +1163,38 @@ * the priority. */ static void -logmsg(int pri, const char *timestamp, const char *msg, const char *from, +logmsg(int pri, char *timestamp, const char *msg, const char *from, int flags) { struct filed *f; int i, fac, msglen, prilev; char prog[NAME_MAX+1]; char buf[MAXLINE+1]; + char nowtimestamp[MAXDATELEN] = ""; dprintf("logmsg: pri %o, flags %x, from %s, msg %s\n", pri, flags, from, msg); - (void)time(&now); - if (timestamp == NULL) - timestamp = ctime(&now) + 4; + (void)gettimeofday(&now, NULL); + if (timestamp == NULL || timestamp[0] == '\0') { + if (ZuluTime) { + struct tm *tm; + size_t l; + tm = gmtime(&now.tv_sec); + l = strftime(nowtimestamp, sizeof(nowtimestamp), "%FT%T", tm); + /* + * Use only millisecond precision as some time has + * passed since syslog(3) was called. + */ + snprintf(nowtimestamp + l, sizeof(nowtimestamp) - l, + ".%03ldZ", now.tv_usec / 1000); + } else + strlcpy(nowtimestamp, ctime(&now.tv_sec) + 4, 16); + + timestamp = nowtimestamp; + } + /* extract facility and priority level */ if (flags & MARK) fac = LOG_NFACILITIES; @@ -1182,7 +1264,8 @@ continue; /* don't output marks to recently written files */ - if ((flags & MARK) && (now - f->f_time) < MarkInterval / 2) + if ((flags & MARK) && + (now.tv_sec - f->f_time) < MarkInterval / 2) continue; /* @@ -1196,7 +1279,7 @@ sizeof(f->f_lasttime)); f->f_prevcount++; dprintf("msg repeated %d times, %ld sec of %d\n", - f->f_prevcount, (long)(now - f->f_time), + f->f_prevcount, (long)(now.tv_sec - f->f_time), repeatinterval[f->f_repeatcount]); /* * If domark would have logged this by now, @@ -1204,7 +1287,7 @@ * but back off so we'll flush less often * in the future. */ - if (now > REPEATTIME(f)) { + if (now.tv_sec > REPEATTIME(f)) { fprintlog(f, flags, (char *)NULL); BACKOFF(f); } @@ -1259,7 +1342,7 @@ if (f->f_type == F_WALL) { /* The time displayed is not synchornized with the other log * destinations (like messages). Following fragment was using - * ctime(&now), which was updating the time every 30 sec. + * ctime(&now.tv_sec), which was updating the time every 30 sec. * With f_lasttime, time is synchronized correctly. */ iov[0] = (struct iovec){ @@ -1361,7 +1444,7 @@ }; } dprintf("Logging to %s", TypeNames[f->f_type]); - f->f_time = now; + f->f_time = now.tv_sec; switch (f->f_type) { case F_UNUSED: @@ -1389,11 +1472,11 @@ /* check for local vs remote messages */ if (strcasecmp(f->f_prevhost, LocalHostName)) l = snprintf(line, sizeof line - 1, - "<%d>%.15s Forwarded from %s: %s", + "<%d>%.32s Forwarded from %s: %s", f->f_prevpri, (char *)iov[0].iov_base, f->f_prevhost, (char *)iov[5].iov_base); else - l = snprintf(line, sizeof line - 1, "<%d>%.15s %s", + l = snprintf(line, sizeof line - 1, "<%d>%.32s %s", f->f_prevpri, (char *)iov[0].iov_base, (char *)iov[5].iov_base); if (l < 0) @@ -2362,7 +2445,7 @@ struct filed *f; struct deadq_entry *dq, *dq0; - now = time((time_t *)NULL); + (void)gettimeofday(&now, NULL); MarkSeq += TIMERINTVL; if (MarkSeq >= MarkInterval) { logmsg(LOG_INFO, NULL, "-- MARK --", LocalHostName, MARK); @@ -2370,7 +2453,7 @@ } STAILQ_FOREACH(f, &fhead, next) { - if (f->f_prevcount && now >= REPEATTIME(f)) { + if (f->f_prevcount && now.tv_sec >= REPEATTIME(f)) { dprintf("flush %s: repeated %d times, %d sec.\n", TypeNames[f->f_type], f->f_prevcount, repeatinterval[f->f_repeatcount]);