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