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