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