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