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