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