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