io: Re-flow and sort multiline Makefile definitions
[glibc.git] / misc / tst-syslog.c
blob7d4405d681545af2d383a8f5fbd422ecd996b1d1
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.
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.
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.
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/>. */
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>
35 static const int facilities[] =
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,
59 static const int priorities[] =
61 LOG_EMERG,
62 LOG_ALERT,
63 LOG_CRIT,
64 LOG_ERR,
65 LOG_WARNING,
66 LOG_NOTICE,
67 LOG_INFO,
68 LOG_DEBUG
71 #define IDENT_LENGTH 64
72 #define MSG_LENGTH 1024
74 #define SYSLOG_MSG_BASE "syslog_message"
75 #define OPENLOG_IDENT "openlog_ident"
76 static char large_message[MSG_LENGTH];
78 struct msg_t
80 int priority;
81 int facility;
82 char ident[IDENT_LENGTH];
83 char msg[MSG_LENGTH];
84 pid_t pid;
87 static void
88 call_vsyslog (int priority, const char *format, ...)
90 va_list ap;
91 va_start (ap, format);
92 vsyslog (priority, format, ap);
93 va_end (ap);
96 static void
97 send_vsyslog (int options)
99 for (size_t i = 0; i < array_length (facilities); i++)
101 for (size_t j = 0; j < array_length (priorities); j++)
103 int facility = facilities[i];
104 int priority = priorities[j];
105 call_vsyslog (facility | priority, "%s %d %d", SYSLOG_MSG_BASE,
106 facility, priority);
111 static void
112 send_syslog (int options)
114 for (size_t i = 0; i < array_length (facilities); i++)
116 for (size_t j = 0; j < array_length (priorities); j++)
118 int facility = facilities[i];
119 int priority = priorities[j];
120 syslog (facility | priority, "%s %d %d", SYSLOG_MSG_BASE, facility,
121 priority);
126 static bool
127 check_syslog_message (const struct msg_t *msg, int msgnum, int options,
128 pid_t pid)
130 if (msgnum == array_length (facilities) * array_length (priorities) - 1)
131 return false;
133 int i = msgnum / array_length (priorities);
134 int j = msgnum % array_length (priorities);
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];
142 TEST_COMPARE (msg->facility, expected_facility);
143 TEST_COMPARE (msg->priority, expected_priority);
145 return true;
148 static void
149 send_syslog_large (int options)
151 int facility = LOG_USER;
152 int priority = LOG_INFO;
154 syslog (facility | priority, "%s %d %d", large_message, facility,
155 priority);
158 static void
159 send_vsyslog_large (int options)
161 int facility = LOG_USER;
162 int priority = LOG_INFO;
164 call_vsyslog (facility | priority, "%s %d %d", large_message, facility,
165 priority);
168 static bool
169 check_syslog_message_large (const struct msg_t *msg, int msgnum, int options,
170 pid_t pid)
172 TEST_COMPARE (msg->facility, LOG_USER);
173 TEST_COMPARE (msg->priority, LOG_INFO);
174 TEST_COMPARE_STRING (msg->msg, large_message);
176 return false;
179 static void
180 send_openlog (int options)
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++)
186 int priority = priorities[j];
187 syslog (priority, "%s %d %d", SYSLOG_MSG_BASE, LOG_LOCAL0, priority);
189 closelog ();
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++)
195 int priority = priorities[j];
196 syslog (LOG_LOCAL7 | priority, "%s %d %d", SYSLOG_MSG_BASE, LOG_LOCAL7,
197 priority);
199 closelog ();
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++)
205 int priority = priorities[j];
206 syslog (priority, "%s %d %d", SYSLOG_MSG_BASE, LOG_KERN, priority);
208 closelog ();
211 static void
212 send_openlog_large (int options)
214 /* Define a non-default IDENT and a not default facility. */
215 openlog (OPENLOG_IDENT, options, LOG_LOCAL0);
217 syslog (LOG_INFO, "%s %d %d", large_message, LOG_LOCAL0, LOG_INFO);
219 closelog ();
222 static bool
223 check_openlog_message (const struct msg_t *msg, int msgnum,
224 int options, pid_t pid)
226 if (msgnum == 3 * array_length (priorities) - 1)
227 return false;
229 int expected_priority = priorities[msgnum % array_length (priorities)];
230 TEST_COMPARE (msg->priority, expected_priority);
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 ? "]" : "");
239 if (msgnum < array_length (priorities))
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);
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);
251 return true;
254 static bool
255 check_openlog_message_large (const struct msg_t *msg, int msgnum,
256 int options, pid_t pid)
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 ? "]" : "");
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);
270 return false;
273 static struct msg_t
274 parse_syslog_msg (const char *msg)
276 struct msg_t r = { .pid = -1 };
277 int number;
278 int wsb, wsa;
280 #define STRINPUT(size) XSTRINPUT(size)
281 #define XSTRINPUT(size) "%" # size "s"
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);
292 r.facility = number & LOG_FACMASK;
293 r.priority = number & LOG_PRIMASK;
295 char *pid_start = strchr (r.ident, '[');
296 if (pid_start != NULL)
298 char *pid_end = strchr (r.ident, ']');
299 if (pid_end != NULL)
300 r.pid = strtoul (pid_start + 1, NULL, 10);
303 return r;
306 static struct msg_t
307 parse_syslog_console (const char *msg)
309 int priority;
310 int facility;
311 struct msg_t r;
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);
319 r.facility = facility;
320 r.priority = priority;
322 return r;
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))
330 struct sockaddr_un addr =
332 .sun_family = AF_UNIX,
333 .sun_path = _PATH_LOG
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);
340 pid_t sender_pid = xfork ();
341 if (sender_pid == 0)
343 syslog_send (options);
344 _exit (0);
347 int msgnum = 0;
348 while (1)
350 char buf[2048];
351 size_t l = xrecvfrom (server_udp, buf, sizeof (buf), 0,
352 (struct sockaddr *) &addr, &addrlen);
353 buf[l] = '\0';
355 struct msg_t msg = parse_syslog_msg (buf);
356 if (!syslog_check (&msg, msgnum++, options, sender_pid))
357 break;
360 xclose (server_udp);
362 int status;
363 xwaitpid (sender_pid, &status, 0);
364 TEST_COMPARE (status, 0);
366 unlink (_PATH_LOG);
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))
374 struct sockaddr_un addr =
376 .sun_family = AF_UNIX,
377 .sun_path = _PATH_LOG
379 socklen_t addrlen = sizeof (addr);
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);
385 pid_t sender_pid = xfork ();
386 if (sender_pid == 0)
388 syslog_send (options);
389 _exit (0);
392 int client_tcp = xaccept (server_tcp, NULL, NULL);
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;
399 while (1)
401 size_t rl = xrecvfrom (client_tcp, rb, rbl - prl, 0, NULL, NULL);
402 if (rl == 0)
403 break;
405 /* Iterate over the buffer to find and check the record. */
406 size_t l = rl + prl;
407 char *b = buf;
408 while (1)
410 /* With TCP each record ends with a '\0'. */
411 char *e = memchr (b, '\0', l);
412 if (e != NULL)
414 struct msg_t msg = parse_syslog_msg (b);
415 if (!syslog_check (&msg, msgnum++, options, sender_pid))
416 break;
418 /* Advance to the next record. */
419 ptrdiff_t diff = e + 1 - b;
420 b += diff;
421 l -= diff;
423 else
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;
434 xclose (client_tcp);
435 xclose (server_tcp);
437 int status;
438 xwaitpid (sender_pid, &status, 0);
439 TEST_COMPARE (status, 0);
441 unlink (_PATH_LOG);
444 static void
445 check_syslog_console_read (FILE *fp)
447 char buf[512];
448 int msgnum = 0;
449 while (fgets (buf, sizeof (buf), fp) != NULL)
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);
456 if (++msgnum == array_length (priorities))
457 break;
461 static void
462 check_syslog_console_read_large (FILE *fp)
464 char buf[2048];
465 TEST_VERIFY (fgets (buf, sizeof (buf), fp) != NULL);
466 struct msg_t msg = parse_syslog_console (buf);
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);
474 static void
475 check_syslog_console (void (*syslog_send)(int),
476 void (*syslog_check)(FILE *fp))
478 xmkfifo (_PATH_CONSOLE, 0666);
480 pid_t sender_pid = xfork ();
481 if (sender_pid == 0)
483 syslog_send (LOG_CONS);
484 _exit (0);
488 FILE *fp = xfopen (_PATH_CONSOLE, "r+");
489 syslog_check (fp);
490 xfclose (fp);
493 int status;
494 xwaitpid (sender_pid, &status, 0);
495 TEST_COMPARE (status, 0);
497 unlink (_PATH_CONSOLE);
500 static void
501 send_openlog_callback (void *clousure)
503 int options = *(int *) clousure;
504 send_openlog (options);
507 static void
508 send_openlog_callback_large (void *clousure)
510 int options = *(int *) clousure;
511 send_openlog_large (options);
514 static void
515 check_syslog_perror (bool large)
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});
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);
532 support_capture_subprocess_check (&result, "tst-openlog-child", 0,
533 sc_allow_stderr);
534 support_capture_subprocess_free (&result);
537 static int
538 do_test (void)
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);
544 /* Also check vsyslog. */
545 check_syslog_udp (send_vsyslog, 0, check_syslog_message);
546 check_syslog_tcp (send_vsyslog, 0, check_syslog_message);
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);
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);
556 /* Check the LOG_CONS option. */
557 check_syslog_console (send_openlog, check_syslog_console_read);
559 /* Check the LOG_PERROR option. */
560 check_syslog_perror (false);
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';
567 check_syslog_udp (send_syslog_large, 0, check_syslog_message_large);
568 check_syslog_tcp (send_syslog_large, 0, check_syslog_message_large);
570 check_syslog_udp (send_vsyslog_large, 0, check_syslog_message_large);
571 check_syslog_tcp (send_vsyslog_large, 0, check_syslog_message_large);
573 check_syslog_udp (send_openlog_large, 0, check_openlog_message_large);
574 check_syslog_tcp (send_openlog_large, 0, check_openlog_message_large);
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);
579 check_syslog_console (send_openlog_large, check_syslog_console_read_large);
581 check_syslog_perror (true);
583 return 0;
586 #include <support/test-driver.c>