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