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