public inbox for libc-alpha@sourceware.org
 help / color / mirror / Atom feed
From: Adhemerval Zanella Netto <adhemerval.zanella@linaro.org>
To: Florian Weimer <fweimer@redhat.com>, libc-alpha@sourceware.org
Subject: Re: [PATCH 11/11] syslog: Use a printf buffer directly to construct the entire packet
Date: Fri, 16 Feb 2024 12:58:46 -0300	[thread overview]
Message-ID: <bc58506f-42bf-415d-92c9-f9b96e11d3e7@linaro.org> (raw)
In-Reply-To: <a709cb1c36c0e92954ebb036adabc350428ee7d5.1707491940.git.fweimer@redhat.com>



On 09/02/24 12:26, Florian Weimer wrote:
> 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)

Maybe encapsulate this on a function (__printf_buffer_write_len or
something)? I think we should avoid mess the internal printf buffer
state where possible.

> +}
> +
>  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, ' ');
>      }

Maybe move the ': ' appending after the if/else:

  if (has_ts)
    {
      [...]
    }
  else
    [...]
  __printf_buffer_puts (&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.  */

s/upposed/supposed

> +  buf.base.written = buf.base.write_base - buf.base.write_ptr;

Maybe encapsulate this on a function (__printf_buffer_reset_pos or
something)?

>  
> -    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');
>  

Ok.

> -	  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  */

      parent reply	other threads:[~2024-02-16 15:58 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 ` [PATCH 11/11] syslog: Use a printf buffer directly to construct the entire packet Florian Weimer
2024-02-14 15:16   ` 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 [this message]

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=bc58506f-42bf-415d-92c9-f9b96e11d3e7@linaro.org \
    --to=adhemerval.zanella@linaro.org \
    --cc=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).