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