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