From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.1 (2015-04-28) on sa.local.altlinux.org X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00 autolearn=ham autolearn_force=no version=3.4.1 X-MC-Unique: Ne3EcrSCOr2JarfdYv781w-1 From: Alexey Gladkov To: ALT Devel discussion list Date: Tue, 27 Oct 2020 12:33:48 +0100 Message-Id: <20201027113351.3373843-4-legion@altlinux.ru> In-Reply-To: <20201027113351.3373843-1-legion@altlinux.ru> References: <20201027113351.3373843-1-legion@altlinux.ru> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: altlinux.ru Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=WINDOWS-1252 Subject: [devel] [PATCH 3/6] syslogd: Implement customization of log file records X-BeenThere: devel@lists.altlinux.org X-Mailman-Version: 2.1.12 Precedence: list Reply-To: ALT Linux Team development discussions List-Id: ALT Linux Team development discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 27 Oct 2020 11:34:09 -0000 Archived-At: List-Archive: List-Post: Signed-off-by: Alexey Gladkov --- syslog.conf | 17 +++ syslogd.c | 303 ++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 277 insertions(+), 43 deletions(-) diff --git a/syslog.conf b/syslog.conf index 801f35f..83fa433 100644 --- a/syslog.conf +++ b/syslog.conf @@ -1,4 +1,21 @@ # /etc/syslog.conf - Configuration file for syslogd(8) +# +# Specifies log format. The log format can contain placeholders for log me= ssage +# elements: +# %t - time of last occurrence. +# %h - host from which this message. +# %m - message text. +# %% - '%' character. +# \\ - '\' character. +# \b - backspace character. +# \t - horizontal tab character. +# \f - form feed character. +# \n - new line character. +# \r - carriage return character. +# +# Default: +#log_format: %t %h %m + # # For info about the format of this file, see "man syslog.conf". # diff --git a/syslogd.c b/syslogd.c index d4aa9e4..214c4e9 100644 --- a/syslogd.c +++ b/syslogd.c @@ -289,16 +289,38 @@ struct sourceinfo { =09unsigned int flags; } sinfo; =20 -enum record_fields_type { -=09RECORD_FIELD_TIME =3D 0, -=09RECORD_FIELD_SEP1, -=09RECORD_FIELD_HOST, -=09RECORD_FIELD_SEP2, -=09RECORD_FIELD_MSG, -=09RECORD_FIELD_EOL, -=09RECORD_FIELD_COUNTS, +enum log_format_type { +=09LOG_FORMAT_NONE =3D 0, +=09LOG_FORMAT_BOL, +=09LOG_FORMAT_TIME, +=09LOG_FORMAT_HOST, +=09LOG_FORMAT_MSG, +=09LOG_FORMAT_EOL, +=09LOG_FORMAT_COUNTS, }; =20 +#define LOG_FORMAT_REPEAT_MAX 5 +#define LOG_FORMAT_FIELDS_MAX LOG_FORMAT_COUNTS * LOG_FORMAT_REPEAT_MAX +#define LOG_FORMAT_IOVEC_MAX LOG_FORMAT_FIELDS_MAX * 2 + 1 + +struct log_format_field { +=09enum log_format_type f_type; +=09struct iovec *f_iov; +}; + +struct log_format { +=09char *line; + +=09struct iovec *iov; +=09size_t iovec_nr; + +=09struct log_format_field *fields; +=09size_t fields_nr; +}; + +static struct log_format log_fmt =3D { 0 }; +static long int iovec_max =3D 0; + static int=09Debug;=09=09=09/* debug flag */ static int=09Compress =3D 1;=09=09/* compress repeated messages flag */ static char=09LocalHostName[MAXHOSTNAMELEN+1];=09/* our hostname */ @@ -307,6 +329,7 @@ static char=09*emptystring =3D ""; static int=09InetInuse =3D 0;=09=09/* non-zero if INET sockets are being u= sed */ static int=09*finet =3D NULL;=09=09/* Internet datagram sockets */ static int=09Initialized =3D 0;=09/* set when we have initialized ourselve= s */ +static int=09LogFormatInitialized =3D 0; /* set when we have initialized l= og_format */ static int=09MarkInterval =3D 20 * 60;=09/* interval between marks in seco= nds */ #ifdef INET6 static int=09family =3D PF_UNSPEC;=09/* protocol family (IPv4, IPv6 or bot= h) */ @@ -340,9 +363,15 @@ void untty(void); void printchopped(const struct sourceinfo* const, char *msg, size_t len, i= nt fd); void printline(const struct sourceinfo* const, char *msg); void logmsg(int pri, char *msg, const struct sourceinfo* const, int flags)= ; +char *get_record_field(struct log_format *log_fmt, enum log_format_type na= me) +=09SYSKLOGD_NONNULL((1)); +void clear_record_fields(struct log_format *log_fmt) +=09SYSKLOGD_NONNULL((1)); +void set_record_field(struct log_format *log_fmt, enum log_format_type nam= e, char *value, ssize_t len) +=09SYSKLOGD_NONNULL((1)); void fprintlog(register struct filed *f, char *from, int flags, char *msg)= ; void endtty(int); -void wallmsg(register struct filed *f, struct iovec iov[RECORD_FIELD_COUNT= S]); +void wallmsg(register struct filed *f, struct log_format *log_fmt); void reapchild(int); const char *cvtaddr(struct sockaddr_storage *f, int len); const char *cvthname(struct sockaddr_storage *f, int len); @@ -358,6 +387,9 @@ int decode(char *name, struct code *codetab); static void verbosef(char *, ...) =09SYSKLOGD_FORMAT((__printf__, 1, 2)) SYSKLOGD_NONNULL((1)); static void allocate_log(void); +int set_log_format_field(struct log_format *log_fmt, size_t i, enum log_fo= rmat_type t, char *s, size_t n) +=09SYSKLOGD_NONNULL((1)); +int parse_log_format(struct log_format *log_fmt, char *s); void sighup_handler(int); =20 #ifdef SYSLOG_UNIXAF @@ -648,6 +680,9 @@ int main(int argc, char **argv) =09if (funix_dir && *funix_dir) =09=09add_funix_dir(funix_dir); =20 +=09if (parse_log_format(&log_fmt, "%t %h %m") < 0) +=09=09exit(1); + =09if ( !(Debug || NoFork) ) =09{ =09=09verbosef("Checking pidfile.\n"); @@ -1454,16 +1489,38 @@ finish: =09sigprocmask(SIG_UNBLOCK, &mask, NULL); } =20 -static inline void set_record_field(struct iovec iov[RECORD_FIELD_COUNTS], -=09=09enum record_fields_type name, char *value, size_t len) +char *get_record_field(struct log_format *log_fmt, enum log_format_type na= me) { -=09iov[name].iov_base =3D value; -=09iov[name].iov_len =3D len =3D=3D -1 ? strlen(value) : len; +=09for (int i =3D 0; i < LOG_FORMAT_FIELDS_MAX && log_fmt->fields[i].f_iov= ; i++) { +=09=09if (log_fmt->fields[i].f_type =3D=3D name) +=09=09=09return log_fmt->fields[i].f_iov->iov_base; +=09} +=09return NULL; +} + +void set_record_field(struct log_format *log_fmt, +=09=09enum log_format_type name, char *value, ssize_t len) +{ +=09size_t iov_len =3D len =3D=3D -1 ? strlen(value) : len; + +=09for (int i =3D 0; i < LOG_FORMAT_FIELDS_MAX && log_fmt->fields[i].f_iov= ; i++) { +=09=09if (log_fmt->fields[i].f_type =3D=3D name) { +=09=09=09log_fmt->fields[i].f_iov->iov_base =3D value; +=09=09=09log_fmt->fields[i].f_iov->iov_len =3D iov_len; +=09=09} +=09} +} + +void clear_record_fields(struct log_format *log_fmt) +{ +=09for (int i =3D 0; i < LOG_FORMAT_FIELDS_MAX && log_fmt->fields[i].f_iov= ; i++) { +=09=09log_fmt->fields[i].f_iov->iov_base =3D NULL; +=09=09log_fmt->fields[i].f_iov->iov_len =3D 0; +=09} } =20 void fprintlog(struct filed *f, char *from, int flags, char *msg) { -=09struct iovec iov[RECORD_FIELD_COUNTS]; =09char repbuf[80]; #ifdef SYSLOG_INET =09register int l; @@ -1475,19 +1532,18 @@ void fprintlog(struct filed *f, char *from, int fla= gs, char *msg) =20 =09verbosef("Called fprintlog, "); =20 -=09set_record_field(iov, RECORD_FIELD_TIME, f->f_lasttime, 15); -=09set_record_field(iov, RECORD_FIELD_SEP1, " ", 1); -=09set_record_field(iov, RECORD_FIELD_HOST, f->f_prevhost, -1); -=09set_record_field(iov, RECORD_FIELD_SEP2, " ", 1); +=09clear_record_fields(&log_fmt); =20 +=09set_record_field(&log_fmt, LOG_FORMAT_TIME, f->f_lasttime, 15); +=09set_record_field(&log_fmt, LOG_FORMAT_HOST, f->f_prevhost, -1); =09if (msg) { -=09=09set_record_field(iov, RECORD_FIELD_MSG, msg, -1); +=09=09set_record_field(&log_fmt, LOG_FORMAT_MSG, msg, -1); =09} else if (f->f_prevcount > 1) { =09=09(void) snprintf(repbuf, sizeof(repbuf), "last message repeated %d ti= mes", =09=09 f->f_prevcount); -=09=09set_record_field(iov, RECORD_FIELD_MSG, repbuf, -1); +=09=09set_record_field(&log_fmt, LOG_FORMAT_MSG, repbuf, -1); =09} else { -=09=09set_record_field(iov, RECORD_FIELD_MSG, f->f_prevline, f->f_prevlen)= ; +=09=09set_record_field(&log_fmt, LOG_FORMAT_MSG, f->f_prevline, f->f_prevl= en); =09} =20 =09verbosef("logging to %s", TypeNames[f->f_type]); @@ -1567,7 +1623,7 @@ void fprintlog(struct filed *f, char *from, int flags= , char *msg) =09=09=09int i; =09=09=09f->f_time =3D now; =09=09=09(void) snprintf(line, sizeof(line), "<%d>%s", f->f_prevpri, \ -=09=09=09=09(char *) iov[RECORD_FIELD_MSG].iov_base); +=09=09=09=09(char *) log_fmt.iov[LOG_FORMAT_MSG].iov_base); =09=09=09l =3D strlen(line); =09=09=09if (l > MAXLINE) =09=09=09=09l =3D MAXLINE; @@ -1615,9 +1671,9 @@ void fprintlog(struct filed *f, char *from, int flags= , char *msg) =09=09f->f_time =3D now; =09=09verbosef(" %s\n", f->f_un.f_fname); =09=09if (f->f_type =3D=3D F_TTY || f->f_type =3D=3D F_CONSOLE) { -=09=09=09set_record_field(iov, RECORD_FIELD_EOL, "\r\n", 2); +=09=09=09set_record_field(&log_fmt, LOG_FORMAT_EOL, "\r\n", 2); =09=09} else { -=09=09=09set_record_field(iov, RECORD_FIELD_EOL, "\n", 1); +=09=09=09set_record_field(&log_fmt, LOG_FORMAT_EOL, "\n", 1); =09=09} =09again: =09=09/* f->f_file =3D=3D -1 is an indicator that we couldn't @@ -1625,7 +1681,7 @@ void fprintlog(struct filed *f, char *from, int flags= , char *msg) =09=09if (f->f_file =3D=3D -1) =09=09=09break; =20 -=09=09if (writev(f->f_file, iov, RECORD_FIELD_COUNTS) < 0) { +=09=09if (writev(f->f_file, log_fmt.iov, LOG_FORMAT_IOVEC_MAX) < 0) { =09=09=09int e =3D errno; =20 =09=09=09/* If a named pipe is full, just ignore it for now */ @@ -1672,8 +1728,8 @@ void fprintlog(struct filed *f, char *from, int flags= , char *msg) =09case F_WALL: =09=09f->f_time =3D now; =09=09verbosef("\n"); -=09=09set_record_field(iov, RECORD_FIELD_EOL, "\r\n", 2); -=09=09wallmsg(f, iov); +=09=09set_record_field(&log_fmt, LOG_FORMAT_EOL, "\r\n", 2); +=09=09wallmsg(f, &log_fmt); =09=09break; =09} /* switch */ =09if (f->f_type !=3D F_FORW_UNKN) @@ -1695,25 +1751,22 @@ void endtty(int sig) *=09world, or a list of approved users. */ =20 -void wallmsg(struct filed *f, struct iovec iov[RECORD_FIELD_COUNTS]) +void wallmsg(struct filed *f, struct log_format *log_fmt) { =09char p[sizeof (_PATH_DEV) + UNAMESZ]; =09register int i; -=09int ttyf, len; +=09int ttyf; =09static int reenter =3D 0; =09struct utmp ut; =09struct utmp *uptr; =09char greetings[200]; =20 -=09(void) &len; - =09if (reenter++) =09=09return; =20 =09/* open the user login file */ =09setutent(); =20 - =09/* =09 * Might as well fork instead of using nonblocking I/O =09 * and doing notty(). @@ -1722,10 +1775,13 @@ void wallmsg(struct filed *f, struct iovec iov[RECO= RD_FIELD_COUNTS]) =09=09(void) signal(SIGTERM, SIG_DFL); =09=09(void) alarm(0); =20 -=09=09(void) snprintf(greetings, sizeof(greetings), -=09=09 "\r\n\7Message from syslogd@%s at %.24s ...\r\n", -=09=09=09(char *) iov[RECORD_FIELD_HOST].iov_base, ctime(&now)); -=09=09len =3D strlen(greetings); +=09=09if (f->f_type =3D=3D F_WALL) { +=09=09=09snprintf(greetings, sizeof(greetings), +=09=09=09 "\r\n\7Message from syslogd@%s at %.24s ...\r\n", +=09=09=09=09get_record_field(log_fmt, LOG_FORMAT_HOST), ctime(&now)); + +=09=09=09set_record_field(log_fmt, LOG_FORMAT_BOL, greetings, -1); +=09=09} =20 =09=09/* scan the user login file */ =09=09while ((uptr =3D getutent())) { @@ -1757,11 +1813,6 @@ void wallmsg(struct filed *f, struct iovec iov[RECOR= D_FIELD_COUNTS]) =09=09=09strcpy(p, _PATH_DEV); =09=09=09strncat(p, ut.ut_line, UNAMESZ); =20 -=09=09=09if (f->f_type =3D=3D F_WALL) { -=09=09=09=09iov[0].iov_base =3D greetings; -=09=09=09=09iov[0].iov_len =3D len; -=09=09=09=09iov[1].iov_len =3D 0; -=09=09=09} =09=09=09if (setjmp(ttybuf) =3D=3D 0) { =09=09=09=09(void) signal(SIGALRM, endtty); =09=09=09=09(void) alarm(15); @@ -1771,7 +1822,7 @@ void wallmsg(struct filed *f, struct iovec iov[RECORD= _FIELD_COUNTS]) =09=09=09=09=09struct stat statb; =20 =09=09=09=09=09if (!fstat(ttyf, &statb) && (statb.st_mode & S_IWRITE)) { -=09=09=09=09=09=09if (writev(ttyf, iov, RECORD_FIELD_COUNTS) < 0) +=09=09=09=09=09=09if (writev(ttyf, log_fmt->iov, LOG_FORMAT_IOVEC_MAX) < 0= ) =09=09=09=09=09=09=09errno =3D 0; /* ignore */ =09=09=09=09=09} =09=09=09=09=09close(ttyf); @@ -1924,7 +1975,6 @@ void debug_switch(int sig) =09signal(SIGUSR1, debug_switch); } =20 - /* * Print syslogd errors some place. */ @@ -1949,6 +1999,11 @@ void logerror(const char *fmt, ...) =09=09=09errno =3D 0; // ignore =09} =20 +=09if (!LogFormatInitialized) { +=09=09fputs(buf, stderr); +=09=09return; +=09} + =09memset(&source, '\0', sizeof(source)); =20 =09source.flags =3D SINFO_ISINTERNAL; @@ -2163,6 +2218,13 @@ void init(void) =20 =09=09*++p =3D '\0'; =20 + +=09=09if (!strncmp("log_format:", cline, 11)) { +=09=09=09for (p =3D cline + 11; isspace(*p); ++p); +=09=09=09parse_log_format(&log_fmt, p); +=09=09=09continue; +=09=09} + =09=09allocate_log(); =09=09f =3D &Files[lognum++]; =20 @@ -2590,6 +2652,161 @@ static void allocate_log(void) =09return; } =20 +int set_log_format_field(struct log_format *log_fmt, size_t i, +=09=09enum log_format_type t, char *s, size_t n) +{ +=09if (i >=3D iovec_max) { +=09=09logerror("Too many parts in the log_format string"); +=09=09return -1; +=09} + +=09if (t !=3D LOG_FORMAT_NONE) { +=09=09if (log_fmt->fields_nr >=3D LOG_FORMAT_FIELDS_MAX) { +=09=09=09logerror("Too many placeholders in the log_format string"); +=09=09=09return -1; +=09=09} + +=09=09log_fmt->fields[log_fmt->fields_nr].f_type =3D t; +=09=09log_fmt->fields[log_fmt->fields_nr].f_iov =3D log_fmt->iov + i; +=09=09log_fmt->fields_nr++; +=09} + +=09log_fmt->iov[i].iov_base =3D s; +=09log_fmt->iov[i].iov_len =3D n; +=09log_fmt->iovec_nr++; + +=09return 0; +} + +int parse_log_format(struct log_format *log_fmt, char *str) +{ +=09char *ptr, *start; +=09int i, special, field_nr; +=09struct log_format new_fmt =3D { 0 }; + +=09iovec_max =3D sysconf(_SC_IOV_MAX); + +=09new_fmt.line =3D calloc(1, LINE_MAX); +=09if (!new_fmt.line) { +=09=09logerror("Cannot allocate log_format string"); +=09=09goto error; +=09} + +=09new_fmt.iov =3D calloc(LOG_FORMAT_IOVEC_MAX, sizeof(struct iovec)); +=09if (!new_fmt.iov) { +=09=09logerror("Cannot allocate records array for log_format string"); +=09=09goto error; +=09} + +=09new_fmt.fields =3D calloc(LOG_FORMAT_FIELDS_MAX, sizeof(struct log_form= at_field)); +=09if (!new_fmt.fields) { +=09=09logerror("Cannot allocate rules array for log_format string"); +=09=09goto error; +=09} + +=09ptr =3D str; +=09i =3D special =3D 0; + +=09while (*ptr !=3D '\0' && i < LINE_MAX) { +=09=09char c =3D *ptr++; + +=09=09switch (c) { +=09=09=09case 'b': if (special) c =3D '\b'; break; +=09=09=09case 'f': if (special) c =3D '\f'; break; +=09=09=09case 'n': if (special) c =3D '\n'; break; +=09=09=09case 'r': if (special) c =3D '\r'; break; +=09=09=09case 't': if (special) c =3D '\t'; break; +=09=09=09case '\\': +=09=09=09=09if (!special) { +=09=09=09=09=09special =3D 1; +=09=09=09=09=09continue; +=09=09=09=09} +=09=09=09=09break; +=09=09} +=09=09new_fmt.line[i++] =3D c; +=09=09special =3D 0; +=09} + +=09field_nr =3D 0; +=09special =3D 0; +=09i =3D 0; + +=09if (set_log_format_field(&new_fmt, field_nr++, LOG_FORMAT_BOL, NULL, 0)= < 0) +=09=09goto error; + +=09start =3D ptr =3D new_fmt.line; + +=09while (*ptr !=3D '\0') { +=09=09enum log_format_type f_type; + +=09=09if (special) { +=09=09=09switch (*ptr) { +=09=09=09=09case 't': f_type =3D LOG_FORMAT_TIME; break; +=09=09=09=09case 'h': f_type =3D LOG_FORMAT_HOST; break; +=09=09=09=09case 'm': f_type =3D LOG_FORMAT_MSG; break; +=09=09=09=09case '%': +=09=09=09=09=09special =3D 0; +=09=09=09=09=09goto create_special; +=09=09=09=09default: +=09=09=09=09=09logerror("unexpected special: '%%%c'", *ptr); +=09=09=09=09=09goto error; +=09=09=09} +=09=09=09special =3D 0; +=09=09=09goto create_field; + +=09=09} else if (*ptr =3D=3D '%') +=09=09=09special =3D 1; +next: +=09=09ptr++; +=09=09continue; +create_field: +=09=09if ((ptr - start - 1) > 0 && +=09=09 set_log_format_field(&new_fmt, field_nr++, +=09=09=09=09LOG_FORMAT_NONE, start, (ptr - start - 1)) < 0) +=09=09=09goto error; + +=09=09if (set_log_format_field(&new_fmt, field_nr++, f_type, NULL, 0) < 0) +=09=09=09goto error; + +=09=09start =3D ptr + 1; +=09=09goto next; +create_special: +=09=09if (set_log_format_field(&new_fmt, field_nr++, +=09=09=09=09LOG_FORMAT_NONE, start, (ptr - start - 1)) < 0) +=09=09=09goto error; + +=09=09start =3D ptr; +=09=09goto next; +=09} + +=09if (special) { +=09=09logerror("unexpected '%%' at the end of line"); +=09=09goto error; +=09} + +=09if (start !=3D ptr && +=09 set_log_format_field(&new_fmt, field_nr++, +=09=09=09LOG_FORMAT_NONE, start, (ptr - start)) < 0) +=09=09goto error; + +=09if (set_log_format_field(&new_fmt, field_nr++, +=09=09=09LOG_FORMAT_EOL, NULL, 0) < 0) +=09=09goto error; + +=09log_fmt->line =3D new_fmt.line; +=09log_fmt->iov =3D new_fmt.iov; +=09log_fmt->fields =3D new_fmt.fields; + +=09LogFormatInitialized =3D 1; + +=09return 0; +error: +=09free(new_fmt.line); +=09free(new_fmt.iov); +=09free(new_fmt.fields); + +=09return -1; +} =20 /* * The following function is resposible for handling a SIGHUP signal. Sin= ce --=20 2.25.4