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