]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal-remote/journal-remote.c
analyze: fix typo
[thirdparty/systemd.git] / src / journal-remote / journal-remote.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <fcntl.h>
4
5 #include "sd-event.h"
6
7 #include "af-list.h"
8 #include "alloc-util.h"
9 #include "errno-util.h"
10 #include "escape.h"
11 #include "fd-util.h"
12 #include "hash-funcs.h"
13 #include "hashmap.h"
14 #include "journal-file-util.h"
15 #include "journal-remote.h"
16 #include "journal-remote-write.h"
17 #include "log.h"
18 #include "socket-util.h"
19 #include "stdio-util.h"
20
21 #define REMOTE_JOURNAL_PATH "/var/log/journal/remote"
22
23 #define filename_escape(s) xescape((s), "/ ")
24
25 static int open_output(RemoteServer *s, Writer *w, const char *host) {
26 _cleanup_free_ char *_filename = NULL;
27 const char *filename;
28 int r;
29
30 assert(s);
31 assert(w);
32
33 switch (s->split_mode) {
34 case JOURNAL_WRITE_SPLIT_NONE:
35 filename = s->output;
36 break;
37
38 case JOURNAL_WRITE_SPLIT_HOST: {
39 _cleanup_free_ char *name = NULL;
40
41 assert(host);
42
43 name = filename_escape(host);
44 if (!name)
45 return log_oom();
46
47 r = asprintf(&_filename, "%s/remote-%s.journal", s->output, name);
48 if (r < 0)
49 return log_oom();
50
51 filename = _filename;
52 break;
53 }
54
55 default:
56 assert_not_reached();
57 }
58
59 r = journal_file_open_reliably(
60 filename,
61 O_RDWR|O_CREAT,
62 s->file_flags,
63 0640,
64 UINT64_MAX,
65 &w->metrics,
66 w->mmap,
67 &w->journal);
68 if (r < 0)
69 return log_error_errno(r, "Failed to open output journal %s: %m", filename);
70
71 log_debug("Opened output file %s", w->journal->path);
72 return 0;
73 }
74
75 /**********************************************************************
76 **********************************************************************
77 **********************************************************************/
78
79 static int init_writer_hashmap(RemoteServer *s) {
80 static const struct hash_ops* const hash_ops[] = {
81 [JOURNAL_WRITE_SPLIT_NONE] = NULL,
82 [JOURNAL_WRITE_SPLIT_HOST] = &string_hash_ops,
83 };
84
85 assert(s);
86 assert(s->split_mode >= 0 && s->split_mode < (int) ELEMENTSOF(hash_ops));
87
88 s->writers = hashmap_new(hash_ops[s->split_mode]);
89 if (!s->writers)
90 return log_oom();
91
92 return 0;
93 }
94
95 int journal_remote_get_writer(RemoteServer *s, const char *host, Writer **writer) {
96 _cleanup_(writer_unrefp) Writer *w = NULL;
97 const void *key;
98 int r;
99
100 assert(s);
101 assert(writer);
102
103 switch (s->split_mode) {
104 case JOURNAL_WRITE_SPLIT_NONE:
105 key = "one and only";
106 break;
107
108 case JOURNAL_WRITE_SPLIT_HOST:
109 assert(host);
110 key = host;
111 break;
112
113 default:
114 assert_not_reached();
115 }
116
117 w = hashmap_get(s->writers, key);
118 if (w)
119 writer_ref(w);
120 else {
121 r = writer_new(s, &w);
122 if (r < 0)
123 return r;
124
125 if (s->split_mode == JOURNAL_WRITE_SPLIT_HOST) {
126 w->hashmap_key = strdup(key);
127 if (!w->hashmap_key)
128 return -ENOMEM;
129 }
130
131 r = open_output(s, w, host);
132 if (r < 0)
133 return r;
134
135 r = hashmap_put(s->writers, w->hashmap_key ?: key, w);
136 if (r < 0)
137 return r;
138 }
139
140 *writer = TAKE_PTR(w);
141 return 0;
142 }
143
144 /**********************************************************************
145 **********************************************************************
146 **********************************************************************/
147
148 /* This should go away as soon as μhttpd allows state to be passed around. */
149 RemoteServer *journal_remote_server_global;
150
151 static int dispatch_raw_source_event(sd_event_source *event,
152 int fd,
153 uint32_t revents,
154 void *userdata);
155 static int dispatch_raw_source_until_block(sd_event_source *event,
156 void *userdata);
157 static int dispatch_blocking_source_event(sd_event_source *event,
158 void *userdata);
159 static int dispatch_raw_connection_event(sd_event_source *event,
160 int fd,
161 uint32_t revents,
162 void *userdata);
163
164 static int get_source_for_fd(RemoteServer *s,
165 int fd, char *name, RemoteSource **source) {
166 Writer *writer;
167 int r;
168
169 /* This takes ownership of name, but only on success. */
170
171 assert(s);
172 assert(fd >= 0);
173 assert(source);
174
175 if (!GREEDY_REALLOC0(s->sources, fd + 1))
176 return log_oom();
177
178 r = journal_remote_get_writer(s, name, &writer);
179 if (r < 0)
180 return log_warning_errno(r, "Failed to get writer for source %s: %m",
181 name);
182
183 if (!s->sources[fd]) {
184 s->sources[fd] = source_new(fd, false, name, writer);
185 if (!s->sources[fd]) {
186 writer_unref(writer);
187 return log_oom();
188 }
189
190 s->active++;
191 }
192
193 *source = s->sources[fd];
194 return 0;
195 }
196
197 static int remove_source(RemoteServer *s, int fd) {
198 RemoteSource *source;
199
200 assert(s);
201 assert(fd >= 0 && fd < (ssize_t) MALLOC_ELEMENTSOF(s->sources));
202
203 source = s->sources[fd];
204 if (source) {
205 /* this closes fd too */
206 source_free(source);
207 s->sources[fd] = NULL;
208 s->active--;
209 }
210
211 return 0;
212 }
213
214 int journal_remote_add_source(RemoteServer *s, int fd, char *name, bool own_name) {
215 RemoteSource *source = NULL;
216 int r;
217
218 /* This takes ownership of name, even on failure, if own_name is true. */
219
220 assert(s);
221 assert(fd >= 0);
222 assert(name);
223
224 if (!own_name) {
225 name = strdup(name);
226 if (!name)
227 return log_oom();
228 }
229
230 r = get_source_for_fd(s, fd, name, &source);
231 if (r < 0) {
232 log_error_errno(r, "Failed to create source for fd:%d (%s): %m",
233 fd, name);
234 free(name);
235 return r;
236 }
237
238 r = sd_event_add_io(s->event, &source->event,
239 fd, EPOLLIN|EPOLLRDHUP|EPOLLPRI,
240 dispatch_raw_source_event, source);
241 if (r == 0) {
242 /* Add additional source for buffer processing. It will be
243 * enabled later. */
244 r = sd_event_add_defer(s->event, &source->buffer_event,
245 dispatch_raw_source_until_block, source);
246 if (r == 0)
247 r = sd_event_source_set_enabled(source->buffer_event, SD_EVENT_OFF);
248 } else if (r == -EPERM) {
249 log_debug("Falling back to sd_event_add_defer for fd:%d (%s)", fd, name);
250 r = sd_event_add_defer(s->event, &source->event,
251 dispatch_blocking_source_event, source);
252 if (r == 0)
253 r = sd_event_source_set_enabled(source->event, SD_EVENT_ON);
254 }
255 if (r < 0) {
256 log_error_errno(r, "Failed to register event source for fd:%d: %m",
257 fd);
258 goto error;
259 }
260
261 r = sd_event_source_set_description(source->event, name);
262 if (r < 0) {
263 log_error_errno(r, "Failed to set source name for fd:%d: %m", fd);
264 goto error;
265 }
266
267 return 1; /* work to do */
268
269 error:
270 remove_source(s, fd);
271 return r;
272 }
273
274 int journal_remote_add_raw_socket(RemoteServer *s, int fd) {
275 _unused_ _cleanup_close_ int fd_ = fd;
276 char name[STRLEN("raw-socket-") + DECIMAL_STR_MAX(int) + 1];
277 int r;
278
279 assert(s);
280 assert(fd >= 0);
281
282 r = sd_event_add_io(s->event, &s->listen_event,
283 fd, EPOLLIN,
284 dispatch_raw_connection_event, s);
285 if (r < 0)
286 return r;
287
288 xsprintf(name, "raw-socket-%d", fd);
289
290 r = sd_event_source_set_description(s->listen_event, name);
291 if (r < 0)
292 return r;
293
294 TAKE_FD(fd_);
295 s->active++;
296 return 0;
297 }
298
299 /**********************************************************************
300 **********************************************************************
301 **********************************************************************/
302
303 int journal_remote_server_init(
304 RemoteServer *s,
305 const char *output,
306 JournalWriteSplitMode split_mode,
307 JournalFileFlags file_flags) {
308
309 int r;
310
311 assert(s);
312
313 assert(journal_remote_server_global == NULL);
314 journal_remote_server_global = s;
315
316 s->split_mode = split_mode;
317 s->file_flags = file_flags;
318
319 if (output)
320 s->output = output;
321 else if (split_mode == JOURNAL_WRITE_SPLIT_NONE)
322 s->output = REMOTE_JOURNAL_PATH "/remote.journal";
323 else if (split_mode == JOURNAL_WRITE_SPLIT_HOST)
324 s->output = REMOTE_JOURNAL_PATH;
325 else
326 assert_not_reached();
327
328 r = sd_event_default(&s->event);
329 if (r < 0)
330 return log_error_errno(r, "Failed to allocate event loop: %m");
331
332 r = init_writer_hashmap(s);
333 if (r < 0)
334 return r;
335
336 return 0;
337 }
338
339 void journal_remote_server_destroy(RemoteServer *s) {
340 size_t i;
341
342 if (!s)
343 return;
344
345 hashmap_free(s->daemons);
346
347 for (i = 0; i < MALLOC_ELEMENTSOF(s->sources); i++)
348 remove_source(s, i);
349 free(s->sources);
350
351 writer_unref(s->_single_writer);
352 hashmap_free(s->writers);
353
354 sd_event_source_unref(s->listen_event);
355 sd_event_unref(s->event);
356
357 if (s == journal_remote_server_global)
358 journal_remote_server_global = NULL;
359
360 /* fds that we're listening on remain open... */
361 }
362
363 /**********************************************************************
364 **********************************************************************
365 **********************************************************************/
366
367 int journal_remote_handle_raw_source(
368 sd_event_source *event,
369 int fd,
370 uint32_t revents,
371 RemoteServer *s) {
372
373 RemoteSource *source;
374 int r;
375
376 /* Returns 1 if there might be more data pending,
377 * 0 if data is currently exhausted, negative on error.
378 */
379
380 assert(s);
381 assert(fd >= 0 && fd < (ssize_t) MALLOC_ELEMENTSOF(s->sources));
382 source = s->sources[fd];
383 assert(source->importer.fd == fd);
384
385 r = process_source(source, s->file_flags);
386 if (journal_importer_eof(&source->importer)) {
387 size_t remaining;
388
389 log_debug("EOF reached with source %s (fd=%d)",
390 source->importer.name, source->importer.fd);
391
392 remaining = journal_importer_bytes_remaining(&source->importer);
393 if (remaining > 0)
394 log_notice("Premature EOF. %zu bytes lost.", remaining);
395 remove_source(s, source->importer.fd);
396 log_debug("%zu active sources remaining", s->active);
397 return 0;
398 } else if (r == -E2BIG) {
399 log_notice("Entry with too many fields, skipped");
400 return 1;
401 } else if (r == -ENOBUFS) {
402 log_notice("Entry too big, skipped");
403 return 1;
404 } else if (r == -EAGAIN) {
405 return 0;
406 } else if (r < 0) {
407 log_debug_errno(r, "Closing connection: %m");
408 remove_source(s, fd);
409 return 0;
410 } else
411 return 1;
412 }
413
414 static int dispatch_raw_source_until_block(sd_event_source *event,
415 void *userdata) {
416 RemoteSource *source = ASSERT_PTR(userdata);
417 int r;
418
419 assert(event);
420
421 /* Make sure event stays around even if source is destroyed */
422 sd_event_source_ref(event);
423
424 r = journal_remote_handle_raw_source(event, source->importer.fd, EPOLLIN, journal_remote_server_global);
425 if (r != 1) {
426 int k;
427
428 /* No more data for now */
429 k = sd_event_source_set_enabled(event, SD_EVENT_OFF);
430 if (k < 0)
431 r = k;
432 }
433
434 sd_event_source_unref(event);
435
436 return r;
437 }
438
439 static int dispatch_raw_source_event(sd_event_source *event,
440 int fd,
441 uint32_t revents,
442 void *userdata) {
443 RemoteSource *source = ASSERT_PTR(userdata);
444 int r;
445
446 assert(source->event);
447 assert(source->buffer_event);
448
449 r = journal_remote_handle_raw_source(event, fd, EPOLLIN, journal_remote_server_global);
450 if (r == 1) {
451 int k;
452
453 /* Might have more data. We need to rerun the handler
454 * until we are sure the buffer is exhausted. */
455 k = sd_event_source_set_enabled(source->buffer_event, SD_EVENT_ON);
456 if (k < 0)
457 r = k;
458 }
459
460 return r;
461 }
462
463 static int dispatch_blocking_source_event(sd_event_source *event,
464 void *userdata) {
465 RemoteSource *source = ASSERT_PTR(userdata);
466
467 return journal_remote_handle_raw_source(event, source->importer.fd, EPOLLIN, journal_remote_server_global);
468 }
469
470 static int accept_connection(
471 const char *type,
472 int fd,
473 SocketAddress *addr,
474 char **hostname) {
475
476 _cleanup_close_ int fd2 = -EBADF;
477 int r;
478
479 assert(addr);
480 assert(hostname);
481
482 log_debug("Accepting new %s connection on fd:%d", type, fd);
483 fd2 = accept4(fd, &addr->sockaddr.sa, &addr->size, SOCK_NONBLOCK|SOCK_CLOEXEC);
484 if (fd2 < 0) {
485 if (ERRNO_IS_ACCEPT_AGAIN(errno))
486 return -EAGAIN;
487
488 return log_error_errno(errno, "accept() on fd:%d failed: %m", fd);
489 }
490
491 switch (socket_address_family(addr)) {
492 case AF_INET:
493 case AF_INET6:
494 case AF_VSOCK:
495 case AF_UNIX: {
496 _cleanup_free_ char *a = NULL;
497 char *b;
498
499 r = socket_address_print(addr, &a);
500 if (r < 0)
501 return log_error_errno(r, "socket_address_print(): %m");
502
503 r = socknameinfo_pretty(&addr->sockaddr.sa, addr->size, &b);
504 if (r < 0)
505 return log_error_errno(r, "Resolving hostname failed: %m");
506
507 log_debug("Accepted %s %s connection from %s",
508 type,
509 af_to_ipv4_ipv6(socket_address_family(addr)),
510 a);
511
512 *hostname = b;
513 return TAKE_FD(fd2);
514 }
515
516 default:
517 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
518 "Rejected %s connection with unsupported family %d",
519 type, socket_address_family(addr));
520 }
521 }
522
523 static int dispatch_raw_connection_event(
524 sd_event_source *event,
525 int fd,
526 uint32_t revents,
527 void *userdata) {
528
529 RemoteServer *s = ASSERT_PTR(userdata);
530 int fd2;
531 SocketAddress addr = {
532 .size = sizeof(union sockaddr_union),
533 .type = SOCK_STREAM,
534 };
535 char *hostname = NULL;
536
537 fd2 = accept_connection("raw", fd, &addr, &hostname);
538 if (fd2 == -EAGAIN)
539 return 0;
540 if (fd2 < 0)
541 return fd2;
542
543 return journal_remote_add_source(s, fd2, hostname, true);
544 }