chiark / gitweb /
journal-upload: add watchdog support
[elogind.git] / src / journal-remote / journal-remote.c
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"
44 #include "fileio.h"
45 #include "microhttpd-util.h"
46
47 #ifdef HAVE_GNUTLS
48 #include <gnutls/gnutls.h>
49 #endif
50
51 #include "journal-remote-parse.h"
52 #include "journal-remote-write.h"
53
54 #define REMOTE_JOURNAL_PATH "/var/log/journal/" SD_ID128_FORMAT_STR "/remote-%s.journal"
55
56 static char* arg_output = NULL;
57 static char* arg_url = NULL;
58 static char* arg_getter = NULL;
59 static char* arg_listen_raw = NULL;
60 static char* arg_listen_http = NULL;
61 static char* arg_listen_https = NULL;
62 static char** arg_files = NULL;
63 static int arg_compress = true;
64 static int arg_seal = false;
65 static int http_socket = -1, https_socket = -1;
66 static char** arg_gnutls_log = NULL;
67
68 static char *key_pem = NULL;
69 static char *cert_pem = NULL;
70 static char *trust_pem = NULL;
71
72 /**********************************************************************
73  **********************************************************************
74  **********************************************************************/
75
76 static 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                 safe_close_pair(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                 safe_close_pair(fd);
105
106                 /* Make sure the child goes away when the parent dies */
107                 if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
108                         _exit(EXIT_FAILURE);
109
110                 /* Check whether our parent died before we were able
111                  * to set the death signal */
112                 if (getppid() != parent_pid)
113                         _exit(EXIT_SUCCESS);
114
115                 execvp(child, argv);
116                 log_error("Failed to exec child %s: %m", child);
117                 _exit(EXIT_FAILURE);
118         }
119
120         r = close(fd[1]);
121         if (r < 0)
122                 log_warning("Failed to close write end of pipe: %m");
123
124         return fd[0];
125 }
126
127 static int spawn_curl(char* url) {
128         char **argv = STRV_MAKE("curl",
129                                 "-HAccept: application/vnd.fdo.journal",
130                                 "--silent",
131                                 "--show-error",
132                                 url);
133         int r;
134
135         r = spawn_child("curl", argv);
136         if (r < 0)
137                 log_error("Failed to spawn curl: %m");
138         return r;
139 }
140
141 static int spawn_getter(char *getter, char *url) {
142         int r;
143         _cleanup_strv_free_ char **words = NULL;
144
145         assert(getter);
146         words = strv_split_quoted(getter);
147         if (!words)
148                 return log_oom();
149
150         r = spawn_child(words[0], words);
151         if (r < 0)
152                 log_error("Failed to spawn getter %s: %m", getter);
153
154         return r;
155 }
156
157 static int open_output(Writer *s, const char* url) {
158         _cleanup_free_ char *name, *output = NULL;
159         char *c;
160         int r;
161
162         assert(url);
163         name = strdup(url);
164         if (!name)
165                 return log_oom();
166
167         for(c = name; *c; c++) {
168                 if (*c == '/' || *c == ':' || *c == ' ')
169                         *c = '~';
170                 else if (*c == '?') {
171                         *c = '\0';
172                         break;
173                 }
174         }
175
176         if (!arg_output) {
177                 sd_id128_t machine;
178                 r = sd_id128_get_machine(&machine);
179                 if (r < 0) {
180                         log_error("failed to determine machine ID128: %s", strerror(-r));
181                         return r;
182                 }
183
184                 r = asprintf(&output, REMOTE_JOURNAL_PATH,
185                              SD_ID128_FORMAT_VAL(machine), name);
186                 if (r < 0)
187                         return log_oom();
188         } else {
189                 r = is_dir(arg_output, true);
190                 if (r > 0) {
191                         r = asprintf(&output,
192                                      "%s/remote-%s.journal", arg_output, name);
193                         if (r < 0)
194                                 return log_oom();
195                 } else {
196                         output = strdup(arg_output);
197                         if (!output)
198                                 return log_oom();
199                 }
200         }
201
202         r = journal_file_open_reliably(output,
203                                        O_RDWR|O_CREAT, 0640,
204                                        arg_compress, arg_seal,
205                                        &s->metrics,
206                                        s->mmap,
207                                        NULL, &s->journal);
208         if (r < 0)
209                 log_error("Failed to open output journal %s: %s",
210                           arg_output, strerror(-r));
211         else
212                 log_info("Opened output file %s", s->journal->path);
213         return r;
214 }
215
216 /**********************************************************************
217  **********************************************************************
218  **********************************************************************/
219
220 typedef struct MHDDaemonWrapper {
221         uint64_t fd;
222         struct MHD_Daemon *daemon;
223
224         sd_event_source *event;
225 } MHDDaemonWrapper;
226
227 typedef struct RemoteServer {
228         RemoteSource **sources;
229         size_t sources_size;
230         size_t active;
231
232         sd_event *events;
233         sd_event_source *sigterm_event, *sigint_event, *listen_event;
234
235         Writer writer;
236
237         Hashmap *daemons;
238 } RemoteServer;
239
240 /* This should go away as soon as Âµhttpd allows state to be passed around. */
241 static RemoteServer *server;
242
243 static int dispatch_raw_source_event(sd_event_source *event,
244                                      int fd,
245                                      uint32_t revents,
246                                      void *userdata);
247 static int dispatch_raw_connection_event(sd_event_source *event,
248                                          int fd,
249                                          uint32_t revents,
250                                          void *userdata);
251 static int dispatch_http_event(sd_event_source *event,
252                                int fd,
253                                uint32_t revents,
254                                void *userdata);
255
256 static int get_source_for_fd(RemoteServer *s, int fd, RemoteSource **source) {
257         assert(fd >= 0);
258         assert(source);
259
260         if (!GREEDY_REALLOC0(s->sources, s->sources_size, fd + 1))
261                 return log_oom();
262
263         if (s->sources[fd] == NULL) {
264                 s->sources[fd] = new0(RemoteSource, 1);
265                 if (!s->sources[fd])
266                         return log_oom();
267                 s->sources[fd]->fd = -1;
268                 s->active++;
269         }
270
271         *source = s->sources[fd];
272         return 0;
273 }
274
275 static int remove_source(RemoteServer *s, int fd) {
276         RemoteSource *source;
277
278         assert(s);
279         assert(fd >= 0 && fd < (ssize_t) s->sources_size);
280
281         source = s->sources[fd];
282         if (source) {
283                 source_free(source);
284                 s->sources[fd] = NULL;
285                 s->active--;
286         }
287
288         close(fd);
289
290         return 0;
291 }
292
293 static int add_source(RemoteServer *s, int fd, const char* name) {
294         RemoteSource *source = NULL;
295         _cleanup_free_ char *realname = NULL;
296         int r;
297
298         assert(s);
299         assert(fd >= 0);
300
301         if (name) {
302                 realname = strdup(name);
303                 if (!realname)
304                         return log_oom();
305         } else {
306                 r = asprintf(&realname, "fd:%d", fd);
307                 if (r < 0)
308                         return log_oom();
309         }
310
311         log_debug("Creating source for fd:%d (%s)", fd, realname);
312
313         r = get_source_for_fd(s, fd, &source);
314         if (r < 0) {
315                 log_error("Failed to create source for fd:%d (%s)", fd, realname);
316                 return r;
317         }
318         assert(source);
319         assert(source->fd < 0);
320         source->fd = fd;
321
322         r = sd_event_add_io(s->events, &source->event,
323                             fd, EPOLLIN, dispatch_raw_source_event, s);
324         if (r < 0) {
325                 log_error("Failed to register event source for fd:%d: %s",
326                           fd, strerror(-r));
327                 goto error;
328         }
329
330         return 1; /* work to do */
331
332  error:
333         remove_source(s, fd);
334         return r;
335 }
336
337 static int add_raw_socket(RemoteServer *s, int fd) {
338         int r;
339
340         r = sd_event_add_io(s->events, &s->listen_event, fd, EPOLLIN,
341                             dispatch_raw_connection_event, s);
342         if (r < 0) {
343                 close(fd);
344                 return r;
345         }
346
347         s->active ++;
348         return 0;
349 }
350
351 static int setup_raw_socket(RemoteServer *s, const char *address) {
352         int fd;
353
354         fd = make_socket_fd(LOG_INFO, address, SOCK_STREAM | SOCK_CLOEXEC);
355         if (fd < 0)
356                 return fd;
357
358         return add_raw_socket(s, fd);
359 }
360
361 /**********************************************************************
362  **********************************************************************
363  **********************************************************************/
364
365 static RemoteSource *request_meta(void **connection_cls) {
366         RemoteSource *source;
367
368         assert(connection_cls);
369         if (*connection_cls)
370                 return *connection_cls;
371
372         source = new0(RemoteSource, 1);
373         if (!source)
374                 return NULL;
375         source->fd = -1;
376
377         log_debug("Added RemoteSource as connection metadata %p", source);
378
379         *connection_cls = source;
380         return source;
381 }
382
383 static void request_meta_free(void *cls,
384                               struct MHD_Connection *connection,
385                               void **connection_cls,
386                               enum MHD_RequestTerminationCode toe) {
387         RemoteSource *s;
388
389         assert(connection_cls);
390         s = *connection_cls;
391
392         log_debug("Cleaning up connection metadata %p", s);
393         source_free(s);
394         *connection_cls = NULL;
395 }
396
397 static int process_http_upload(
398                 struct MHD_Connection *connection,
399                 const char *upload_data,
400                 size_t *upload_data_size,
401                 RemoteSource *source) {
402
403         bool finished = false;
404         int r;
405
406         assert(source);
407
408         log_debug("request_handler_upload: connection %p, %zu bytes",
409                   connection, *upload_data_size);
410
411         if (*upload_data_size) {
412                 log_info("Received %zu bytes", *upload_data_size);
413
414                 r = push_data(source, upload_data, *upload_data_size);
415                 if (r < 0)
416                         return mhd_respond_oom(connection);
417
418                 *upload_data_size = 0;
419         } else
420                 finished = true;
421
422         while (true) {
423                 r = process_source(source, &server->writer, arg_compress, arg_seal);
424                 if (r == -EAGAIN || r == -EWOULDBLOCK)
425                         break;
426                 else if (r < 0) {
427                         log_warning("Failed to process data for connection %p", connection);
428                         if (r == -E2BIG)
429                                 return mhd_respondf(connection,
430                                                     MHD_HTTP_REQUEST_ENTITY_TOO_LARGE,
431                                                     "Entry is too large, maximum is %u bytes.\n",
432                                                     DATA_SIZE_MAX);
433                         else
434                                 return mhd_respondf(connection,
435                                                     MHD_HTTP_UNPROCESSABLE_ENTITY,
436                                                     "Processing failed: %s.", strerror(-r));
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");
447                 return mhd_respond(connection, MHD_HTTP_EXPECTATION_FAILED,
448                                    "Trailing data not processed.");
449         }
450
451         return mhd_respond(connection, MHD_HTTP_ACCEPTED, "OK.\n");
452 };
453
454 static 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"))
480                 return mhd_respond(connection, MHD_HTTP_METHOD_NOT_ACCEPTABLE,
481                                    "Unsupported method.\n");
482
483         if (!streq(url, "/upload"))
484                 return mhd_respond(connection, MHD_HTTP_NOT_FOUND,
485                                    "Not found.\n");
486
487         header = MHD_lookup_connection_value(connection,
488                                              MHD_HEADER_KIND, "Content-Type");
489         if (!header || !streq(header, "application/vnd.fdo.journal"))
490                 return mhd_respond(connection, MHD_HTTP_UNSUPPORTED_MEDIA_TYPE,
491                                    "Content-Type: application/vnd.fdo.journal"
492                                    " is required.\n");
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
505 static 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
603 error:
604         MHD_stop_daemon(d->daemon);
605         free(d->daemon);
606         free(d);
607         return r;
608 }
609
610 static 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
622 static 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         r = MHD_run(d->daemon);
632         if (r == MHD_NO) {
633                 log_error("MHD_run failed!");
634                 // XXX: unregister daemon
635                 return -EINVAL;
636         }
637
638         return 1; /* work to do */
639 }
640
641 /**********************************************************************
642  **********************************************************************
643  **********************************************************************/
644
645 static int dispatch_sigterm(sd_event_source *event,
646                             const struct signalfd_siginfo *si,
647                             void *userdata) {
648         RemoteServer *s = userdata;
649
650         assert(s);
651
652         log_received_signal(LOG_INFO, si);
653
654         sd_event_exit(s->events, 0);
655         return 0;
656 }
657
658 static int setup_signals(RemoteServer *s) {
659         sigset_t mask;
660         int r;
661
662         assert(s);
663
664         assert_se(sigemptyset(&mask) == 0);
665         sigset_add_many(&mask, SIGINT, SIGTERM, -1);
666         assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
667
668         r = sd_event_add_signal(s->events, &s->sigterm_event, SIGTERM, dispatch_sigterm, s);
669         if (r < 0)
670                 return r;
671
672         r = sd_event_add_signal(s->events, &s->sigint_event, SIGINT, dispatch_sigterm, s);
673         if (r < 0)
674                 return r;
675
676         return 0;
677 }
678
679 static int fd_fd(const char *spec) {
680         int fd, r;
681
682         r = safe_atoi(spec, &fd);
683         if (r < 0)
684                 return r;
685
686         if (fd >= 0)
687                 return -ENOENT;
688
689         return -fd;
690 }
691
692
693 static int remoteserver_init(RemoteServer *s) {
694         int r, n, fd;
695         const char *output_name = NULL;
696         char **file;
697
698         assert(s);
699
700         sd_event_default(&s->events);
701
702         setup_signals(s);
703
704         assert(server == NULL);
705         server = s;
706
707         n = sd_listen_fds(true);
708         if (n < 0) {
709                 log_error("Failed to read listening file descriptors from environment: %s",
710                           strerror(-n));
711                 return n;
712         } else
713                 log_info("Received %d descriptors", n);
714
715         if (MAX(http_socket, https_socket) >= SD_LISTEN_FDS_START + n) {
716                 log_error("Received fewer sockets than expected");
717                 return -EBADFD;
718         }
719
720         for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
721                 if (sd_is_socket(fd, AF_UNSPEC, 0, false)) {
722                         log_info("Received a listening socket (fd:%d)", fd);
723
724                         if (fd == http_socket)
725                                 r = setup_microhttpd_server(s, fd, false);
726                         else if (fd == https_socket)
727                                 r = setup_microhttpd_server(s, fd, true);
728                         else
729                                 r = add_raw_socket(s, fd);
730                 } else if (sd_is_socket(fd, AF_UNSPEC, 0, true)) {
731                         log_info("Received a connection socket (fd:%d)", fd);
732
733                         r = add_source(s, fd, NULL);
734                 } else {
735                         log_error("Unknown socket passed on fd:%d", fd);
736
737                         return -EINVAL;
738                 }
739
740                 if(r < 0) {
741                         log_error("Failed to register socket (fd:%d): %s",
742                                   fd, strerror(-r));
743                         return r;
744                 }
745
746                 output_name = "socket";
747         }
748
749         if (arg_url) {
750                 _cleanup_free_ char *url = NULL;
751                 _cleanup_strv_free_ char **urlv = strv_new(arg_url, "/entries", NULL);
752                 if (!urlv)
753                         return log_oom();
754                 url = strv_join(urlv, "");
755                 if (!url)
756                         return log_oom();
757
758                 if (arg_getter) {
759                         log_info("Spawning getter %s...", url);
760                         fd = spawn_getter(arg_getter, url);
761                 } else {
762                         log_info("Spawning curl %s...", url);
763                         fd = spawn_curl(url);
764                 }
765                 if (fd < 0)
766                         return fd;
767
768                 r = add_source(s, fd, arg_url);
769                 if (r < 0)
770                         return r;
771
772                 output_name = arg_url;
773         }
774
775         if (arg_listen_raw) {
776                 log_info("Listening on a socket...");
777                 r = setup_raw_socket(s, arg_listen_raw);
778                 if (r < 0)
779                         return r;
780
781                 output_name = arg_listen_raw;
782         }
783
784         if (arg_listen_http) {
785                 r = setup_microhttpd_socket(s, arg_listen_http, false);
786                 if (r < 0)
787                         return r;
788
789                 output_name = arg_listen_http;
790         }
791
792         if (arg_listen_https) {
793                 r = setup_microhttpd_socket(s, arg_listen_https, true);
794                 if (r < 0)
795                         return r;
796
797                 output_name = arg_listen_https;
798         }
799
800         STRV_FOREACH(file, arg_files) {
801                 if (streq(*file, "-")) {
802                         log_info("Reading standard input...");
803
804                         fd = STDIN_FILENO;
805                         output_name = "stdin";
806                 } else {
807                         log_info("Reading file %s...", *file);
808
809                         fd = open(*file, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
810                         if (fd < 0) {
811                                 log_error("Failed to open %s: %m", *file);
812                                 return -errno;
813                         }
814                         output_name = *file;
815                 }
816
817                 r = add_source(s, fd, output_name);
818                 if (r < 0)
819                         return r;
820         }
821
822         if (s->active == 0) {
823                 log_error("Zarro sources specified");
824                 return -EINVAL;
825         }
826
827         if (!!n + !!arg_url + !!arg_listen_raw + !!arg_files)
828                 output_name = "multiple";
829
830         r = writer_init(&s->writer);
831         if (r < 0)
832                 return r;
833
834         r = open_output(&s->writer, output_name);
835         return r;
836 }
837
838 static int server_destroy(RemoteServer *s) {
839         int r;
840         size_t i;
841         MHDDaemonWrapper *d;
842
843         r = writer_close(&s->writer);
844
845         while ((d = hashmap_steal_first(s->daemons))) {
846                 MHD_stop_daemon(d->daemon);
847                 sd_event_source_unref(d->event);
848                 free(d);
849         }
850
851         hashmap_free(s->daemons);
852
853         assert(s->sources_size == 0 || s->sources);
854         for (i = 0; i < s->sources_size; i++)
855                 remove_source(s, i);
856
857         free(s->sources);
858
859         sd_event_source_unref(s->sigterm_event);
860         sd_event_source_unref(s->sigint_event);
861         sd_event_source_unref(s->listen_event);
862         sd_event_unref(s->events);
863
864         /* fds that we're listening on remain open... */
865
866         return r;
867 }
868
869 /**********************************************************************
870  **********************************************************************
871  **********************************************************************/
872
873 static int dispatch_raw_source_event(sd_event_source *event,
874                                      int fd,
875                                      uint32_t revents,
876                                      void *userdata) {
877
878         RemoteServer *s = userdata;
879         RemoteSource *source;
880         int r;
881
882         assert(fd >= 0 && fd < (ssize_t) s->sources_size);
883         source = s->sources[fd];
884         assert(source->fd == fd);
885
886         r = process_source(source, &s->writer, arg_compress, arg_seal);
887         if (source->state == STATE_EOF) {
888                 log_info("EOF reached with source fd:%d (%s)",
889                          source->fd, source->name);
890                 if (source_non_empty(source))
891                         log_warning("EOF reached with incomplete data");
892                 remove_source(s, source->fd);
893                 log_info("%zd active source remaining", s->active);
894         } else if (r == -E2BIG) {
895                 log_error("Entry too big, skipped");
896                 r = 1;
897         }
898
899         return r;
900 }
901
902 static int accept_connection(const char* type, int fd, SocketAddress *addr) {
903         int fd2, r;
904
905         log_debug("Accepting new %s connection on fd:%d", type, fd);
906         fd2 = accept4(fd, &addr->sockaddr.sa, &addr->size, SOCK_NONBLOCK|SOCK_CLOEXEC);
907         if (fd2 < 0) {
908                 log_error("accept() on fd:%d failed: %m", fd);
909                 return -errno;
910         }
911
912         switch(socket_address_family(addr)) {
913         case AF_INET:
914         case AF_INET6: {
915                 char* _cleanup_free_ a = NULL;
916
917                 r = socket_address_print(addr, &a);
918                 if (r < 0) {
919                         log_error("socket_address_print(): %s", strerror(-r));
920                         close(fd2);
921                         return r;
922                 }
923
924                 log_info("Accepted %s %s connection from %s",
925                          type,
926                          socket_address_family(addr) == AF_INET ? "IP" : "IPv6",
927                          a);
928
929                 return fd2;
930         };
931         default:
932                 log_error("Rejected %s connection with unsupported family %d",
933                           type, socket_address_family(addr));
934                 close(fd2);
935
936                 return -EINVAL;
937         }
938 }
939
940 static int dispatch_raw_connection_event(sd_event_source *event,
941                                          int fd,
942                                          uint32_t revents,
943                                          void *userdata) {
944         RemoteServer *s = userdata;
945         int fd2;
946         SocketAddress addr = {
947                 .size = sizeof(union sockaddr_union),
948                 .type = SOCK_STREAM,
949         };
950
951         fd2 = accept_connection("raw", fd, &addr);
952         if (fd2 < 0)
953                 return fd2;
954
955         return add_source(s, fd2, NULL);
956 }
957
958 /**********************************************************************
959  **********************************************************************
960  **********************************************************************/
961
962 static void help(void) {
963         printf("%s [OPTIONS...] {FILE|-}...\n\n"
964                "Write external journal events to a journal file.\n\n"
965                "Options:\n"
966                "  --url=URL            Read events from systemd-journal-gatewayd at URL\n"
967                "  --getter=COMMAND     Read events from the output of COMMAND\n"
968                "  --listen-raw=ADDR    Listen for connections at ADDR\n"
969                "  --listen-http=ADDR   Listen for HTTP connections at ADDR\n"
970                "  --listen-https=ADDR  Listen for HTTPS connections at ADDR\n"
971                "  -o --output=FILE|DIR Write output to FILE or DIR/external-*.journal\n"
972                "  --[no-]compress      Use XZ-compression in the output journal (default: yes)\n"
973                "  --[no-]seal          Use Event sealing in the output journal (default: no)\n"
974                "  --key=FILENAME       Specify key in PEM format\n"
975                "  --cert=FILENAME      Specify certificate in PEM format\n"
976                "  --trust=FILENAME     Specify CA certificate in PEM format\n"
977                "  --gnutls-log=CATEGORY...\n"
978                "                       Specify a list of gnutls logging categories\n"
979                "  -h --help            Show this help and exit\n"
980                "  --version            Print version string and exit\n"
981                "\n"
982                "Note: file descriptors from sd_listen_fds() will be consumed, too.\n"
983                , program_invocation_short_name);
984 }
985
986 static int parse_argv(int argc, char *argv[]) {
987         enum {
988                 ARG_VERSION = 0x100,
989                 ARG_URL,
990                 ARG_LISTEN_RAW,
991                 ARG_LISTEN_HTTP,
992                 ARG_LISTEN_HTTPS,
993                 ARG_GETTER,
994                 ARG_COMPRESS,
995                 ARG_NO_COMPRESS,
996                 ARG_SEAL,
997                 ARG_NO_SEAL,
998                 ARG_KEY,
999                 ARG_CERT,
1000                 ARG_TRUST,
1001                 ARG_GNUTLS_LOG,
1002         };
1003
1004         static const struct option options[] = {
1005                 { "help",         no_argument,       NULL, 'h'              },
1006                 { "version",      no_argument,       NULL, ARG_VERSION      },
1007                 { "url",          required_argument, NULL, ARG_URL          },
1008                 { "getter",       required_argument, NULL, ARG_GETTER       },
1009                 { "listen-raw",   required_argument, NULL, ARG_LISTEN_RAW   },
1010                 { "listen-http",  required_argument, NULL, ARG_LISTEN_HTTP  },
1011                 { "listen-https", required_argument, NULL, ARG_LISTEN_HTTPS },
1012                 { "output",       required_argument, NULL, 'o'              },
1013                 { "compress",     no_argument,       NULL, ARG_COMPRESS     },
1014                 { "no-compress",  no_argument,       NULL, ARG_NO_COMPRESS  },
1015                 { "seal",         no_argument,       NULL, ARG_SEAL         },
1016                 { "no-seal",      no_argument,       NULL, ARG_NO_SEAL      },
1017                 { "key",          required_argument, NULL, ARG_KEY          },
1018                 { "cert",         required_argument, NULL, ARG_CERT         },
1019                 { "trust",        required_argument, NULL, ARG_TRUST        },
1020                 { "gnutls-log",   required_argument, NULL, ARG_GNUTLS_LOG   },
1021                 {}
1022         };
1023
1024         int c, r;
1025
1026         assert(argc >= 0);
1027         assert(argv);
1028
1029         while ((c = getopt_long(argc, argv, "ho:", options, NULL)) >= 0)
1030                 switch(c) {
1031                 case 'h':
1032                         help();
1033                         return 0 /* done */;
1034
1035                 case ARG_VERSION:
1036                         puts(PACKAGE_STRING);
1037                         puts(SYSTEMD_FEATURES);
1038                         return 0 /* done */;
1039
1040                 case ARG_URL:
1041                         if (arg_url) {
1042                                 log_error("cannot currently set more than one --url");
1043                                 return -EINVAL;
1044                         }
1045
1046                         arg_url = optarg;
1047                         break;
1048
1049                 case ARG_GETTER:
1050                         if (arg_getter) {
1051                                 log_error("cannot currently use --getter more than once");
1052                                 return -EINVAL;
1053                         }
1054
1055                         arg_getter = optarg;
1056                         break;
1057
1058                 case ARG_LISTEN_RAW:
1059                         if (arg_listen_raw) {
1060                                 log_error("cannot currently use --listen-raw more than once");
1061                                 return -EINVAL;
1062                         }
1063
1064                         arg_listen_raw = optarg;
1065                         break;
1066
1067                 case ARG_LISTEN_HTTP:
1068                         if (arg_listen_http || http_socket >= 0) {
1069                                 log_error("cannot currently use --listen-http more than once");
1070                                 return -EINVAL;
1071                         }
1072
1073                         r = fd_fd(optarg);
1074                         if (r >= 0)
1075                                 http_socket = r;
1076                         else if (r == -ENOENT)
1077                                 arg_listen_http = optarg;
1078                         else {
1079                                 log_error("Invalid port/fd specification %s: %s",
1080                                           optarg, strerror(-r));
1081                                 return -EINVAL;
1082                         }
1083
1084                         break;
1085
1086                 case ARG_LISTEN_HTTPS:
1087                         if (arg_listen_https || https_socket >= 0) {
1088                                 log_error("cannot currently use --listen-https more than once");
1089                                 return -EINVAL;
1090                         }
1091
1092                         r = fd_fd(optarg);
1093                         if (r >= 0)
1094                                 https_socket = r;
1095                         else if (r == -ENOENT)
1096                                 arg_listen_https = optarg;
1097                         else {
1098                                 log_error("Invalid port/fd specification %s: %s",
1099                                           optarg, strerror(-r));
1100                                 return -EINVAL;
1101                         }
1102
1103                         break;
1104
1105                 case ARG_KEY:
1106                         if (key_pem) {
1107                                 log_error("Key file specified twice");
1108                                 return -EINVAL;
1109                         }
1110                         r = read_full_file(optarg, &key_pem, NULL);
1111                         if (r < 0) {
1112                                 log_error("Failed to read key file: %s", strerror(-r));
1113                                 return r;
1114                         }
1115                         assert(key_pem);
1116                         break;
1117
1118                 case ARG_CERT:
1119                         if (cert_pem) {
1120                                 log_error("Certificate file specified twice");
1121                                 return -EINVAL;
1122                         }
1123                         r = read_full_file(optarg, &cert_pem, NULL);
1124                         if (r < 0) {
1125                                 log_error("Failed to read certificate file: %s", strerror(-r));
1126                                 return r;
1127                         }
1128                         assert(cert_pem);
1129                         break;
1130
1131                 case ARG_TRUST:
1132 #ifdef HAVE_GNUTLS
1133                         if (trust_pem) {
1134                                 log_error("CA certificate file specified twice");
1135                                 return -EINVAL;
1136                         }
1137                         r = read_full_file(optarg, &trust_pem, NULL);
1138                         if (r < 0) {
1139                                 log_error("Failed to read CA certificate file: %s", strerror(-r));
1140                                 return r;
1141                         }
1142                         assert(trust_pem);
1143                         break;
1144 #else
1145                         log_error("Option --trust is not available.");
1146                         return -EINVAL;
1147 #endif
1148
1149                 case 'o':
1150                         if (arg_output) {
1151                                 log_error("cannot use --output/-o more than once");
1152                                 return -EINVAL;
1153                         }
1154
1155                         arg_output = optarg;
1156                         break;
1157
1158                 case ARG_COMPRESS:
1159                         arg_compress = true;
1160                         break;
1161                 case ARG_NO_COMPRESS:
1162                         arg_compress = false;
1163                         break;
1164                 case ARG_SEAL:
1165                         arg_seal = true;
1166                         break;
1167                 case ARG_NO_SEAL:
1168                         arg_seal = false;
1169                         break;
1170
1171                 case ARG_GNUTLS_LOG: {
1172 #ifdef HAVE_GNUTLS
1173                         char *word, *state;
1174                         size_t size;
1175
1176                         FOREACH_WORD_SEPARATOR(word, size, optarg, ",", state) {
1177                                 char *cat;
1178
1179                                 cat = strndup(word, size);
1180                                 if (!cat)
1181                                         return log_oom();
1182
1183                                 if (strv_consume(&arg_gnutls_log, cat) < 0)
1184                                         return log_oom();
1185                         }
1186                         break;
1187 #else
1188                         log_error("Option --gnutls-log is not available.");
1189                         return -EINVAL;
1190 #endif
1191                 }
1192
1193                 case '?':
1194                         return -EINVAL;
1195
1196                 default:
1197                         log_error("Unknown option code %c", c);
1198                         return -EINVAL;
1199                 }
1200
1201         if (arg_listen_https && !(key_pem && cert_pem)) {
1202                 log_error("Options --key and --cert must be used when using HTTPS.");
1203                 return -EINVAL;
1204         }
1205
1206         if (optind < argc)
1207                 arg_files = argv + optind;
1208
1209         return 1 /* work to do */;
1210 }
1211
1212 static int setup_gnutls_logger(char **categories) {
1213         if (!arg_listen_http && !arg_listen_https)
1214                 return 0;
1215
1216 #ifdef HAVE_GNUTLS
1217         {
1218                 char **cat;
1219                 int r;
1220
1221                 gnutls_global_set_log_function(log_func_gnutls);
1222
1223                 if (categories)
1224                         STRV_FOREACH(cat, categories) {
1225                                 r = log_enable_gnutls_category(*cat);
1226                                 if (r < 0)
1227                                         return r;
1228                         }
1229                 else
1230                         log_reset_gnutls_level();
1231         }
1232 #endif
1233
1234         return 0;
1235 }
1236
1237 int main(int argc, char **argv) {
1238         RemoteServer s = {};
1239         int r, r2;
1240
1241         log_show_color(true);
1242         log_parse_environment();
1243
1244         r = parse_argv(argc, argv);
1245         if (r <= 0)
1246                 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
1247
1248         r = setup_gnutls_logger(arg_gnutls_log);
1249         if (r < 0)
1250                 return EXIT_FAILURE;
1251
1252         if (remoteserver_init(&s) < 0)
1253                 return EXIT_FAILURE;
1254
1255         sd_event_set_watchdog(s.events, true);
1256
1257         log_debug("%s running as pid "PID_FMT,
1258                   program_invocation_short_name, getpid());
1259         sd_notify(false,
1260                   "READY=1\n"
1261                   "STATUS=Processing requests...");
1262
1263         while (s.active) {
1264                 r = sd_event_get_state(s.events);
1265                 if (r < 0)
1266                         break;
1267                 if (r == SD_EVENT_FINISHED)
1268                         break;
1269
1270                 r = sd_event_run(s.events, -1);
1271                 if (r < 0) {
1272                         log_error("Failed to run event loop: %s", strerror(-r));
1273                         break;
1274                 }
1275         }
1276
1277         log_info("Finishing after writing %" PRIu64 " entries", s.writer.seqnum);
1278         r2 = server_destroy(&s);
1279
1280         sd_notify(false, "STATUS=Shutting down...");
1281
1282         return r >= 0 && r2 >= 0 ? EXIT_SUCCESS : EXIT_FAILURE;
1283 }