]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal-remote/journal-remote.c
tree-wide: drop redundant _cleanup_ macros (#8810)
[thirdparty/systemd.git] / src / journal-remote / journal-remote.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
fdfccdbc
ZJS
2/***
3 This file is part of systemd.
4
5 Copyright 2012 Zbigniew Jędrzejewski-Szmek
fdfccdbc
ZJS
6***/
7
8#include <errno.h>
9#include <fcntl.h>
3f6fd1ba 10#include <getopt.h>
fdfccdbc
ZJS
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14#include <sys/prctl.h>
15#include <sys/socket.h>
fdfccdbc 16#include <unistd.h>
5e38eb93 17#include <stdint.h>
3f6fd1ba 18
fdfccdbc 19#include "sd-daemon.h"
3f6fd1ba 20
b5efdb8a 21#include "alloc-util.h"
3f6fd1ba 22#include "conf-parser.h"
a0f29c76 23#include "def.h"
4f5dd394 24#include "escape.h"
3ffd4af2 25#include "fd-util.h"
3f6fd1ba 26#include "fileio.h"
fdfccdbc 27#include "journal-file.h"
4f5dd394 28#include "journal-remote-write.h"
3ffd4af2 29#include "journal-remote.h"
fdfccdbc 30#include "journald-native.h"
fdfccdbc 31#include "macro.h"
6bedfcbb 32#include "parse-util.h"
dccca82b 33#include "process-util.h"
3f6fd1ba
LP
34#include "signal-util.h"
35#include "socket-util.h"
8fcde012 36#include "stat-util.h"
15a5e950 37#include "stdio-util.h"
8b43440b 38#include "string-table.h"
07630cea 39#include "string-util.h"
fdfccdbc 40#include "strv.h"
fdfccdbc 41
8201af08 42#define REMOTE_JOURNAL_PATH "/var/log/journal/remote"
fdfccdbc 43
799a8f39
ZJS
44#define PRIV_KEY_FILE CERTIFICATE_ROOT "/private/journal-remote.pem"
45#define CERT_FILE CERTIFICATE_ROOT "/certs/journal-remote.pem"
46#define TRUST_FILE CERTIFICATE_ROOT "/ca/trusted.pem"
ad95fd1d 47
fdfccdbc
ZJS
48static char* arg_url = NULL;
49static char* arg_getter = NULL;
fdfccdbc 50static char* arg_listen_raw = NULL;
cc64d017
ZJS
51static char* arg_listen_http = NULL;
52static char* arg_listen_https = NULL;
8a8d55f2 53static char** arg_files = NULL;
fdfccdbc
ZJS
54static int arg_compress = true;
55static int arg_seal = false;
8a8d55f2 56static int http_socket = -1, https_socket = -1;
5937a4d4 57static char** arg_gnutls_log = NULL;
fdfccdbc 58
cb30d3ba 59static JournalWriteSplitMode arg_split_mode = _JOURNAL_WRITE_SPLIT_INVALID;
8201af08
ZJS
60static char* arg_output = NULL;
61
ad95fd1d
ZJS
62static char *arg_key = NULL;
63static char *arg_cert = NULL;
64static char *arg_trust = NULL;
65static bool arg_trust_all = false;
cc64d017 66
fdfccdbc
ZJS
67/**********************************************************************
68 **********************************************************************
69 **********************************************************************/
70
71static int spawn_child(const char* child, char** argv) {
4c253ed1
LP
72 pid_t child_pid;
73 int fd[2], r;
fdfccdbc 74
4a62c710
MS
75 if (pipe(fd) < 0)
76 return log_error_errno(errno, "Failed to create pager pipe: %m");
fdfccdbc 77
b6e1fff1 78 r = safe_fork("(remote)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &child_pid);
4c253ed1 79 if (r < 0) {
3d94f76c 80 safe_close_pair(fd);
b6e1fff1 81 return r;
fdfccdbc
ZJS
82 }
83
84 /* In the child */
4c253ed1 85 if (r == 0) {
2b33ab09 86 safe_close(fd[0]);
ce30c8dc 87
2b33ab09 88 r = rearrange_stdio(STDIN_FILENO, fd[1], STDERR_FILENO);
fdfccdbc 89 if (r < 0) {
2b33ab09 90 log_error_errno(r, "Failed to dup pipe to stdout: %m");
fdfccdbc
ZJS
91 _exit(EXIT_FAILURE);
92 }
93
fdfccdbc 94 execvp(child, argv);
56f64d95 95 log_error_errno(errno, "Failed to exec child %s: %m", child);
fdfccdbc
ZJS
96 _exit(EXIT_FAILURE);
97 }
98
2b33ab09 99 safe_close(fd[1]);
fdfccdbc 100
800d3f34
DD
101 r = fd_nonblock(fd[0], true);
102 if (r < 0)
103 log_warning_errno(errno, "Failed to set child pipe to non-blocking: %m");
104
fdfccdbc
ZJS
105 return fd[0];
106}
107
8201af08 108static int spawn_curl(const char* url) {
fdfccdbc
ZJS
109 char **argv = STRV_MAKE("curl",
110 "-HAccept: application/vnd.fdo.journal",
111 "--silent",
112 "--show-error",
113 url);
114 int r;
115
116 r = spawn_child("curl", argv);
117 if (r < 0)
709f6e46 118 log_error_errno(r, "Failed to spawn curl: %m");
fdfccdbc
ZJS
119 return r;
120}
121
2f1acf6f 122static int spawn_getter(const char *getter) {
fdfccdbc 123 int r;
c8b32e11 124 _cleanup_strv_free_ char **words = NULL;
fdfccdbc
ZJS
125
126 assert(getter);
8adaf7bd 127 r = strv_split_extract(&words, getter, WHITESPACE, EXTRACT_QUOTES);
23bbb0de
MS
128 if (r < 0)
129 return log_error_errno(r, "Failed to split getter option: %m");
fdfccdbc
ZJS
130
131 r = spawn_child(words[0], words);
132 if (r < 0)
709f6e46 133 log_error_errno(r, "Failed to spawn getter %s: %m", getter);
fdfccdbc
ZJS
134
135 return r;
136}
137
9ff48d09 138#define filename_escape(s) xescape((s), "/ ")
8201af08
ZJS
139
140static int open_output(Writer *w, const char* host) {
141 _cleanup_free_ char *_output = NULL;
142 const char *output;
fdfccdbc
ZJS
143 int r;
144
8201af08
ZJS
145 switch (arg_split_mode) {
146 case JOURNAL_WRITE_SPLIT_NONE:
147 output = arg_output ?: REMOTE_JOURNAL_PATH "/remote.journal";
148 break;
fdfccdbc 149
8201af08
ZJS
150 case JOURNAL_WRITE_SPLIT_HOST: {
151 _cleanup_free_ char *name;
fdfccdbc 152
8201af08 153 assert(host);
fdfccdbc 154
8201af08
ZJS
155 name = filename_escape(host);
156 if (!name)
157 return log_oom();
158
159 r = asprintf(&_output, "%s/remote-%s.journal",
160 arg_output ?: REMOTE_JOURNAL_PATH,
161 name);
fdfccdbc
ZJS
162 if (r < 0)
163 return log_oom();
8201af08
ZJS
164
165 output = _output;
166 break;
167 }
168
169 default:
170 assert_not_reached("what?");
fdfccdbc
ZJS
171 }
172
173 r = journal_file_open_reliably(output,
174 O_RDWR|O_CREAT, 0640,
57850536 175 arg_compress, (uint64_t) -1, arg_seal,
8201af08 176 &w->metrics,
b58c888f 177 w->mmap, NULL,
8201af08 178 NULL, &w->journal);
fdfccdbc 179 if (r < 0)
c33b3297
MS
180 log_error_errno(r, "Failed to open output journal %s: %m",
181 output);
fdfccdbc 182 else
0e72da6f 183 log_debug("Opened output file %s", w->journal->path);
fdfccdbc
ZJS
184 return r;
185}
186
cc64d017
ZJS
187/**********************************************************************
188 **********************************************************************
189 **********************************************************************/
190
9ff48d09 191static int init_writer_hashmap(RemoteServer *s) {
d5099efc
MS
192 static const struct hash_ops *hash_ops[] = {
193 [JOURNAL_WRITE_SPLIT_NONE] = NULL,
194 [JOURNAL_WRITE_SPLIT_HOST] = &string_hash_ops,
9ff48d09
ZJS
195 };
196
d5099efc 197 assert(arg_split_mode >= 0 && arg_split_mode < (int) ELEMENTSOF(hash_ops));
cc64d017 198
d5099efc 199 s->writers = hashmap_new(hash_ops[arg_split_mode]);
9ff48d09
ZJS
200 if (!s->writers)
201 return log_oom();
202
203 return 0;
204}
205
8e766630
LP
206static int get_writer(RemoteServer *s, const char *host, Writer **writer) {
207 _cleanup_(writer_unrefp) Writer *w = NULL;
9ff48d09 208 const void *key;
9ff48d09
ZJS
209 int r;
210
211 switch(arg_split_mode) {
212 case JOURNAL_WRITE_SPLIT_NONE:
213 key = "one and only";
214 break;
215
216 case JOURNAL_WRITE_SPLIT_HOST:
217 assert(host);
218 key = host;
219 break;
220
221 default:
222 assert_not_reached("what split mode?");
223 }
224
225 w = hashmap_get(s->writers, key);
226 if (w)
227 writer_ref(w);
228 else {
229 w = writer_new(s);
230 if (!w)
231 return log_oom();
232
233 if (arg_split_mode == JOURNAL_WRITE_SPLIT_HOST) {
234 w->hashmap_key = strdup(key);
235 if (!w->hashmap_key)
236 return log_oom();
237 }
cc64d017 238
9ff48d09
ZJS
239 r = open_output(w, host);
240 if (r < 0)
241 return r;
fdfccdbc 242
9ff48d09
ZJS
243 r = hashmap_put(s->writers, w->hashmap_key ?: key, w);
244 if (r < 0)
245 return r;
246 }
fdfccdbc 247
1cc6c93a
YW
248 *writer = TAKE_PTR(w);
249
9ff48d09
ZJS
250 return 0;
251}
cc64d017 252
9ff48d09
ZJS
253/**********************************************************************
254 **********************************************************************
255 **********************************************************************/
fdfccdbc 256
cc64d017
ZJS
257/* This should go away as soon as µhttpd allows state to be passed around. */
258static RemoteServer *server;
259
fdfccdbc
ZJS
260static int dispatch_raw_source_event(sd_event_source *event,
261 int fd,
262 uint32_t revents,
263 void *userdata);
043945b9
ZJS
264static int dispatch_raw_source_until_block(sd_event_source *event,
265 void *userdata);
70f1b2dd
ZJS
266static int dispatch_blocking_source_event(sd_event_source *event,
267 void *userdata);
fdfccdbc
ZJS
268static int dispatch_raw_connection_event(sd_event_source *event,
269 int fd,
270 uint32_t revents,
271 void *userdata);
5e38eb93
MS
272static int null_timer_event_handler(sd_event_source *s,
273 uint64_t usec,
274 void *userdata);
cc64d017
ZJS
275static int dispatch_http_event(sd_event_source *event,
276 int fd,
277 uint32_t revents,
278 void *userdata);
fdfccdbc 279
9ff48d09
ZJS
280static int get_source_for_fd(RemoteServer *s,
281 int fd, char *name, RemoteSource **source) {
282 Writer *writer;
283 int r;
284
1f8af042
ZJS
285 /* This takes ownership of name, but only on success. */
286
fdfccdbc
ZJS
287 assert(fd >= 0);
288 assert(source);
289
ca2d3784 290 if (!GREEDY_REALLOC0(s->sources, s->sources_size, fd + 1))
fdfccdbc
ZJS
291 return log_oom();
292
9ff48d09 293 r = get_writer(s, name, &writer);
eb56eb9b
MS
294 if (r < 0)
295 return log_warning_errno(r, "Failed to get writer for source %s: %m",
296 name);
9ff48d09 297
fdfccdbc 298 if (s->sources[fd] == NULL) {
9ff48d09
ZJS
299 s->sources[fd] = source_new(fd, false, name, writer);
300 if (!s->sources[fd]) {
301 writer_unref(writer);
fdfccdbc 302 return log_oom();
9ff48d09
ZJS
303 }
304
fdfccdbc
ZJS
305 s->active++;
306 }
307
308 *source = s->sources[fd];
309 return 0;
310}
311
312static int remove_source(RemoteServer *s, int fd) {
313 RemoteSource *source;
314
315 assert(s);
ca2d3784 316 assert(fd >= 0 && fd < (ssize_t) s->sources_size);
fdfccdbc
ZJS
317
318 source = s->sources[fd];
319 if (source) {
8201af08 320 /* this closes fd too */
fdfccdbc
ZJS
321 source_free(source);
322 s->sources[fd] = NULL;
323 s->active--;
324 }
325
fdfccdbc
ZJS
326 return 0;
327}
328
9ff48d09 329static int add_source(RemoteServer *s, int fd, char* name, bool own_name) {
8201af08 330
a7f7d1bd 331 RemoteSource *source = NULL;
fdfccdbc
ZJS
332 int r;
333
1f8af042
ZJS
334 /* This takes ownership of name, even on failure, if own_name is true. */
335
fdfccdbc
ZJS
336 assert(s);
337 assert(fd >= 0);
9ff48d09 338 assert(name);
fdfccdbc 339
9ff48d09
ZJS
340 if (!own_name) {
341 name = strdup(name);
342 if (!name)
343 return log_oom();
344 }
fdfccdbc 345
9ff48d09 346 r = get_source_for_fd(s, fd, name, &source);
fdfccdbc 347 if (r < 0) {
c33b3297
MS
348 log_error_errno(r, "Failed to create source for fd:%d (%s): %m",
349 fd, name);
1f8af042 350 free(name);
fdfccdbc
ZJS
351 return r;
352 }
8201af08 353
fdfccdbc 354 r = sd_event_add_io(s->events, &source->event,
8201af08 355 fd, EPOLLIN|EPOLLRDHUP|EPOLLPRI,
043945b9
ZJS
356 dispatch_raw_source_event, source);
357 if (r == 0) {
358 /* Add additional source for buffer processing. It will be
359 * enabled later. */
360 r = sd_event_add_defer(s->events, &source->buffer_event,
361 dispatch_raw_source_until_block, source);
362 if (r == 0)
363 sd_event_source_set_enabled(source->buffer_event, SD_EVENT_OFF);
364 } else if (r == -EPERM) {
70f1b2dd
ZJS
365 log_debug("Falling back to sd_event_add_defer for fd:%d (%s)", fd, name);
366 r = sd_event_add_defer(s->events, &source->event,
367 dispatch_blocking_source_event, source);
368 if (r == 0)
369 sd_event_source_set_enabled(source->event, SD_EVENT_ON);
370 }
fdfccdbc 371 if (r < 0) {
c33b3297
MS
372 log_error_errno(r, "Failed to register event source for fd:%d: %m",
373 fd);
fdfccdbc
ZJS
374 goto error;
375 }
376
356779df 377 r = sd_event_source_set_description(source->event, name);
43300d9d 378 if (r < 0) {
da927ba9 379 log_error_errno(r, "Failed to set source name for fd:%d: %m", fd);
43300d9d
ZJS
380 goto error;
381 }
382
fdfccdbc
ZJS
383 return 1; /* work to do */
384
385 error:
386 remove_source(s, fd);
387 return r;
388}
389
8a8d55f2
ZJS
390static int add_raw_socket(RemoteServer *s, int fd) {
391 int r;
43300d9d 392 _cleanup_close_ int fd_ = fd;
fbd0b64f 393 char name[STRLEN("raw-socket-") + DECIMAL_STR_MAX(int) + 1];
43300d9d
ZJS
394
395 assert(fd >= 0);
fdfccdbc 396
8201af08
ZJS
397 r = sd_event_add_io(s->events, &s->listen_event,
398 fd, EPOLLIN,
fdfccdbc 399 dispatch_raw_connection_event, s);
43300d9d 400 if (r < 0)
fdfccdbc 401 return r;
fdfccdbc 402
5ffa8c81 403 xsprintf(name, "raw-socket-%d", fd);
43300d9d 404
356779df 405 r = sd_event_source_set_description(s->listen_event, name);
43300d9d
ZJS
406 if (r < 0)
407 return r;
408
409 fd_ = -1;
313cefa1 410 s->active++;
fdfccdbc
ZJS
411 return 0;
412}
413
8a8d55f2
ZJS
414static int setup_raw_socket(RemoteServer *s, const char *address) {
415 int fd;
416
7b7afdfc 417 fd = make_socket_fd(LOG_INFO, address, SOCK_STREAM, SOCK_CLOEXEC);
8a8d55f2
ZJS
418 if (fd < 0)
419 return fd;
420
421 return add_raw_socket(s, fd);
422}
423
8201af08
ZJS
424/**********************************************************************
425 **********************************************************************
426 **********************************************************************/
427
9ce998b9 428static int request_meta(void **connection_cls, int fd, char *hostname) {
cc64d017 429 RemoteSource *source;
9ff48d09
ZJS
430 Writer *writer;
431 int r;
cc64d017
ZJS
432
433 assert(connection_cls);
9ff48d09 434 if (*connection_cls)
9ce998b9 435 return 0;
9ff48d09
ZJS
436
437 r = get_writer(server, hostname, &writer);
eb56eb9b
MS
438 if (r < 0)
439 return log_warning_errno(r, "Failed to get writer for source %s: %m",
440 hostname);
cc64d017 441
9ff48d09 442 source = source_new(fd, true, hostname, writer);
8201af08 443 if (!source) {
9ff48d09 444 writer_unref(writer);
9ce998b9 445 return log_oom();
8201af08
ZJS
446 }
447
cc64d017
ZJS
448 log_debug("Added RemoteSource as connection metadata %p", source);
449
450 *connection_cls = source;
9ce998b9 451 return 0;
cc64d017
ZJS
452}
453
454static void request_meta_free(void *cls,
455 struct MHD_Connection *connection,
456 void **connection_cls,
457 enum MHD_RequestTerminationCode toe) {
458 RemoteSource *s;
459
460 assert(connection_cls);
461 s = *connection_cls;
462
9ce998b9
ZJS
463 if (s) {
464 log_debug("Cleaning up connection metadata %p", s);
465 source_free(s);
466 *connection_cls = NULL;
467 }
cc64d017
ZJS
468}
469
470static int process_http_upload(
471 struct MHD_Connection *connection,
472 const char *upload_data,
473 size_t *upload_data_size,
474 RemoteSource *source) {
475
8201af08 476 bool finished = false;
4a0a6ac0 477 size_t remaining;
9ff48d09 478 int r;
cc64d017
ZJS
479
480 assert(source);
481
cb41ff29
ZJS
482 log_trace("%s: connection %p, %zu bytes",
483 __func__, connection, *upload_data_size);
cc64d017
ZJS
484
485 if (*upload_data_size) {
cb41ff29 486 log_trace("Received %zu bytes", *upload_data_size);
cc64d017 487
b18453ed
ZJS
488 r = journal_importer_push_data(&source->importer,
489 upload_data, *upload_data_size);
851d4e2a 490 if (r < 0)
e7216d11 491 return mhd_respond_oom(connection);
851d4e2a 492
cc64d017
ZJS
493 *upload_data_size = 0;
494 } else
495 finished = true;
496
57255510 497 for (;;) {
9ff48d09 498 r = process_source(source, arg_compress, arg_seal);
ff55c3c7 499 if (r == -EAGAIN)
cc64d017
ZJS
500 break;
501 else if (r < 0) {
502 log_warning("Failed to process data for connection %p", connection);
851d4e2a
ZJS
503 if (r == -E2BIG)
504 return mhd_respondf(connection,
58eb2798 505 r, MHD_HTTP_PAYLOAD_TOO_LARGE,
f5e757f1 506 "Entry is too large, maximum is " STRINGIFY(DATA_SIZE_MAX) " bytes.");
851d4e2a
ZJS
507 else
508 return mhd_respondf(connection,
1b4cd646
ZJS
509 r, MHD_HTTP_UNPROCESSABLE_ENTITY,
510 "Processing failed: %m.");
cc64d017
ZJS
511 }
512 }
513
514 if (!finished)
515 return MHD_YES;
516
517 /* The upload is finished */
518
b18453ed 519 remaining = journal_importer_bytes_remaining(&source->importer);
4a0a6ac0 520 if (remaining > 0) {
1b4cd646
ZJS
521 log_warning("Premature EOF byte. %zu bytes lost.", remaining);
522 return mhd_respondf(connection,
523 0, MHD_HTTP_EXPECTATION_FAILED,
4a0a6ac0
ZJS
524 "Premature EOF. %zu bytes of trailing data not processed.",
525 remaining);
cc64d017
ZJS
526 }
527
f5e757f1 528 return mhd_respond(connection, MHD_HTTP_ACCEPTED, "OK.");
cc64d017
ZJS
529};
530
531static int request_handler(
532 void *cls,
533 struct MHD_Connection *connection,
534 const char *url,
535 const char *method,
536 const char *version,
537 const char *upload_data,
538 size_t *upload_data_size,
539 void **connection_cls) {
540
541 const char *header;
8201af08 542 int r, code, fd;
9ff48d09 543 _cleanup_free_ char *hostname = NULL;
cc64d017
ZJS
544
545 assert(connection);
546 assert(connection_cls);
547 assert(url);
548 assert(method);
549
cb41ff29 550 log_trace("Handling a connection %s %s %s", method, url, version);
cc64d017
ZJS
551
552 if (*connection_cls)
553 return process_http_upload(connection,
554 upload_data, upload_data_size,
555 *connection_cls);
556
557 if (!streq(method, "POST"))
f5e757f1 558 return mhd_respond(connection, MHD_HTTP_NOT_ACCEPTABLE, "Unsupported method.");
cc64d017
ZJS
559
560 if (!streq(url, "/upload"))
f5e757f1 561 return mhd_respond(connection, MHD_HTTP_NOT_FOUND, "Not found.");
cc64d017
ZJS
562
563 header = MHD_lookup_connection_value(connection,
564 MHD_HEADER_KIND, "Content-Type");
565 if (!header || !streq(header, "application/vnd.fdo.journal"))
e7216d11 566 return mhd_respond(connection, MHD_HTTP_UNSUPPORTED_MEDIA_TYPE,
f5e757f1 567 "Content-Type: application/vnd.fdo.journal is required.");
cc64d017 568
8201af08
ZJS
569 {
570 const union MHD_ConnectionInfo *ci;
571
572 ci = MHD_get_connection_info(connection,
573 MHD_CONNECTION_INFO_CONNECTION_FD);
574 if (!ci) {
575 log_error("MHD_get_connection_info failed: cannot get remote fd");
576 return mhd_respond(connection, MHD_HTTP_INTERNAL_SERVER_ERROR,
f5e757f1 577 "Cannot check remote address.");
8201af08
ZJS
578 }
579
580 fd = ci->connect_fd;
581 assert(fd >= 0);
582 }
583
ad95fd1d 584 if (server->check_trust) {
8201af08 585 r = check_permissions(connection, &code, &hostname);
cc64d017
ZJS
586 if (r < 0)
587 return code;
8201af08 588 } else {
366b7db4 589 r = getpeername_pretty(fd, false, &hostname);
1f6b4113 590 if (r < 0)
8201af08 591 return mhd_respond(connection, MHD_HTTP_INTERNAL_SERVER_ERROR,
f5e757f1 592 "Cannot check remote hostname.");
cc64d017
ZJS
593 }
594
8201af08
ZJS
595 assert(hostname);
596
9ce998b9
ZJS
597 r = request_meta(connection_cls, fd, hostname);
598 if (r == -ENOMEM)
cc64d017 599 return respond_oom(connection);
9ce998b9 600 else if (r < 0)
1b4cd646 601 return mhd_respondf(connection, r, MHD_HTTP_INTERNAL_SERVER_ERROR, "%m");
9ce998b9 602
9ff48d09 603 hostname = NULL;
cc64d017
ZJS
604 return MHD_YES;
605}
606
ad95fd1d
ZJS
607static int setup_microhttpd_server(RemoteServer *s,
608 int fd,
609 const char *key,
610 const char *cert,
611 const char *trust) {
cc64d017
ZJS
612 struct MHD_OptionItem opts[] = {
613 { MHD_OPTION_NOTIFY_COMPLETED, (intptr_t) request_meta_free},
614 { MHD_OPTION_EXTERNAL_LOGGER, (intptr_t) microhttpd_logger},
615 { MHD_OPTION_LISTEN_SOCKET, fd},
8c6bcbf8 616 { MHD_OPTION_CONNECTION_MEMORY_LIMIT, 128*1024},
cc64d017
ZJS
617 { MHD_OPTION_END},
618 { MHD_OPTION_END},
619 { MHD_OPTION_END},
01058587 620 { MHD_OPTION_END},
cc64d017 621 { MHD_OPTION_END}};
5e3efccf 622 int opts_pos = 4;
cc64d017
ZJS
623 int flags =
624 MHD_USE_DEBUG |
ef08ced6 625 MHD_USE_DUAL_STACK |
21b6ff36 626 MHD_USE_EPOLL |
21b6ff36 627 MHD_USE_ITC;
cc64d017
ZJS
628
629 const union MHD_DaemonInfo *info;
630 int r, epoll_fd;
631 MHDDaemonWrapper *d;
632
633 assert(fd >= 0);
634
635 r = fd_nonblock(fd, true);
f647962d
MS
636 if (r < 0)
637 return log_error_errno(r, "Failed to make fd:%d nonblocking: %m", fd);
cc64d017 638
01058587 639/* MHD_OPTION_STRICT_FOR_CLIENT is introduced in microhttpd 0.9.54,
e2e8683e
YW
640 * and MHD_USE_PEDANTIC_CHECKS will be deprecated in future.
641 * If MHD_USE_PEDANTIC_CHECKS is '#define'd, then it is deprecated
642 * and we should use MHD_OPTION_STRICT_FOR_CLIENT. On the other hand,
643 * if MHD_USE_PEDANTIC_CHECKS is not '#define'd, then it is not
644 * deprecated yet and there exists an enum element with the same name.
645 * So we can safely use it. */
01058587
YW
646#ifdef MHD_USE_PEDANTIC_CHECKS
647 opts[opts_pos++] = (struct MHD_OptionItem)
648 {MHD_OPTION_STRICT_FOR_CLIENT, 1};
649#else
650 flags |= MHD_USE_PEDANTIC_CHECKS;
651#endif
652
ad95fd1d
ZJS
653 if (key) {
654 assert(cert);
655
cc64d017 656 opts[opts_pos++] = (struct MHD_OptionItem)
ad95fd1d 657 {MHD_OPTION_HTTPS_MEM_KEY, 0, (char*) key};
cc64d017 658 opts[opts_pos++] = (struct MHD_OptionItem)
ad95fd1d 659 {MHD_OPTION_HTTPS_MEM_CERT, 0, (char*) cert};
cc64d017 660
c831aa75 661 flags |= MHD_USE_TLS;
cc64d017 662
ad95fd1d 663 if (trust)
cc64d017 664 opts[opts_pos++] = (struct MHD_OptionItem)
ad95fd1d 665 {MHD_OPTION_HTTPS_MEM_TRUST, 0, (char*) trust};
cc64d017
ZJS
666 }
667
668 d = new(MHDDaemonWrapper, 1);
669 if (!d)
670 return log_oom();
671
672 d->fd = (uint64_t) fd;
673
674 d->daemon = MHD_start_daemon(flags, 0,
675 NULL, NULL,
676 request_handler, NULL,
677 MHD_OPTION_ARRAY, opts,
678 MHD_OPTION_END);
679 if (!d->daemon) {
680 log_error("Failed to start µhttp daemon");
681 r = -EINVAL;
682 goto error;
683 }
684
685 log_debug("Started MHD %s daemon on fd:%d (wrapper @ %p)",
ad95fd1d 686 key ? "HTTPS" : "HTTP", fd, d);
cc64d017 687
cc64d017
ZJS
688 info = MHD_get_daemon_info(d->daemon, MHD_DAEMON_INFO_EPOLL_FD_LINUX_ONLY);
689 if (!info) {
690 log_error("µhttp returned NULL daemon info");
15411c0c 691 r = -EOPNOTSUPP;
cc64d017
ZJS
692 goto error;
693 }
694
695 epoll_fd = info->listen_fd;
696 if (epoll_fd < 0) {
697 log_error("µhttp epoll fd is invalid");
698 r = -EUCLEAN;
699 goto error;
700 }
701
5e38eb93 702 r = sd_event_add_io(s->events, &d->io_event,
8201af08
ZJS
703 epoll_fd, EPOLLIN,
704 dispatch_http_event, d);
cc64d017 705 if (r < 0) {
da927ba9 706 log_error_errno(r, "Failed to add event callback: %m");
cc64d017
ZJS
707 goto error;
708 }
709
5e38eb93
MS
710 r = sd_event_source_set_description(d->io_event, "io_event");
711 if (r < 0) {
712 log_error_errno(r, "Failed to set source name: %m");
713 goto error;
714 }
715
716 r = sd_event_add_time(s->events, &d->timer_event,
57850536 717 CLOCK_MONOTONIC, (uint64_t) -1, 0,
5e38eb93
MS
718 null_timer_event_handler, d);
719 if (r < 0) {
720 log_error_errno(r, "Failed to add timer_event: %m");
721 goto error;
722 }
723
2e293b4a 724 r = sd_event_source_set_description(d->timer_event, "timer_event");
43300d9d 725 if (r < 0) {
da927ba9 726 log_error_errno(r, "Failed to set source name: %m");
43300d9d
ZJS
727 goto error;
728 }
729
d5099efc 730 r = hashmap_ensure_allocated(&s->daemons, &uint64_hash_ops);
cc64d017
ZJS
731 if (r < 0) {
732 log_oom();
733 goto error;
734 }
735
736 r = hashmap_put(s->daemons, &d->fd, d);
737 if (r < 0) {
da927ba9 738 log_error_errno(r, "Failed to add daemon to hashmap: %m");
cc64d017
ZJS
739 goto error;
740 }
741
313cefa1 742 s->active++;
cc64d017
ZJS
743 return 0;
744
745error:
746 MHD_stop_daemon(d->daemon);
747 free(d->daemon);
748 free(d);
749 return r;
750}
751
752static int setup_microhttpd_socket(RemoteServer *s,
753 const char *address,
ad95fd1d
ZJS
754 const char *key,
755 const char *cert,
756 const char *trust) {
cc64d017
ZJS
757 int fd;
758
7b7afdfc 759 fd = make_socket_fd(LOG_DEBUG, address, SOCK_STREAM, SOCK_CLOEXEC);
cc64d017
ZJS
760 if (fd < 0)
761 return fd;
762
ad95fd1d 763 return setup_microhttpd_server(s, fd, key, cert, trust);
cc64d017
ZJS
764}
765
5e38eb93
MS
766static int null_timer_event_handler(sd_event_source *timer_event,
767 uint64_t usec,
768 void *userdata) {
769 return dispatch_http_event(timer_event, 0, 0, userdata);
770}
771
cc64d017
ZJS
772static int dispatch_http_event(sd_event_source *event,
773 int fd,
774 uint32_t revents,
775 void *userdata) {
776 MHDDaemonWrapper *d = userdata;
777 int r;
5e38eb93 778 MHD_UNSIGNED_LONG_LONG timeout = ULONG_LONG_MAX;
cc64d017
ZJS
779
780 assert(d);
781
723afa4f 782 r = MHD_run(d->daemon);
cc64d017 783 if (r == MHD_NO) {
723afa4f 784 log_error("MHD_run failed!");
cc64d017
ZJS
785 // XXX: unregister daemon
786 return -EINVAL;
787 }
5e38eb93
MS
788 if (MHD_get_timeout(d->daemon, &timeout) == MHD_NO)
789 timeout = ULONG_LONG_MAX;
790
791 r = sd_event_source_set_time(d->timer_event, timeout);
792 if (r < 0) {
793 log_warning_errno(r, "Unable to set event loop timeout: %m, this may result in indefinite blocking!");
794 return 1;
795 }
796
797 r = sd_event_source_set_enabled(d->timer_event, SD_EVENT_ON);
798 if (r < 0)
799 log_warning_errno(r, "Unable to enable timer_event: %m, this may result in indefinite blocking!");
cc64d017
ZJS
800
801 return 1; /* work to do */
802}
803
fdfccdbc
ZJS
804/**********************************************************************
805 **********************************************************************
806 **********************************************************************/
807
fdfccdbc 808static int setup_signals(RemoteServer *s) {
fdfccdbc
ZJS
809 int r;
810
811 assert(s);
812
72c0a2c2 813 assert_se(sigprocmask_many(SIG_SETMASK, NULL, SIGINT, SIGTERM, -1) >= 0);
fdfccdbc 814
43300d9d
ZJS
815 r = sd_event_add_signal(s->events, &s->sigterm_event, SIGTERM, NULL, s);
816 if (r < 0)
817 return r;
818
43300d9d 819 r = sd_event_add_signal(s->events, &s->sigint_event, SIGINT, NULL, s);
fdfccdbc
ZJS
820 if (r < 0)
821 return r;
822
fdfccdbc
ZJS
823 return 0;
824}
825
42b6bf75
ZJS
826static int negative_fd(const char *spec) {
827 /* Return a non-positive number as its inverse, -EINVAL otherwise. */
828
8a8d55f2
ZJS
829 int fd, r;
830
831 r = safe_atoi(spec, &fd);
832 if (r < 0)
833 return r;
834
42b6bf75
ZJS
835 if (fd > 0)
836 return -EINVAL;
837 else
838 return -fd;
8a8d55f2
ZJS
839}
840
ad95fd1d
ZJS
841static int remoteserver_init(RemoteServer *s,
842 const char* key,
843 const char* cert,
844 const char* trust) {
fdfccdbc 845 int r, n, fd;
8a8d55f2 846 char **file;
fdfccdbc
ZJS
847
848 assert(s);
849
ad95fd1d
ZJS
850 if ((arg_listen_raw || arg_listen_http) && trust) {
851 log_error("Option --trust makes all non-HTTPS connections untrusted.");
852 return -EINVAL;
853 }
854
b1604b34 855 r = sd_event_default(&s->events);
23bbb0de
MS
856 if (r < 0)
857 return log_error_errno(r, "Failed to allocate event loop: %m");
fdfccdbc
ZJS
858
859 setup_signals(s);
860
cc64d017
ZJS
861 assert(server == NULL);
862 server = s;
863
22259a00
JL
864 r = init_writer_hashmap(s);
865 if (r < 0)
866 return r;
867
fdfccdbc 868 n = sd_listen_fds(true);
eb56eb9b
MS
869 if (n < 0)
870 return log_error_errno(n, "Failed to read listening file descriptors from environment: %m");
871 else
0e72da6f 872 log_debug("Received %d descriptors", n);
fdfccdbc 873
8a8d55f2
ZJS
874 if (MAX(http_socket, https_socket) >= SD_LISTEN_FDS_START + n) {
875 log_error("Received fewer sockets than expected");
876 return -EBADFD;
877 }
878
fdfccdbc 879 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
42b6bf75 880 if (sd_is_socket(fd, AF_UNSPEC, 0, true)) {
0e72da6f 881 log_debug("Received a listening socket (fd:%d)", fd);
8a8d55f2
ZJS
882
883 if (fd == http_socket)
ad95fd1d 884 r = setup_microhttpd_server(s, fd, NULL, NULL, NULL);
8a8d55f2 885 else if (fd == https_socket)
ad95fd1d 886 r = setup_microhttpd_server(s, fd, key, cert, trust);
8a8d55f2
ZJS
887 else
888 r = add_raw_socket(s, fd);
42b6bf75 889 } else if (sd_is_socket(fd, AF_UNSPEC, 0, false)) {
9ff48d09 890 char *hostname;
8201af08 891
366b7db4 892 r = getpeername_pretty(fd, false, &hostname);
23bbb0de
MS
893 if (r < 0)
894 return log_error_errno(r, "Failed to retrieve remote name: %m");
8201af08 895
0e72da6f 896 log_debug("Received a connection socket (fd:%d) from %s", fd, hostname);
fdfccdbc 897
9ff48d09 898 r = add_source(s, fd, hostname, true);
fdfccdbc
ZJS
899 } else {
900 log_error("Unknown socket passed on fd:%d", fd);
8a8d55f2 901
fdfccdbc
ZJS
902 return -EINVAL;
903 }
8a8d55f2 904
23bbb0de
MS
905 if (r < 0)
906 return log_error_errno(r, "Failed to register socket (fd:%d): %m",
907 fd);
fdfccdbc
ZJS
908 }
909
2f1acf6f
YW
910 if (arg_getter) {
911 log_info("Spawning getter %s...", arg_getter);
912 fd = spawn_getter(arg_getter);
913 if (fd < 0)
914 return fd;
915
916 r = add_source(s, fd, (char*) arg_output, false);
917 if (r < 0)
918 return r;
919 }
8201af08 920
fdfccdbc 921 if (arg_url) {
d10accb0
YW
922 const char *url;
923 char *hostname, *p;
fdfccdbc 924
b68f6b0a
YW
925 if (!strstr(arg_url, "/entries")) {
926 if (endswith(arg_url, "/"))
927 url = strjoina(arg_url, "entries");
928 else
929 url = strjoina(arg_url, "/entries");
fdfccdbc 930 }
b68f6b0a
YW
931 else
932 url = strdupa(arg_url);
fdfccdbc 933
2f1acf6f
YW
934 log_info("Spawning curl %s...", url);
935 fd = spawn_curl(url);
fdfccdbc
ZJS
936 if (fd < 0)
937 return fd;
938
8201af08
ZJS
939 hostname =
940 startswith(arg_url, "https://") ?:
941 startswith(arg_url, "http://") ?:
942 arg_url;
943
d10accb0 944 hostname = strdupa(hostname);
d10accb0
YW
945 if ((p = strchr(hostname, '/')))
946 *p = '\0';
947 if ((p = strchr(hostname, ':')))
948 *p = '\0';
949
950 r = add_source(s, fd, hostname, false);
fdfccdbc
ZJS
951 if (r < 0)
952 return r;
fdfccdbc
ZJS
953 }
954
955 if (arg_listen_raw) {
0e72da6f 956 log_debug("Listening on a socket...");
fdfccdbc
ZJS
957 r = setup_raw_socket(s, arg_listen_raw);
958 if (r < 0)
959 return r;
fdfccdbc
ZJS
960 }
961
cc64d017 962 if (arg_listen_http) {
ad95fd1d 963 r = setup_microhttpd_socket(s, arg_listen_http, NULL, NULL, NULL);
cc64d017
ZJS
964 if (r < 0)
965 return r;
cc64d017
ZJS
966 }
967
968 if (arg_listen_https) {
ad95fd1d 969 r = setup_microhttpd_socket(s, arg_listen_https, key, cert, trust);
cc64d017
ZJS
970 if (r < 0)
971 return r;
cc64d017
ZJS
972 }
973
8a8d55f2 974 STRV_FOREACH(file, arg_files) {
40780a62
ZJS
975 const char *output_name;
976
8a8d55f2 977 if (streq(*file, "-")) {
0e72da6f 978 log_debug("Using standard input as source.");
8a8d55f2
ZJS
979
980 fd = STDIN_FILENO;
981 output_name = "stdin";
982 } else {
0e72da6f 983 log_debug("Reading file %s...", *file);
8a8d55f2
ZJS
984
985 fd = open(*file, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
4a62c710
MS
986 if (fd < 0)
987 return log_error_errno(errno, "Failed to open %s: %m", *file);
8a8d55f2
ZJS
988 output_name = *file;
989 }
990
9ff48d09 991 r = add_source(s, fd, (char*) output_name, false);
fdfccdbc
ZJS
992 if (r < 0)
993 return r;
fdfccdbc
ZJS
994 }
995
996 if (s->active == 0) {
f823da67 997 log_error("Zero sources specified");
fdfccdbc
ZJS
998 return -EINVAL;
999 }
1000
8201af08
ZJS
1001 if (arg_split_mode == JOURNAL_WRITE_SPLIT_NONE) {
1002 /* In this case we know what the writer will be
1003 called, so we can create it and verify that we can
1004 create output as expected. */
9ff48d09 1005 r = get_writer(s, NULL, &s->_single_writer);
8201af08
ZJS
1006 if (r < 0)
1007 return r;
1008 }
1009
1010 return 0;
fdfccdbc
ZJS
1011}
1012
1599f593
ZJS
1013static void MHDDaemonWrapper_free(MHDDaemonWrapper *d) {
1014 MHD_stop_daemon(d->daemon);
1015 sd_event_source_unref(d->io_event);
1016 sd_event_source_unref(d->timer_event);
1017 free(d);
1018}
1019
9ff48d09 1020static void server_destroy(RemoteServer *s) {
ca2d3784 1021 size_t i;
cc64d017 1022
1599f593 1023 hashmap_free_with_destructor(s->daemons, MHDDaemonWrapper_free);
cc64d017 1024
fdfccdbc 1025 assert(s->sources_size == 0 || s->sources);
cc64d017 1026 for (i = 0; i < s->sources_size; i++)
fdfccdbc 1027 remove_source(s, i);
fdfccdbc
ZJS
1028 free(s->sources);
1029
9ff48d09
ZJS
1030 writer_unref(s->_single_writer);
1031 hashmap_free(s->writers);
1032
fdfccdbc
ZJS
1033 sd_event_source_unref(s->sigterm_event);
1034 sd_event_source_unref(s->sigint_event);
1035 sd_event_source_unref(s->listen_event);
1036 sd_event_unref(s->events);
1037
1038 /* fds that we're listening on remain open... */
fdfccdbc
ZJS
1039}
1040
1041/**********************************************************************
1042 **********************************************************************
1043 **********************************************************************/
1044
043945b9
ZJS
1045static int handle_raw_source(sd_event_source *event,
1046 int fd,
1047 uint32_t revents,
1048 RemoteServer *s) {
fdfccdbc 1049
fdfccdbc
ZJS
1050 RemoteSource *source;
1051 int r;
1052
043945b9
ZJS
1053 /* Returns 1 if there might be more data pending,
1054 * 0 if data is currently exhausted, negative on error.
1055 */
1056
ca2d3784 1057 assert(fd >= 0 && fd < (ssize_t) s->sources_size);
fdfccdbc 1058 source = s->sources[fd];
b18453ed 1059 assert(source->importer.fd == fd);
fdfccdbc 1060
9ff48d09 1061 r = process_source(source, arg_compress, arg_seal);
b18453ed 1062 if (journal_importer_eof(&source->importer)) {
4a0a6ac0
ZJS
1063 size_t remaining;
1064
b18453ed
ZJS
1065 log_debug("EOF reached with source %s (fd=%d)",
1066 source->importer.name, source->importer.fd);
4a0a6ac0 1067
b18453ed 1068 remaining = journal_importer_bytes_remaining(&source->importer);
4a0a6ac0 1069 if (remaining > 0)
0e72da6f 1070 log_notice("Premature EOF. %zu bytes lost.", remaining);
b18453ed 1071 remove_source(s, source->importer.fd);
0e72da6f 1072 log_debug("%zu active sources remaining", s->active);
8201af08 1073 return 0;
fdfccdbc 1074 } else if (r == -E2BIG) {
0e72da6f 1075 log_notice_errno(E2BIG, "Entry too big, skipped");
8201af08 1076 return 1;
ff55c3c7 1077 } else if (r == -EAGAIN) {
8201af08
ZJS
1078 return 0;
1079 } else if (r < 0) {
0e72da6f 1080 log_debug_errno(r, "Closing connection: %m");
8201af08
ZJS
1081 remove_source(server, fd);
1082 return 0;
1083 } else
1084 return 1;
fdfccdbc
ZJS
1085}
1086
043945b9
ZJS
1087static int dispatch_raw_source_until_block(sd_event_source *event,
1088 void *userdata) {
1089 RemoteSource *source = userdata;
1090 int r;
1091
1092 /* Make sure event stays around even if source is destroyed */
1093 sd_event_source_ref(event);
1094
b18453ed 1095 r = handle_raw_source(event, source->importer.fd, EPOLLIN, server);
043945b9
ZJS
1096 if (r != 1)
1097 /* No more data for now */
1098 sd_event_source_set_enabled(event, SD_EVENT_OFF);
1099
1100 sd_event_source_unref(event);
1101
1102 return r;
1103}
1104
1105static int dispatch_raw_source_event(sd_event_source *event,
1106 int fd,
1107 uint32_t revents,
1108 void *userdata) {
1109 RemoteSource *source = userdata;
1110 int r;
1111
1112 assert(source->event);
1113 assert(source->buffer_event);
1114
1115 r = handle_raw_source(event, fd, EPOLLIN, server);
1116 if (r == 1)
1117 /* Might have more data. We need to rerun the handler
1118 * until we are sure the buffer is exhausted. */
1119 sd_event_source_set_enabled(source->buffer_event, SD_EVENT_ON);
1120
1121 return r;
1122}
1123
70f1b2dd
ZJS
1124static int dispatch_blocking_source_event(sd_event_source *event,
1125 void *userdata) {
1126 RemoteSource *source = userdata;
1127
b18453ed 1128 return handle_raw_source(event, source->importer.fd, EPOLLIN, server);
70f1b2dd
ZJS
1129}
1130
8201af08
ZJS
1131static int accept_connection(const char* type, int fd,
1132 SocketAddress *addr, char **hostname) {
fdfccdbc
ZJS
1133 int fd2, r;
1134
cc64d017
ZJS
1135 log_debug("Accepting new %s connection on fd:%d", type, fd);
1136 fd2 = accept4(fd, &addr->sockaddr.sa, &addr->size, SOCK_NONBLOCK|SOCK_CLOEXEC);
4a62c710
MS
1137 if (fd2 < 0)
1138 return log_error_errno(errno, "accept() on fd:%d failed: %m", fd);
fdfccdbc 1139
cc64d017 1140 switch(socket_address_family(addr)) {
fdfccdbc
ZJS
1141 case AF_INET:
1142 case AF_INET6: {
8201af08
ZJS
1143 _cleanup_free_ char *a = NULL;
1144 char *b;
fdfccdbc 1145
cc64d017 1146 r = socket_address_print(addr, &a);
fdfccdbc 1147 if (r < 0) {
da927ba9 1148 log_error_errno(r, "socket_address_print(): %m");
fdfccdbc
ZJS
1149 close(fd2);
1150 return r;
1151 }
1152
8201af08
ZJS
1153 r = socknameinfo_pretty(&addr->sockaddr, addr->size, &b);
1154 if (r < 0) {
1938ac51 1155 log_error_errno(r, "Resolving hostname failed: %m");
8201af08
ZJS
1156 close(fd2);
1157 return r;
1158 }
1159
0e72da6f
ZJS
1160 log_debug("Accepted %s %s connection from %s",
1161 type,
1162 socket_address_family(addr) == AF_INET ? "IP" : "IPv6",
1163 a);
cc64d017 1164
8201af08
ZJS
1165 *hostname = b;
1166
cc64d017 1167 return fd2;
fdfccdbc
ZJS
1168 };
1169 default:
cc64d017
ZJS
1170 log_error("Rejected %s connection with unsupported family %d",
1171 type, socket_address_family(addr));
fdfccdbc 1172 close(fd2);
cc64d017 1173
fdfccdbc
ZJS
1174 return -EINVAL;
1175 }
cc64d017 1176}
fdfccdbc 1177
cc64d017
ZJS
1178static int dispatch_raw_connection_event(sd_event_source *event,
1179 int fd,
1180 uint32_t revents,
1181 void *userdata) {
1182 RemoteServer *s = userdata;
1f8af042 1183 int fd2;
cc64d017
ZJS
1184 SocketAddress addr = {
1185 .size = sizeof(union sockaddr_union),
1186 .type = SOCK_STREAM,
1187 };
a7f7d1bd 1188 char *hostname = NULL;
fdfccdbc 1189
8201af08 1190 fd2 = accept_connection("raw", fd, &addr, &hostname);
cc64d017
ZJS
1191 if (fd2 < 0)
1192 return fd2;
fdfccdbc 1193
1f8af042 1194 return add_source(s, fd2, hostname, true);
cc64d017 1195}
fdfccdbc
ZJS
1196
1197/**********************************************************************
1198 **********************************************************************
1199 **********************************************************************/
1200
8201af08
ZJS
1201static const char* const journal_write_split_mode_table[_JOURNAL_WRITE_SPLIT_MAX] = {
1202 [JOURNAL_WRITE_SPLIT_NONE] = "none",
1203 [JOURNAL_WRITE_SPLIT_HOST] = "host",
1204};
1205
1206DEFINE_PRIVATE_STRING_TABLE_LOOKUP(journal_write_split_mode, JournalWriteSplitMode);
1207static DEFINE_CONFIG_PARSE_ENUM(config_parse_write_split_mode,
1208 journal_write_split_mode,
1209 JournalWriteSplitMode,
1210 "Failed to parse split mode setting");
1211
ad95fd1d
ZJS
1212static int parse_config(void) {
1213 const ConfigTableItem items[] = {
9d3737f1 1214 { "Remote", "Seal", config_parse_bool, 0, &arg_seal },
8201af08
ZJS
1215 { "Remote", "SplitMode", config_parse_write_split_mode, 0, &arg_split_mode },
1216 { "Remote", "ServerKeyFile", config_parse_path, 0, &arg_key },
1217 { "Remote", "ServerCertificateFile", config_parse_path, 0, &arg_cert },
1218 { "Remote", "TrustedCertificateFile", config_parse_path, 0, &arg_trust },
ad95fd1d 1219 {}};
ad95fd1d 1220
43688c49 1221 return config_parse_many_nulstr(PKGSYSCONFDIR "/journal-remote.conf",
da412854
YW
1222 CONF_PATHS_NULSTR("systemd/journal-remote.conf.d"),
1223 "Remote\0", config_item_table_lookup, items,
bcde742e 1224 CONFIG_PARSE_WARN, NULL);
ad95fd1d
ZJS
1225}
1226
5c879495 1227static void help(void) {
8a8d55f2 1228 printf("%s [OPTIONS...] {FILE|-}...\n\n"
a83f4037 1229 "Write external journal events to journal file(s).\n\n"
a7736b14
ZJS
1230 " -h --help Show this help\n"
1231 " --version Show package version\n"
1232 " --url=URL Read events from systemd-journal-gatewayd at URL\n"
1233 " --getter=COMMAND Read events from the output of COMMAND\n"
1234 " --listen-raw=ADDR Listen for connections at ADDR\n"
1235 " --listen-http=ADDR Listen for HTTP connections at ADDR\n"
1236 " --listen-https=ADDR Listen for HTTPS connections at ADDR\n"
1237 " -o --output=FILE|DIR Write output to FILE or DIR/external-*.journal\n"
1238 " --compress[=BOOL] XZ-compress the output journal (default: yes)\n"
1239 " --seal[=BOOL] Use event sealing (default: no)\n"
1240 " --key=FILENAME SSL key in PEM format (default:\n"
1241 " \"" PRIV_KEY_FILE "\")\n"
1242 " --cert=FILENAME SSL certificate in PEM format (default:\n"
1243 " \"" CERT_FILE "\")\n"
1244 " --trust=FILENAME|all SSL CA certificate or disable checking (default:\n"
1245 " \"" TRUST_FILE "\")\n"
dad29dff 1246 " --gnutls-log=CATEGORY...\n"
a7736b14
ZJS
1247 " Specify a list of gnutls logging categories\n"
1248 " --split-mode=none|host How many output files to create\n"
fdfccdbc
ZJS
1249 "\n"
1250 "Note: file descriptors from sd_listen_fds() will be consumed, too.\n"
1251 , program_invocation_short_name);
fdfccdbc
ZJS
1252}
1253
1254static int parse_argv(int argc, char *argv[]) {
1255 enum {
1256 ARG_VERSION = 0x100,
1257 ARG_URL,
1258 ARG_LISTEN_RAW,
cc64d017
ZJS
1259 ARG_LISTEN_HTTP,
1260 ARG_LISTEN_HTTPS,
fdfccdbc 1261 ARG_GETTER,
8201af08 1262 ARG_SPLIT_MODE,
fdfccdbc 1263 ARG_COMPRESS,
fdfccdbc 1264 ARG_SEAL,
cc64d017
ZJS
1265 ARG_KEY,
1266 ARG_CERT,
1267 ARG_TRUST,
5937a4d4 1268 ARG_GNUTLS_LOG,
fdfccdbc
ZJS
1269 };
1270
1271 static const struct option options[] = {
1272 { "help", no_argument, NULL, 'h' },
1273 { "version", no_argument, NULL, ARG_VERSION },
1274 { "url", required_argument, NULL, ARG_URL },
1275 { "getter", required_argument, NULL, ARG_GETTER },
1276 { "listen-raw", required_argument, NULL, ARG_LISTEN_RAW },
cc64d017
ZJS
1277 { "listen-http", required_argument, NULL, ARG_LISTEN_HTTP },
1278 { "listen-https", required_argument, NULL, ARG_LISTEN_HTTPS },
fdfccdbc 1279 { "output", required_argument, NULL, 'o' },
8201af08 1280 { "split-mode", required_argument, NULL, ARG_SPLIT_MODE },
dad29dff
LP
1281 { "compress", optional_argument, NULL, ARG_COMPRESS },
1282 { "seal", optional_argument, NULL, ARG_SEAL },
cc64d017
ZJS
1283 { "key", required_argument, NULL, ARG_KEY },
1284 { "cert", required_argument, NULL, ARG_CERT },
1285 { "trust", required_argument, NULL, ARG_TRUST },
5937a4d4 1286 { "gnutls-log", required_argument, NULL, ARG_GNUTLS_LOG },
fdfccdbc
ZJS
1287 {}
1288 };
1289
cc64d017 1290 int c, r;
8201af08 1291 bool type_a, type_b;
fdfccdbc
ZJS
1292
1293 assert(argc >= 0);
1294 assert(argv);
1295
1296 while ((c = getopt_long(argc, argv, "ho:", options, NULL)) >= 0)
1297 switch(c) {
1298 case 'h':
1299 help();
1300 return 0 /* done */;
1301
1302 case ARG_VERSION:
3f6fd1ba 1303 return version();
fdfccdbc
ZJS
1304
1305 case ARG_URL:
1306 if (arg_url) {
1307 log_error("cannot currently set more than one --url");
1308 return -EINVAL;
1309 }
1310
1311 arg_url = optarg;
1312 break;
1313
1314 case ARG_GETTER:
1315 if (arg_getter) {
1316 log_error("cannot currently use --getter more than once");
1317 return -EINVAL;
1318 }
1319
1320 arg_getter = optarg;
1321 break;
1322
1323 case ARG_LISTEN_RAW:
1324 if (arg_listen_raw) {
1325 log_error("cannot currently use --listen-raw more than once");
1326 return -EINVAL;
1327 }
1328
1329 arg_listen_raw = optarg;
1330 break;
1331
cc64d017 1332 case ARG_LISTEN_HTTP:
8a8d55f2 1333 if (arg_listen_http || http_socket >= 0) {
cc64d017
ZJS
1334 log_error("cannot currently use --listen-http more than once");
1335 return -EINVAL;
1336 }
1337
42b6bf75 1338 r = negative_fd(optarg);
8a8d55f2
ZJS
1339 if (r >= 0)
1340 http_socket = r;
24739b7b 1341 else
8a8d55f2 1342 arg_listen_http = optarg;
cc64d017
ZJS
1343 break;
1344
1345 case ARG_LISTEN_HTTPS:
8a8d55f2 1346 if (arg_listen_https || https_socket >= 0) {
cc64d017
ZJS
1347 log_error("cannot currently use --listen-https more than once");
1348 return -EINVAL;
1349 }
1350
42b6bf75 1351 r = negative_fd(optarg);
8a8d55f2
ZJS
1352 if (r >= 0)
1353 https_socket = r;
24739b7b 1354 else
8a8d55f2 1355 arg_listen_https = optarg;
8a8d55f2 1356
cc64d017
ZJS
1357 break;
1358
1359 case ARG_KEY:
ad95fd1d 1360 if (arg_key) {
cc64d017
ZJS
1361 log_error("Key file specified twice");
1362 return -EINVAL;
1363 }
ad95fd1d
ZJS
1364
1365 arg_key = strdup(optarg);
1366 if (!arg_key)
1367 return log_oom();
1368
cc64d017
ZJS
1369 break;
1370
1371 case ARG_CERT:
ad95fd1d 1372 if (arg_cert) {
cc64d017
ZJS
1373 log_error("Certificate file specified twice");
1374 return -EINVAL;
1375 }
ad95fd1d
ZJS
1376
1377 arg_cert = strdup(optarg);
1378 if (!arg_cert)
1379 return log_oom();
1380
cc64d017
ZJS
1381 break;
1382
1383 case ARG_TRUST:
ad95fd1d
ZJS
1384 if (arg_trust || arg_trust_all) {
1385 log_error("Confusing trusted CA configuration");
cc64d017
ZJS
1386 return -EINVAL;
1387 }
ad95fd1d
ZJS
1388
1389 if (streq(optarg, "all"))
1390 arg_trust_all = true;
1391 else {
349cc4a5 1392#if HAVE_GNUTLS
ad95fd1d
ZJS
1393 arg_trust = strdup(optarg);
1394 if (!arg_trust)
1395 return log_oom();
cc64d017 1396#else
ad95fd1d
ZJS
1397 log_error("Option --trust is not available.");
1398 return -EINVAL;
cc64d017 1399#endif
ad95fd1d
ZJS
1400 }
1401
1402 break;
cc64d017 1403
fdfccdbc
ZJS
1404 case 'o':
1405 if (arg_output) {
1406 log_error("cannot use --output/-o more than once");
1407 return -EINVAL;
1408 }
1409
1410 arg_output = optarg;
1411 break;
1412
8201af08
ZJS
1413 case ARG_SPLIT_MODE:
1414 arg_split_mode = journal_write_split_mode_from_string(optarg);
1415 if (arg_split_mode == _JOURNAL_WRITE_SPLIT_INVALID) {
1416 log_error("Invalid split mode: %s", optarg);
1417 return -EINVAL;
1418 }
1419 break;
1420
fdfccdbc 1421 case ARG_COMPRESS:
dad29dff
LP
1422 if (optarg) {
1423 r = parse_boolean(optarg);
1424 if (r < 0) {
1425 log_error("Failed to parse --compress= parameter.");
1426 return -EINVAL;
1427 }
1428
1429 arg_compress = !!r;
1430 } else
1431 arg_compress = true;
1432
fdfccdbc 1433 break;
dad29dff 1434
fdfccdbc 1435 case ARG_SEAL:
dad29dff
LP
1436 if (optarg) {
1437 r = parse_boolean(optarg);
1438 if (r < 0) {
1439 log_error("Failed to parse --seal= parameter.");
1440 return -EINVAL;
1441 }
1442
1443 arg_seal = !!r;
1444 } else
1445 arg_seal = true;
1446
fdfccdbc
ZJS
1447 break;
1448
5937a4d4 1449 case ARG_GNUTLS_LOG: {
349cc4a5 1450#if HAVE_GNUTLS
39583258 1451 const char* p = optarg;
b1f48e3c
SS
1452 for (;;) {
1453 _cleanup_free_ char *word = NULL;
5937a4d4 1454
b1f48e3c
SS
1455 r = extract_first_word(&p, &word, ",", 0);
1456 if (r < 0)
1457 return log_error_errno(r, "Failed to parse --gnutls-log= argument: %m");
5937a4d4 1458
b1f48e3c
SS
1459 if (r == 0)
1460 break;
5937a4d4 1461
b1f48e3c 1462 if (strv_push(&arg_gnutls_log, word) < 0)
5937a4d4 1463 return log_oom();
b1f48e3c
SS
1464
1465 word = NULL;
5937a4d4
ZJS
1466 }
1467 break;
1468#else
1469 log_error("Option --gnutls-log is not available.");
1470 return -EINVAL;
1471#endif
1472 }
1473
fdfccdbc
ZJS
1474 case '?':
1475 return -EINVAL;
1476
1477 default:
601185b4 1478 assert_not_reached("Unknown option code.");
fdfccdbc
ZJS
1479 }
1480
8a8d55f2
ZJS
1481 if (optind < argc)
1482 arg_files = argv + optind;
fdfccdbc 1483
8201af08
ZJS
1484 type_a = arg_getter || !strv_isempty(arg_files);
1485 type_b = arg_url
1486 || arg_listen_raw
1487 || arg_listen_http || arg_listen_https
1488 || sd_listen_fds(false) > 0;
1489 if (type_a && type_b) {
1490 log_error("Cannot use file input or --getter with "
1491 "--arg-listen-... or socket activation.");
1492 return -EINVAL;
1493 }
1494 if (type_a) {
1495 if (!arg_output) {
1496 log_error("Option --output must be specified with file input or --getter.");
1497 return -EINVAL;
1498 }
1499
cb30d3ba
YW
1500 if (!IN_SET(arg_split_mode, JOURNAL_WRITE_SPLIT_NONE, _JOURNAL_WRITE_SPLIT_INVALID)) {
1501 log_error("For active sources, only --split-mode=none is allowed.");
1502 return -EINVAL;
1503 }
1504
8201af08
ZJS
1505 arg_split_mode = JOURNAL_WRITE_SPLIT_NONE;
1506 }
1507
cb30d3ba
YW
1508 if (arg_split_mode == _JOURNAL_WRITE_SPLIT_INVALID)
1509 arg_split_mode = JOURNAL_WRITE_SPLIT_HOST;
1510
6b1b9f75
YW
1511 if (arg_split_mode == JOURNAL_WRITE_SPLIT_NONE && arg_output) {
1512 if (is_dir(arg_output, true) > 0) {
1513 log_error("For SplitMode=none, output must be a file.");
1514 return -EINVAL;
1515 }
1516 if (!endswith(arg_output, ".journal")) {
1517 log_error("For SplitMode=none, output file name must end with .journal.");
1518 return -EINVAL;
1519 }
8201af08
ZJS
1520 }
1521
1522 if (arg_split_mode == JOURNAL_WRITE_SPLIT_HOST
1523 && arg_output && is_dir(arg_output, true) <= 0) {
1524 log_error("For SplitMode=host, output must be a directory.");
1525 return -EINVAL;
1526 }
1527
1528 log_debug("Full config: SplitMode=%s Key=%s Cert=%s Trust=%s",
1529 journal_write_split_mode_to_string(arg_split_mode),
1530 strna(arg_key),
1531 strna(arg_cert),
1532 strna(arg_trust));
1533
fdfccdbc
ZJS
1534 return 1 /* work to do */;
1535}
1536
ad95fd1d
ZJS
1537static int load_certificates(char **key, char **cert, char **trust) {
1538 int r;
1539
799a8f39 1540 r = read_full_file(arg_key ?: PRIV_KEY_FILE, key, NULL);
eb56eb9b
MS
1541 if (r < 0)
1542 return log_error_errno(r, "Failed to read key from file '%s': %m",
1543 arg_key ?: PRIV_KEY_FILE);
ad95fd1d
ZJS
1544
1545 r = read_full_file(arg_cert ?: CERT_FILE, cert, NULL);
eb56eb9b
MS
1546 if (r < 0)
1547 return log_error_errno(r, "Failed to read certificate from file '%s': %m",
1548 arg_cert ?: CERT_FILE);
ad95fd1d
ZJS
1549
1550 if (arg_trust_all)
1551 log_info("Certificate checking disabled.");
1552 else {
1553 r = read_full_file(arg_trust ?: TRUST_FILE, trust, NULL);
eb56eb9b
MS
1554 if (r < 0)
1555 return log_error_errno(r, "Failed to read CA certificate file '%s': %m",
1556 arg_trust ?: TRUST_FILE);
ad95fd1d
ZJS
1557 }
1558
1559 return 0;
1560}
1561
fdfccdbc
ZJS
1562int main(int argc, char **argv) {
1563 RemoteServer s = {};
9ff48d09 1564 int r;
ad95fd1d 1565 _cleanup_free_ char *key = NULL, *cert = NULL, *trust = NULL;
fdfccdbc 1566
fdfccdbc
ZJS
1567 log_show_color(true);
1568 log_parse_environment();
1569
ad95fd1d
ZJS
1570 r = parse_config();
1571 if (r < 0)
1572 return EXIT_FAILURE;
1573
fdfccdbc
ZJS
1574 r = parse_argv(argc, argv);
1575 if (r <= 0)
1576 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
1577
d357562c
ZJS
1578 if (arg_listen_http || arg_listen_https) {
1579 r = setup_gnutls_logger(arg_gnutls_log);
1580 if (r < 0)
1581 return EXIT_FAILURE;
1582 }
cc64d017 1583
8201af08
ZJS
1584 if (arg_listen_https || https_socket >= 0)
1585 if (load_certificates(&key, &cert, &trust) < 0)
1586 return EXIT_FAILURE;
ad95fd1d
ZJS
1587
1588 if (remoteserver_init(&s, key, cert, trust) < 0)
fdfccdbc
ZJS
1589 return EXIT_FAILURE;
1590
a7736b14
ZJS
1591 r = sd_event_set_watchdog(s.events, true);
1592 if (r < 0)
da927ba9 1593 log_error_errno(r, "Failed to enable watchdog: %m");
a7736b14 1594 else
2d37cd53 1595 log_debug("Watchdog is %sd.", enable_disable(r > 0));
a3152e76 1596
de0671ee 1597 log_debug("%s running as pid "PID_FMT,
df0ff127 1598 program_invocation_short_name, getpid_cached());
fdfccdbc
ZJS
1599 sd_notify(false,
1600 "READY=1\n"
1601 "STATUS=Processing requests...");
1602
1603 while (s.active) {
1604 r = sd_event_get_state(s.events);
1605 if (r < 0)
1606 break;
1607 if (r == SD_EVENT_FINISHED)
1608 break;
1609
1610 r = sd_event_run(s.events, -1);
1611 if (r < 0) {
da927ba9 1612 log_error_errno(r, "Failed to run event loop: %m");
fdfccdbc
ZJS
1613 break;
1614 }
1615 }
1616
af4ec430
LP
1617 sd_notifyf(false,
1618 "STOPPING=1\n"
1619 "STATUS=Shutting down after writing %" PRIu64 " entries...", s.event_count);
9ff48d09 1620 log_info("Finishing after writing %" PRIu64 " entries", s.event_count);
fdfccdbc 1621
af4ec430 1622 server_destroy(&s);
fdfccdbc 1623
ad95fd1d
ZJS
1624 free(arg_key);
1625 free(arg_cert);
1626 free(arg_trust);
1627
9ff48d09 1628 return r >= 0 ? EXIT_SUCCESS : EXIT_FAILURE;
fdfccdbc 1629}