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