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 journal_send(int fd, const char *buf, size_t len) 27 { 28 struct iovec iov[4]; 29 unsigned int n = 0; 30 int priority = rte_log_cur_msg_loglevel() - 1; 31 char msg[] = "MESSAGE="; 32 char newline = '\n'; 33 char pbuf[32]; /* "PRIORITY=N\n" */ 34 35 iov[n].iov_base = msg; 36 iov[n++].iov_len = strlen(msg); 37 38 iov[n].iov_base = (char *)(uintptr_t)buf; 39 iov[n++].iov_len = len; 40 41 /* if message doesn't end with newline, one will be applied. */ 42 if (buf[len - 1] != '\n') { 43 iov[n].iov_base = &newline; 44 iov[n++].iov_len = 1; 45 } 46 47 /* priority value between 0 ("emerg") and 7 ("debug") */ 48 iov[n].iov_base = pbuf; 49 iov[n++].iov_len = snprintf(pbuf, sizeof(pbuf), 50 "PRIORITY=%d\n", priority); 51 return writev(fd, iov, n); 52 } 53 54 55 /* wrapper for log stream to put messages into journal */ 56 static ssize_t 57 journal_log_write(void *c, const char *buf, size_t size) 58 { 59 int fd = (uintptr_t)c; 60 61 return journal_send(fd, buf, size); 62 } 63 64 static int 65 journal_log_close(void *c) 66 { 67 int fd = (uintptr_t)c; 68 69 close(fd); 70 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 log_journal_enabled(void) 86 { 87 char *jenv, *endp = NULL; 88 struct stat st; 89 unsigned long dev, ino; 90 91 jenv = getenv("JOURNAL_STREAM"); 92 if (jenv == NULL) 93 return false; 94 95 if (fstat(STDERR_FILENO, &st) < 0) 96 return false; 97 98 /* systemd sets colon-separated list of device and inode number */ 99 dev = strtoul(jenv, &endp, 10); 100 if (endp == NULL || *endp != ':') 101 return false; /* missing colon */ 102 103 ino = strtoul(endp + 1, NULL, 10); 104 105 return dev == st.st_dev && ino == st.st_ino; 106 } 107 108 /* Connect to systemd's journal service */ 109 FILE * 110 log_journal_open(const char *id) 111 { 112 char syslog_id[PATH_MAX]; 113 FILE *log_stream; 114 int len; 115 struct sockaddr_un sun = { 116 .sun_family = AF_UNIX, 117 .sun_path = "/run/systemd/journal/socket", 118 }; 119 int jfd = -1; 120 121 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 if (len >= (int)sizeof(syslog_id)) 126 return NULL; 127 128 jfd = socket(AF_UNIX, SOCK_DGRAM, 0); 129 if (jfd < 0) { 130 perror("socket"); 131 goto error; 132 } 133 134 if (connect(jfd, (struct sockaddr *)&sun, sizeof(sun)) < 0) { 135 perror("connect"); 136 goto error; 137 } 138 139 /* Send identifier as first message */ 140 if (write(jfd, syslog_id, len) != len) { 141 perror("write"); 142 goto error; 143 } 144 145 /* redirect other log messages to journal */ 146 log_stream = fopencookie((void *)(uintptr_t)jfd, "w", journal_log_func); 147 if (log_stream != NULL) 148 return log_stream; 149 150 error: 151 close(jfd); 152 return NULL; 153 } 154