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