Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause */
2 : :
3 : : #include <limits.h>
4 : : #include <stdbool.h>
5 : : #include <stdint.h>
6 : : #include <stdio.h>
7 : : #include <stdlib.h>
8 : : #include <string.h>
9 : : #include <sys/socket.h>
10 : : #include <sys/stat.h>
11 : : #include <sys/uio.h>
12 : : #include <sys/un.h>
13 : : #include <unistd.h>
14 : :
15 : : #include <rte_log.h>
16 : :
17 : : #include "log_private.h"
18 : :
19 : : /*
20 : : * Send structured message using journal protocol
21 : : * See: https://systemd.io/JOURNAL_NATIVE_PROTOCOL/
22 : : *
23 : : * Uses writev() to ensure that whole log message is in one datagram
24 : : */
25 : : static int
26 : 0 : journal_send(int fd, const char *buf, size_t len)
27 : : {
28 : : struct iovec iov[4];
29 : : unsigned int n = 0;
30 : 0 : int priority = rte_log_cur_msg_loglevel() - 1;
31 : 0 : char msg[] = "MESSAGE=";
32 : 0 : char newline = '\n';
33 : : char pbuf[32]; /* "PRIORITY=N\n" */
34 : :
35 : 0 : iov[n].iov_base = msg;
36 : 0 : iov[n++].iov_len = strlen(msg);
37 : :
38 : 0 : iov[n].iov_base = (char *)(uintptr_t)buf;
39 : 0 : iov[n++].iov_len = len;
40 : :
41 : : /* if message doesn't end with newline, one will be applied. */
42 [ # # ]: 0 : if (buf[len - 1] != '\n') {
43 : 0 : iov[n].iov_base = &newline;
44 : 0 : iov[n++].iov_len = 1;
45 : : }
46 : :
47 : : /* priority value between 0 ("emerg") and 7 ("debug") */
48 : 0 : iov[n].iov_base = pbuf;
49 : 0 : iov[n++].iov_len = snprintf(pbuf, sizeof(pbuf),
50 : : "PRIORITY=%d\n", priority);
51 : 0 : return writev(fd, iov, n);
52 : : }
53 : :
54 : :
55 : : /* wrapper for log stream to put messages into journal */
56 : : static ssize_t
57 : 0 : journal_log_write(void *c, const char *buf, size_t size)
58 : : {
59 : 0 : int fd = (uintptr_t)c;
60 : :
61 : 0 : return journal_send(fd, buf, size);
62 : : }
63 : :
64 : : static int
65 : 0 : journal_log_close(void *c)
66 : : {
67 : 0 : int fd = (uintptr_t)c;
68 : :
69 : 0 : close(fd);
70 : 0 : return 0;
71 : : }
72 : :
73 : : static cookie_io_functions_t journal_log_func = {
74 : : .write = journal_log_write,
75 : : .close = journal_log_close,
76 : : };
77 : :
78 : : /*
79 : : * Check if stderr is going to system journal.
80 : : * This is the documented way to handle systemd journal
81 : : *
82 : : * See: https://systemd.io/JOURNAL_NATIVE_PROTOCOL/
83 : : */
84 : : bool
85 : 251 : log_journal_enabled(void)
86 : : {
87 : 251 : char *jenv, *endp = NULL;
88 : : struct stat st;
89 : : unsigned long dev, ino;
90 : :
91 : 251 : jenv = getenv("JOURNAL_STREAM");
92 [ - + ]: 251 : if (jenv == NULL)
93 : : return false;
94 : :
95 [ # # ]: 0 : if (fstat(STDERR_FILENO, &st) < 0)
96 : : return false;
97 : :
98 : : /* systemd sets colon-separated list of device and inode number */
99 : 0 : dev = strtoul(jenv, &endp, 10);
100 [ # # # # ]: 0 : if (endp == NULL || *endp != ':')
101 : : return false; /* missing colon */
102 : :
103 : 0 : ino = strtoul(endp + 1, NULL, 10);
104 : :
105 [ # # # # ]: 0 : return dev == st.st_dev && ino == st.st_ino;
106 : : }
107 : :
108 : : /* Connect to systemd's journal service */
109 : : FILE *
110 : 0 : log_journal_open(const char *id)
111 : : {
112 : : char syslog_id[PATH_MAX];
113 : : FILE *log_stream;
114 : : int len;
115 : 0 : struct sockaddr_un sun = {
116 : : .sun_family = AF_UNIX,
117 : : .sun_path = "/run/systemd/journal/socket",
118 : : };
119 : : int jfd = -1;
120 : :
121 : 0 : len = snprintf(syslog_id, sizeof(syslog_id),
122 : : "SYSLOG_IDENTIFIER=%s\nSYSLOG_PID=%u", id, getpid());
123 : :
124 : : /* Detect truncation of message and fallback to no journal */
125 [ # # ]: 0 : if (len >= (int)sizeof(syslog_id))
126 : : return NULL;
127 : :
128 : 0 : jfd = socket(AF_UNIX, SOCK_DGRAM, 0);
129 [ # # ]: 0 : if (jfd < 0) {
130 : 0 : perror("socket");
131 : 0 : goto error;
132 : : }
133 : :
134 [ # # ]: 0 : if (connect(jfd, (struct sockaddr *)&sun, sizeof(sun)) < 0) {
135 : 0 : perror("connect");
136 : 0 : goto error;
137 : : }
138 : :
139 : : /* Send identifier as first message */
140 [ # # ]: 0 : if (write(jfd, syslog_id, len) != len) {
141 : 0 : perror("write");
142 : 0 : goto error;
143 : : }
144 : :
145 : : /* redirect other log messages to journal */
146 : 0 : log_stream = fopencookie((void *)(uintptr_t)jfd, "w", journal_log_func);
147 [ # # ]: 0 : if (log_stream != NULL)
148 : : return log_stream;
149 : :
150 : 0 : error:
151 : 0 : close(jfd);
152 : 0 : return NULL;
153 : : }
|