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