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