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