]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal-remote/journal-remote-main.c
af927046c6a347eb485795fc18a76d5159425b7c
[thirdparty/systemd.git] / src / journal-remote / journal-remote-main.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <getopt.h>
4 #include <unistd.h>
5
6 #include "sd-daemon.h"
7
8 #include "conf-parser.h"
9 #include "def.h"
10 #include "fd-util.h"
11 #include "fileio.h"
12 #include "journal-remote-write.h"
13 #include "journal-remote.h"
14 #include "pretty-print.h"
15 #include "process-util.h"
16 #include "rlimit-util.h"
17 #include "signal-util.h"
18 #include "socket-util.h"
19 #include "stat-util.h"
20 #include "string-table.h"
21 #include "strv.h"
22
23 #define PRIV_KEY_FILE CERTIFICATE_ROOT "/private/journal-remote.pem"
24 #define CERT_FILE CERTIFICATE_ROOT "/certs/journal-remote.pem"
25 #define TRUST_FILE CERTIFICATE_ROOT "/ca/trusted.pem"
26
27 static char* arg_url = NULL;
28 static char* arg_getter = NULL;
29 static char* arg_listen_raw = NULL;
30 static char* arg_listen_http = NULL;
31 static char* arg_listen_https = NULL;
32 static char** arg_files = NULL;
33 static int arg_compress = true;
34 static int arg_seal = false;
35 static int http_socket = -1, https_socket = -1;
36 static char** arg_gnutls_log = NULL;
37
38 static JournalWriteSplitMode arg_split_mode = _JOURNAL_WRITE_SPLIT_INVALID;
39 static char* arg_output = NULL;
40
41 static char *arg_key = NULL;
42 static char *arg_cert = NULL;
43 static char *arg_trust = NULL;
44 static bool arg_trust_all = false;
45
46 static const char* const journal_write_split_mode_table[_JOURNAL_WRITE_SPLIT_MAX] = {
47 [JOURNAL_WRITE_SPLIT_NONE] = "none",
48 [JOURNAL_WRITE_SPLIT_HOST] = "host",
49 };
50
51 DEFINE_PRIVATE_STRING_TABLE_LOOKUP(journal_write_split_mode, JournalWriteSplitMode);
52 static DEFINE_CONFIG_PARSE_ENUM(config_parse_write_split_mode,
53 journal_write_split_mode,
54 JournalWriteSplitMode,
55 "Failed to parse split mode setting");
56
57 /**********************************************************************
58 **********************************************************************
59 **********************************************************************/
60
61 static int spawn_child(const char* child, char** argv) {
62 pid_t child_pid;
63 int fd[2], r;
64
65 if (pipe(fd) < 0)
66 return log_error_errno(errno, "Failed to create pager pipe: %m");
67
68 r = safe_fork("(remote)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &child_pid);
69 if (r < 0) {
70 safe_close_pair(fd);
71 return r;
72 }
73
74 /* In the child */
75 if (r == 0) {
76 safe_close(fd[0]);
77
78 r = rearrange_stdio(STDIN_FILENO, fd[1], STDERR_FILENO);
79 if (r < 0) {
80 log_error_errno(r, "Failed to dup pipe to stdout: %m");
81 _exit(EXIT_FAILURE);
82 }
83
84 execvp(child, argv);
85 log_error_errno(errno, "Failed to exec child %s: %m", child);
86 _exit(EXIT_FAILURE);
87 }
88
89 safe_close(fd[1]);
90
91 r = fd_nonblock(fd[0], true);
92 if (r < 0)
93 log_warning_errno(errno, "Failed to set child pipe to non-blocking: %m");
94
95 return fd[0];
96 }
97
98 static int spawn_curl(const char* url) {
99 char **argv = STRV_MAKE("curl",
100 "-HAccept: application/vnd.fdo.journal",
101 "--silent",
102 "--show-error",
103 url);
104 int r;
105
106 r = spawn_child("curl", argv);
107 if (r < 0)
108 log_error_errno(r, "Failed to spawn curl: %m");
109 return r;
110 }
111
112 static int spawn_getter(const char *getter) {
113 int r;
114 _cleanup_strv_free_ char **words = NULL;
115
116 assert(getter);
117 r = strv_split_extract(&words, getter, WHITESPACE, EXTRACT_QUOTES);
118 if (r < 0)
119 return log_error_errno(r, "Failed to split getter option: %m");
120
121 r = spawn_child(words[0], words);
122 if (r < 0)
123 log_error_errno(r, "Failed to spawn getter %s: %m", getter);
124
125 return r;
126 }
127
128 /**********************************************************************
129 **********************************************************************
130 **********************************************************************/
131
132 static int null_timer_event_handler(sd_event_source *s,
133 uint64_t usec,
134 void *userdata);
135 static int dispatch_http_event(sd_event_source *event,
136 int fd,
137 uint32_t revents,
138 void *userdata);
139
140 static int request_meta(void **connection_cls, int fd, char *hostname) {
141 RemoteSource *source;
142 Writer *writer;
143 int r;
144
145 assert(connection_cls);
146 if (*connection_cls)
147 return 0;
148
149 r = journal_remote_get_writer(journal_remote_server_global, hostname, &writer);
150 if (r < 0)
151 return log_warning_errno(r, "Failed to get writer for source %s: %m",
152 hostname);
153
154 source = source_new(fd, true, hostname, writer);
155 if (!source) {
156 writer_unref(writer);
157 return log_oom();
158 }
159
160 log_debug("Added RemoteSource as connection metadata %p", source);
161
162 *connection_cls = source;
163 return 0;
164 }
165
166 static void request_meta_free(void *cls,
167 struct MHD_Connection *connection,
168 void **connection_cls,
169 enum MHD_RequestTerminationCode toe) {
170 RemoteSource *s;
171
172 assert(connection_cls);
173 s = *connection_cls;
174
175 if (s) {
176 log_debug("Cleaning up connection metadata %p", s);
177 source_free(s);
178 *connection_cls = NULL;
179 }
180 }
181
182 static int process_http_upload(
183 struct MHD_Connection *connection,
184 const char *upload_data,
185 size_t *upload_data_size,
186 RemoteSource *source) {
187
188 bool finished = false;
189 size_t remaining;
190 int r;
191
192 assert(source);
193
194 log_trace("%s: connection %p, %zu bytes",
195 __func__, connection, *upload_data_size);
196
197 if (*upload_data_size) {
198 log_trace("Received %zu bytes", *upload_data_size);
199
200 r = journal_importer_push_data(&source->importer,
201 upload_data, *upload_data_size);
202 if (r < 0)
203 return mhd_respond_oom(connection);
204
205 *upload_data_size = 0;
206 } else
207 finished = true;
208
209 for (;;) {
210 r = process_source(source,
211 journal_remote_server_global->compress,
212 journal_remote_server_global->seal);
213 if (r == -EAGAIN)
214 break;
215 else if (r < 0) {
216 log_warning("Failed to process data for connection %p", connection);
217 if (r == -E2BIG)
218 return mhd_respondf(connection,
219 r, MHD_HTTP_PAYLOAD_TOO_LARGE,
220 "Entry is too large, maximum is " STRINGIFY(DATA_SIZE_MAX) " bytes.");
221 else
222 return mhd_respondf(connection,
223 r, MHD_HTTP_UNPROCESSABLE_ENTITY,
224 "Processing failed: %m.");
225 }
226 }
227
228 if (!finished)
229 return MHD_YES;
230
231 /* The upload is finished */
232
233 remaining = journal_importer_bytes_remaining(&source->importer);
234 if (remaining > 0) {
235 log_warning("Premature EOF byte. %zu bytes lost.", remaining);
236 return mhd_respondf(connection,
237 0, MHD_HTTP_EXPECTATION_FAILED,
238 "Premature EOF. %zu bytes of trailing data not processed.",
239 remaining);
240 }
241
242 return mhd_respond(connection, MHD_HTTP_ACCEPTED, "OK.");
243 };
244
245 static int request_handler(
246 void *cls,
247 struct MHD_Connection *connection,
248 const char *url,
249 const char *method,
250 const char *version,
251 const char *upload_data,
252 size_t *upload_data_size,
253 void **connection_cls) {
254
255 const char *header;
256 int r, code, fd;
257 _cleanup_free_ char *hostname = NULL;
258
259 assert(connection);
260 assert(connection_cls);
261 assert(url);
262 assert(method);
263
264 log_trace("Handling a connection %s %s %s", method, url, version);
265
266 if (*connection_cls)
267 return process_http_upload(connection,
268 upload_data, upload_data_size,
269 *connection_cls);
270
271 if (!streq(method, "POST"))
272 return mhd_respond(connection, MHD_HTTP_NOT_ACCEPTABLE, "Unsupported method.");
273
274 if (!streq(url, "/upload"))
275 return mhd_respond(connection, MHD_HTTP_NOT_FOUND, "Not found.");
276
277 header = MHD_lookup_connection_value(connection,
278 MHD_HEADER_KIND, "Content-Type");
279 if (!header || !streq(header, "application/vnd.fdo.journal"))
280 return mhd_respond(connection, MHD_HTTP_UNSUPPORTED_MEDIA_TYPE,
281 "Content-Type: application/vnd.fdo.journal is required.");
282
283 {
284 const union MHD_ConnectionInfo *ci;
285
286 ci = MHD_get_connection_info(connection,
287 MHD_CONNECTION_INFO_CONNECTION_FD);
288 if (!ci) {
289 log_error("MHD_get_connection_info failed: cannot get remote fd");
290 return mhd_respond(connection, MHD_HTTP_INTERNAL_SERVER_ERROR,
291 "Cannot check remote address.");
292 }
293
294 fd = ci->connect_fd;
295 assert(fd >= 0);
296 }
297
298 if (journal_remote_server_global->check_trust) {
299 r = check_permissions(connection, &code, &hostname);
300 if (r < 0)
301 return code;
302 } else {
303 r = getpeername_pretty(fd, false, &hostname);
304 if (r < 0)
305 return mhd_respond(connection, MHD_HTTP_INTERNAL_SERVER_ERROR,
306 "Cannot check remote hostname.");
307 }
308
309 assert(hostname);
310
311 r = request_meta(connection_cls, fd, hostname);
312 if (r == -ENOMEM)
313 return respond_oom(connection);
314 else if (r < 0)
315 return mhd_respondf(connection, r, MHD_HTTP_INTERNAL_SERVER_ERROR, "%m");
316
317 hostname = NULL;
318 return MHD_YES;
319 }
320
321 static int setup_microhttpd_server(RemoteServer *s,
322 int fd,
323 const char *key,
324 const char *cert,
325 const char *trust) {
326 struct MHD_OptionItem opts[] = {
327 { MHD_OPTION_NOTIFY_COMPLETED, (intptr_t) request_meta_free},
328 { MHD_OPTION_EXTERNAL_LOGGER, (intptr_t) microhttpd_logger},
329 { MHD_OPTION_LISTEN_SOCKET, fd},
330 { MHD_OPTION_CONNECTION_MEMORY_LIMIT, 128*1024},
331 { MHD_OPTION_END},
332 { MHD_OPTION_END},
333 { MHD_OPTION_END},
334 { MHD_OPTION_END},
335 { MHD_OPTION_END}};
336 int opts_pos = 4;
337 int flags =
338 MHD_USE_DEBUG |
339 MHD_USE_DUAL_STACK |
340 MHD_USE_EPOLL |
341 MHD_USE_ITC;
342
343 const union MHD_DaemonInfo *info;
344 int r, epoll_fd;
345 MHDDaemonWrapper *d;
346
347 assert(fd >= 0);
348
349 r = fd_nonblock(fd, true);
350 if (r < 0)
351 return log_error_errno(r, "Failed to make fd:%d nonblocking: %m", fd);
352
353 /* MHD_OPTION_STRICT_FOR_CLIENT is introduced in microhttpd 0.9.54,
354 * and MHD_USE_PEDANTIC_CHECKS will be deprecated in future.
355 * If MHD_USE_PEDANTIC_CHECKS is '#define'd, then it is deprecated
356 * and we should use MHD_OPTION_STRICT_FOR_CLIENT. On the other hand,
357 * if MHD_USE_PEDANTIC_CHECKS is not '#define'd, then it is not
358 * deprecated yet and there exists an enum element with the same name.
359 * So we can safely use it. */
360 #ifdef MHD_USE_PEDANTIC_CHECKS
361 opts[opts_pos++] = (struct MHD_OptionItem)
362 {MHD_OPTION_STRICT_FOR_CLIENT, 1};
363 #else
364 flags |= MHD_USE_PEDANTIC_CHECKS;
365 #endif
366
367 if (key) {
368 assert(cert);
369
370 opts[opts_pos++] = (struct MHD_OptionItem)
371 {MHD_OPTION_HTTPS_MEM_KEY, 0, (char*) key};
372 opts[opts_pos++] = (struct MHD_OptionItem)
373 {MHD_OPTION_HTTPS_MEM_CERT, 0, (char*) cert};
374
375 flags |= MHD_USE_TLS;
376
377 if (trust)
378 opts[opts_pos++] = (struct MHD_OptionItem)
379 {MHD_OPTION_HTTPS_MEM_TRUST, 0, (char*) trust};
380 }
381
382 d = new(MHDDaemonWrapper, 1);
383 if (!d)
384 return log_oom();
385
386 d->fd = (uint64_t) fd;
387
388 d->daemon = MHD_start_daemon(flags, 0,
389 NULL, NULL,
390 request_handler, NULL,
391 MHD_OPTION_ARRAY, opts,
392 MHD_OPTION_END);
393 if (!d->daemon) {
394 log_error("Failed to start µhttp daemon");
395 r = -EINVAL;
396 goto error;
397 }
398
399 log_debug("Started MHD %s daemon on fd:%d (wrapper @ %p)",
400 key ? "HTTPS" : "HTTP", fd, d);
401
402 info = MHD_get_daemon_info(d->daemon, MHD_DAEMON_INFO_EPOLL_FD_LINUX_ONLY);
403 if (!info) {
404 log_error("µhttp returned NULL daemon info");
405 r = -EOPNOTSUPP;
406 goto error;
407 }
408
409 epoll_fd = info->listen_fd;
410 if (epoll_fd < 0) {
411 log_error("µhttp epoll fd is invalid");
412 r = -EUCLEAN;
413 goto error;
414 }
415
416 r = sd_event_add_io(s->events, &d->io_event,
417 epoll_fd, EPOLLIN,
418 dispatch_http_event, d);
419 if (r < 0) {
420 log_error_errno(r, "Failed to add event callback: %m");
421 goto error;
422 }
423
424 r = sd_event_source_set_description(d->io_event, "io_event");
425 if (r < 0) {
426 log_error_errno(r, "Failed to set source name: %m");
427 goto error;
428 }
429
430 r = sd_event_add_time(s->events, &d->timer_event,
431 CLOCK_MONOTONIC, (uint64_t) -1, 0,
432 null_timer_event_handler, d);
433 if (r < 0) {
434 log_error_errno(r, "Failed to add timer_event: %m");
435 goto error;
436 }
437
438 r = sd_event_source_set_description(d->timer_event, "timer_event");
439 if (r < 0) {
440 log_error_errno(r, "Failed to set source name: %m");
441 goto error;
442 }
443
444 r = hashmap_ensure_allocated(&s->daemons, &uint64_hash_ops);
445 if (r < 0) {
446 log_oom();
447 goto error;
448 }
449
450 r = hashmap_put(s->daemons, &d->fd, d);
451 if (r < 0) {
452 log_error_errno(r, "Failed to add daemon to hashmap: %m");
453 goto error;
454 }
455
456 s->active++;
457 return 0;
458
459 error:
460 MHD_stop_daemon(d->daemon);
461 free(d->daemon);
462 free(d);
463 return r;
464 }
465
466 static int setup_microhttpd_socket(RemoteServer *s,
467 const char *address,
468 const char *key,
469 const char *cert,
470 const char *trust) {
471 int fd;
472
473 fd = make_socket_fd(LOG_DEBUG, address, SOCK_STREAM, SOCK_CLOEXEC);
474 if (fd < 0)
475 return fd;
476
477 return setup_microhttpd_server(s, fd, key, cert, trust);
478 }
479
480 static int null_timer_event_handler(sd_event_source *timer_event,
481 uint64_t usec,
482 void *userdata) {
483 return dispatch_http_event(timer_event, 0, 0, userdata);
484 }
485
486 static int dispatch_http_event(sd_event_source *event,
487 int fd,
488 uint32_t revents,
489 void *userdata) {
490 MHDDaemonWrapper *d = userdata;
491 int r;
492 MHD_UNSIGNED_LONG_LONG timeout = ULONG_LONG_MAX;
493
494 assert(d);
495
496 r = MHD_run(d->daemon);
497 if (r == MHD_NO) {
498 log_error("MHD_run failed!");
499 // XXX: unregister daemon
500 return -EINVAL;
501 }
502 if (MHD_get_timeout(d->daemon, &timeout) == MHD_NO)
503 timeout = ULONG_LONG_MAX;
504
505 r = sd_event_source_set_time(d->timer_event, timeout);
506 if (r < 0) {
507 log_warning_errno(r, "Unable to set event loop timeout: %m, this may result in indefinite blocking!");
508 return 1;
509 }
510
511 r = sd_event_source_set_enabled(d->timer_event, SD_EVENT_ON);
512 if (r < 0)
513 log_warning_errno(r, "Unable to enable timer_event: %m, this may result in indefinite blocking!");
514
515 return 1; /* work to do */
516 }
517
518 /**********************************************************************
519 **********************************************************************
520 **********************************************************************/
521
522 static int setup_signals(RemoteServer *s) {
523 int r;
524
525 assert(s);
526
527 assert_se(sigprocmask_many(SIG_SETMASK, NULL, SIGINT, SIGTERM, -1) >= 0);
528
529 r = sd_event_add_signal(s->events, &s->sigterm_event, SIGTERM, NULL, s);
530 if (r < 0)
531 return r;
532
533 r = sd_event_add_signal(s->events, &s->sigint_event, SIGINT, NULL, s);
534 if (r < 0)
535 return r;
536
537 return 0;
538 }
539
540 static int setup_raw_socket(RemoteServer *s, const char *address) {
541 int fd;
542
543 fd = make_socket_fd(LOG_INFO, address, SOCK_STREAM, SOCK_CLOEXEC);
544 if (fd < 0)
545 return fd;
546
547 return journal_remote_add_raw_socket(s, fd);
548 }
549
550 static int create_remoteserver(
551 RemoteServer *s,
552 const char* key,
553 const char* cert,
554 const char* trust) {
555
556 int r, n, fd;
557 char **file;
558
559 r = journal_remote_server_init(s, arg_output, arg_split_mode, arg_compress, arg_seal);
560 if (r < 0)
561 return r;
562
563 r = setup_signals(s);
564 if (r < 0)
565 return log_error_errno(r, "Failed to set up signals: %m");
566
567 n = sd_listen_fds(true);
568 if (n < 0)
569 return log_error_errno(n, "Failed to read listening file descriptors from environment: %m");
570 else
571 log_debug("Received %d descriptors", n);
572
573 if (MAX(http_socket, https_socket) >= SD_LISTEN_FDS_START + n) {
574 log_error("Received fewer sockets than expected");
575 return -EBADFD;
576 }
577
578 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
579 if (sd_is_socket(fd, AF_UNSPEC, 0, true)) {
580 log_debug("Received a listening socket (fd:%d)", fd);
581
582 if (fd == http_socket)
583 r = setup_microhttpd_server(s, fd, NULL, NULL, NULL);
584 else if (fd == https_socket)
585 r = setup_microhttpd_server(s, fd, key, cert, trust);
586 else
587 r = journal_remote_add_raw_socket(s, fd);
588 } else if (sd_is_socket(fd, AF_UNSPEC, 0, false)) {
589 char *hostname;
590
591 r = getpeername_pretty(fd, false, &hostname);
592 if (r < 0)
593 return log_error_errno(r, "Failed to retrieve remote name: %m");
594
595 log_debug("Received a connection socket (fd:%d) from %s", fd, hostname);
596
597 r = journal_remote_add_source(s, fd, hostname, true);
598 } else {
599 log_error("Unknown socket passed on fd:%d", fd);
600
601 return -EINVAL;
602 }
603
604 if (r < 0)
605 return log_error_errno(r, "Failed to register socket (fd:%d): %m",
606 fd);
607 }
608
609 if (arg_getter) {
610 log_info("Spawning getter %s...", arg_getter);
611 fd = spawn_getter(arg_getter);
612 if (fd < 0)
613 return fd;
614
615 r = journal_remote_add_source(s, fd, (char*) arg_output, false);
616 if (r < 0)
617 return r;
618 }
619
620 if (arg_url) {
621 const char *url;
622 char *hostname, *p;
623
624 if (!strstr(arg_url, "/entries")) {
625 if (endswith(arg_url, "/"))
626 url = strjoina(arg_url, "entries");
627 else
628 url = strjoina(arg_url, "/entries");
629 }
630 else
631 url = strdupa(arg_url);
632
633 log_info("Spawning curl %s...", url);
634 fd = spawn_curl(url);
635 if (fd < 0)
636 return fd;
637
638 hostname =
639 startswith(arg_url, "https://") ?:
640 startswith(arg_url, "http://") ?:
641 arg_url;
642
643 hostname = strdupa(hostname);
644 if ((p = strchr(hostname, '/')))
645 *p = '\0';
646 if ((p = strchr(hostname, ':')))
647 *p = '\0';
648
649 r = journal_remote_add_source(s, fd, hostname, false);
650 if (r < 0)
651 return r;
652 }
653
654 if (arg_listen_raw) {
655 log_debug("Listening on a socket...");
656 r = setup_raw_socket(s, arg_listen_raw);
657 if (r < 0)
658 return r;
659 }
660
661 if (arg_listen_http) {
662 r = setup_microhttpd_socket(s, arg_listen_http, NULL, NULL, NULL);
663 if (r < 0)
664 return r;
665 }
666
667 if (arg_listen_https) {
668 r = setup_microhttpd_socket(s, arg_listen_https, key, cert, trust);
669 if (r < 0)
670 return r;
671 }
672
673 STRV_FOREACH(file, arg_files) {
674 const char *output_name;
675
676 if (streq(*file, "-")) {
677 log_debug("Using standard input as source.");
678
679 fd = STDIN_FILENO;
680 output_name = "stdin";
681 } else {
682 log_debug("Reading file %s...", *file);
683
684 fd = open(*file, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
685 if (fd < 0)
686 return log_error_errno(errno, "Failed to open %s: %m", *file);
687 output_name = *file;
688 }
689
690 r = journal_remote_add_source(s, fd, (char*) output_name, false);
691 if (r < 0)
692 return r;
693 }
694
695 if (s->active == 0) {
696 log_error("Zero sources specified");
697 return -EINVAL;
698 }
699
700 if (arg_split_mode == JOURNAL_WRITE_SPLIT_NONE) {
701 /* In this case we know what the writer will be
702 called, so we can create it and verify that we can
703 create output as expected. */
704 r = journal_remote_get_writer(s, NULL, &s->_single_writer);
705 if (r < 0)
706 return r;
707 }
708
709 return 0;
710 }
711
712 static int negative_fd(const char *spec) {
713 /* Return a non-positive number as its inverse, -EINVAL otherwise. */
714
715 int fd, r;
716
717 r = safe_atoi(spec, &fd);
718 if (r < 0)
719 return r;
720
721 if (fd > 0)
722 return -EINVAL;
723 else
724 return -fd;
725 }
726
727 static int parse_config(void) {
728 const ConfigTableItem items[] = {
729 { "Remote", "Seal", config_parse_bool, 0, &arg_seal },
730 { "Remote", "SplitMode", config_parse_write_split_mode, 0, &arg_split_mode },
731 { "Remote", "ServerKeyFile", config_parse_path, 0, &arg_key },
732 { "Remote", "ServerCertificateFile", config_parse_path, 0, &arg_cert },
733 { "Remote", "TrustedCertificateFile", config_parse_path, 0, &arg_trust },
734 {}
735 };
736
737 return config_parse_many_nulstr(PKGSYSCONFDIR "/journal-remote.conf",
738 CONF_PATHS_NULSTR("systemd/journal-remote.conf.d"),
739 "Remote\0", config_item_table_lookup, items,
740 CONFIG_PARSE_WARN, NULL);
741 }
742
743 static int help(void) {
744 _cleanup_free_ char *link = NULL;
745 int r;
746
747 r = terminal_urlify_man("systemd-journal-remote.service", "8", &link);
748 if (r < 0)
749 return log_oom();
750
751 printf("%s [OPTIONS...] {FILE|-}...\n\n"
752 "Write external journal events to journal file(s).\n\n"
753 " -h --help Show this help\n"
754 " --version Show package version\n"
755 " --url=URL Read events from systemd-journal-gatewayd at URL\n"
756 " --getter=COMMAND Read events from the output of COMMAND\n"
757 " --listen-raw=ADDR Listen for connections at ADDR\n"
758 " --listen-http=ADDR Listen for HTTP connections at ADDR\n"
759 " --listen-https=ADDR Listen for HTTPS connections at ADDR\n"
760 " -o --output=FILE|DIR Write output to FILE or DIR/external-*.journal\n"
761 " --compress[=BOOL] XZ-compress the output journal (default: yes)\n"
762 " --seal[=BOOL] Use event sealing (default: no)\n"
763 " --key=FILENAME SSL key in PEM format (default:\n"
764 " \"" PRIV_KEY_FILE "\")\n"
765 " --cert=FILENAME SSL certificate in PEM format (default:\n"
766 " \"" CERT_FILE "\")\n"
767 " --trust=FILENAME|all SSL CA certificate or disable checking (default:\n"
768 " \"" TRUST_FILE "\")\n"
769 " --gnutls-log=CATEGORY...\n"
770 " Specify a list of gnutls logging categories\n"
771 " --split-mode=none|host How many output files to create\n"
772 "\nNote: file descriptors from sd_listen_fds() will be consumed, too.\n"
773 "\nSee the %s for details.\n"
774 , program_invocation_short_name
775 , link
776 );
777
778 return 0;
779 }
780
781 static int parse_argv(int argc, char *argv[]) {
782 enum {
783 ARG_VERSION = 0x100,
784 ARG_URL,
785 ARG_LISTEN_RAW,
786 ARG_LISTEN_HTTP,
787 ARG_LISTEN_HTTPS,
788 ARG_GETTER,
789 ARG_SPLIT_MODE,
790 ARG_COMPRESS,
791 ARG_SEAL,
792 ARG_KEY,
793 ARG_CERT,
794 ARG_TRUST,
795 ARG_GNUTLS_LOG,
796 };
797
798 static const struct option options[] = {
799 { "help", no_argument, NULL, 'h' },
800 { "version", no_argument, NULL, ARG_VERSION },
801 { "url", required_argument, NULL, ARG_URL },
802 { "getter", required_argument, NULL, ARG_GETTER },
803 { "listen-raw", required_argument, NULL, ARG_LISTEN_RAW },
804 { "listen-http", required_argument, NULL, ARG_LISTEN_HTTP },
805 { "listen-https", required_argument, NULL, ARG_LISTEN_HTTPS },
806 { "output", required_argument, NULL, 'o' },
807 { "split-mode", required_argument, NULL, ARG_SPLIT_MODE },
808 { "compress", optional_argument, NULL, ARG_COMPRESS },
809 { "seal", optional_argument, NULL, ARG_SEAL },
810 { "key", required_argument, NULL, ARG_KEY },
811 { "cert", required_argument, NULL, ARG_CERT },
812 { "trust", required_argument, NULL, ARG_TRUST },
813 { "gnutls-log", required_argument, NULL, ARG_GNUTLS_LOG },
814 {}
815 };
816
817 int c, r;
818 bool type_a, type_b;
819
820 assert(argc >= 0);
821 assert(argv);
822
823 while ((c = getopt_long(argc, argv, "ho:", options, NULL)) >= 0)
824 switch(c) {
825
826 case 'h':
827 return help();
828
829 case ARG_VERSION:
830 return version();
831
832 case ARG_URL:
833 if (arg_url) {
834 log_error("cannot currently set more than one --url");
835 return -EINVAL;
836 }
837
838 arg_url = optarg;
839 break;
840
841 case ARG_GETTER:
842 if (arg_getter) {
843 log_error("cannot currently use --getter more than once");
844 return -EINVAL;
845 }
846
847 arg_getter = optarg;
848 break;
849
850 case ARG_LISTEN_RAW:
851 if (arg_listen_raw) {
852 log_error("cannot currently use --listen-raw more than once");
853 return -EINVAL;
854 }
855
856 arg_listen_raw = optarg;
857 break;
858
859 case ARG_LISTEN_HTTP:
860 if (arg_listen_http || http_socket >= 0) {
861 log_error("cannot currently use --listen-http more than once");
862 return -EINVAL;
863 }
864
865 r = negative_fd(optarg);
866 if (r >= 0)
867 http_socket = r;
868 else
869 arg_listen_http = optarg;
870 break;
871
872 case ARG_LISTEN_HTTPS:
873 if (arg_listen_https || https_socket >= 0) {
874 log_error("cannot currently use --listen-https more than once");
875 return -EINVAL;
876 }
877
878 r = negative_fd(optarg);
879 if (r >= 0)
880 https_socket = r;
881 else
882 arg_listen_https = optarg;
883
884 break;
885
886 case ARG_KEY:
887 if (arg_key) {
888 log_error("Key file specified twice");
889 return -EINVAL;
890 }
891
892 arg_key = strdup(optarg);
893 if (!arg_key)
894 return log_oom();
895
896 break;
897
898 case ARG_CERT:
899 if (arg_cert) {
900 log_error("Certificate file specified twice");
901 return -EINVAL;
902 }
903
904 arg_cert = strdup(optarg);
905 if (!arg_cert)
906 return log_oom();
907
908 break;
909
910 case ARG_TRUST:
911 if (arg_trust || arg_trust_all) {
912 log_error("Confusing trusted CA configuration");
913 return -EINVAL;
914 }
915
916 if (streq(optarg, "all"))
917 arg_trust_all = true;
918 else {
919 #if HAVE_GNUTLS
920 arg_trust = strdup(optarg);
921 if (!arg_trust)
922 return log_oom();
923 #else
924 log_error("Option --trust is not available.");
925 return -EINVAL;
926 #endif
927 }
928
929 break;
930
931 case 'o':
932 if (arg_output) {
933 log_error("cannot use --output/-o more than once");
934 return -EINVAL;
935 }
936
937 arg_output = optarg;
938 break;
939
940 case ARG_SPLIT_MODE:
941 arg_split_mode = journal_write_split_mode_from_string(optarg);
942 if (arg_split_mode == _JOURNAL_WRITE_SPLIT_INVALID) {
943 log_error("Invalid split mode: %s", optarg);
944 return -EINVAL;
945 }
946 break;
947
948 case ARG_COMPRESS:
949 if (optarg) {
950 r = parse_boolean(optarg);
951 if (r < 0) {
952 log_error("Failed to parse --compress= parameter.");
953 return -EINVAL;
954 }
955
956 arg_compress = !!r;
957 } else
958 arg_compress = true;
959
960 break;
961
962 case ARG_SEAL:
963 if (optarg) {
964 r = parse_boolean(optarg);
965 if (r < 0) {
966 log_error("Failed to parse --seal= parameter.");
967 return -EINVAL;
968 }
969
970 arg_seal = !!r;
971 } else
972 arg_seal = true;
973
974 break;
975
976 case ARG_GNUTLS_LOG: {
977 #if HAVE_GNUTLS
978 const char* p = optarg;
979 for (;;) {
980 _cleanup_free_ char *word = NULL;
981
982 r = extract_first_word(&p, &word, ",", 0);
983 if (r < 0)
984 return log_error_errno(r, "Failed to parse --gnutls-log= argument: %m");
985
986 if (r == 0)
987 break;
988
989 if (strv_push(&arg_gnutls_log, word) < 0)
990 return log_oom();
991
992 word = NULL;
993 }
994 break;
995 #else
996 log_error("Option --gnutls-log is not available.");
997 return -EINVAL;
998 #endif
999 }
1000
1001 case '?':
1002 return -EINVAL;
1003
1004 default:
1005 assert_not_reached("Unknown option code.");
1006 }
1007
1008 if (optind < argc)
1009 arg_files = argv + optind;
1010
1011 type_a = arg_getter || !strv_isempty(arg_files);
1012 type_b = arg_url
1013 || arg_listen_raw
1014 || arg_listen_http || arg_listen_https
1015 || sd_listen_fds(false) > 0;
1016 if (type_a && type_b) {
1017 log_error("Cannot use file input or --getter with "
1018 "--arg-listen-... or socket activation.");
1019 return -EINVAL;
1020 }
1021 if (type_a) {
1022 if (!arg_output) {
1023 log_error("Option --output must be specified with file input or --getter.");
1024 return -EINVAL;
1025 }
1026
1027 if (!IN_SET(arg_split_mode, JOURNAL_WRITE_SPLIT_NONE, _JOURNAL_WRITE_SPLIT_INVALID)) {
1028 log_error("For active sources, only --split-mode=none is allowed.");
1029 return -EINVAL;
1030 }
1031
1032 arg_split_mode = JOURNAL_WRITE_SPLIT_NONE;
1033 }
1034
1035 if (arg_split_mode == _JOURNAL_WRITE_SPLIT_INVALID)
1036 arg_split_mode = JOURNAL_WRITE_SPLIT_HOST;
1037
1038 if (arg_split_mode == JOURNAL_WRITE_SPLIT_NONE && arg_output) {
1039 if (is_dir(arg_output, true) > 0) {
1040 log_error("For SplitMode=none, output must be a file.");
1041 return -EINVAL;
1042 }
1043 if (!endswith(arg_output, ".journal")) {
1044 log_error("For SplitMode=none, output file name must end with .journal.");
1045 return -EINVAL;
1046 }
1047 }
1048
1049 if (arg_split_mode == JOURNAL_WRITE_SPLIT_HOST
1050 && arg_output && is_dir(arg_output, true) <= 0) {
1051 log_error("For SplitMode=host, output must be a directory.");
1052 return -EINVAL;
1053 }
1054
1055 log_debug("Full config: SplitMode=%s Key=%s Cert=%s Trust=%s",
1056 journal_write_split_mode_to_string(arg_split_mode),
1057 strna(arg_key),
1058 strna(arg_cert),
1059 strna(arg_trust));
1060
1061 return 1 /* work to do */;
1062 }
1063
1064 static int load_certificates(char **key, char **cert, char **trust) {
1065 int r;
1066
1067 r = read_full_file(arg_key ?: PRIV_KEY_FILE, key, NULL);
1068 if (r < 0)
1069 return log_error_errno(r, "Failed to read key from file '%s': %m",
1070 arg_key ?: PRIV_KEY_FILE);
1071
1072 r = read_full_file(arg_cert ?: CERT_FILE, cert, NULL);
1073 if (r < 0)
1074 return log_error_errno(r, "Failed to read certificate from file '%s': %m",
1075 arg_cert ?: CERT_FILE);
1076
1077 if (arg_trust_all)
1078 log_info("Certificate checking disabled.");
1079 else {
1080 r = read_full_file(arg_trust ?: TRUST_FILE, trust, NULL);
1081 if (r < 0)
1082 return log_error_errno(r, "Failed to read CA certificate file '%s': %m",
1083 arg_trust ?: TRUST_FILE);
1084 }
1085
1086 if ((arg_listen_raw || arg_listen_http) && *trust) {
1087 log_error("Option --trust makes all non-HTTPS connections untrusted.");
1088 return -EINVAL;
1089 }
1090
1091 return 0;
1092 }
1093
1094 int main(int argc, char **argv) {
1095 RemoteServer s = {};
1096 int r;
1097 _cleanup_free_ char *key = NULL, *cert = NULL, *trust = NULL;
1098
1099 log_show_color(true);
1100 log_parse_environment();
1101
1102 /* The journal merging logic potentially needs a lot of fds. */
1103 (void) rlimit_nofile_bump(HIGH_RLIMIT_NOFILE);
1104
1105 r = parse_config();
1106 if (r < 0)
1107 return EXIT_FAILURE;
1108
1109 r = parse_argv(argc, argv);
1110 if (r <= 0)
1111 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
1112
1113 if (arg_listen_http || arg_listen_https) {
1114 r = setup_gnutls_logger(arg_gnutls_log);
1115 if (r < 0)
1116 return EXIT_FAILURE;
1117 }
1118
1119 if (arg_listen_https || https_socket >= 0) {
1120 if (load_certificates(&key, &cert, &trust) < 0)
1121 return EXIT_FAILURE;
1122 s.check_trust = !arg_trust_all;
1123 }
1124
1125 if (create_remoteserver(&s, key, cert, trust) < 0)
1126 return EXIT_FAILURE;
1127
1128 r = sd_event_set_watchdog(s.events, true);
1129 if (r < 0)
1130 log_error_errno(r, "Failed to enable watchdog: %m");
1131 else
1132 log_debug("Watchdog is %sd.", enable_disable(r > 0));
1133
1134 log_debug("%s running as pid "PID_FMT,
1135 program_invocation_short_name, getpid_cached());
1136 sd_notify(false,
1137 "READY=1\n"
1138 "STATUS=Processing requests...");
1139
1140 while (s.active) {
1141 r = sd_event_get_state(s.events);
1142 if (r < 0)
1143 break;
1144 if (r == SD_EVENT_FINISHED)
1145 break;
1146
1147 r = sd_event_run(s.events, -1);
1148 if (r < 0) {
1149 log_error_errno(r, "Failed to run event loop: %m");
1150 break;
1151 }
1152 }
1153
1154 sd_notifyf(false,
1155 "STOPPING=1\n"
1156 "STATUS=Shutting down after writing %" PRIu64 " entries...", s.event_count);
1157 log_info("Finishing after writing %" PRIu64 " entries", s.event_count);
1158
1159 journal_remote_server_destroy(&s);
1160
1161 free(arg_key);
1162 free(arg_cert);
1163 free(arg_trust);
1164
1165 return r >= 0 ? EXIT_SUCCESS : EXIT_FAILURE;
1166 }