master
c 143 lines 3.97 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "systemd-journal-helpers.h"
4 #include "../libnetdata.h"
5
6 bool is_path_unix_socket(const char *path) {
7 // Check if the path is valid
8 if(!path || !*path)
9 return false;
10
11 struct stat statbuf;
12
13 // Use stat to check if the file exists and is a socket
14 if (stat(path, &statbuf) == -1)
15 // The file does not exist or cannot be accessed
16 return false;
17
18 // Check if the file is a socket
19 if (S_ISSOCK(statbuf.st_mode))
20 return true;
21
22 return false;
23 }
24
25 bool is_stderr_connected_to_journal(void) {
26 const char *journal_stream = getenv("JOURNAL_STREAM");
27 if (!journal_stream)
28 return false; // JOURNAL_STREAM is not set
29
30 struct stat stderr_stat;
31 if (fstat(STDERR_FILENO, &stderr_stat) < 0)
32 return false; // Error in getting stderr info
33
34 // Parse device and inode from JOURNAL_STREAM
35 char *endptr;
36 long journal_dev = strtol(journal_stream, &endptr, 10);
37 if (*endptr != ':')
38 return false; // Format error in JOURNAL_STREAM
39
40 long journal_ino = strtol(endptr + 1, NULL, 10);
41
42 return (stderr_stat.st_dev == (dev_t)journal_dev) && (stderr_stat.st_ino == (ino_t)journal_ino);
43 }
44
45 int journal_direct_fd(const char *path) {
46 if(!path || !*path)
47 path = JOURNAL_DIRECT_SOCKET;
48
49 if(!is_path_unix_socket(path))
50 return -1;
51
52 int fd = socket(AF_UNIX, SOCK_DGRAM| DEFAULT_SOCKET_FLAGS, 0);
53 if (fd < 0) return -1;
54
55 sock_setcloexec(fd, true);
56
57 struct sockaddr_un addr;
58 memset(&addr, 0, sizeof(struct sockaddr_un));
59 addr.sun_family = AF_UNIX;
60 strncpyz(addr.sun_path, path, sizeof(addr.sun_path) - 1);
61
62 // Connect the socket (optional, but can simplify send operations)
63 if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
64 close(fd);
65 return -1;
66 }
67
68 return fd;
69 }
70
71 static inline bool journal_send_with_memfd(int fd __maybe_unused, const char *msg __maybe_unused, size_t msg_len __maybe_unused) {
72 #if defined(__NR_memfd_create) && defined(MFD_ALLOW_SEALING) && defined(F_ADD_SEALS) && defined(F_SEAL_SHRINK) && defined(F_SEAL_GROW) && defined(F_SEAL_WRITE)
73 // Create a memory file descriptor
74 int memfd = (int)syscall(__NR_memfd_create, "journald", MFD_ALLOW_SEALING);
75 if (memfd < 0) return false;
76
77 // Write data to the memfd
78 if (write(memfd, msg, msg_len) != (ssize_t)msg_len) {
79 close(memfd);
80 return false;
81 }
82
83 // Seal the memfd to make it immutable
84 if (fcntl(memfd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE) < 0) {
85 close(memfd);
86 return false;
87 }
88
89 struct iovec iov = {0};
90 struct msghdr msghdr = {0};
91 struct cmsghdr *cmsghdr;
92 char cmsgbuf[CMSG_SPACE(sizeof(int))];
93
94 msghdr.msg_iov = &iov;
95 msghdr.msg_iovlen = 1;
96 msghdr.msg_control = cmsgbuf;
97 msghdr.msg_controllen = sizeof(cmsgbuf);
98
99 cmsghdr = CMSG_FIRSTHDR(&msghdr);
100 if(!cmsghdr) {
101 close(memfd);
102 return false;
103 }
104
105 cmsghdr->cmsg_level = SOL_SOCKET;
106 cmsghdr->cmsg_type = SCM_RIGHTS;
107 cmsghdr->cmsg_len = CMSG_LEN(sizeof(int));
108 memcpy(CMSG_DATA(cmsghdr), &memfd, sizeof(int));
109
110 ssize_t r = sendmsg(fd, &msghdr, 0);
111
112 close(memfd);
113 return r >= 0;
114 #else
115 return false;
116 #endif
117 }
118
119 bool journal_direct_send(int fd, const char *msg, size_t msg_len) {
120 // Send the datagram
121 if (send(fd, msg, msg_len, 0) < 0) {
122 if(errno != EMSGSIZE)
123 return false;
124
125 // datagram is too large, fallback to memfd
126 if(!journal_send_with_memfd(fd, msg, msg_len))
127 return false;
128 }
129
130 return true;
131 }
132
133 void journal_construct_path(char *dst, size_t dst_len, const char *host_prefix, const char *namespace_str) {
134 if(!host_prefix)
135 host_prefix = "";
136
137 if(namespace_str)
138 snprintfz(dst, dst_len, "%s/run/systemd/journal.%s/socket",
139 host_prefix, namespace_str);
140 else
141 snprintfz(dst, dst_len, "%s" JOURNAL_DIRECT_SOCKET,
142 host_prefix);
143 }