(root)/
glibc-2.38/
misc/
tst-syslog.c
       1  /* Basic tests for syslog interfaces.
       2     Copyright (C) 2022-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  #include <array_length.h>
      20  #include <fcntl.h>
      21  #include <paths.h>
      22  #include <netinet/in.h>
      23  #include <support/capture_subprocess.h>
      24  #include <support/check.h>
      25  #include <support/xstdio.h>
      26  #include <support/xsocket.h>
      27  #include <support/xunistd.h>
      28  #include <stdarg.h>
      29  #include <stdbool.h>
      30  #include <stddef.h>
      31  #include <stdlib.h>
      32  #include <syslog.h>
      33  #include <sys/un.h>
      34  
      35  static const int facilities[] =
      36    {
      37      LOG_KERN,
      38      LOG_USER,
      39      LOG_MAIL,
      40      LOG_DAEMON,
      41      LOG_AUTH,
      42      LOG_SYSLOG,
      43      LOG_LPR,
      44      LOG_NEWS,
      45      LOG_UUCP,
      46      LOG_CRON,
      47      LOG_AUTHPRIV,
      48      LOG_FTP,
      49      LOG_LOCAL0,
      50      LOG_LOCAL1,
      51      LOG_LOCAL2,
      52      LOG_LOCAL3,
      53      LOG_LOCAL4,
      54      LOG_LOCAL5,
      55      LOG_LOCAL6,
      56      LOG_LOCAL7,
      57    };
      58  
      59  static const int priorities[] =
      60    {
      61      LOG_EMERG,
      62      LOG_ALERT,
      63      LOG_CRIT,
      64      LOG_ERR,
      65      LOG_WARNING,
      66      LOG_NOTICE,
      67      LOG_INFO,
      68      LOG_DEBUG
      69    };
      70  
      71  #define IDENT_LENGTH 64
      72  #define MSG_LENGTH   1024
      73  
      74  #define SYSLOG_MSG_BASE "syslog_message"
      75  #define OPENLOG_IDENT   "openlog_ident"
      76  static char large_message[MSG_LENGTH];
      77  
      78  struct msg_t
      79    {
      80      int priority;
      81      int facility;
      82      char ident[IDENT_LENGTH];
      83      char msg[MSG_LENGTH];
      84      pid_t pid;
      85    };
      86  
      87  static void
      88  call_vsyslog (int priority, const char *format, ...)
      89  {
      90    va_list ap;
      91    va_start (ap, format);
      92    vsyslog (priority, format, ap);
      93    va_end (ap);
      94  }
      95  
      96  static void
      97  send_vsyslog (int options)
      98  {
      99    for (size_t i = 0; i < array_length (facilities); i++)
     100      {
     101        for (size_t j = 0; j < array_length (priorities); j++)
     102          {
     103            int facility = facilities[i];
     104            int priority = priorities[j];
     105            call_vsyslog (facility | priority, "%s %d %d", SYSLOG_MSG_BASE,
     106                          facility, priority);
     107          }
     108      }
     109  }
     110  
     111  static void
     112  send_syslog (int options)
     113  {
     114    for (size_t i = 0; i < array_length (facilities); i++)
     115      {
     116        for (size_t j = 0; j < array_length (priorities); j++)
     117          {
     118            int facility = facilities[i];
     119            int priority = priorities[j];
     120            syslog (facility | priority, "%s %d %d", SYSLOG_MSG_BASE, facility,
     121                    priority);
     122          }
     123      }
     124  }
     125  
     126  static bool
     127  check_syslog_message (const struct msg_t *msg, int msgnum, int options,
     128                        pid_t pid)
     129  {
     130    if (msgnum == array_length (facilities) * array_length (priorities) - 1)
     131      return false;
     132  
     133    int i = msgnum / array_length (priorities);
     134    int j = msgnum % array_length (priorities);
     135  
     136    int expected_facility = facilities[i];
     137    /* With no preceding openlog, syslog default to LOG_USER.  */
     138    if (expected_facility == LOG_KERN)
     139        expected_facility = LOG_USER;
     140    int expected_priority = priorities[j];
     141  
     142    TEST_COMPARE (msg->facility, expected_facility);
     143    TEST_COMPARE (msg->priority, expected_priority);
     144  
     145    return true;
     146  }
     147  
     148  static void
     149  send_syslog_large (int options)
     150  {
     151    int facility = LOG_USER;
     152    int priority = LOG_INFO;
     153  
     154    syslog (facility | priority, "%s %d %d", large_message, facility,
     155  	  priority);
     156  }
     157  
     158  static void
     159  send_vsyslog_large (int options)
     160  {
     161    int facility = LOG_USER;
     162    int priority = LOG_INFO;
     163  
     164    call_vsyslog (facility | priority, "%s %d %d", large_message, facility,
     165  		priority);
     166  }
     167  
     168  static bool
     169  check_syslog_message_large (const struct msg_t *msg, int msgnum, int options,
     170  			    pid_t pid)
     171  {
     172    TEST_COMPARE (msg->facility, LOG_USER);
     173    TEST_COMPARE (msg->priority, LOG_INFO);
     174    TEST_COMPARE_STRING (msg->msg, large_message);
     175  
     176    return false;
     177  }
     178  
     179  static void
     180  send_openlog (int options)
     181  {
     182    /* Define a non-default IDENT and a not default facility.  */
     183    openlog (OPENLOG_IDENT, options, LOG_LOCAL0);
     184    for (size_t j = 0; j < array_length (priorities); j++)
     185      {
     186        int priority = priorities[j];
     187        syslog (priority, "%s %d %d", SYSLOG_MSG_BASE, LOG_LOCAL0, priority);
     188      }
     189    closelog ();
     190  
     191    /* Back to the default IDENT with a non default facility.  */
     192    openlog (NULL, 0, LOG_LOCAL6);
     193    for (size_t j = 0; j < array_length (priorities); j++)
     194      {
     195        int priority = priorities[j];
     196        syslog (LOG_LOCAL7 | priority, "%s %d %d", SYSLOG_MSG_BASE, LOG_LOCAL7,
     197          priority);
     198      }
     199    closelog ();
     200  
     201    /* LOG_KERN does not change the internal default facility.  */
     202    openlog (NULL, 0, LOG_KERN);
     203    for (size_t j = 0; j < array_length (priorities); j++)
     204      {
     205        int priority = priorities[j];
     206        syslog (priority, "%s %d %d", SYSLOG_MSG_BASE, LOG_KERN, priority);
     207      }
     208    closelog ();
     209  }
     210  
     211  static void
     212  send_openlog_large (int options)
     213  {
     214    /* Define a non-default IDENT and a not default facility.  */
     215    openlog (OPENLOG_IDENT, options, LOG_LOCAL0);
     216  
     217    syslog (LOG_INFO, "%s %d %d", large_message, LOG_LOCAL0, LOG_INFO);
     218  
     219    closelog ();
     220  }
     221  
     222  static bool
     223  check_openlog_message (const struct msg_t *msg, int msgnum,
     224                         int options, pid_t pid)
     225  {
     226    if (msgnum == 3 * array_length (priorities) - 1)
     227      return false;
     228  
     229    int expected_priority = priorities[msgnum % array_length (priorities)];
     230    TEST_COMPARE (msg->priority, expected_priority);
     231  
     232    char expected_ident[IDENT_LENGTH];
     233    snprintf (expected_ident, sizeof (expected_ident), "%s%s%.0d%s:",
     234              OPENLOG_IDENT,
     235              options & LOG_PID ? "[" : "",
     236              options & LOG_PID ? pid : 0,
     237              options & LOG_PID ? "]" : "");
     238  
     239    if (msgnum < array_length (priorities))
     240      {
     241        if (options & LOG_PID)
     242          TEST_COMPARE (msg->pid, pid);
     243        TEST_COMPARE_STRING (msg->ident, expected_ident);
     244        TEST_COMPARE (msg->facility, LOG_LOCAL0);
     245      }
     246    else if (msgnum < 2 * array_length (priorities))
     247      TEST_COMPARE (msg->facility, LOG_LOCAL7);
     248    else if (msgnum < 3 * array_length (priorities))
     249      TEST_COMPARE (msg->facility, LOG_KERN);
     250  
     251    return true;
     252  }
     253  
     254  static bool
     255  check_openlog_message_large (const struct msg_t *msg, int msgnum,
     256  			     int options, pid_t pid)
     257  {
     258    char expected_ident[IDENT_LENGTH];
     259    snprintf (expected_ident, sizeof (expected_ident), "%s%s%.0d%s:",
     260              OPENLOG_IDENT,
     261              options & LOG_PID ? "[" : "",
     262              options & LOG_PID ? pid : 0,
     263              options & LOG_PID ? "]" : "");
     264  
     265    TEST_COMPARE_STRING (msg->ident, expected_ident);
     266    TEST_COMPARE_STRING (msg->msg, large_message);
     267    TEST_COMPARE (msg->priority, LOG_INFO);
     268    TEST_COMPARE (msg->facility, LOG_LOCAL0);
     269  
     270    return false;
     271  }
     272  
     273  static struct msg_t
     274  parse_syslog_msg (const char *msg)
     275  {
     276    struct msg_t r = { .pid = -1 };
     277    int number;
     278    int wsb, wsa;
     279  
     280  #define STRINPUT(size)  XSTRINPUT(size)
     281  #define XSTRINPUT(size) "%" # size "s"
     282  
     283    /* The message in the form:
     284       <179>Apr  8 14:51:19 tst-syslog: message 176 3  */
     285    int n = sscanf (msg, "<%3d>%*s %*d %*d:%*d:%*d%n %n" STRINPUT(IDENT_LENGTH)
     286  		       " " STRINPUT(MSG_LENGTH) " %*d %*d",
     287                    &number, &wsb, &wsa, r.ident, r.msg);
     288    TEST_COMPARE (n, 3);
     289    /* It should only one space between timestamp and message.  */
     290    TEST_COMPARE (wsa - wsb, 1);
     291  
     292    r.facility = number & LOG_FACMASK;
     293    r.priority = number & LOG_PRIMASK;
     294  
     295    char *pid_start = strchr (r.ident, '[');
     296    if (pid_start != NULL)
     297      {
     298         char *pid_end = strchr (r.ident, ']');
     299         if (pid_end != NULL)
     300           r.pid = strtoul (pid_start + 1, NULL, 10);
     301      }
     302  
     303    return r;
     304  }
     305  
     306  static struct msg_t
     307  parse_syslog_console (const char *msg)
     308  {
     309    int priority;
     310    int facility;
     311    struct msg_t r;
     312  
     313    /* The message in the form:
     314       openlog_ident: syslog_message 128 0  */
     315    int n = sscanf (msg, STRINPUT(IDENT_LENGTH) " " STRINPUT(MSG_LENGTH) " %d %d",
     316        r.ident, r.msg, &facility, &priority);
     317    TEST_COMPARE (n, 4);
     318  
     319    r.facility = facility;
     320    r.priority = priority;
     321  
     322    return r;
     323  }
     324  
     325  static void
     326  check_syslog_udp (void (*syslog_send)(int), int options,
     327                    bool (*syslog_check)(const struct msg_t *, int, int,
     328                                         pid_t))
     329  {
     330    struct sockaddr_un addr =
     331      {
     332        .sun_family = AF_UNIX,
     333        .sun_path = _PATH_LOG
     334      };
     335  
     336    socklen_t addrlen = sizeof (addr);
     337    int server_udp = xsocket (AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0);
     338    xbind (server_udp, (struct sockaddr *) &addr, addrlen);
     339  
     340    pid_t sender_pid = xfork ();
     341    if (sender_pid == 0)
     342      {
     343        syslog_send (options);
     344        _exit (0);
     345      }
     346  
     347    int msgnum = 0;
     348    while (1)
     349      {
     350        char buf[2048];
     351        size_t l = xrecvfrom (server_udp, buf, sizeof (buf), 0,
     352                              (struct sockaddr *) &addr, &addrlen);
     353        buf[l] = '\0';
     354  
     355        struct msg_t msg = parse_syslog_msg (buf);
     356        if (!syslog_check (&msg, msgnum++, options, sender_pid))
     357          break;
     358       }
     359  
     360    xclose (server_udp);
     361  
     362    int status;
     363    xwaitpid (sender_pid, &status, 0);
     364    TEST_COMPARE (status, 0);
     365  
     366    unlink (_PATH_LOG);
     367  }
     368  
     369  static void
     370  check_syslog_tcp (void (*syslog_send)(int), int options,
     371                    bool (*syslog_check)(const struct msg_t *, int, int,
     372                                         pid_t))
     373  {
     374    struct sockaddr_un addr =
     375      {
     376        .sun_family = AF_UNIX,
     377        .sun_path = _PATH_LOG
     378      };
     379    socklen_t addrlen = sizeof (addr);
     380  
     381    int server_tcp = xsocket (AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
     382    xbind (server_tcp, (struct sockaddr *) &addr, addrlen);
     383    xlisten (server_tcp, 5);
     384  
     385    pid_t sender_pid = xfork ();
     386    if (sender_pid == 0)
     387      {
     388        syslog_send (options);
     389        _exit (0);
     390      }
     391  
     392    int client_tcp = xaccept (server_tcp, NULL, NULL);
     393  
     394    char buf[2048], *rb = buf;
     395    size_t rbl = sizeof (buf);
     396    size_t prl = 0;  /* Track the size of the partial record.  */
     397    int msgnum = 0;
     398  
     399    while (1)
     400      {
     401        size_t rl = xrecvfrom (client_tcp, rb, rbl - prl, 0, NULL, NULL);
     402        if (rl == 0)
     403          break;
     404  
     405        /* Iterate over the buffer to find and check the record.  */
     406        size_t l = rl + prl;
     407        char *b = buf;
     408        while (1)
     409  	{
     410            /* With TCP each record ends with a '\0'.  */
     411            char *e = memchr (b, '\0', l);
     412            if (e != NULL)
     413              {
     414                struct msg_t msg = parse_syslog_msg (b);
     415                if (!syslog_check (&msg, msgnum++, options, sender_pid))
     416                  break;
     417  
     418  	      /* Advance to the next record.  */
     419  	      ptrdiff_t diff = e + 1 - b;
     420  	      b += diff;
     421  	      l -= diff;
     422  	    }
     423  	  else
     424  	    {
     425                /* Move the partial record to the start of the buffer.  */
     426  	      memmove (buf, b, l);
     427  	      rb = buf + l;
     428  	      prl = l;
     429  	      break;
     430              }
     431          }
     432      }
     433  
     434    xclose (client_tcp);
     435    xclose (server_tcp);
     436  
     437    int status;
     438    xwaitpid (sender_pid, &status, 0);
     439    TEST_COMPARE (status, 0);
     440  
     441    unlink (_PATH_LOG);
     442  }
     443  
     444  static void
     445  check_syslog_console_read (FILE *fp)
     446  {
     447    char buf[512];
     448    int msgnum = 0;
     449    while (fgets (buf, sizeof (buf), fp) != NULL)
     450      {
     451        struct msg_t msg = parse_syslog_console (buf);
     452        TEST_COMPARE_STRING (msg.ident, OPENLOG_IDENT ":");
     453        TEST_COMPARE (msg.priority, priorities[msgnum]);
     454        TEST_COMPARE (msg.facility, LOG_LOCAL0);
     455  
     456        if (++msgnum == array_length (priorities))
     457          break;
     458      }
     459  }
     460  
     461  static void
     462  check_syslog_console_read_large (FILE *fp)
     463  {
     464    char buf[2048];
     465    TEST_VERIFY (fgets (buf, sizeof (buf), fp) != NULL);
     466    struct msg_t msg = parse_syslog_console (buf);
     467  
     468    TEST_COMPARE_STRING (msg.ident, OPENLOG_IDENT ":");
     469    TEST_COMPARE_STRING (msg.msg, large_message);
     470    TEST_COMPARE (msg.priority, LOG_INFO);
     471    TEST_COMPARE (msg.facility, LOG_LOCAL0);
     472  }
     473  
     474  static void
     475  check_syslog_console (void (*syslog_send)(int),
     476  		      void (*syslog_check)(FILE *fp))
     477  {
     478    xmkfifo (_PATH_CONSOLE, 0666);
     479  
     480    pid_t sender_pid = xfork ();
     481    if (sender_pid == 0)
     482      {
     483        syslog_send (LOG_CONS);
     484        _exit (0);
     485      }
     486  
     487    {
     488      FILE *fp = xfopen (_PATH_CONSOLE, "r+");
     489      syslog_check (fp);
     490      xfclose (fp);
     491    }
     492  
     493    int status;
     494    xwaitpid (sender_pid, &status, 0);
     495    TEST_COMPARE (status, 0);
     496  
     497    unlink (_PATH_CONSOLE);
     498  }
     499  
     500  static void
     501  send_openlog_callback (void *clousure)
     502  {
     503    int options = *(int *) clousure;
     504    send_openlog (options);
     505  }
     506  
     507  static void
     508  send_openlog_callback_large (void *clousure)
     509  {
     510    int options = *(int *) clousure;
     511    send_openlog_large (options);
     512  }
     513  
     514  static void
     515  check_syslog_perror (bool large)
     516  {
     517    struct support_capture_subprocess result;
     518    result = support_capture_subprocess (large
     519  				       ? send_openlog_callback_large
     520  				       : send_openlog_callback,
     521                                         &(int){LOG_PERROR});
     522  
     523    FILE *mfp = fmemopen (result.err.buffer, result.err.length, "r");
     524    if (mfp == NULL)
     525      FAIL_EXIT1 ("fmemopen: %m");
     526    if (large)
     527      check_syslog_console_read_large (mfp);
     528    else
     529      check_syslog_console_read (mfp);
     530    xfclose (mfp);
     531  
     532    support_capture_subprocess_check (&result, "tst-openlog-child", 0,
     533                                      sc_allow_stderr);
     534    support_capture_subprocess_free (&result);
     535  }
     536  
     537  static int
     538  do_test (void)
     539  {
     540    /* Send every combination of facility/priority over UDP and TCP.  */
     541    check_syslog_udp (send_syslog, 0, check_syslog_message);
     542    check_syslog_tcp (send_syslog, 0, check_syslog_message);
     543  
     544    /* Also check vsyslog.  */
     545    check_syslog_udp (send_vsyslog, 0, check_syslog_message);
     546    check_syslog_tcp (send_vsyslog, 0, check_syslog_message);
     547  
     548    /* Run some openlog/syslog/closelog combinations.  */
     549    check_syslog_udp (send_openlog, 0, check_openlog_message);
     550    check_syslog_tcp (send_openlog, 0, check_openlog_message);
     551  
     552    /* Check the LOG_PID option.  */
     553    check_syslog_udp (send_openlog, LOG_PID, check_openlog_message);
     554    check_syslog_tcp (send_openlog, LOG_PID, check_openlog_message);
     555  
     556    /* Check the LOG_CONS option.  */
     557    check_syslog_console (send_openlog, check_syslog_console_read);
     558  
     559    /* Check the LOG_PERROR option.  */
     560    check_syslog_perror (false);
     561  
     562    /* Similar tests as before, but with a large message to trigger the
     563       syslog path that uses dynamically allocated memory.  */
     564    memset (large_message, 'a', sizeof large_message - 1);
     565    large_message[sizeof large_message - 1] = '\0';
     566  
     567    check_syslog_udp (send_syslog_large, 0, check_syslog_message_large);
     568    check_syslog_tcp (send_syslog_large, 0, check_syslog_message_large);
     569  
     570    check_syslog_udp (send_vsyslog_large, 0, check_syslog_message_large);
     571    check_syslog_tcp (send_vsyslog_large, 0, check_syslog_message_large);
     572  
     573    check_syslog_udp (send_openlog_large, 0, check_openlog_message_large);
     574    check_syslog_tcp (send_openlog_large, 0, check_openlog_message_large);
     575  
     576    check_syslog_udp (send_openlog_large, LOG_PID, check_openlog_message_large);
     577    check_syslog_tcp (send_openlog_large, LOG_PID, check_openlog_message_large);
     578  
     579    check_syslog_console (send_openlog_large, check_syslog_console_read_large);
     580  
     581    check_syslog_perror (true);
     582  
     583    return 0;
     584  }
     585  
     586  #include <support/test-driver.c>