1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include <getopt.h>
4 #include <unistd.h>
5 
6 #include "sd-daemon.h"
7 
8 #include "conf-parser.h"
9 #include "daemon-util.h"
10 #include "def.h"
11 #include "fd-util.h"
12 #include "fileio.h"
13 #include "journal-remote-write.h"
14 #include "journal-remote.h"
15 #include "main-func.h"
16 #include "memory-util.h"
17 #include "parse-argument.h"
18 #include "pretty-print.h"
19 #include "process-util.h"
20 #include "rlimit-util.h"
21 #include "signal-util.h"
22 #include "socket-netlink.h"
23 #include "socket-util.h"
24 #include "stat-util.h"
25 #include "string-table.h"
26 #include "strv.h"
27 
28 #define PRIV_KEY_FILE CERTIFICATE_ROOT "/private/journal-remote.pem"
29 #define CERT_FILE     CERTIFICATE_ROOT "/certs/journal-remote.pem"
30 #define TRUST_FILE    CERTIFICATE_ROOT "/ca/trusted.pem"
31 
32 static const char* arg_url = NULL;
33 static const char* arg_getter = NULL;
34 static const char* arg_listen_raw = NULL;
35 static const char* arg_listen_http = NULL;
36 static const char* arg_listen_https = NULL;
37 static char** arg_files = NULL; /* Do not free this. */
38 static bool arg_compress = true;
39 static bool arg_seal = false;
40 static int http_socket = -1, https_socket = -1;
41 static char** arg_gnutls_log = NULL;
42 
43 static JournalWriteSplitMode arg_split_mode = _JOURNAL_WRITE_SPLIT_INVALID;
44 static const char* arg_output = NULL;
45 
46 static char *arg_key = NULL;
47 static char *arg_cert = NULL;
48 static char *arg_trust = NULL;
49 #if HAVE_GNUTLS
50 static bool arg_trust_all = false;
51 #else
52 static bool arg_trust_all = true;
53 #endif
54 
55 STATIC_DESTRUCTOR_REGISTER(arg_gnutls_log, strv_freep);
56 STATIC_DESTRUCTOR_REGISTER(arg_key, freep);
57 STATIC_DESTRUCTOR_REGISTER(arg_cert, freep);
58 STATIC_DESTRUCTOR_REGISTER(arg_trust, freep);
59 
60 static const char* const journal_write_split_mode_table[_JOURNAL_WRITE_SPLIT_MAX] = {
61         [JOURNAL_WRITE_SPLIT_NONE] = "none",
62         [JOURNAL_WRITE_SPLIT_HOST] = "host",
63 };
64 
65 DEFINE_PRIVATE_STRING_TABLE_LOOKUP(journal_write_split_mode, JournalWriteSplitMode);
66 static DEFINE_CONFIG_PARSE_ENUM(config_parse_write_split_mode,
67                                 journal_write_split_mode,
68                                 JournalWriteSplitMode,
69                                 "Failed to parse split mode setting");
70 
71 /**********************************************************************
72  **********************************************************************
73  **********************************************************************/
74 
spawn_child(const char * child,char ** argv)75 static int spawn_child(const char* child, char** argv) {
76         pid_t child_pid;
77         int fd[2], r;
78 
79         if (pipe(fd) < 0)
80                 return log_error_errno(errno, "Failed to create pager pipe: %m");
81 
82         r = safe_fork("(remote)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &child_pid);
83         if (r < 0) {
84                 safe_close_pair(fd);
85                 return r;
86         }
87 
88         /* In the child */
89         if (r == 0) {
90                 fd[0] = safe_close(fd[0]);
91 
92                 r = rearrange_stdio(STDIN_FILENO, TAKE_FD(fd[1]), STDERR_FILENO);
93                 if (r < 0) {
94                         log_error_errno(r, "Failed to dup pipe to stdout: %m");
95                         _exit(EXIT_FAILURE);
96                 }
97 
98                 (void) rlimit_nofile_safe();
99 
100                 execvp(child, argv);
101                 log_error_errno(errno, "Failed to exec child %s: %m", child);
102                 _exit(EXIT_FAILURE);
103         }
104 
105         safe_close(fd[1]);
106 
107         r = fd_nonblock(fd[0], true);
108         if (r < 0)
109                 log_warning_errno(errno, "Failed to set child pipe to non-blocking: %m");
110 
111         return fd[0];
112 }
113 
spawn_curl(const char * url)114 static int spawn_curl(const char* url) {
115         char **argv = STRV_MAKE("curl",
116                                 "-HAccept: application/vnd.fdo.journal",
117                                 "--silent",
118                                 "--show-error",
119                                 url);
120         int r;
121 
122         r = spawn_child("curl", argv);
123         if (r < 0)
124                 log_error_errno(r, "Failed to spawn curl: %m");
125         return r;
126 }
127 
spawn_getter(const char * getter)128 static int spawn_getter(const char *getter) {
129         int r;
130         _cleanup_strv_free_ char **words = NULL;
131 
132         assert(getter);
133         r = strv_split_full(&words, getter, WHITESPACE, EXTRACT_UNQUOTE);
134         if (r < 0)
135                 return log_error_errno(r, "Failed to split getter option: %m");
136 
137         r = spawn_child(words[0], words);
138         if (r < 0)
139                 log_error_errno(r, "Failed to spawn getter %s: %m", getter);
140 
141         return r;
142 }
143 
144 /**********************************************************************
145  **********************************************************************
146  **********************************************************************/
147 
148 static int null_timer_event_handler(sd_event_source *s,
149                                 uint64_t usec,
150                                 void *userdata);
151 static int dispatch_http_event(sd_event_source *event,
152                                int fd,
153                                uint32_t revents,
154                                void *userdata);
155 
request_meta(void ** connection_cls,int fd,char * hostname)156 static int request_meta(void **connection_cls, int fd, char *hostname) {
157         RemoteSource *source;
158         Writer *writer;
159         int r;
160 
161         assert(connection_cls);
162         if (*connection_cls)
163                 return 0;
164 
165         r = journal_remote_get_writer(journal_remote_server_global, hostname, &writer);
166         if (r < 0)
167                 return log_warning_errno(r, "Failed to get writer for source %s: %m",
168                                          hostname);
169 
170         source = source_new(fd, true, hostname, writer);
171         if (!source) {
172                 writer_unref(writer);
173                 return log_oom();
174         }
175 
176         log_debug("Added RemoteSource as connection metadata %p", source);
177 
178         *connection_cls = source;
179         return 0;
180 }
181 
request_meta_free(void * cls,struct MHD_Connection * connection,void ** connection_cls,enum MHD_RequestTerminationCode toe)182 static void request_meta_free(void *cls,
183                               struct MHD_Connection *connection,
184                               void **connection_cls,
185                               enum MHD_RequestTerminationCode toe) {
186         RemoteSource *s;
187 
188         assert(connection_cls);
189         s = *connection_cls;
190 
191         if (s) {
192                 log_debug("Cleaning up connection metadata %p", s);
193                 source_free(s);
194                 *connection_cls = NULL;
195         }
196 }
197 
process_http_upload(struct MHD_Connection * connection,const char * upload_data,size_t * upload_data_size,RemoteSource * source)198 static int process_http_upload(
199                 struct MHD_Connection *connection,
200                 const char *upload_data,
201                 size_t *upload_data_size,
202                 RemoteSource *source) {
203 
204         bool finished = false;
205         size_t remaining;
206         int r;
207 
208         assert(source);
209 
210         log_trace("%s: connection %p, %zu bytes",
211                   __func__, connection, *upload_data_size);
212 
213         if (*upload_data_size) {
214                 log_trace("Received %zu bytes", *upload_data_size);
215 
216                 r = journal_importer_push_data(&source->importer,
217                                                upload_data, *upload_data_size);
218                 if (r < 0)
219                         return mhd_respond_oom(connection);
220 
221                 *upload_data_size = 0;
222         } else
223                 finished = true;
224 
225         for (;;) {
226                 r = process_source(source, journal_remote_server_global->file_flags);
227                 if (r == -EAGAIN)
228                         break;
229                 if (r < 0) {
230                         if (r == -ENOBUFS)
231                                 log_warning_errno(r, "Entry is above the maximum of %u, aborting connection %p.",
232                                                   DATA_SIZE_MAX, connection);
233                         else if (r == -E2BIG)
234                                 log_warning_errno(r, "Entry with more fields than the maximum of %u, aborting connection %p.",
235                                                   ENTRY_FIELD_COUNT_MAX, connection);
236                         else
237                                 log_warning_errno(r, "Failed to process data, aborting connection %p: %m",
238                                                   connection);
239                         return MHD_NO;
240                 }
241         }
242 
243         if (!finished)
244                 return MHD_YES;
245 
246         /* The upload is finished */
247 
248         remaining = journal_importer_bytes_remaining(&source->importer);
249         if (remaining > 0) {
250                 log_warning("Premature EOF byte. %zu bytes lost.", remaining);
251                 return mhd_respondf(connection,
252                                     0, MHD_HTTP_EXPECTATION_FAILED,
253                                     "Premature EOF. %zu bytes of trailing data not processed.",
254                                     remaining);
255         }
256 
257         return mhd_respond(connection, MHD_HTTP_ACCEPTED, "OK.");
258 };
259 
request_handler(void * cls,struct MHD_Connection * connection,const char * url,const char * method,const char * version,const char * upload_data,size_t * upload_data_size,void ** connection_cls)260 static mhd_result request_handler(
261                 void *cls,
262                 struct MHD_Connection *connection,
263                 const char *url,
264                 const char *method,
265                 const char *version,
266                 const char *upload_data,
267                 size_t *upload_data_size,
268                 void **connection_cls) {
269 
270         const char *header;
271         int r, code, fd;
272         _cleanup_free_ char *hostname = NULL;
273         bool chunked = false;
274 
275         assert(connection);
276         assert(connection_cls);
277         assert(url);
278         assert(method);
279 
280         log_trace("Handling a connection %s %s %s", method, url, version);
281 
282         if (*connection_cls)
283                 return process_http_upload(connection,
284                                            upload_data, upload_data_size,
285                                            *connection_cls);
286 
287         if (!streq(method, "POST"))
288                 return mhd_respond(connection, MHD_HTTP_NOT_ACCEPTABLE, "Unsupported method.");
289 
290         if (!streq(url, "/upload"))
291                 return mhd_respond(connection, MHD_HTTP_NOT_FOUND, "Not found.");
292 
293         header = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "Content-Type");
294         if (!header || !streq(header, "application/vnd.fdo.journal"))
295                 return mhd_respond(connection, MHD_HTTP_UNSUPPORTED_MEDIA_TYPE,
296                                    "Content-Type: application/vnd.fdo.journal is required.");
297 
298         header = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "Transfer-Encoding");
299         if (header) {
300                 if (!strcaseeq(header, "chunked"))
301                         return mhd_respondf(connection, 0, MHD_HTTP_BAD_REQUEST,
302                                             "Unsupported Transfer-Encoding type: %s", header);
303 
304                 chunked = true;
305         }
306 
307         header = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "Content-Length");
308         if (header) {
309                 size_t len;
310 
311                 if (chunked)
312                         return mhd_respond(connection, MHD_HTTP_BAD_REQUEST,
313                                            "Content-Length must not specified when Transfer-Encoding type is 'chuncked'");
314 
315                 r = safe_atozu(header, &len);
316                 if (r < 0)
317                         return mhd_respondf(connection, r, MHD_HTTP_LENGTH_REQUIRED,
318                                             "Content-Length: %s cannot be parsed: %m", header);
319 
320                 if (len > ENTRY_SIZE_MAX)
321                         /* When serialized, an entry of maximum size might be slightly larger,
322                          * so this does not correspond exactly to the limit in journald. Oh well.
323                          */
324                         return mhd_respondf(connection, 0, MHD_HTTP_CONTENT_TOO_LARGE,
325                                             "Payload larger than maximum size of %u bytes", ENTRY_SIZE_MAX);
326         }
327 
328         {
329                 const union MHD_ConnectionInfo *ci;
330 
331                 ci = MHD_get_connection_info(connection,
332                                              MHD_CONNECTION_INFO_CONNECTION_FD);
333                 if (!ci) {
334                         log_error("MHD_get_connection_info failed: cannot get remote fd");
335                         return mhd_respond(connection, MHD_HTTP_INTERNAL_SERVER_ERROR,
336                                            "Cannot check remote address.");
337                 }
338 
339                 fd = ci->connect_fd;
340                 assert(fd >= 0);
341         }
342 
343         if (journal_remote_server_global->check_trust) {
344                 r = check_permissions(connection, &code, &hostname);
345                 if (r < 0)
346                         return code;
347         } else {
348                 r = getpeername_pretty(fd, false, &hostname);
349                 if (r < 0)
350                         return mhd_respond(connection, MHD_HTTP_INTERNAL_SERVER_ERROR,
351                                            "Cannot check remote hostname.");
352         }
353 
354         assert(hostname);
355 
356         r = request_meta(connection_cls, fd, hostname);
357         if (r == -ENOMEM)
358                 return respond_oom(connection);
359         else if (r < 0)
360                 return mhd_respondf(connection, r, MHD_HTTP_INTERNAL_SERVER_ERROR, "%m");
361 
362         hostname = NULL;
363         return MHD_YES;
364 }
365 
setup_microhttpd_server(RemoteServer * s,int fd,const char * key,const char * cert,const char * trust)366 static int setup_microhttpd_server(RemoteServer *s,
367                                    int fd,
368                                    const char *key,
369                                    const char *cert,
370                                    const char *trust) {
371         struct MHD_OptionItem opts[] = {
372                 { MHD_OPTION_NOTIFY_COMPLETED, (intptr_t) request_meta_free},
373                 { MHD_OPTION_EXTERNAL_LOGGER, (intptr_t) microhttpd_logger},
374                 { MHD_OPTION_LISTEN_SOCKET, fd},
375                 { MHD_OPTION_CONNECTION_MEMORY_LIMIT, 128*1024},
376                 { MHD_OPTION_END},
377                 { MHD_OPTION_END},
378                 { MHD_OPTION_END},
379                 { MHD_OPTION_END},
380                 { MHD_OPTION_END}};
381         int opts_pos = 4;
382         int flags =
383                 MHD_USE_DEBUG |
384                 MHD_USE_DUAL_STACK |
385                 MHD_USE_EPOLL |
386                 MHD_USE_ITC;
387 
388         const union MHD_DaemonInfo *info;
389         int r, epoll_fd;
390         MHDDaemonWrapper *d;
391 
392         assert(fd >= 0);
393 
394         r = fd_nonblock(fd, true);
395         if (r < 0)
396                 return log_error_errno(r, "Failed to make fd:%d nonblocking: %m", fd);
397 
398 /* MHD_OPTION_STRICT_FOR_CLIENT is introduced in microhttpd 0.9.54,
399  * and MHD_USE_PEDANTIC_CHECKS will be deprecated in future.
400  * If MHD_USE_PEDANTIC_CHECKS is '#define'd, then it is deprecated
401  * and we should use MHD_OPTION_STRICT_FOR_CLIENT. On the other hand,
402  * if MHD_USE_PEDANTIC_CHECKS is not '#define'd, then it is not
403  * deprecated yet and there exists an enum element with the same name.
404  * So we can safely use it. */
405 #ifdef MHD_USE_PEDANTIC_CHECKS
406         opts[opts_pos++] = (struct MHD_OptionItem)
407                 {MHD_OPTION_STRICT_FOR_CLIENT, 1};
408 #else
409         flags |= MHD_USE_PEDANTIC_CHECKS;
410 #endif
411 
412         if (key) {
413                 assert(cert);
414 
415                 opts[opts_pos++] = (struct MHD_OptionItem)
416                         {MHD_OPTION_HTTPS_MEM_KEY, 0, (char*) key};
417                 opts[opts_pos++] = (struct MHD_OptionItem)
418                         {MHD_OPTION_HTTPS_MEM_CERT, 0, (char*) cert};
419 
420                 flags |= MHD_USE_TLS;
421 
422                 if (trust)
423                         opts[opts_pos++] = (struct MHD_OptionItem)
424                                 {MHD_OPTION_HTTPS_MEM_TRUST, 0, (char*) trust};
425         }
426 
427         d = new(MHDDaemonWrapper, 1);
428         if (!d)
429                 return log_oom();
430 
431         d->fd = (uint64_t) fd;
432 
433         d->daemon = MHD_start_daemon(flags, 0,
434                                      NULL, NULL,
435                                      request_handler, NULL,
436                                      MHD_OPTION_ARRAY, opts,
437                                      MHD_OPTION_END);
438         if (!d->daemon) {
439                 log_error("Failed to start µhttp daemon");
440                 r = -EINVAL;
441                 goto error;
442         }
443 
444         log_debug("Started MHD %s daemon on fd:%d (wrapper @ %p)",
445                   key ? "HTTPS" : "HTTP", fd, d);
446 
447         info = MHD_get_daemon_info(d->daemon, MHD_DAEMON_INFO_EPOLL_FD_LINUX_ONLY);
448         if (!info) {
449                 log_error("µhttp returned NULL daemon info");
450                 r = -EOPNOTSUPP;
451                 goto error;
452         }
453 
454         epoll_fd = info->listen_fd;
455         if (epoll_fd < 0) {
456                 log_error("µhttp epoll fd is invalid");
457                 r = -EUCLEAN;
458                 goto error;
459         }
460 
461         r = sd_event_add_io(s->events, &d->io_event,
462                             epoll_fd, EPOLLIN,
463                             dispatch_http_event, d);
464         if (r < 0) {
465                 log_error_errno(r, "Failed to add event callback: %m");
466                 goto error;
467         }
468 
469         r = sd_event_source_set_description(d->io_event, "io_event");
470         if (r < 0) {
471                 log_error_errno(r, "Failed to set source name: %m");
472                 goto error;
473         }
474 
475         r = sd_event_add_time(s->events, &d->timer_event,
476                               CLOCK_MONOTONIC, UINT64_MAX, 0,
477                               null_timer_event_handler, d);
478         if (r < 0) {
479                 log_error_errno(r, "Failed to add timer_event: %m");
480                 goto error;
481         }
482 
483         r = sd_event_source_set_description(d->timer_event, "timer_event");
484         if (r < 0) {
485                 log_error_errno(r, "Failed to set source name: %m");
486                 goto error;
487         }
488 
489         r = hashmap_ensure_put(&s->daemons, &uint64_hash_ops, &d->fd, d);
490         if (r == -ENOMEM) {
491                 log_oom();
492                 goto error;
493         }
494         if (r < 0) {
495                 log_error_errno(r, "Failed to add daemon to hashmap: %m");
496                 goto error;
497         }
498 
499         s->active++;
500         return 0;
501 
502 error:
503         MHD_stop_daemon(d->daemon);
504         free(d->daemon);
505         free(d);
506         return r;
507 }
508 
setup_microhttpd_socket(RemoteServer * s,const char * address,const char * key,const char * cert,const char * trust)509 static int setup_microhttpd_socket(RemoteServer *s,
510                                    const char *address,
511                                    const char *key,
512                                    const char *cert,
513                                    const char *trust) {
514         int fd;
515 
516         fd = make_socket_fd(LOG_DEBUG, address, SOCK_STREAM, SOCK_CLOEXEC);
517         if (fd < 0)
518                 return fd;
519 
520         return setup_microhttpd_server(s, fd, key, cert, trust);
521 }
522 
null_timer_event_handler(sd_event_source * timer_event,uint64_t usec,void * userdata)523 static int null_timer_event_handler(sd_event_source *timer_event,
524                                     uint64_t usec,
525                                     void *userdata) {
526         return dispatch_http_event(timer_event, 0, 0, userdata);
527 }
528 
dispatch_http_event(sd_event_source * event,int fd,uint32_t revents,void * userdata)529 static int dispatch_http_event(sd_event_source *event,
530                                int fd,
531                                uint32_t revents,
532                                void *userdata) {
533         MHDDaemonWrapper *d = userdata;
534         int r;
535         MHD_UNSIGNED_LONG_LONG timeout = ULLONG_MAX;
536 
537         assert(d);
538 
539         r = MHD_run(d->daemon);
540         if (r == MHD_NO)
541                 // FIXME: unregister daemon
542                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
543                                        "MHD_run failed!");
544         if (MHD_get_timeout(d->daemon, &timeout) == MHD_NO)
545                 timeout = ULLONG_MAX;
546 
547         r = sd_event_source_set_time(d->timer_event, timeout);
548         if (r < 0) {
549                 log_warning_errno(r, "Unable to set event loop timeout: %m, this may result in indefinite blocking!");
550                 return 1;
551         }
552 
553         r = sd_event_source_set_enabled(d->timer_event, SD_EVENT_ON);
554         if (r < 0)
555                 log_warning_errno(r, "Unable to enable timer_event: %m, this may result in indefinite blocking!");
556 
557         return 1; /* work to do */
558 }
559 
560 /**********************************************************************
561  **********************************************************************
562  **********************************************************************/
563 
setup_signals(RemoteServer * s)564 static int setup_signals(RemoteServer *s) {
565         int r;
566 
567         assert(s);
568 
569         assert_se(sigprocmask_many(SIG_SETMASK, NULL, SIGINT, SIGTERM, -1) >= 0);
570 
571         r = sd_event_add_signal(s->events, &s->sigterm_event, SIGTERM, NULL, s);
572         if (r < 0)
573                 return r;
574 
575         r = sd_event_add_signal(s->events, &s->sigint_event, SIGINT, NULL, s);
576         if (r < 0)
577                 return r;
578 
579         return 0;
580 }
581 
setup_raw_socket(RemoteServer * s,const char * address)582 static int setup_raw_socket(RemoteServer *s, const char *address) {
583         int fd;
584 
585         fd = make_socket_fd(LOG_INFO, address, SOCK_STREAM, SOCK_CLOEXEC);
586         if (fd < 0)
587                 return fd;
588 
589         return journal_remote_add_raw_socket(s, fd);
590 }
591 
create_remoteserver(RemoteServer * s,const char * key,const char * cert,const char * trust)592 static int create_remoteserver(
593                 RemoteServer *s,
594                 const char* key,
595                 const char* cert,
596                 const char* trust) {
597 
598         int r, n, fd;
599 
600         r = journal_remote_server_init(
601                         s,
602                         arg_output,
603                         arg_split_mode,
604                         (arg_compress ? JOURNAL_COMPRESS : 0) |
605                         (arg_seal ? JOURNAL_SEAL : 0));
606         if (r < 0)
607                 return r;
608 
609         r = setup_signals(s);
610         if (r < 0)
611                 return log_error_errno(r, "Failed to set up signals: %m");
612 
613         n = sd_listen_fds(true);
614         if (n < 0)
615                 return log_error_errno(n, "Failed to read listening file descriptors from environment: %m");
616         else
617                 log_debug("Received %d descriptors", n);
618 
619         if (MAX(http_socket, https_socket) >= SD_LISTEN_FDS_START + n)
620                 return log_error_errno(SYNTHETIC_ERRNO(EBADFD),
621                                        "Received fewer sockets than expected");
622 
623         for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
624                 if (sd_is_socket(fd, AF_UNSPEC, 0, true)) {
625                         log_debug("Received a listening socket (fd:%d)", fd);
626 
627                         if (fd == http_socket)
628                                 r = setup_microhttpd_server(s, fd, NULL, NULL, NULL);
629                         else if (fd == https_socket)
630                                 r = setup_microhttpd_server(s, fd, key, cert, trust);
631                         else
632                                 r = journal_remote_add_raw_socket(s, fd);
633                 } else if (sd_is_socket(fd, AF_UNSPEC, 0, false)) {
634                         char *hostname;
635 
636                         r = getpeername_pretty(fd, false, &hostname);
637                         if (r < 0)
638                                 return log_error_errno(r, "Failed to retrieve remote name: %m");
639 
640                         log_debug("Received a connection socket (fd:%d) from %s", fd, hostname);
641 
642                         r = journal_remote_add_source(s, fd, hostname, true);
643                 } else
644                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
645                                                "Unknown socket passed on fd:%d", fd);
646 
647                 if (r < 0)
648                         return log_error_errno(r, "Failed to register socket (fd:%d): %m", fd);
649         }
650 
651         if (arg_getter) {
652                 log_info("Spawning getter %s...", arg_getter);
653                 fd = spawn_getter(arg_getter);
654                 if (fd < 0)
655                         return fd;
656 
657                 r = journal_remote_add_source(s, fd, (char*) arg_output, false);
658                 if (r < 0)
659                         return r;
660         }
661 
662         if (arg_url) {
663                 const char *url, *hostname;
664 
665                 if (!strstr(arg_url, "/entries")) {
666                         if (endswith(arg_url, "/"))
667                                 url = strjoina(arg_url, "entries");
668                         else
669                                 url = strjoina(arg_url, "/entries");
670                 } else
671                         url = strdupa_safe(arg_url);
672 
673                 log_info("Spawning curl %s...", url);
674                 fd = spawn_curl(url);
675                 if (fd < 0)
676                         return fd;
677 
678                 hostname = STARTSWITH_SET(arg_url, "https://", "http://");
679                 if (!hostname)
680                         hostname = arg_url;
681 
682                 hostname = strndupa_safe(hostname, strcspn(hostname, "/:"));
683 
684                 r = journal_remote_add_source(s, fd, (char *) hostname, false);
685                 if (r < 0)
686                         return r;
687         }
688 
689         if (arg_listen_raw) {
690                 log_debug("Listening on a socket...");
691                 r = setup_raw_socket(s, arg_listen_raw);
692                 if (r < 0)
693                         return r;
694         }
695 
696         if (arg_listen_http) {
697                 r = setup_microhttpd_socket(s, arg_listen_http, NULL, NULL, NULL);
698                 if (r < 0)
699                         return r;
700         }
701 
702         if (arg_listen_https) {
703                 r = setup_microhttpd_socket(s, arg_listen_https, key, cert, trust);
704                 if (r < 0)
705                         return r;
706         }
707 
708         STRV_FOREACH(file, arg_files) {
709                 const char *output_name;
710 
711                 if (streq(*file, "-")) {
712                         log_debug("Using standard input as source.");
713 
714                         fd = STDIN_FILENO;
715                         output_name = "stdin";
716                 } else {
717                         log_debug("Reading file %s...", *file);
718 
719                         fd = open(*file, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
720                         if (fd < 0)
721                                 return log_error_errno(errno, "Failed to open %s: %m", *file);
722                         output_name = *file;
723                 }
724 
725                 r = journal_remote_add_source(s, fd, (char*) output_name, false);
726                 if (r < 0)
727                         return r;
728         }
729 
730         if (s->active == 0)
731                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
732                                        "Zero sources specified");
733 
734         if (arg_split_mode == JOURNAL_WRITE_SPLIT_NONE) {
735                 /* In this case we know what the writer will be
736                    called, so we can create it and verify that we can
737                    create output as expected. */
738                 r = journal_remote_get_writer(s, NULL, &s->_single_writer);
739                 if (r < 0)
740                         return r;
741         }
742 
743         return 0;
744 }
745 
negative_fd(const char * spec)746 static int negative_fd(const char *spec) {
747         /* Return a non-positive number as its inverse, -EINVAL otherwise. */
748 
749         int fd, r;
750 
751         r = safe_atoi(spec, &fd);
752         if (r < 0)
753                 return r;
754 
755         if (fd > 0)
756                 return -EINVAL;
757         else
758                 return -fd;
759 }
760 
parse_config(void)761 static int parse_config(void) {
762         const ConfigTableItem items[] = {
763                 { "Remote",  "Seal",                   config_parse_bool,             0, &arg_seal       },
764                 { "Remote",  "SplitMode",              config_parse_write_split_mode, 0, &arg_split_mode },
765                 { "Remote",  "ServerKeyFile",          config_parse_path,             0, &arg_key        },
766                 { "Remote",  "ServerCertificateFile",  config_parse_path,             0, &arg_cert       },
767                 { "Remote",  "TrustedCertificateFile", config_parse_path,             0, &arg_trust      },
768                 {}
769         };
770 
771         return config_parse_many_nulstr(
772                         PKGSYSCONFDIR "/journal-remote.conf",
773                         CONF_PATHS_NULSTR("systemd/journal-remote.conf.d"),
774                         "Remote\0",
775                         config_item_table_lookup, items,
776                         CONFIG_PARSE_WARN,
777                         NULL,
778                         NULL);
779 }
780 
help(void)781 static int help(void) {
782         _cleanup_free_ char *link = NULL;
783         int r;
784 
785         r = terminal_urlify_man("systemd-journal-remote.service", "8", &link);
786         if (r < 0)
787                 return log_oom();
788 
789         printf("%s [OPTIONS...] {FILE|-}...\n\n"
790                "Write external journal events to journal file(s).\n\n"
791                "  -h --help                 Show this help\n"
792                "     --version              Show package version\n"
793                "     --url=URL              Read events from systemd-journal-gatewayd at URL\n"
794                "     --getter=COMMAND       Read events from the output of COMMAND\n"
795                "     --listen-raw=ADDR      Listen for connections at ADDR\n"
796                "     --listen-http=ADDR     Listen for HTTP connections at ADDR\n"
797                "     --listen-https=ADDR    Listen for HTTPS connections at ADDR\n"
798                "  -o --output=FILE|DIR      Write output to FILE or DIR/external-*.journal\n"
799                "     --compress[=BOOL]      Use compression in the output journal (default: yes)\n"
800                "     --seal[=BOOL]          Use event sealing (default: no)\n"
801                "     --key=FILENAME         SSL key in PEM format (default:\n"
802                "                            \"" PRIV_KEY_FILE "\")\n"
803                "     --cert=FILENAME        SSL certificate in PEM format (default:\n"
804                "                            \"" CERT_FILE "\")\n"
805                "     --trust=FILENAME|all   SSL CA certificate or disable checking (default:\n"
806                "                            \"" TRUST_FILE "\")\n"
807                "     --gnutls-log=CATEGORY...\n"
808                "                            Specify a list of gnutls logging categories\n"
809                "     --split-mode=none|host How many output files to create\n"
810                "\nNote: file descriptors from sd_listen_fds() will be consumed, too.\n"
811                "\nSee the %s for details.\n",
812                program_invocation_short_name,
813                link);
814 
815         return 0;
816 }
817 
parse_argv(int argc,char * argv[])818 static int parse_argv(int argc, char *argv[]) {
819         enum {
820                 ARG_VERSION = 0x100,
821                 ARG_URL,
822                 ARG_LISTEN_RAW,
823                 ARG_LISTEN_HTTP,
824                 ARG_LISTEN_HTTPS,
825                 ARG_GETTER,
826                 ARG_SPLIT_MODE,
827                 ARG_COMPRESS,
828                 ARG_SEAL,
829                 ARG_KEY,
830                 ARG_CERT,
831                 ARG_TRUST,
832                 ARG_GNUTLS_LOG,
833         };
834 
835         static const struct option options[] = {
836                 { "help",         no_argument,       NULL, 'h'              },
837                 { "version",      no_argument,       NULL, ARG_VERSION      },
838                 { "url",          required_argument, NULL, ARG_URL          },
839                 { "getter",       required_argument, NULL, ARG_GETTER       },
840                 { "listen-raw",   required_argument, NULL, ARG_LISTEN_RAW   },
841                 { "listen-http",  required_argument, NULL, ARG_LISTEN_HTTP  },
842                 { "listen-https", required_argument, NULL, ARG_LISTEN_HTTPS },
843                 { "output",       required_argument, NULL, 'o'              },
844                 { "split-mode",   required_argument, NULL, ARG_SPLIT_MODE   },
845                 { "compress",     optional_argument, NULL, ARG_COMPRESS     },
846                 { "seal",         optional_argument, NULL, ARG_SEAL         },
847                 { "key",          required_argument, NULL, ARG_KEY          },
848                 { "cert",         required_argument, NULL, ARG_CERT         },
849                 { "trust",        required_argument, NULL, ARG_TRUST        },
850                 { "gnutls-log",   required_argument, NULL, ARG_GNUTLS_LOG   },
851                 {}
852         };
853 
854         int c, r;
855         bool type_a, type_b;
856 
857         assert(argc >= 0);
858         assert(argv);
859 
860         while ((c = getopt_long(argc, argv, "ho:", options, NULL)) >= 0)
861                 switch (c) {
862 
863                 case 'h':
864                         return help();
865 
866                 case ARG_VERSION:
867                         return version();
868 
869                 case ARG_URL:
870                         if (arg_url)
871                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
872                                                        "cannot currently set more than one --url");
873 
874                         arg_url = optarg;
875                         break;
876 
877                 case ARG_GETTER:
878                         if (arg_getter)
879                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
880                                                        "cannot currently use --getter more than once");
881 
882                         arg_getter = optarg;
883                         break;
884 
885                 case ARG_LISTEN_RAW:
886                         if (arg_listen_raw)
887                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
888                                                        "cannot currently use --listen-raw more than once");
889 
890                         arg_listen_raw = optarg;
891                         break;
892 
893                 case ARG_LISTEN_HTTP:
894                         if (arg_listen_http || http_socket >= 0)
895                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
896                                                        "cannot currently use --listen-http more than once");
897 
898                         r = negative_fd(optarg);
899                         if (r >= 0)
900                                 http_socket = r;
901                         else
902                                 arg_listen_http = optarg;
903                         break;
904 
905                 case ARG_LISTEN_HTTPS:
906                         if (arg_listen_https || https_socket >= 0)
907                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
908                                                        "cannot currently use --listen-https more than once");
909 
910                         r = negative_fd(optarg);
911                         if (r >= 0)
912                                 https_socket = r;
913                         else
914                                 arg_listen_https = optarg;
915 
916                         break;
917 
918                 case ARG_KEY:
919                         if (arg_key)
920                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
921                                                        "Key file specified twice");
922 
923                         arg_key = strdup(optarg);
924                         if (!arg_key)
925                                 return log_oom();
926 
927                         break;
928 
929                 case ARG_CERT:
930                         if (arg_cert)
931                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
932                                                        "Certificate file specified twice");
933 
934                         arg_cert = strdup(optarg);
935                         if (!arg_cert)
936                                 return log_oom();
937 
938                         break;
939 
940                 case ARG_TRUST:
941 #if HAVE_GNUTLS
942                         if (arg_trust || arg_trust_all)
943                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
944                                                        "Confusing trusted CA configuration");
945 
946                         if (streq(optarg, "all"))
947                                 arg_trust_all = true;
948                         else {
949                                 arg_trust = strdup(optarg);
950                                 if (!arg_trust)
951                                         return log_oom();
952                         }
953 #else
954                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
955                                                "Option --trust is not available.");
956 #endif
957                         break;
958 
959                 case 'o':
960                         if (arg_output)
961                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
962                                                        "cannot use --output/-o more than once");
963 
964                         arg_output = optarg;
965                         break;
966 
967                 case ARG_SPLIT_MODE:
968                         arg_split_mode = journal_write_split_mode_from_string(optarg);
969                         if (arg_split_mode == _JOURNAL_WRITE_SPLIT_INVALID)
970                                 return log_error_errno(arg_split_mode, "Invalid split mode: %s", optarg);
971                         break;
972 
973                 case ARG_COMPRESS:
974                         r = parse_boolean_argument("--compress", optarg, &arg_compress);
975                         if (r < 0)
976                                 return r;
977                         break;
978 
979                 case ARG_SEAL:
980                         r = parse_boolean_argument("--seal", optarg, &arg_seal);
981                         if (r < 0)
982                                 return r;
983                         break;
984 
985                 case ARG_GNUTLS_LOG:
986 #if HAVE_GNUTLS
987                         for (const char* p = optarg;;) {
988                                 _cleanup_free_ char *word = NULL;
989 
990                                 r = extract_first_word(&p, &word, ",", 0);
991                                 if (r < 0)
992                                         return log_error_errno(r, "Failed to parse --gnutls-log= argument: %m");
993                                 if (r == 0)
994                                         break;
995 
996                                 if (strv_push(&arg_gnutls_log, word) < 0)
997                                         return log_oom();
998 
999                                 word = NULL;
1000                         }
1001                         break;
1002 #else
1003                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1004                                                "Option --gnutls-log is not available.");
1005 #endif
1006 
1007                 case '?':
1008                         return -EINVAL;
1009 
1010                 default:
1011                         assert_not_reached();
1012                 }
1013 
1014         if (optind < argc)
1015                 arg_files = argv + optind;
1016 
1017         type_a = arg_getter || !strv_isempty(arg_files);
1018         type_b = arg_url
1019                 || arg_listen_raw
1020                 || arg_listen_http || arg_listen_https
1021                 || sd_listen_fds(false) > 0;
1022         if (type_a && type_b)
1023                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1024                                        "Cannot use file input or --getter with "
1025                                        "--arg-listen-... or socket activation.");
1026         if (type_a) {
1027                 if (!arg_output)
1028                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1029                                                "Option --output must be specified with file input or --getter.");
1030 
1031                 if (!IN_SET(arg_split_mode, JOURNAL_WRITE_SPLIT_NONE, _JOURNAL_WRITE_SPLIT_INVALID))
1032                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1033                                                "For active sources, only --split-mode=none is allowed.");
1034 
1035                 arg_split_mode = JOURNAL_WRITE_SPLIT_NONE;
1036         }
1037 
1038         if (arg_split_mode == _JOURNAL_WRITE_SPLIT_INVALID)
1039                 arg_split_mode = JOURNAL_WRITE_SPLIT_HOST;
1040 
1041         if (arg_split_mode == JOURNAL_WRITE_SPLIT_NONE && arg_output) {
1042                 if (is_dir(arg_output, true) > 0)
1043                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1044                                                "For SplitMode=none, output must be a file.");
1045                 if (!endswith(arg_output, ".journal"))
1046                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1047                                                "For SplitMode=none, output file name must end with .journal.");
1048         }
1049 
1050         if (arg_split_mode == JOURNAL_WRITE_SPLIT_HOST
1051             && arg_output && is_dir(arg_output, true) <= 0)
1052                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1053                                        "For SplitMode=host, output must be a directory.");
1054 
1055         log_debug("Full config: SplitMode=%s Key=%s Cert=%s Trust=%s",
1056                   journal_write_split_mode_to_string(arg_split_mode),
1057                   strna(arg_key),
1058                   strna(arg_cert),
1059                   strna(arg_trust));
1060 
1061         return 1 /* work to do */;
1062 }
1063 
load_certificates(char ** key,char ** cert,char ** trust)1064 static int load_certificates(char **key, char **cert, char **trust) {
1065         int r;
1066 
1067         r = read_full_file_full(
1068                         AT_FDCWD, arg_key ?: PRIV_KEY_FILE, UINT64_MAX, SIZE_MAX,
1069                         READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET,
1070                         NULL,
1071                         key, NULL);
1072         if (r < 0)
1073                 return log_error_errno(r, "Failed to read key from file '%s': %m",
1074                                        arg_key ?: PRIV_KEY_FILE);
1075 
1076         r = read_full_file_full(
1077                         AT_FDCWD, arg_cert ?: CERT_FILE, UINT64_MAX, SIZE_MAX,
1078                         READ_FULL_FILE_CONNECT_SOCKET,
1079                         NULL,
1080                         cert, NULL);
1081         if (r < 0)
1082                 return log_error_errno(r, "Failed to read certificate from file '%s': %m",
1083                                        arg_cert ?: CERT_FILE);
1084 
1085         if (arg_trust_all)
1086                 log_info("Certificate checking disabled.");
1087         else {
1088                 r = read_full_file_full(
1089                                 AT_FDCWD, arg_trust ?: TRUST_FILE, UINT64_MAX, SIZE_MAX,
1090                                 READ_FULL_FILE_CONNECT_SOCKET,
1091                                 NULL,
1092                                 trust, NULL);
1093                 if (r < 0)
1094                         return log_error_errno(r, "Failed to read CA certificate file '%s': %m",
1095                                                arg_trust ?: TRUST_FILE);
1096         }
1097 
1098         if ((arg_listen_raw || arg_listen_http) && *trust)
1099                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1100                                        "Option --trust makes all non-HTTPS connections untrusted.");
1101 
1102         return 0;
1103 }
1104 
run(int argc,char ** argv)1105 static int run(int argc, char **argv) {
1106         _cleanup_(journal_remote_server_destroy) RemoteServer s = {};
1107         _unused_ _cleanup_(notify_on_cleanup) const char *notify_message = NULL;
1108         _cleanup_(erase_and_freep) char *key = NULL;
1109         _cleanup_free_ char *cert = NULL, *trust = NULL;
1110         int r;
1111 
1112         log_show_color(true);
1113         log_parse_environment();
1114 
1115         /* The journal merging logic potentially needs a lot of fds. */
1116         (void) rlimit_nofile_bump(HIGH_RLIMIT_NOFILE);
1117 
1118         r = parse_config();
1119         if (r < 0)
1120                 return r;
1121 
1122         r = parse_argv(argc, argv);
1123         if (r <= 0)
1124                 return r;
1125 
1126         if (arg_listen_http || arg_listen_https) {
1127                 r = setup_gnutls_logger(arg_gnutls_log);
1128                 if (r < 0)
1129                         return r;
1130         }
1131 
1132         if (arg_listen_https || https_socket >= 0) {
1133                 r = load_certificates(&key, &cert, &trust);
1134                 if (r < 0)
1135                         return r;
1136 
1137                 s.check_trust = !arg_trust_all;
1138         }
1139 
1140         r = create_remoteserver(&s, key, cert, trust);
1141         if (r < 0)
1142                 return r;
1143 
1144         r = sd_event_set_watchdog(s.events, true);
1145         if (r < 0)
1146                 return log_error_errno(r, "Failed to enable watchdog: %m");
1147 
1148         log_debug("Watchdog is %sd.", enable_disable(r > 0));
1149 
1150         log_debug("%s running as pid "PID_FMT,
1151                   program_invocation_short_name, getpid_cached());
1152 
1153         notify_message = notify_start(NOTIFY_READY, NOTIFY_STOPPING);
1154 
1155         while (s.active) {
1156                 r = sd_event_get_state(s.events);
1157                 if (r < 0)
1158                         return r;
1159                 if (r == SD_EVENT_FINISHED)
1160                         break;
1161 
1162                 r = sd_event_run(s.events, -1);
1163                 if (r < 0)
1164                         return log_error_errno(r, "Failed to run event loop: %m");
1165         }
1166 
1167         notify_message = NULL;
1168         (void) sd_notifyf(false,
1169                           "STOPPING=1\n"
1170                           "STATUS=Shutting down after writing %" PRIu64 " entries...", s.event_count);
1171 
1172         log_info("Finishing after writing %" PRIu64 " entries", s.event_count);
1173 
1174         return 0;
1175 }
1176 
1177 DEFINE_MAIN_FUNCTION(run);
1178