]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal-remote/journal-remote-main.c
c46e0acdd369354d4a20ec64484b1d4e6d2e2614
[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 // FIXME: unregister daemon
499 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
500 "MHD_run failed!");
501 if (MHD_get_timeout(d->daemon, &timeout) == MHD_NO)
502 timeout = ULONG_LONG_MAX;
503
504 r = sd_event_source_set_time(d->timer_event, timeout);
505 if (r < 0) {
506 log_warning_errno(r, "Unable to set event loop timeout: %m, this may result in indefinite blocking!");
507 return 1;
508 }
509
510 r = sd_event_source_set_enabled(d->timer_event, SD_EVENT_ON);
511 if (r < 0)
512 log_warning_errno(r, "Unable to enable timer_event: %m, this may result in indefinite blocking!");
513
514 return 1; /* work to do */
515 }
516
517 /**********************************************************************
518 **********************************************************************
519 **********************************************************************/
520
521 static int setup_signals(RemoteServer *s) {
522 int r;
523
524 assert(s);
525
526 assert_se(sigprocmask_many(SIG_SETMASK, NULL, SIGINT, SIGTERM, -1) >= 0);
527
528 r = sd_event_add_signal(s->events, &s->sigterm_event, SIGTERM, NULL, s);
529 if (r < 0)
530 return r;
531
532 r = sd_event_add_signal(s->events, &s->sigint_event, SIGINT, NULL, s);
533 if (r < 0)
534 return r;
535
536 return 0;
537 }
538
539 static int setup_raw_socket(RemoteServer *s, const char *address) {
540 int fd;
541
542 fd = make_socket_fd(LOG_INFO, address, SOCK_STREAM, SOCK_CLOEXEC);
543 if (fd < 0)
544 return fd;
545
546 return journal_remote_add_raw_socket(s, fd);
547 }
548
549 static int create_remoteserver(
550 RemoteServer *s,
551 const char* key,
552 const char* cert,
553 const char* trust) {
554
555 int r, n, fd;
556 char **file;
557
558 r = journal_remote_server_init(s, arg_output, arg_split_mode, arg_compress, arg_seal);
559 if (r < 0)
560 return r;
561
562 r = setup_signals(s);
563 if (r < 0)
564 return log_error_errno(r, "Failed to set up signals: %m");
565
566 n = sd_listen_fds(true);
567 if (n < 0)
568 return log_error_errno(n, "Failed to read listening file descriptors from environment: %m");
569 else
570 log_debug("Received %d descriptors", n);
571
572 if (MAX(http_socket, https_socket) >= SD_LISTEN_FDS_START + n)
573 return log_error_errno(SYNTHETIC_ERRNO(EBADFD),
574 "Received fewer sockets than expected");
575
576 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
577 if (sd_is_socket(fd, AF_UNSPEC, 0, true)) {
578 log_debug("Received a listening socket (fd:%d)", fd);
579
580 if (fd == http_socket)
581 r = setup_microhttpd_server(s, fd, NULL, NULL, NULL);
582 else if (fd == https_socket)
583 r = setup_microhttpd_server(s, fd, key, cert, trust);
584 else
585 r = journal_remote_add_raw_socket(s, fd);
586 } else if (sd_is_socket(fd, AF_UNSPEC, 0, false)) {
587 char *hostname;
588
589 r = getpeername_pretty(fd, false, &hostname);
590 if (r < 0)
591 return log_error_errno(r, "Failed to retrieve remote name: %m");
592
593 log_debug("Received a connection socket (fd:%d) from %s", fd, hostname);
594
595 r = journal_remote_add_source(s, fd, hostname, true);
596 } else
597 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
598 "Unknown socket passed on fd:%d", fd);
599
600 if (r < 0)
601 return log_error_errno(r, "Failed to register socket (fd:%d): %m", fd);
602 }
603
604 if (arg_getter) {
605 log_info("Spawning getter %s...", arg_getter);
606 fd = spawn_getter(arg_getter);
607 if (fd < 0)
608 return fd;
609
610 r = journal_remote_add_source(s, fd, (char*) arg_output, false);
611 if (r < 0)
612 return r;
613 }
614
615 if (arg_url) {
616 const char *url;
617 char *hostname;
618
619 if (!strstr(arg_url, "/entries")) {
620 if (endswith(arg_url, "/"))
621 url = strjoina(arg_url, "entries");
622 else
623 url = strjoina(arg_url, "/entries");
624 } else
625 url = strdupa(arg_url);
626
627 log_info("Spawning curl %s...", url);
628 fd = spawn_curl(url);
629 if (fd < 0)
630 return fd;
631
632 hostname = STARTSWITH_SET(arg_url, "https://", "http://");
633 if (!hostname)
634 hostname = arg_url;
635
636 hostname = strndupa(hostname, strcspn(hostname, "/:"));
637
638 r = journal_remote_add_source(s, fd, hostname, false);
639 if (r < 0)
640 return r;
641 }
642
643 if (arg_listen_raw) {
644 log_debug("Listening on a socket...");
645 r = setup_raw_socket(s, arg_listen_raw);
646 if (r < 0)
647 return r;
648 }
649
650 if (arg_listen_http) {
651 r = setup_microhttpd_socket(s, arg_listen_http, NULL, NULL, NULL);
652 if (r < 0)
653 return r;
654 }
655
656 if (arg_listen_https) {
657 r = setup_microhttpd_socket(s, arg_listen_https, key, cert, trust);
658 if (r < 0)
659 return r;
660 }
661
662 STRV_FOREACH(file, arg_files) {
663 const char *output_name;
664
665 if (streq(*file, "-")) {
666 log_debug("Using standard input as source.");
667
668 fd = STDIN_FILENO;
669 output_name = "stdin";
670 } else {
671 log_debug("Reading file %s...", *file);
672
673 fd = open(*file, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
674 if (fd < 0)
675 return log_error_errno(errno, "Failed to open %s: %m", *file);
676 output_name = *file;
677 }
678
679 r = journal_remote_add_source(s, fd, (char*) output_name, false);
680 if (r < 0)
681 return r;
682 }
683
684 if (s->active == 0)
685 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
686 "Zero sources specified");
687
688 if (arg_split_mode == JOURNAL_WRITE_SPLIT_NONE) {
689 /* In this case we know what the writer will be
690 called, so we can create it and verify that we can
691 create output as expected. */
692 r = journal_remote_get_writer(s, NULL, &s->_single_writer);
693 if (r < 0)
694 return r;
695 }
696
697 return 0;
698 }
699
700 static int negative_fd(const char *spec) {
701 /* Return a non-positive number as its inverse, -EINVAL otherwise. */
702
703 int fd, r;
704
705 r = safe_atoi(spec, &fd);
706 if (r < 0)
707 return r;
708
709 if (fd > 0)
710 return -EINVAL;
711 else
712 return -fd;
713 }
714
715 static int parse_config(void) {
716 const ConfigTableItem items[] = {
717 { "Remote", "Seal", config_parse_bool, 0, &arg_seal },
718 { "Remote", "SplitMode", config_parse_write_split_mode, 0, &arg_split_mode },
719 { "Remote", "ServerKeyFile", config_parse_path, 0, &arg_key },
720 { "Remote", "ServerCertificateFile", config_parse_path, 0, &arg_cert },
721 { "Remote", "TrustedCertificateFile", config_parse_path, 0, &arg_trust },
722 {}
723 };
724
725 return config_parse_many_nulstr(PKGSYSCONFDIR "/journal-remote.conf",
726 CONF_PATHS_NULSTR("systemd/journal-remote.conf.d"),
727 "Remote\0", config_item_table_lookup, items,
728 CONFIG_PARSE_WARN, NULL);
729 }
730
731 static int help(void) {
732 _cleanup_free_ char *link = NULL;
733 int r;
734
735 r = terminal_urlify_man("systemd-journal-remote.service", "8", &link);
736 if (r < 0)
737 return log_oom();
738
739 printf("%s [OPTIONS...] {FILE|-}...\n\n"
740 "Write external journal events to journal file(s).\n\n"
741 " -h --help Show this help\n"
742 " --version Show package version\n"
743 " --url=URL Read events from systemd-journal-gatewayd at URL\n"
744 " --getter=COMMAND Read events from the output of COMMAND\n"
745 " --listen-raw=ADDR Listen for connections at ADDR\n"
746 " --listen-http=ADDR Listen for HTTP connections at ADDR\n"
747 " --listen-https=ADDR Listen for HTTPS connections at ADDR\n"
748 " -o --output=FILE|DIR Write output to FILE or DIR/external-*.journal\n"
749 " --compress[=BOOL] XZ-compress the output journal (default: yes)\n"
750 " --seal[=BOOL] Use event sealing (default: no)\n"
751 " --key=FILENAME SSL key in PEM format (default:\n"
752 " \"" PRIV_KEY_FILE "\")\n"
753 " --cert=FILENAME SSL certificate in PEM format (default:\n"
754 " \"" CERT_FILE "\")\n"
755 " --trust=FILENAME|all SSL CA certificate or disable checking (default:\n"
756 " \"" TRUST_FILE "\")\n"
757 " --gnutls-log=CATEGORY...\n"
758 " Specify a list of gnutls logging categories\n"
759 " --split-mode=none|host How many output files to create\n"
760 "\nNote: file descriptors from sd_listen_fds() will be consumed, too.\n"
761 "\nSee the %s for details.\n"
762 , program_invocation_short_name
763 , link
764 );
765
766 return 0;
767 }
768
769 static int parse_argv(int argc, char *argv[]) {
770 enum {
771 ARG_VERSION = 0x100,
772 ARG_URL,
773 ARG_LISTEN_RAW,
774 ARG_LISTEN_HTTP,
775 ARG_LISTEN_HTTPS,
776 ARG_GETTER,
777 ARG_SPLIT_MODE,
778 ARG_COMPRESS,
779 ARG_SEAL,
780 ARG_KEY,
781 ARG_CERT,
782 ARG_TRUST,
783 ARG_GNUTLS_LOG,
784 };
785
786 static const struct option options[] = {
787 { "help", no_argument, NULL, 'h' },
788 { "version", no_argument, NULL, ARG_VERSION },
789 { "url", required_argument, NULL, ARG_URL },
790 { "getter", required_argument, NULL, ARG_GETTER },
791 { "listen-raw", required_argument, NULL, ARG_LISTEN_RAW },
792 { "listen-http", required_argument, NULL, ARG_LISTEN_HTTP },
793 { "listen-https", required_argument, NULL, ARG_LISTEN_HTTPS },
794 { "output", required_argument, NULL, 'o' },
795 { "split-mode", required_argument, NULL, ARG_SPLIT_MODE },
796 { "compress", optional_argument, NULL, ARG_COMPRESS },
797 { "seal", optional_argument, NULL, ARG_SEAL },
798 { "key", required_argument, NULL, ARG_KEY },
799 { "cert", required_argument, NULL, ARG_CERT },
800 { "trust", required_argument, NULL, ARG_TRUST },
801 { "gnutls-log", required_argument, NULL, ARG_GNUTLS_LOG },
802 {}
803 };
804
805 int c, r;
806 bool type_a, type_b;
807
808 assert(argc >= 0);
809 assert(argv);
810
811 while ((c = getopt_long(argc, argv, "ho:", options, NULL)) >= 0)
812 switch(c) {
813
814 case 'h':
815 return help();
816
817 case ARG_VERSION:
818 return version();
819
820 case ARG_URL:
821 if (arg_url)
822 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
823 "cannot currently set more than one --url");
824
825 arg_url = optarg;
826 break;
827
828 case ARG_GETTER:
829 if (arg_getter)
830 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
831 "cannot currently use --getter more than once");
832
833 arg_getter = optarg;
834 break;
835
836 case ARG_LISTEN_RAW:
837 if (arg_listen_raw)
838 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
839 "cannot currently use --listen-raw more than once");
840
841 arg_listen_raw = optarg;
842 break;
843
844 case ARG_LISTEN_HTTP:
845 if (arg_listen_http || http_socket >= 0)
846 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
847 "cannot currently use --listen-http more than once");
848
849 r = negative_fd(optarg);
850 if (r >= 0)
851 http_socket = r;
852 else
853 arg_listen_http = optarg;
854 break;
855
856 case ARG_LISTEN_HTTPS:
857 if (arg_listen_https || https_socket >= 0)
858 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
859 "cannot currently use --listen-https more than once");
860
861 r = negative_fd(optarg);
862 if (r >= 0)
863 https_socket = r;
864 else
865 arg_listen_https = optarg;
866
867 break;
868
869 case ARG_KEY:
870 if (arg_key)
871 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
872 "Key file specified twice");
873
874 arg_key = strdup(optarg);
875 if (!arg_key)
876 return log_oom();
877
878 break;
879
880 case ARG_CERT:
881 if (arg_cert)
882 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
883 "Certificate file specified twice");
884
885 arg_cert = strdup(optarg);
886 if (!arg_cert)
887 return log_oom();
888
889 break;
890
891 case ARG_TRUST:
892 if (arg_trust || arg_trust_all)
893 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
894 "Confusing trusted CA configuration");
895
896 if (streq(optarg, "all"))
897 arg_trust_all = true;
898 else {
899 #if HAVE_GNUTLS
900 arg_trust = strdup(optarg);
901 if (!arg_trust)
902 return log_oom();
903 #else
904 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
905 "Option --trust is not available.");
906 #endif
907 }
908
909 break;
910
911 case 'o':
912 if (arg_output)
913 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
914 "cannot use --output/-o more than once");
915
916 arg_output = optarg;
917 break;
918
919 case ARG_SPLIT_MODE:
920 arg_split_mode = journal_write_split_mode_from_string(optarg);
921 if (arg_split_mode == _JOURNAL_WRITE_SPLIT_INVALID)
922 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
923 "Invalid split mode: %s", optarg);
924 break;
925
926 case ARG_COMPRESS:
927 if (optarg) {
928 r = parse_boolean(optarg);
929 if (r < 0)
930 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
931 "Failed to parse --compress= parameter.");
932
933 arg_compress = !!r;
934 } else
935 arg_compress = true;
936
937 break;
938
939 case ARG_SEAL:
940 if (optarg) {
941 r = parse_boolean(optarg);
942 if (r < 0)
943 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
944 "Failed to parse --seal= parameter.");
945
946 arg_seal = !!r;
947 } else
948 arg_seal = true;
949
950 break;
951
952 case ARG_GNUTLS_LOG: {
953 #if HAVE_GNUTLS
954 const char* p = optarg;
955 for (;;) {
956 _cleanup_free_ char *word = NULL;
957
958 r = extract_first_word(&p, &word, ",", 0);
959 if (r < 0)
960 return log_error_errno(r, "Failed to parse --gnutls-log= argument: %m");
961 if (r == 0)
962 break;
963
964 if (strv_push(&arg_gnutls_log, word) < 0)
965 return log_oom();
966
967 word = NULL;
968 }
969 break;
970 #else
971 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
972 "Option --gnutls-log is not available.");
973 #endif
974 }
975
976 case '?':
977 return -EINVAL;
978
979 default:
980 assert_not_reached("Unknown option code.");
981 }
982
983 if (optind < argc)
984 arg_files = argv + optind;
985
986 type_a = arg_getter || !strv_isempty(arg_files);
987 type_b = arg_url
988 || arg_listen_raw
989 || arg_listen_http || arg_listen_https
990 || sd_listen_fds(false) > 0;
991 if (type_a && type_b)
992 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
993 "Cannot use file input or --getter with "
994 "--arg-listen-... or socket activation.");
995 if (type_a) {
996 if (!arg_output)
997 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
998 "Option --output must be specified with file input or --getter.");
999
1000 if (!IN_SET(arg_split_mode, JOURNAL_WRITE_SPLIT_NONE, _JOURNAL_WRITE_SPLIT_INVALID))
1001 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1002 "For active sources, only --split-mode=none is allowed.");
1003
1004 arg_split_mode = JOURNAL_WRITE_SPLIT_NONE;
1005 }
1006
1007 if (arg_split_mode == _JOURNAL_WRITE_SPLIT_INVALID)
1008 arg_split_mode = JOURNAL_WRITE_SPLIT_HOST;
1009
1010 if (arg_split_mode == JOURNAL_WRITE_SPLIT_NONE && arg_output) {
1011 if (is_dir(arg_output, true) > 0)
1012 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1013 "For SplitMode=none, output must be a file.");
1014 if (!endswith(arg_output, ".journal"))
1015 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1016 "For SplitMode=none, output file name must end with .journal.");
1017 }
1018
1019 if (arg_split_mode == JOURNAL_WRITE_SPLIT_HOST
1020 && arg_output && is_dir(arg_output, true) <= 0)
1021 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1022 "For SplitMode=host, output must be a directory.");
1023
1024 log_debug("Full config: SplitMode=%s Key=%s Cert=%s Trust=%s",
1025 journal_write_split_mode_to_string(arg_split_mode),
1026 strna(arg_key),
1027 strna(arg_cert),
1028 strna(arg_trust));
1029
1030 return 1 /* work to do */;
1031 }
1032
1033 static int load_certificates(char **key, char **cert, char **trust) {
1034 int r;
1035
1036 r = read_full_file(arg_key ?: PRIV_KEY_FILE, key, NULL);
1037 if (r < 0)
1038 return log_error_errno(r, "Failed to read key from file '%s': %m",
1039 arg_key ?: PRIV_KEY_FILE);
1040
1041 r = read_full_file(arg_cert ?: CERT_FILE, cert, NULL);
1042 if (r < 0)
1043 return log_error_errno(r, "Failed to read certificate from file '%s': %m",
1044 arg_cert ?: CERT_FILE);
1045
1046 if (arg_trust_all)
1047 log_info("Certificate checking disabled.");
1048 else {
1049 r = read_full_file(arg_trust ?: TRUST_FILE, trust, NULL);
1050 if (r < 0)
1051 return log_error_errno(r, "Failed to read CA certificate file '%s': %m",
1052 arg_trust ?: TRUST_FILE);
1053 }
1054
1055 if ((arg_listen_raw || arg_listen_http) && *trust)
1056 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1057 "Option --trust makes all non-HTTPS connections untrusted.");
1058
1059 return 0;
1060 }
1061
1062 int main(int argc, char **argv) {
1063 RemoteServer s = {};
1064 int r;
1065 _cleanup_free_ char *key = NULL, *cert = NULL, *trust = NULL;
1066
1067 log_show_color(true);
1068 log_parse_environment();
1069
1070 /* The journal merging logic potentially needs a lot of fds. */
1071 (void) rlimit_nofile_bump(HIGH_RLIMIT_NOFILE);
1072
1073 r = parse_config();
1074 if (r < 0)
1075 return EXIT_FAILURE;
1076
1077 r = parse_argv(argc, argv);
1078 if (r <= 0)
1079 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
1080
1081 if (arg_listen_http || arg_listen_https) {
1082 r = setup_gnutls_logger(arg_gnutls_log);
1083 if (r < 0)
1084 return EXIT_FAILURE;
1085 }
1086
1087 if (arg_listen_https || https_socket >= 0) {
1088 if (load_certificates(&key, &cert, &trust) < 0)
1089 return EXIT_FAILURE;
1090 s.check_trust = !arg_trust_all;
1091 }
1092
1093 if (create_remoteserver(&s, key, cert, trust) < 0)
1094 return EXIT_FAILURE;
1095
1096 r = sd_event_set_watchdog(s.events, true);
1097 if (r < 0)
1098 log_error_errno(r, "Failed to enable watchdog: %m");
1099 else
1100 log_debug("Watchdog is %sd.", enable_disable(r > 0));
1101
1102 log_debug("%s running as pid "PID_FMT,
1103 program_invocation_short_name, getpid_cached());
1104 sd_notify(false,
1105 "READY=1\n"
1106 "STATUS=Processing requests...");
1107
1108 while (s.active) {
1109 r = sd_event_get_state(s.events);
1110 if (r < 0)
1111 break;
1112 if (r == SD_EVENT_FINISHED)
1113 break;
1114
1115 r = sd_event_run(s.events, -1);
1116 if (r < 0) {
1117 log_error_errno(r, "Failed to run event loop: %m");
1118 break;
1119 }
1120 }
1121
1122 sd_notifyf(false,
1123 "STOPPING=1\n"
1124 "STATUS=Shutting down after writing %" PRIu64 " entries...", s.event_count);
1125 log_info("Finishing after writing %" PRIu64 " entries", s.event_count);
1126
1127 journal_remote_server_destroy(&s);
1128
1129 free(arg_key);
1130 free(arg_cert);
1131 free(arg_trust);
1132
1133 return r >= 0 ? EXIT_SUCCESS : EXIT_FAILURE;
1134 }