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