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