]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/journal-remote.c
sd-event: rework API to support CLOCK_REALTIME_ALARM and CLOCK_BOOTTIME_ALARM, too
[thirdparty/systemd.git] / src / journal / 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>
24#include <inttypes.h>
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28#include <sys/prctl.h>
29#include <sys/socket.h>
30#include <sys/stat.h>
31#include <sys/types.h>
32#include <unistd.h>
33#include <getopt.h>
34
35#include "sd-daemon.h"
36#include "sd-event.h"
37#include "journal-file.h"
38#include "journald-native.h"
39#include "socket-util.h"
40#include "mkdir.h"
41#include "build.h"
42#include "macro.h"
43#include "strv.h"
cc64d017
ZJS
44#include "fileio.h"
45#include "socket-util.h"
46#include "microhttpd-util.h"
47
48#ifdef HAVE_GNUTLS
49#include <gnutls/gnutls.h>
50#endif
fdfccdbc
ZJS
51
52#include "journal-remote-parse.h"
53#include "journal-remote-write.h"
54
55#define REMOTE_JOURNAL_PATH "/var/log/journal/" SD_ID128_FORMAT_STR "/remote-%s.journal"
56
57static char* arg_output = NULL;
58static char* arg_url = NULL;
59static char* arg_getter = NULL;
fdfccdbc 60static char* arg_listen_raw = NULL;
cc64d017
ZJS
61static char* arg_listen_http = NULL;
62static char* arg_listen_https = NULL;
8a8d55f2 63static char** arg_files = NULL;
fdfccdbc
ZJS
64static int arg_compress = true;
65static int arg_seal = false;
8a8d55f2 66static int http_socket = -1, https_socket = -1;
fdfccdbc 67
cc64d017
ZJS
68static char *key_pem = NULL;
69static char *cert_pem = NULL;
70static char *trust_pem = NULL;
71
fdfccdbc
ZJS
72/**********************************************************************
73 **********************************************************************
74 **********************************************************************/
75
76static int spawn_child(const char* child, char** argv) {
77 int fd[2];
78 pid_t parent_pid, child_pid;
79 int r;
80
81 if (pipe(fd) < 0) {
82 log_error("Failed to create pager pipe: %m");
83 return -errno;
84 }
85
86 parent_pid = getpid();
87
88 child_pid = fork();
89 if (child_pid < 0) {
90 r = -errno;
91 log_error("Failed to fork: %m");
92 close_pipe(fd);
93 return r;
94 }
95
96 /* In the child */
97 if (child_pid == 0) {
98 r = dup2(fd[1], STDOUT_FILENO);
99 if (r < 0) {
100 log_error("Failed to dup pipe to stdout: %m");
101 _exit(EXIT_FAILURE);
102 }
103
104 r = close_pipe(fd);
105 if (r < 0)
106 log_warning("Failed to close pipe fds: %m");
107
108 /* Make sure the child goes away when the parent dies */
109 if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
110 _exit(EXIT_FAILURE);
111
112 /* Check whether our parent died before we were able
113 * to set the death signal */
114 if (getppid() != parent_pid)
115 _exit(EXIT_SUCCESS);
116
117 execvp(child, argv);
118 log_error("Failed to exec child %s: %m", child);
119 _exit(EXIT_FAILURE);
120 }
121
122 r = close(fd[1]);
123 if (r < 0)
124 log_warning("Failed to close write end of pipe: %m");
125
126 return fd[0];
127}
128
129static int spawn_curl(char* url) {
130 char **argv = STRV_MAKE("curl",
131 "-HAccept: application/vnd.fdo.journal",
132 "--silent",
133 "--show-error",
134 url);
135 int r;
136
137 r = spawn_child("curl", argv);
138 if (r < 0)
139 log_error("Failed to spawn curl: %m");
140 return r;
141}
142
143static int spawn_getter(char *getter, char *url) {
144 int r;
fd1e5b62 145 char _cleanup_strv_free_ **words = NULL;
fdfccdbc
ZJS
146
147 assert(getter);
148 words = strv_split_quoted(getter);
149 if (!words)
150 return log_oom();
151
152 r = spawn_child(words[0], words);
153 if (r < 0)
154 log_error("Failed to spawn getter %s: %m", getter);
155
156 return r;
157}
158
159static int open_output(Writer *s, const char* url) {
160 char _cleanup_free_ *name, *output = NULL;
161 char *c;
162 int r;
163
164 assert(url);
165 name = strdup(url);
166 if (!name)
167 return log_oom();
168
169 for(c = name; *c; c++) {
170 if (*c == '/' || *c == ':' || *c == ' ')
171 *c = '~';
172 else if (*c == '?') {
173 *c = '\0';
174 break;
175 }
176 }
177
178 if (!arg_output) {
179 sd_id128_t machine;
180 r = sd_id128_get_machine(&machine);
181 if (r < 0) {
182 log_error("failed to determine machine ID128: %s", strerror(-r));
183 return r;
184 }
185
186 r = asprintf(&output, REMOTE_JOURNAL_PATH,
187 SD_ID128_FORMAT_VAL(machine), name);
188 if (r < 0)
189 return log_oom();
190 } else {
191 r = is_dir(arg_output);
192 if (r > 0) {
193 r = asprintf(&output,
194 "%s/remote-%s.journal", arg_output, name);
195 if (r < 0)
196 return log_oom();
197 } else {
198 output = strdup(arg_output);
199 if (!output)
200 return log_oom();
201 }
202 }
203
204 r = journal_file_open_reliably(output,
205 O_RDWR|O_CREAT, 0640,
206 arg_compress, arg_seal,
207 &s->metrics,
208 s->mmap,
209 NULL, &s->journal);
210 if (r < 0)
211 log_error("Failed to open output journal %s: %s",
212 arg_output, strerror(-r));
213 else
214 log_info("Opened output file %s", s->journal->path);
215 return r;
216}
217
cc64d017
ZJS
218/**********************************************************************
219 **********************************************************************
220 **********************************************************************/
221
222typedef struct MHDDaemonWrapper {
223 uint64_t fd;
224 struct MHD_Daemon *daemon;
225
226 sd_event_source *event;
227} MHDDaemonWrapper;
228
fdfccdbc
ZJS
229typedef struct RemoteServer {
230 RemoteSource **sources;
231 ssize_t sources_size;
232 ssize_t active;
233
234 sd_event *events;
235 sd_event_source *sigterm_event, *sigint_event, *listen_event;
236
237 Writer writer;
cc64d017
ZJS
238
239 Hashmap *daemons;
fdfccdbc
ZJS
240} RemoteServer;
241
cc64d017
ZJS
242/* This should go away as soon as µhttpd allows state to be passed around. */
243static RemoteServer *server;
244
fdfccdbc
ZJS
245static int dispatch_raw_source_event(sd_event_source *event,
246 int fd,
247 uint32_t revents,
248 void *userdata);
249static int dispatch_raw_connection_event(sd_event_source *event,
250 int fd,
251 uint32_t revents,
252 void *userdata);
cc64d017
ZJS
253static int dispatch_http_event(sd_event_source *event,
254 int fd,
255 uint32_t revents,
256 void *userdata);
fdfccdbc
ZJS
257
258static int get_source_for_fd(RemoteServer *s, int fd, RemoteSource **source) {
259 assert(fd >= 0);
260 assert(source);
261
262 if (!GREEDY_REALLOC0_T(s->sources, s->sources_size, fd + 1))
263 return log_oom();
264
265 if (s->sources[fd] == NULL) {
266 s->sources[fd] = new0(RemoteSource, 1);
267 if (!s->sources[fd])
268 return log_oom();
269 s->sources[fd]->fd = -1;
270 s->active++;
271 }
272
273 *source = s->sources[fd];
274 return 0;
275}
276
277static int remove_source(RemoteServer *s, int fd) {
278 RemoteSource *source;
279
280 assert(s);
281 assert(fd >= 0);
282 assert(fd < s->sources_size);
283
284 source = s->sources[fd];
285 if (source) {
286 source_free(source);
287 s->sources[fd] = NULL;
288 s->active--;
289 }
290
291 close(fd);
292
293 return 0;
294}
295
296static int add_source(RemoteServer *s, int fd, const char* name) {
297 RemoteSource *source = NULL;
298 char *realname;
299 int r;
300
301 assert(s);
302 assert(fd >= 0);
303
304 if (name) {
305 realname = strdup(name);
306 if (!realname)
307 return log_oom();
308 } else {
309 r = asprintf(&realname, "fd:%d", fd);
310 if (r < 0)
311 return log_oom();
312 }
313
314 log_debug("Creating source for fd:%d (%s)", fd, name);
315
316 r = get_source_for_fd(s, fd, &source);
317 if (r < 0) {
318 log_error("Failed to create source for fd:%d (%s)", fd, name);
319 return r;
320 }
321 assert(source);
322 assert(source->fd < 0);
323 source->fd = fd;
324
325 r = sd_event_add_io(s->events, &source->event,
326 fd, EPOLLIN, dispatch_raw_source_event, s);
327 if (r < 0) {
328 log_error("Failed to register event source for fd:%d: %s",
329 fd, strerror(-r));
330 goto error;
331 }
332
333 return 1; /* work to do */
334
335 error:
336 remove_source(s, fd);
337 return r;
338}
339
8a8d55f2
ZJS
340static int add_raw_socket(RemoteServer *s, int fd) {
341 int r;
fdfccdbc
ZJS
342
343 r = sd_event_add_io(s->events, &s->listen_event, fd, EPOLLIN,
344 dispatch_raw_connection_event, s);
345 if (r < 0) {
346 close(fd);
347 return r;
348 }
349
350 s->active ++;
351 return 0;
352}
353
8a8d55f2
ZJS
354static int setup_raw_socket(RemoteServer *s, const char *address) {
355 int fd;
356
357 fd = make_socket_fd(LOG_INFO, address, SOCK_STREAM | SOCK_CLOEXEC);
358 if (fd < 0)
359 return fd;
360
361 return add_raw_socket(s, fd);
362}
363
cc64d017
ZJS
364/**********************************************************************
365 **********************************************************************
366 **********************************************************************/
367
368static RemoteSource *request_meta(void **connection_cls) {
369 RemoteSource *source;
370
371 assert(connection_cls);
372 if (*connection_cls)
373 return *connection_cls;
374
375 source = new0(RemoteSource, 1);
376 if (!source)
377 return NULL;
378 source->fd = -1;
379
380 log_debug("Added RemoteSource as connection metadata %p", source);
381
382 *connection_cls = source;
383 return source;
384}
385
386static void request_meta_free(void *cls,
387 struct MHD_Connection *connection,
388 void **connection_cls,
389 enum MHD_RequestTerminationCode toe) {
390 RemoteSource *s;
391
392 assert(connection_cls);
393 s = *connection_cls;
394
395 log_debug("Cleaning up connection metadata %p", s);
396 source_free(s);
397 *connection_cls = NULL;
398}
399
400static int process_http_upload(
401 struct MHD_Connection *connection,
402 const char *upload_data,
403 size_t *upload_data_size,
404 RemoteSource *source) {
405
406 bool finished = false;
407 int r;
408
409 assert(source);
410
411 log_debug("request_handler_upload: connection %p, %zu bytes",
412 connection, *upload_data_size);
413
414 if (*upload_data_size) {
415 log_info("Received %zu bytes", *upload_data_size);
416
417 r = push_data(source, upload_data, *upload_data_size);
418 if (r < 0) {
419 log_error("Failed to store received data of size %zu: %s",
420 *upload_data_size, strerror(-r));
e7216d11 421 return mhd_respond_oom(connection);
cc64d017
ZJS
422 }
423 *upload_data_size = 0;
424 } else
425 finished = true;
426
427 while (true) {
428 r = process_source(source, &server->writer, arg_compress, arg_seal);
429 if (r == -E2BIG)
430 log_warning("Entry too big, skipped");
431 else if (r == -EAGAIN || r == -EWOULDBLOCK)
432 break;
433 else if (r < 0) {
434 log_warning("Failed to process data for connection %p", connection);
e7216d11
ZJS
435 return mhd_respondf(connection, MHD_HTTP_UNPROCESSABLE_ENTITY,
436 "Processing failed: %s", strerror(-r));
cc64d017
ZJS
437 }
438 }
439
440 if (!finished)
441 return MHD_YES;
442
443 /* The upload is finished */
444
445 if (source_non_empty(source)) {
446 log_warning("EOF reached with incomplete data");
e7216d11
ZJS
447 return mhd_respond(connection, MHD_HTTP_EXPECTATION_FAILED,
448 "Trailing data not processed.");
cc64d017
ZJS
449 }
450
e7216d11 451 return mhd_respond(connection, MHD_HTTP_ACCEPTED, "OK.\n");
cc64d017
ZJS
452};
453
454static int request_handler(
455 void *cls,
456 struct MHD_Connection *connection,
457 const char *url,
458 const char *method,
459 const char *version,
460 const char *upload_data,
461 size_t *upload_data_size,
462 void **connection_cls) {
463
464 const char *header;
465 int r ,code;
466
467 assert(connection);
468 assert(connection_cls);
469 assert(url);
470 assert(method);
471
472 log_debug("Handling a connection %s %s %s", method, url, version);
473
474 if (*connection_cls)
475 return process_http_upload(connection,
476 upload_data, upload_data_size,
477 *connection_cls);
478
479 if (!streq(method, "POST"))
e7216d11
ZJS
480 return mhd_respond(connection, MHD_HTTP_METHOD_NOT_ACCEPTABLE,
481 "Unsupported method.\n");
cc64d017
ZJS
482
483 if (!streq(url, "/upload"))
e7216d11
ZJS
484 return mhd_respond(connection, MHD_HTTP_NOT_FOUND,
485 "Not found.\n");
cc64d017
ZJS
486
487 header = MHD_lookup_connection_value(connection,
488 MHD_HEADER_KIND, "Content-Type");
489 if (!header || !streq(header, "application/vnd.fdo.journal"))
e7216d11
ZJS
490 return mhd_respond(connection, MHD_HTTP_UNSUPPORTED_MEDIA_TYPE,
491 "Content-Type: application/vnd.fdo.journal"
492 " is required.\n");
cc64d017
ZJS
493
494 if (trust_pem) {
495 r = check_permissions(connection, &code);
496 if (r < 0)
497 return code;
498 }
499
500 if (!request_meta(connection_cls))
501 return respond_oom(connection);
502 return MHD_YES;
503}
504
505static int setup_microhttpd_server(RemoteServer *s, int fd, bool https) {
506 struct MHD_OptionItem opts[] = {
507 { MHD_OPTION_NOTIFY_COMPLETED, (intptr_t) request_meta_free},
508 { MHD_OPTION_EXTERNAL_LOGGER, (intptr_t) microhttpd_logger},
509 { MHD_OPTION_LISTEN_SOCKET, fd},
510 { MHD_OPTION_END},
511 { MHD_OPTION_END},
512 { MHD_OPTION_END},
513 { MHD_OPTION_END}};
514 int opts_pos = 3;
515 int flags =
516 MHD_USE_DEBUG |
517 MHD_USE_PEDANTIC_CHECKS |
518 MHD_USE_EPOLL_LINUX_ONLY |
519 MHD_USE_DUAL_STACK;
520
521 const union MHD_DaemonInfo *info;
522 int r, epoll_fd;
523 MHDDaemonWrapper *d;
524
525 assert(fd >= 0);
526
527 r = fd_nonblock(fd, true);
528 if (r < 0) {
529 log_error("Failed to make fd:%d nonblocking: %s", fd, strerror(-r));
530 return r;
531 }
532
533 if (https) {
534 opts[opts_pos++] = (struct MHD_OptionItem)
535 {MHD_OPTION_HTTPS_MEM_KEY, 0, key_pem};
536 opts[opts_pos++] = (struct MHD_OptionItem)
537 {MHD_OPTION_HTTPS_MEM_CERT, 0, cert_pem};
538
539 flags |= MHD_USE_SSL;
540
541 if (trust_pem)
542 opts[opts_pos++] = (struct MHD_OptionItem)
543 {MHD_OPTION_HTTPS_MEM_TRUST, 0, trust_pem};
544 }
545
546 d = new(MHDDaemonWrapper, 1);
547 if (!d)
548 return log_oom();
549
550 d->fd = (uint64_t) fd;
551
552 d->daemon = MHD_start_daemon(flags, 0,
553 NULL, NULL,
554 request_handler, NULL,
555 MHD_OPTION_ARRAY, opts,
556 MHD_OPTION_END);
557 if (!d->daemon) {
558 log_error("Failed to start µhttp daemon");
559 r = -EINVAL;
560 goto error;
561 }
562
563 log_debug("Started MHD %s daemon on fd:%d (wrapper @ %p)",
564 https ? "HTTPS" : "HTTP", fd, d);
565
566
567 info = MHD_get_daemon_info(d->daemon, MHD_DAEMON_INFO_EPOLL_FD_LINUX_ONLY);
568 if (!info) {
569 log_error("µhttp returned NULL daemon info");
570 r = -ENOTSUP;
571 goto error;
572 }
573
574 epoll_fd = info->listen_fd;
575 if (epoll_fd < 0) {
576 log_error("µhttp epoll fd is invalid");
577 r = -EUCLEAN;
578 goto error;
579 }
580
581 r = sd_event_add_io(s->events, &d->event,
582 epoll_fd, EPOLLIN, dispatch_http_event, d);
583 if (r < 0) {
584 log_error("Failed to add event callback: %s", strerror(-r));
585 goto error;
586 }
587
588 r = hashmap_ensure_allocated(&s->daemons, uint64_hash_func, uint64_compare_func);
589 if (r < 0) {
590 log_oom();
591 goto error;
592 }
593
594 r = hashmap_put(s->daemons, &d->fd, d);
595 if (r < 0) {
596 log_error("Failed to add daemon to hashmap: %s", strerror(-r));
597 goto error;
598 }
599
600 s->active ++;
601 return 0;
602
603error:
604 MHD_stop_daemon(d->daemon);
605 free(d->daemon);
606 free(d);
607 return r;
608}
609
610static int setup_microhttpd_socket(RemoteServer *s,
611 const char *address,
612 bool https) {
613 int fd;
614
615 fd = make_socket_fd(LOG_INFO, address, SOCK_STREAM | SOCK_CLOEXEC);
616 if (fd < 0)
617 return fd;
618
619 return setup_microhttpd_server(s, fd, https);
620}
621
622static int dispatch_http_event(sd_event_source *event,
623 int fd,
624 uint32_t revents,
625 void *userdata) {
626 MHDDaemonWrapper *d = userdata;
627 int r;
628
629 assert(d);
630
631 log_info("%s", __func__);
632
633 r = MHD_run(d->daemon);
634 if (r == MHD_NO) {
635 log_error("MHD_run failed!");
636 // XXX: unregister daemon
637 return -EINVAL;
638 }
639
640 return 1; /* work to do */
641}
642
fdfccdbc
ZJS
643/**********************************************************************
644 **********************************************************************
645 **********************************************************************/
646
647static int dispatch_sigterm(sd_event_source *event,
648 const struct signalfd_siginfo *si,
649 void *userdata) {
650 RemoteServer *s = userdata;
651
652 assert(s);
653
654 log_received_signal(LOG_INFO, si);
655
656 sd_event_exit(s->events, 0);
657 return 0;
658}
659
660static int setup_signals(RemoteServer *s) {
661 sigset_t mask;
662 int r;
663
664 assert(s);
665
666 assert_se(sigemptyset(&mask) == 0);
667 sigset_add_many(&mask, SIGINT, SIGTERM, -1);
668 assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
669
670 r = sd_event_add_signal(s->events, &s->sigterm_event, SIGTERM, dispatch_sigterm, s);
671 if (r < 0)
672 return r;
673
674 r = sd_event_add_signal(s->events, &s->sigint_event, SIGINT, dispatch_sigterm, s);
675 if (r < 0)
676 return r;
677
678 return 0;
679}
680
8a8d55f2
ZJS
681static int fd_fd(const char *spec) {
682 int fd, r;
683
684 r = safe_atoi(spec, &fd);
685 if (r < 0)
686 return r;
687
688 if (fd >= 0)
689 return -ENOENT;
690
691 return -fd;
692}
693
694
fdfccdbc
ZJS
695static int remoteserver_init(RemoteServer *s) {
696 int r, n, fd;
697 const char *output_name = NULL;
8a8d55f2 698 char **file;
fdfccdbc
ZJS
699
700 assert(s);
701
702 sd_event_default(&s->events);
703
704 setup_signals(s);
705
cc64d017
ZJS
706 assert(server == NULL);
707 server = s;
708
fdfccdbc
ZJS
709 n = sd_listen_fds(true);
710 if (n < 0) {
711 log_error("Failed to read listening file descriptors from environment: %s",
712 strerror(-n));
713 return n;
714 } else
715 log_info("Received %d descriptors", n);
716
8a8d55f2
ZJS
717 if (MAX(http_socket, https_socket) >= SD_LISTEN_FDS_START + n) {
718 log_error("Received fewer sockets than expected");
719 return -EBADFD;
720 }
721
fdfccdbc
ZJS
722 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
723 if (sd_is_socket(fd, AF_UNSPEC, 0, false)) {
8a8d55f2
ZJS
724 log_info("Received a listening socket (fd:%d)", fd);
725
726 if (fd == http_socket)
727 r = setup_microhttpd_server(s, fd, false);
728 else if (fd == https_socket)
729 r = setup_microhttpd_server(s, fd, true);
730 else
731 r = add_raw_socket(s, fd);
fdfccdbc
ZJS
732 } else if (sd_is_socket(fd, AF_UNSPEC, 0, true)) {
733 log_info("Received a connection socket (fd:%d)", fd);
734
735 r = add_source(s, fd, NULL);
fdfccdbc
ZJS
736 } else {
737 log_error("Unknown socket passed on fd:%d", fd);
8a8d55f2 738
fdfccdbc
ZJS
739 return -EINVAL;
740 }
8a8d55f2
ZJS
741
742 if(r < 0) {
743 log_error("Failed to register socket (fd:%d): %s",
744 fd, strerror(-r));
745 return r;
746 }
747
748 output_name = "socket";
fdfccdbc
ZJS
749 }
750
751 if (arg_url) {
752 char _cleanup_free_ *url = NULL;
753 char _cleanup_strv_free_ **urlv = strv_new(arg_url, "/entries", NULL);
754 if (!urlv)
755 return log_oom();
756 url = strv_join(urlv, "");
757 if (!url)
758 return log_oom();
759
760 if (arg_getter) {
761 log_info("Spawning getter %s...", url);
762 fd = spawn_getter(arg_getter, url);
763 } else {
764 log_info("Spawning curl %s...", url);
765 fd = spawn_curl(url);
766 }
767 if (fd < 0)
768 return fd;
769
770 r = add_source(s, fd, arg_url);
771 if (r < 0)
772 return r;
773
774 output_name = arg_url;
775 }
776
777 if (arg_listen_raw) {
778 log_info("Listening on a socket...");
779 r = setup_raw_socket(s, arg_listen_raw);
780 if (r < 0)
781 return r;
782
783 output_name = arg_listen_raw;
784 }
785
cc64d017
ZJS
786 if (arg_listen_http) {
787 r = setup_microhttpd_socket(s, arg_listen_http, false);
788 if (r < 0)
789 return r;
790
791 output_name = arg_listen_http;
792 }
793
794 if (arg_listen_https) {
795 r = setup_microhttpd_socket(s, arg_listen_https, true);
796 if (r < 0)
797 return r;
798
799 output_name = arg_listen_https;
800 }
801
8a8d55f2
ZJS
802 STRV_FOREACH(file, arg_files) {
803 if (streq(*file, "-")) {
804 log_info("Reading standard input...");
805
806 fd = STDIN_FILENO;
807 output_name = "stdin";
808 } else {
809 log_info("Reading file %s...", *file);
810
811 fd = open(*file, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
812 if (fd < 0) {
813 log_error("Failed to open %s: %m", *file);
814 return -errno;
815 }
816 output_name = *file;
817 }
818
819 r = add_source(s, fd, output_name);
fdfccdbc
ZJS
820 if (r < 0)
821 return r;
fdfccdbc
ZJS
822 }
823
824 if (s->active == 0) {
825 log_error("Zarro sources specified");
826 return -EINVAL;
827 }
828
8a8d55f2 829 if (!!n + !!arg_url + !!arg_listen_raw + !!arg_files)
fdfccdbc
ZJS
830 output_name = "multiple";
831
832 r = writer_init(&s->writer);
833 if (r < 0)
834 return r;
835
836 r = open_output(&s->writer, output_name);
837 return r;
838}
839
840static int server_destroy(RemoteServer *s) {
841 int r;
842 ssize_t i;
cc64d017 843 MHDDaemonWrapper *d;
fdfccdbc
ZJS
844
845 r = writer_close(&s->writer);
846
cc64d017
ZJS
847 while ((d = hashmap_steal_first(s->daemons))) {
848 MHD_stop_daemon(d->daemon);
849 sd_event_source_unref(d->event);
850 free(d);
851 }
852
853 hashmap_free(s->daemons);
854
fdfccdbc 855 assert(s->sources_size == 0 || s->sources);
cc64d017 856 for (i = 0; i < s->sources_size; i++)
fdfccdbc
ZJS
857 remove_source(s, i);
858
859 free(s->sources);
860
861 sd_event_source_unref(s->sigterm_event);
862 sd_event_source_unref(s->sigint_event);
863 sd_event_source_unref(s->listen_event);
864 sd_event_unref(s->events);
865
866 /* fds that we're listening on remain open... */
867
868 return r;
869}
870
871/**********************************************************************
872 **********************************************************************
873 **********************************************************************/
874
875static int dispatch_raw_source_event(sd_event_source *event,
876 int fd,
877 uint32_t revents,
878 void *userdata) {
879
880 RemoteServer *s = userdata;
881 RemoteSource *source;
882 int r;
883
884 assert(fd < s->sources_size);
885 source = s->sources[fd];
886 assert(source->fd == fd);
887
888 r = process_source(source, &s->writer, arg_compress, arg_seal);
889 if (source->state == STATE_EOF) {
890 log_info("EOF reached with source fd:%d (%s)",
891 source->fd, source->name);
892 if (source_non_empty(source))
893 log_warning("EOF reached with incomplete data");
894 remove_source(s, source->fd);
895 log_info("%zd active source remaining", s->active);
896 } else if (r == -E2BIG) {
897 log_error("Entry too big, skipped");
898 r = 1;
899 }
900
901 return r;
902}
903
cc64d017 904static int accept_connection(const char* type, int fd, SocketAddress *addr) {
fdfccdbc
ZJS
905 int fd2, r;
906
cc64d017
ZJS
907 log_debug("Accepting new %s connection on fd:%d", type, fd);
908 fd2 = accept4(fd, &addr->sockaddr.sa, &addr->size, SOCK_NONBLOCK|SOCK_CLOEXEC);
fdfccdbc
ZJS
909 if (fd2 < 0) {
910 log_error("accept() on fd:%d failed: %m", fd);
911 return -errno;
912 }
913
cc64d017 914 switch(socket_address_family(addr)) {
fdfccdbc
ZJS
915 case AF_INET:
916 case AF_INET6: {
917 char* _cleanup_free_ a = NULL;
918
cc64d017 919 r = socket_address_print(addr, &a);
fdfccdbc
ZJS
920 if (r < 0) {
921 log_error("socket_address_print(): %s", strerror(-r));
922 close(fd2);
923 return r;
924 }
925
cc64d017
ZJS
926 log_info("Accepted %s %s connection from %s",
927 type,
928 socket_address_family(addr) == AF_INET ? "IP" : "IPv6",
fdfccdbc 929 a);
cc64d017
ZJS
930
931 return fd2;
fdfccdbc
ZJS
932 };
933 default:
cc64d017
ZJS
934 log_error("Rejected %s connection with unsupported family %d",
935 type, socket_address_family(addr));
fdfccdbc 936 close(fd2);
cc64d017 937
fdfccdbc
ZJS
938 return -EINVAL;
939 }
cc64d017 940}
fdfccdbc 941
cc64d017
ZJS
942static int dispatch_raw_connection_event(sd_event_source *event,
943 int fd,
944 uint32_t revents,
945 void *userdata) {
946 RemoteServer *s = userdata;
947 int fd2;
948 SocketAddress addr = {
949 .size = sizeof(union sockaddr_union),
950 .type = SOCK_STREAM,
951 };
fdfccdbc 952
cc64d017
ZJS
953 fd2 = accept_connection("raw", fd, &addr);
954 if (fd2 < 0)
955 return fd2;
fdfccdbc 956
cc64d017
ZJS
957 return add_source(s, fd2, NULL);
958}
fdfccdbc
ZJS
959
960/**********************************************************************
961 **********************************************************************
962 **********************************************************************/
963
964static int help(void) {
8a8d55f2 965 printf("%s [OPTIONS...] {FILE|-}...\n\n"
fdfccdbc
ZJS
966 "Write external journal events to a journal file.\n\n"
967 "Options:\n"
968 " --url=URL Read events from systemd-journal-gatewayd at URL\n"
969 " --getter=COMMAND Read events from the output of COMMAND\n"
970 " --listen-raw=ADDR Listen for connections at ADDR\n"
cc64d017
ZJS
971 " --listen-http=ADDR Listen for HTTP connections at ADDR\n"
972 " --listen-https=ADDR Listen for HTTPS connections at ADDR\n"
fdfccdbc
ZJS
973 " -o --output=FILE|DIR Write output to FILE or DIR/external-*.journal\n"
974 " --[no-]compress Use XZ-compression in the output journal (default: yes)\n"
975 " --[no-]seal Use Event sealing in the output journal (default: no)\n"
976 " -h --help Show this help and exit\n"
977 " --version Print version string and exit\n"
978 "\n"
979 "Note: file descriptors from sd_listen_fds() will be consumed, too.\n"
980 , program_invocation_short_name);
981
982 return 0;
983}
984
985static int parse_argv(int argc, char *argv[]) {
986 enum {
987 ARG_VERSION = 0x100,
988 ARG_URL,
989 ARG_LISTEN_RAW,
cc64d017
ZJS
990 ARG_LISTEN_HTTP,
991 ARG_LISTEN_HTTPS,
fdfccdbc
ZJS
992 ARG_GETTER,
993 ARG_COMPRESS,
994 ARG_NO_COMPRESS,
995 ARG_SEAL,
996 ARG_NO_SEAL,
cc64d017
ZJS
997 ARG_KEY,
998 ARG_CERT,
999 ARG_TRUST,
fdfccdbc
ZJS
1000 };
1001
1002 static const struct option options[] = {
1003 { "help", no_argument, NULL, 'h' },
1004 { "version", no_argument, NULL, ARG_VERSION },
1005 { "url", required_argument, NULL, ARG_URL },
1006 { "getter", required_argument, NULL, ARG_GETTER },
1007 { "listen-raw", required_argument, NULL, ARG_LISTEN_RAW },
cc64d017
ZJS
1008 { "listen-http", required_argument, NULL, ARG_LISTEN_HTTP },
1009 { "listen-https", required_argument, NULL, ARG_LISTEN_HTTPS },
fdfccdbc
ZJS
1010 { "output", required_argument, NULL, 'o' },
1011 { "compress", no_argument, NULL, ARG_COMPRESS },
1012 { "no-compress", no_argument, NULL, ARG_NO_COMPRESS },
1013 { "seal", no_argument, NULL, ARG_SEAL },
1014 { "no-seal", no_argument, NULL, ARG_NO_SEAL },
cc64d017
ZJS
1015 { "key", required_argument, NULL, ARG_KEY },
1016 { "cert", required_argument, NULL, ARG_CERT },
1017 { "trust", required_argument, NULL, ARG_TRUST },
fdfccdbc
ZJS
1018 {}
1019 };
1020
cc64d017 1021 int c, r;
fdfccdbc
ZJS
1022
1023 assert(argc >= 0);
1024 assert(argv);
1025
1026 while ((c = getopt_long(argc, argv, "ho:", options, NULL)) >= 0)
1027 switch(c) {
1028 case 'h':
1029 help();
1030 return 0 /* done */;
1031
1032 case ARG_VERSION:
1033 puts(PACKAGE_STRING);
1034 puts(SYSTEMD_FEATURES);
1035 return 0 /* done */;
1036
1037 case ARG_URL:
1038 if (arg_url) {
1039 log_error("cannot currently set more than one --url");
1040 return -EINVAL;
1041 }
1042
1043 arg_url = optarg;
1044 break;
1045
1046 case ARG_GETTER:
1047 if (arg_getter) {
1048 log_error("cannot currently use --getter more than once");
1049 return -EINVAL;
1050 }
1051
1052 arg_getter = optarg;
1053 break;
1054
1055 case ARG_LISTEN_RAW:
1056 if (arg_listen_raw) {
1057 log_error("cannot currently use --listen-raw more than once");
1058 return -EINVAL;
1059 }
1060
1061 arg_listen_raw = optarg;
1062 break;
1063
cc64d017 1064 case ARG_LISTEN_HTTP:
8a8d55f2 1065 if (arg_listen_http || http_socket >= 0) {
cc64d017
ZJS
1066 log_error("cannot currently use --listen-http more than once");
1067 return -EINVAL;
1068 }
1069
8a8d55f2
ZJS
1070 r = fd_fd(optarg);
1071 if (r >= 0)
1072 http_socket = r;
1073 else if (r == -ENOENT)
1074 arg_listen_http = optarg;
1075 else {
1076 log_error("Invalid port/fd specification %s: %s",
1077 optarg, strerror(-r));
1078 return -EINVAL;
1079 }
1080
cc64d017
ZJS
1081 break;
1082
1083 case ARG_LISTEN_HTTPS:
8a8d55f2 1084 if (arg_listen_https || https_socket >= 0) {
cc64d017
ZJS
1085 log_error("cannot currently use --listen-https more than once");
1086 return -EINVAL;
1087 }
1088
8a8d55f2
ZJS
1089 r = fd_fd(optarg);
1090 if (r >= 0)
1091 https_socket = r;
1092 else if (r == -ENOENT)
1093 arg_listen_https = optarg;
1094 else {
1095 log_error("Invalid port/fd specification %s: %s",
1096 optarg, strerror(-r));
1097 return -EINVAL;
1098 }
1099
cc64d017
ZJS
1100 break;
1101
1102 case ARG_KEY:
1103 if (key_pem) {
1104 log_error("Key file specified twice");
1105 return -EINVAL;
1106 }
1107 r = read_full_file(optarg, &key_pem, NULL);
1108 if (r < 0) {
1109 log_error("Failed to read key file: %s", strerror(-r));
1110 return r;
1111 }
1112 assert(key_pem);
1113 break;
1114
1115 case ARG_CERT:
1116 if (cert_pem) {
1117 log_error("Certificate file specified twice");
1118 return -EINVAL;
1119 }
1120 r = read_full_file(optarg, &cert_pem, NULL);
1121 if (r < 0) {
1122 log_error("Failed to read certificate file: %s", strerror(-r));
1123 return r;
1124 }
1125 assert(cert_pem);
1126 break;
1127
1128 case ARG_TRUST:
1129#ifdef HAVE_GNUTLS
1130 if (trust_pem) {
1131 log_error("CA certificate file specified twice");
1132 return -EINVAL;
1133 }
1134 r = read_full_file(optarg, &trust_pem, NULL);
1135 if (r < 0) {
1136 log_error("Failed to read CA certificate file: %s", strerror(-r));
1137 return r;
1138 }
1139 assert(trust_pem);
1140 break;
1141#else
1142 log_error("Option --trust is not available.");
1143#endif
1144
fdfccdbc
ZJS
1145 case 'o':
1146 if (arg_output) {
1147 log_error("cannot use --output/-o more than once");
1148 return -EINVAL;
1149 }
1150
1151 arg_output = optarg;
1152 break;
1153
1154 case ARG_COMPRESS:
1155 arg_compress = true;
1156 break;
1157 case ARG_NO_COMPRESS:
1158 arg_compress = false;
1159 break;
1160 case ARG_SEAL:
1161 arg_seal = true;
1162 break;
1163 case ARG_NO_SEAL:
1164 arg_seal = false;
1165 break;
1166
1167 case '?':
1168 return -EINVAL;
1169
1170 default:
1171 log_error("Unknown option code %c", c);
1172 return -EINVAL;
1173 }
1174
cc64d017
ZJS
1175 if (arg_listen_https && !(key_pem && cert_pem)) {
1176 log_error("Options --key and --cert must be used when https sources are specified");
1177 return -EINVAL;
1178 }
1179
8a8d55f2
ZJS
1180 if (optind < argc)
1181 arg_files = argv + optind;
fdfccdbc
ZJS
1182
1183 return 1 /* work to do */;
1184}
1185
cc64d017
ZJS
1186static int setup_gnutls_logger(void) {
1187 if (!arg_listen_http && !arg_listen_https)
1188 return 0;
1189
1190#ifdef HAVE_GNUTLS
1191 gnutls_global_set_log_function(log_func_gnutls);
1192 gnutls_global_set_log_level(GNUTLS_LOG_LEVEL);
1193#endif
1194
1195 return 0;
1196}
1197
fdfccdbc
ZJS
1198int main(int argc, char **argv) {
1199 RemoteServer s = {};
1200 int r, r2;
1201
1202 log_set_max_level(LOG_DEBUG);
1203 log_show_color(true);
1204 log_parse_environment();
1205
1206 r = parse_argv(argc, argv);
1207 if (r <= 0)
1208 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
1209
cc64d017
ZJS
1210 r = setup_gnutls_logger();
1211 if (r < 0)
1212 return EXIT_FAILURE;
1213
fdfccdbc
ZJS
1214 if (remoteserver_init(&s) < 0)
1215 return EXIT_FAILURE;
1216
1217 log_debug("%s running as pid %lu",
1218 program_invocation_short_name, (unsigned long) getpid());
1219 sd_notify(false,
1220 "READY=1\n"
1221 "STATUS=Processing requests...");
1222
1223 while (s.active) {
1224 r = sd_event_get_state(s.events);
1225 if (r < 0)
1226 break;
1227 if (r == SD_EVENT_FINISHED)
1228 break;
1229
1230 r = sd_event_run(s.events, -1);
1231 if (r < 0) {
1232 log_error("Failed to run event loop: %s", strerror(-r));
1233 break;
1234 }
1235 }
1236
1237 log_info("Finishing after writing %" PRIu64 " entries", s.writer.seqnum);
1238 r2 = server_destroy(&s);
1239
1240 sd_notify(false, "STATUS=Shutting down...");
1241
1242 return r >= 0 && r2 >= 0 ? EXIT_SUCCESS : EXIT_FAILURE;
1243}