master
c 290 lines 9.11 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "nd_log-internals.h"
4
5 bool nd_log_journal_systemd_init(void) {
6 #ifdef HAVE_SYSTEMD
7 nd_log.journal.initialized = true;
8 #else
9 nd_log.journal.initialized = false;
10 #endif
11
12 return nd_log.journal.initialized;
13 }
14
15 static int nd_log_journal_direct_fd_find_and_open(char *filename, size_t size) {
16 int fd;
17
18 if(netdata_configured_host_prefix && *netdata_configured_host_prefix) {
19 journal_construct_path(filename, size, netdata_configured_host_prefix, "netdata");
20 if (is_path_unix_socket(filename) && (fd = journal_direct_fd(filename)) != -1)
21 return fd;
22
23 journal_construct_path(filename, size, netdata_configured_host_prefix, NULL);
24 if (is_path_unix_socket(filename) && (fd = journal_direct_fd(filename)) != -1)
25 return fd;
26 }
27
28 journal_construct_path(filename, size, NULL, "netdata");
29 if (is_path_unix_socket(filename) && (fd = journal_direct_fd(filename)) != -1)
30 return fd;
31
32 journal_construct_path(filename, size, NULL, NULL);
33 if (is_path_unix_socket(filename) && (fd = journal_direct_fd(filename)) != -1)
34 return fd;
35
36 return -1;
37 }
38
39 bool nd_log_journal_socket_available(void) {
40 char filename[FILENAME_MAX];
41 int fd = nd_log_journal_direct_fd_find_and_open(filename, sizeof(filename));
42 if(fd == -1) return false;
43 close(fd);
44 return true;
45 }
46
47 static void nd_log_journal_direct_set_env(void) {
48 if(nd_log.sources[NDLS_COLLECTORS].method == NDLM_JOURNAL)
49 nd_setenv("NETDATA_SYSTEMD_JOURNAL_PATH", nd_log.journal_direct.filename, 1);
50 }
51
52 bool nd_log_journal_direct_init(const char *path) {
53 if(nd_log.journal_direct.initialized) {
54 nd_log_journal_direct_set_env();
55 return true;
56 }
57
58 int fd;
59 char filename[FILENAME_MAX];
60 if(!is_path_unix_socket(path))
61 fd = nd_log_journal_direct_fd_find_and_open(filename, sizeof(filename));
62 else {
63 snprintfz(filename, sizeof(filename), "%s", path);
64 fd = journal_direct_fd(filename);
65 }
66
67 if(fd < 0)
68 return false;
69
70 nd_log.journal_direct.fd = fd;
71 nd_log.journal_direct.initialized = true;
72
73 strncpyz(nd_log.journal_direct.filename, filename, sizeof(nd_log.journal_direct.filename) - 1);
74 nd_log_journal_direct_set_env();
75
76 return true;
77 }
78
79 bool nd_logger_journal_libsystemd(struct log_field *fields __maybe_unused, size_t fields_max __maybe_unused) {
80 #ifdef HAVE_SYSTEMD
81
82 // --- FIELD_PARSER_VERSIONS ---
83 //
84 // IMPORTANT:
85 // THERE ARE 6 VERSIONS OF THIS CODE
86 //
87 // 1. journal (direct socket API),
88 // 2. journal (libsystemd API),
89 // 3. logfmt,
90 // 4. json,
91 // 5. convert to uint64
92 // 6. convert to int64
93 //
94 // UPDATE ALL OF THEM FOR NEW FEATURES OR FIXES
95
96 struct iovec iov[THREAD_FIELDS_MAX];
97 int iov_count = 0;
98
99 CLEAN_BUFFER *tmp = NULL;
100
101 for (size_t i = 0; i < fields_max; i++) {
102 if (!fields[i].entry.set || !fields[i].journal)
103 continue;
104
105 const char *key = fields[i].journal;
106 char *value = NULL;
107 int rc = 0;
108 switch (fields[i].entry.type) {
109 case NDFT_TXT:
110 if(*fields[i].entry.txt)
111 rc = asprintf(&value, "%s=%s", key, fields[i].entry.txt);
112 break;
113 case NDFT_STR:
114 rc = asprintf(&value, "%s=%s", key, string2str(fields[i].entry.str));
115 break;
116 case NDFT_BFR:
117 if(buffer_strlen(fields[i].entry.bfr))
118 rc = asprintf(&value, "%s=%s", key, buffer_tostring(fields[i].entry.bfr));
119 break;
120 case NDFT_U64:
121 rc = asprintf(&value, "%s=%" PRIu64, key, fields[i].entry.u64);
122 break;
123 case NDFT_I64:
124 rc = asprintf(&value, "%s=%" PRId64, key, fields[i].entry.i64);
125 break;
126 case NDFT_DBL:
127 rc = asprintf(&value, "%s=%f", key, fields[i].entry.dbl);
128 break;
129 case NDFT_UUID:
130 if(!uuid_is_null(*fields[i].entry.uuid)) {
131 char u[UUID_COMPACT_STR_LEN];
132 uuid_unparse_lower_compact(*fields[i].entry.uuid, u);
133 rc = asprintf(&value, "%s=%s", key, u);
134 }
135 break;
136 case NDFT_CALLBACK: {
137 if(!tmp)
138 tmp = buffer_create(1024, NULL);
139 else
140 buffer_flush(tmp);
141 if(fields[i].entry.cb.formatter(tmp, fields[i].entry.cb.formatter_data))
142 rc = asprintf(&value, "%s=%s", key, buffer_tostring(tmp));
143 }
144 break;
145 default:
146 rc = asprintf(&value, "%s=%s", key, "UNHANDLED");
147 break;
148 }
149
150 if (rc != -1 && value) {
151 iov[iov_count].iov_base = value;
152 iov[iov_count].iov_len = strlen(value);
153 iov_count++;
154 }
155 }
156
157 static bool sockets_before[1024];
158 bool detect_systemd_socket = __atomic_load_n(&nd_log.journal.first_msg, __ATOMIC_RELAXED) == false;
159 if(detect_systemd_socket) {
160 for(int i = 3 ; (size_t)i < _countof(sockets_before); i++)
161 sockets_before[i] = fd_is_socket(i);
162 }
163
164 int r = sd_journal_sendv(iov, iov_count);
165
166 if(r == 0 && detect_systemd_socket) {
167 __atomic_store_n(&nd_log.journal.first_msg, true, __ATOMIC_RELAXED);
168
169 // this is the first successful libsystemd log
170 // let's detect its fd number (we need it for the spawn server)
171
172 for(int i = 3 ; (size_t)i < _countof(sockets_before); i++) {
173 if (!sockets_before[i] && fd_is_socket(i)) {
174 nd_log.journal.fd = i;
175 break;
176 }
177 }
178 }
179
180 for (int i = 0; i < iov_count; i++)
181 free(iov[i].iov_base);
182
183 return r == 0;
184 #else
185 return false;
186 #endif
187 }
188
189 bool nd_logger_journal_direct(struct log_field *fields, size_t fields_max) {
190 if(!nd_log.journal_direct.initialized)
191 return false;
192
193 // --- FIELD_PARSER_VERSIONS ---
194 //
195 // IMPORTANT:
196 // THERE ARE 6 VERSIONS OF THIS CODE
197 //
198 // 1. journal (direct socket API),
199 // 2. journal (libsystemd API),
200 // 3. logfmt,
201 // 4. json,
202 // 5. convert to uint64
203 // 6. convert to int64
204 //
205 // UPDATE ALL OF THEM FOR NEW FEATURES OR FIXES
206
207 CLEAN_BUFFER *wb = buffer_create(4096, NULL);
208 CLEAN_BUFFER *tmp = NULL;
209
210 for (size_t i = 0; i < fields_max; i++) {
211 if (!fields[i].entry.set || !fields[i].journal)
212 continue;
213
214 const char *key = fields[i].journal;
215
216 const char *s = NULL;
217 switch(fields[i].entry.type) {
218 case NDFT_TXT:
219 s = fields[i].entry.txt;
220 break;
221 case NDFT_STR:
222 s = string2str(fields[i].entry.str);
223 break;
224 case NDFT_BFR:
225 s = buffer_tostring(fields[i].entry.bfr);
226 break;
227 case NDFT_U64:
228 buffer_strcat(wb, key);
229 buffer_putc(wb, '=');
230 buffer_print_uint64(wb, fields[i].entry.u64);
231 buffer_putc(wb, '\n');
232 break;
233 case NDFT_I64:
234 buffer_strcat(wb, key);
235 buffer_putc(wb, '=');
236 buffer_print_int64(wb, fields[i].entry.i64);
237 buffer_putc(wb, '\n');
238 break;
239 case NDFT_DBL:
240 buffer_strcat(wb, key);
241 buffer_putc(wb, '=');
242 buffer_print_netdata_double(wb, fields[i].entry.dbl);
243 buffer_putc(wb, '\n');
244 break;
245 case NDFT_UUID:
246 if(!uuid_is_null(*fields[i].entry.uuid)) {
247 char u[UUID_COMPACT_STR_LEN];
248 uuid_unparse_lower_compact(*fields[i].entry.uuid, u);
249 buffer_strcat(wb, key);
250 buffer_putc(wb, '=');
251 buffer_fast_strcat(wb, u, sizeof(u) - 1);
252 buffer_putc(wb, '\n');
253 }
254 break;
255 case NDFT_CALLBACK: {
256 if(!tmp)
257 tmp = buffer_create(1024, NULL);
258 else
259 buffer_flush(tmp);
260 if(fields[i].entry.cb.formatter(tmp, fields[i].entry.cb.formatter_data))
261 s = buffer_tostring(tmp);
262 else
263 s = NULL;
264 }
265 break;
266 default:
267 s = "UNHANDLED";
268 break;
269 }
270
271 if(s && *s) {
272 buffer_strcat(wb, key);
273 if(!strchr(s, '\n')) {
274 buffer_putc(wb, '=');
275 buffer_strcat(wb, s);
276 buffer_putc(wb, '\n');
277 }
278 else {
279 buffer_putc(wb, '\n');
280 size_t size = strlen(s);
281 uint64_t le_size = htole64(size);
282 buffer_memcat(wb, &le_size, sizeof(le_size));
283 buffer_memcat(wb, s, size);
284 buffer_putc(wb, '\n');
285 }
286 }
287 }
288
289 return journal_direct_send(nd_log.journal_direct.fd, buffer_tostring(wb), buffer_strlen(wb));
290 }