public inbox for libc-alpha@sourceware.org
 help / color / mirror / Atom feed
From: Florian Weimer <fweimer@redhat.com>
To: libc-alpha@sourceware.org
Subject: [PATCH 11/11] syslog: Use a printf buffer directly to construct the entire packet
Date: Fri, 09 Feb 2024 16:26:01 +0100	[thread overview]
Message-ID: <a709cb1c36c0e92954ebb036adabc350428ee7d5.1707491940.git.fweimer@redhat.com> (raw)
In-Reply-To: <cover.1707491940.git.fweimer@redhat.com>

This defers buffer management largely to the asprintf implementation.
It is quite close to the original implementation around
open_memstream, except that an on-stack buffer is used for shorter
messages, and that strftime no longer writes directly into the
buffer.

The new version no longer uses the (slow) %n format specifier.
It also fixes an issue in the localtime_r failure path, where
the message is prefixed with ": " due to an incorrect placement
of the %n specifier.
---
 misc/syslog.c | 184 ++++++++++++++++----------------------------------
 1 file changed, 58 insertions(+), 126 deletions(-)

diff --git a/misc/syslog.c b/misc/syslog.c
index 68ee3aef5f..50cb252f47 100644
--- a/misc/syslog.c
+++ b/misc/syslog.c
@@ -38,13 +38,11 @@ static char sccsid[] = "@(#)syslog.c	8.4 (Berkeley) 3/18/94";
 #include <paths.h>
 #include <stdarg.h>
 #include <stdlib.h>
-#include <stdio.h>
-#include <stdio_ext.h>
 #include <sys/socket.h>
-#include <sys/uio.h>
 #include <sys/un.h>
 #include <syslog.h>
 #include <limits.h>
+#include <printf_buffer.h>
 
 static int LogType = SOCK_DGRAM;	/* type of socket connection */
 static int LogFile = -1;		/* fd for log */
@@ -60,27 +58,23 @@ __libc_lock_define_initialized (static, syslog_lock)
 static void openlog_internal (const char *, int, int);
 static void closelog_internal (void);
 
-struct cleanup_arg
+/* Unlock and deallocate the buffer.  */
+static void
+cancel_handler_buf (void *ptr)
 {
-  void *buf;
-  struct sigaction *oldaction;
-};
+  __libc_lock_unlock (syslog_lock);
+
+  struct __printf_buffer_asprintf *buf = ptr;
+  __printf_buffer_asprintf_free (buf);
+}
 
+/* Only unlock.  */
 static void
 cancel_handler (void *ptr)
 {
-  /* Restore the old signal handler.  */
-  struct cleanup_arg *clarg = (struct cleanup_arg *) ptr;
-
-  if (clarg != NULL)
-    /* Free the memstream buffer,  */
-    free (clarg->buf);
-
-  /* Free the lock.  */
   __libc_lock_unlock (syslog_lock);
 }
 
-
 /*
  * syslog, vsyslog --
  *	print message on log file; output is intended for syslogd(8).
@@ -122,16 +116,21 @@ __vsyslog_chk (int pri, int flag, const char *fmt, va_list ap)
   __vsyslog_internal (pri, fmt, ap, (flag > 0) ? PRINTF_FORTIFY : 0);
 }
 
+/* Send the buffer contents over the syslog socket.  */
+static ssize_t
+__syslog_send (struct __printf_buffer *buf)
+{
+  /* Send the trailing NUL byte for LogType == SOCK_STREAM  only.  */
+  return __send (LogFile, buf->write_base,
+		 buf->write_ptr - buf->write_base - (LogType != SOCK_STREAM),
+		 MSG_NOSIGNAL);
+}
+
 void
 __vsyslog_internal (int pri, const char *fmt, va_list ap,
 		    unsigned int mode_flags)
 {
-  /* Try to use a static buffer as an optimization.  */
-  char bufs[1024];
-  char *buf = bufs;
-  size_t bufsize;
-
-  int msgoff;
+  ptrdiff_t msgoff;
   int saved_errno = errno;
 
 #define	INTERNALLOG LOG_ERR|LOG_CONS|LOG_PERROR|LOG_PID
@@ -144,8 +143,9 @@ __vsyslog_internal (int pri, const char *fmt, va_list ap,
 
   /* Prepare for multiple users.  We have to take care: most syscalls we are
      using are cancellation points.  */
-  struct cleanup_arg clarg = { NULL, NULL };
-  __libc_cleanup_push (cancel_handler, &clarg);
+  struct __printf_buffer_asprintf buf;
+  __printf_buffer_asprintf_init (&buf);
+  __libc_cleanup_push (cancel_handler_buf, &buf);
   __libc_lock_lock (syslog_lock);
 
   /* Check priority against setlogmask values. */
@@ -173,122 +173,56 @@ __vsyslog_internal (int pri, const char *fmt, va_list ap,
     __strftime_l (timestamp, sizeof timestamp, "%h %e %T ", now_tmp,
 		  _nl_C_locobj_ptr);
 
-#define SYSLOG_HEADER(__pri, __timestamp, __msgoff, pid) \
-  "<%d>%s%n%s%s%.0d%s: ",                                \
-  __pri, __timestamp, __msgoff,                          \
-  LogTag == NULL ? __progname : LogTag,                  \
-  "[" + (pid == 0), pid, "]" + (pid == 0)
+  __printf_buffer (&buf.base, "<%d>", pri);
 
-#define SYSLOG_HEADER_WITHOUT_TS(__pri, __msgoff)        \
-  "<%d>: %n", __pri, __msgoff
-
-  int l, vl;
   if (has_ts)
-    l = __snprintf (bufs, sizeof bufs,
-		    SYSLOG_HEADER (pri, timestamp, &msgoff, pid));
-  else
-    l = __snprintf (bufs, sizeof bufs,
-		    SYSLOG_HEADER_WITHOUT_TS (pri, &msgoff));
-  if (l < 0)
-    goto out;
-
-  char *pos;
-  size_t len;
-
-  if (l < sizeof bufs)
     {
-      /* At this point, there is still a chance that we can print the
-         remaining part of the log into bufs and use that.  */
-      pos = bufs + l;
-      len = sizeof (bufs) - l;
+      __printf_buffer_puts (&buf.base, timestamp);
+      msgoff = buf.base.write_ptr - buf.base.write_base;
+       __printf_buffer_puts (&buf.base, LogTag == NULL ? __progname : LogTag);
+      if (pid != 0)
+	__printf_buffer (&buf.base, "[%d]", pid);
+      __printf_buffer_putc (&buf.base, ':');
+      __printf_buffer_putc (&buf.base, ' ');
     }
   else
     {
-      buf = NULL;
-      /* We already know that bufs is too small to use for this log message.
-         The next vsnprintf into bufs is used only to calculate the total
-         required buffer length.  We will discard bufs contents and allocate
-         an appropriately sized buffer later instead.  */
-      pos = bufs;
-      len = sizeof (bufs);
+      msgoff = buf.base.write_ptr - buf.base.write_base;
+      __printf_buffer_putc (&buf.base, ':');
+      __printf_buffer_putc (&buf.base, ' ');
     }
 
-  {
-    va_list apc;
-    va_copy (apc, ap);
-
-    /* Restore errno for %m format.  */
-    __set_errno (saved_errno);
-
-    vl = __vsnprintf_internal (pos, len, fmt, apc, mode_flags);
-    va_end (apc);
+  /* Do not count the bytes written to the buffer so far.  This value
+     is negative and accounts for the existing buffer contents, which
+     is not upposed to be visible to the caller.  */
+  buf.base.written = buf.base.write_base - buf.base.write_ptr;
 
-    if (vl < 0 || vl >= INT_MAX - l)
-      goto out;
+  errno = saved_errno;
+  __vprintf_buffer (&buf.base, fmt, ap, mode_flags);
 
-    if (vl >= len)
-      buf = NULL;
-
-    bufsize = l + vl;
-  }
-
-  if (buf == NULL)
-    {
-      buf = malloc ((bufsize + 1) * sizeof (char));
-      if (buf != NULL)
-	{
-	  /* Tell the cancellation handler to free this buffer.  */
-	  clarg.buf = buf;
+  /* We may need the terminator if we are sending over a SOCK_STREAM
+     connection.  Add this unconditionally to simplify error handling.  */
+  __printf_buffer_putc (&buf.base, '\0');
 
-	  int cl;
-	  if (has_ts)
-	    cl = __snprintf (buf, l + 1,
-			     SYSLOG_HEADER (pri, timestamp, &msgoff, pid));
-	  else
-	    cl = __snprintf (buf, l + 1,
-			     SYSLOG_HEADER_WITHOUT_TS (pri, &msgoff));
-	  if (cl != l)
-	    goto out;
-
-	  va_list apc;
-	  va_copy (apc, ap);
-	  cl = __vsnprintf_internal (buf + l, bufsize - l + 1, fmt, apc,
-				     mode_flags);
-	  va_end (apc);
-
-	  if (cl != vl)
-	    goto out;
-	}
-      else
-        {
-          int bl;
-	  /* Nothing much to do but emit an error message.  */
-          bl = __snprintf (bufs, sizeof bufs,
-                           "out of memory[%d]", __getpid ());
-          if (bl < 0 || bl >= sizeof bufs)
-            goto out;
-
-          bufsize = bl;
-          buf = bufs;
-          msgoff = 0;
-        }
-    }
+  if (__printf_buffer_has_failed (&buf.base))
+    goto out;
 
   /* Output to stderr if requested. */
   if (LogStat & LOG_PERROR)
-    __dprintf (STDERR_FILENO, "%s%s", buf + msgoff,
-	       "\n" + (buf[bufsize - 1] == '\n'));
+    {
+      /* The buffer always starts with '<' and a digit, so there are
+	 at least two bytes, and -2 is valid offset from the next
+	 to-be-written byte.  -2 accounts for the NUL byte.  */
+      int has_nl = buf.base.write_ptr[-2] == '\n';
+      __dprintf (STDERR_FILENO, "%s%s", buf.base.write_base + msgoff,
+		 "\n" + has_nl);
+    }
 
   /* Get connected, output the message to the local logger.  */
   if (!connected)
     openlog_internal (NULL, LogStat | LOG_NDELAY, LogFacility);
 
-  /* If we have a SOCK_STREAM connection, also send ASCII NUL as a record
-     terminator.  */
-  if (LogType == SOCK_STREAM)
-    ++bufsize;
-
-  if (!connected || __send (LogFile, buf, bufsize, MSG_NOSIGNAL) < 0)
+  if (!connected || __syslog_send (&buf.base) < 0)
     {
       if (connected)
 	{
@@ -297,7 +231,7 @@ __vsyslog_internal (int pri, const char *fmt, va_list ap,
 	  openlog_internal (NULL, LogStat | LOG_NDELAY, LogFacility);
 	}
 
-      if (!connected || __send (LogFile, buf, bufsize, MSG_NOSIGNAL) < 0)
+      if (!connected || __syslog_send (&buf.base) < 0)
 	{
 	  closelog_internal ();	/* attempt re-open next time */
 	  /*
@@ -311,7 +245,7 @@ __vsyslog_internal (int pri, const char *fmt, va_list ap,
 	      (fd = __open (_PATH_CONSOLE, O_WRONLY | O_NOCTTY
 			    | O_CLOEXEC, 0)) >= 0)
 	    {
-	      __dprintf (fd, "%s\r\n", buf + msgoff);
+	      __dprintf (fd, "%s\r\n", buf.base.write_base + msgoff);
 	      __close (fd);
 	    }
 	}
@@ -321,9 +255,7 @@ out:
   /* End of critical section.  */
   __libc_cleanup_pop (0);
   __libc_lock_unlock (syslog_lock);
-
-  if (buf != bufs)
-    free (buf);
+  __printf_buffer_asprintf_free (&buf);
 }
 
 /* AF_UNIX address of local logger  */
-- 
2.43.0


  parent reply	other threads:[~2024-02-09 15:26 UTC|newest]

Thread overview: 34+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-02-09 15:24 [PATCH 00/11] Build getdomainname, gethostname, syslog with fortification Florian Weimer
2024-02-09 15:24 ` [PATCH 01/11] misc: Build getdomainname " Florian Weimer
2024-02-12 17:14   ` Adhemerval Zanella Netto
2024-02-12 17:30     ` Andreas Schwab
2024-02-12 19:29       ` Florian Weimer
2024-02-13  9:12         ` Andreas Schwab
2024-02-13  9:23           ` Florian Weimer
2024-02-13  9:32             ` Andreas Schwab
2024-02-09 15:24 ` [PATCH 02/11] misc: Build gethostname " Florian Weimer
2024-02-12 17:25   ` Adhemerval Zanella Netto
2024-02-09 15:25 ` [PATCH 03/11] libio: Add fortify wrapper for internal __snprintf Florian Weimer
2024-02-12 17:34   ` Adhemerval Zanella Netto
2024-02-13 12:13     ` Florian Weimer
2024-02-13 13:16       ` Adhemerval Zanella Netto
2024-02-09 15:25 ` [PATCH 04/11] syslog: Update misc/tst-syslog to check reported %n value Florian Weimer
2024-02-13 11:59   ` Adhemerval Zanella Netto
2024-02-15 13:23     ` Florian Weimer
2024-02-09 15:25 ` [PATCH 05/11] syslog: Build with fortification Florian Weimer
2024-02-13 12:26   ` Adhemerval Zanella Netto
2024-02-09 15:25 ` [PATCH 06/11] stdio: Rename __printf_buffer to __vfprintf_buffer Florian Weimer
2024-02-16 13:40   ` Adhemerval Zanella Netto
2024-02-09 15:25 ` [PATCH 07/11] libio: Extract __printf_buffer_asprintf_init from asprintf implementation Florian Weimer
2024-02-16 14:04   ` Adhemerval Zanella Netto
2024-02-09 15:25 ` [PATCH 08/11] stdio-common: Introduce the __printf_buffer function Florian Weimer
2024-02-16 14:12   ` Adhemerval Zanella Netto
2024-02-09 15:25 ` [PATCH 09/11] stdio-common: Allow skipping initial bytes in __printf_buffer for %n Florian Weimer
2024-02-16 14:13   ` Adhemerval Zanella Netto
2024-02-09 15:25 ` [PATCH 10/11] stdio-common: Support large offsets with %lln Florian Weimer
2024-02-16 14:20   ` Adhemerval Zanella Netto
2024-02-09 15:26 ` Florian Weimer [this message]
2024-02-14 15:16   ` [PATCH 11/11] syslog: Use a printf buffer directly to construct the entire packet Adhemerval Zanella Netto
2024-02-15 13:02     ` Florian Weimer
2024-02-16 13:35       ` Adhemerval Zanella Netto
2024-02-16 15:58   ` Adhemerval Zanella Netto

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=a709cb1c36c0e92954ebb036adabc350428ee7d5.1707491940.git.fweimer@redhat.com \
    --to=fweimer@redhat.com \
    --cc=libc-alpha@sourceware.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).