]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal-remote/journal-upload.c
tree-wide: simplify x ? x : y to x ?: y where applicable
[thirdparty/systemd.git] / src / journal-remote / journal-upload.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <curl/curl.h>
4 #include <fcntl.h>
5 #include <getopt.h>
6 #include <stdio.h>
7 #include <sys/stat.h>
8 #include <unistd.h>
9
10 #include "sd-daemon.h"
11
12 #include "alloc-util.h"
13 #include "build.h"
14 #include "conf-parser.h"
15 #include "constants.h"
16 #include "daemon-util.h"
17 #include "env-file.h"
18 #include "fd-util.h"
19 #include "fileio.h"
20 #include "format-util.h"
21 #include "fs-util.h"
22 #include "glob-util.h"
23 #include "journal-upload.h"
24 #include "log.h"
25 #include "main-func.h"
26 #include "mkdir.h"
27 #include "parse-argument.h"
28 #include "parse-helpers.h"
29 #include "pretty-print.h"
30 #include "process-util.h"
31 #include "rlimit-util.h"
32 #include "sigbus.h"
33 #include "signal-util.h"
34 #include "string-util.h"
35 #include "strv.h"
36 #include "tmpfile-util.h"
37 #include "version.h"
38
39 #define PRIV_KEY_FILE CERTIFICATE_ROOT "/private/journal-upload.pem"
40 #define CERT_FILE CERTIFICATE_ROOT "/certs/journal-upload.pem"
41 #define TRUST_FILE CERTIFICATE_ROOT "/ca/trusted.pem"
42 #define DEFAULT_PORT 19532
43
44 static const char* arg_url = NULL;
45 static const char *arg_key = NULL;
46 static const char *arg_cert = NULL;
47 static const char *arg_trust = NULL;
48 static const char *arg_directory = NULL;
49 static char **arg_file = NULL;
50 static const char *arg_cursor = NULL;
51 static bool arg_after_cursor = false;
52 static int arg_journal_type = 0;
53 static const char *arg_machine = NULL;
54 static bool arg_merge = false;
55 static int arg_follow = -1;
56 static const char *arg_save_state = NULL;
57 static usec_t arg_network_timeout_usec = USEC_INFINITY;
58
59 static void close_fd_input(Uploader *u);
60
61 #define SERVER_ANSWER_KEEP 2048
62
63 #define STATE_FILE "/var/lib/systemd/journal-upload/state"
64
65 #define easy_setopt(curl, opt, value, level, cmd) \
66 do { \
67 code = curl_easy_setopt(curl, opt, value); \
68 if (code) { \
69 log_full(level, \
70 "curl_easy_setopt " #opt " failed: %s", \
71 curl_easy_strerror(code)); \
72 cmd; \
73 } \
74 } while (0)
75
76 DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(CURL*, curl_easy_cleanup, NULL);
77 DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct curl_slist*, curl_slist_free_all, NULL);
78
79 static size_t output_callback(char *buf,
80 size_t size,
81 size_t nmemb,
82 void *userp) {
83 Uploader *u = ASSERT_PTR(userp);
84
85 log_debug("The server answers (%zu bytes): %.*s",
86 size*nmemb, (int)(size*nmemb), buf);
87
88 if (nmemb && !u->answer) {
89 u->answer = strndup(buf, size*nmemb);
90 if (!u->answer)
91 log_warning("Failed to store server answer (%zu bytes): out of memory", size*nmemb);
92 }
93
94 return size * nmemb;
95 }
96
97 static int check_cursor_updating(Uploader *u) {
98 _cleanup_free_ char *temp_path = NULL;
99 _cleanup_fclose_ FILE *f = NULL;
100 int r;
101
102 if (!u->state_file)
103 return 0;
104
105 r = mkdir_parents(u->state_file, 0755);
106 if (r < 0)
107 return log_error_errno(r, "Cannot create parent directory of state file %s: %m",
108 u->state_file);
109
110 r = fopen_temporary(u->state_file, &f, &temp_path);
111 if (r < 0)
112 return log_error_errno(r, "Cannot save state to %s: %m",
113 u->state_file);
114 (void) unlink(temp_path);
115
116 return 0;
117 }
118
119 static int update_cursor_state(Uploader *u) {
120 _cleanup_(unlink_and_freep) char *temp_path = NULL;
121 _cleanup_fclose_ FILE *f = NULL;
122 int r;
123
124 if (!u->state_file || !u->last_cursor)
125 return 0;
126
127 r = fopen_temporary(u->state_file, &f, &temp_path);
128 if (r < 0)
129 goto fail;
130
131 fprintf(f,
132 "# This is private data. Do not parse.\n"
133 "LAST_CURSOR=%s\n",
134 u->last_cursor);
135
136 r = fflush_and_check(f);
137 if (r < 0)
138 goto fail;
139
140 if (rename(temp_path, u->state_file) < 0) {
141 r = -errno;
142 goto fail;
143 }
144
145 temp_path = mfree(temp_path);
146 return 0;
147
148 fail:
149 (void) unlink(u->state_file);
150
151 return log_error_errno(r, "Failed to save state %s: %m", u->state_file);
152 }
153
154 static int load_cursor_state(Uploader *u) {
155 int r;
156
157 if (!u->state_file)
158 return 0;
159
160 r = parse_env_file(NULL, u->state_file, "LAST_CURSOR", &u->last_cursor);
161 if (r == -ENOENT)
162 log_debug("State file %s is not present.", u->state_file);
163 else if (r < 0)
164 return log_error_errno(r, "Failed to read state file %s: %m",
165 u->state_file);
166 else
167 log_debug("Last cursor was %s", u->last_cursor);
168
169 return 0;
170 }
171
172 int start_upload(Uploader *u,
173 size_t (*input_callback)(void *ptr,
174 size_t size,
175 size_t nmemb,
176 void *userdata),
177 void *data) {
178 CURLcode code;
179
180 assert(u);
181 assert(input_callback);
182
183 if (!u->header) {
184 _cleanup_(curl_slist_free_allp) struct curl_slist *h = NULL;
185 struct curl_slist *l;
186
187 h = curl_slist_append(NULL, "Content-Type: application/vnd.fdo.journal");
188 if (!h)
189 return log_oom();
190
191 l = curl_slist_append(h, "Transfer-Encoding: chunked");
192 if (!l)
193 return log_oom();
194 h = l;
195
196 l = curl_slist_append(h, "Accept: text/plain");
197 if (!l)
198 return log_oom();
199 h = l;
200
201 u->header = TAKE_PTR(h);
202 }
203
204 if (!u->easy) {
205 _cleanup_(curl_easy_cleanupp) CURL *curl = NULL;
206
207 curl = curl_easy_init();
208 if (!curl)
209 return log_error_errno(SYNTHETIC_ERRNO(ENOSR),
210 "Call to curl_easy_init failed.");
211
212 /* If configured, set a timeout for the curl operation. */
213 if (arg_network_timeout_usec != USEC_INFINITY)
214 easy_setopt(curl, CURLOPT_TIMEOUT,
215 (long) DIV_ROUND_UP(arg_network_timeout_usec, USEC_PER_SEC),
216 LOG_ERR, return -EXFULL);
217
218 /* tell it to POST to the URL */
219 easy_setopt(curl, CURLOPT_POST, 1L,
220 LOG_ERR, return -EXFULL);
221
222 easy_setopt(curl, CURLOPT_ERRORBUFFER, u->error,
223 LOG_ERR, return -EXFULL);
224
225 /* set where to write to */
226 easy_setopt(curl, CURLOPT_WRITEFUNCTION, output_callback,
227 LOG_ERR, return -EXFULL);
228
229 easy_setopt(curl, CURLOPT_WRITEDATA, data,
230 LOG_ERR, return -EXFULL);
231
232 /* set where to read from */
233 easy_setopt(curl, CURLOPT_READFUNCTION, input_callback,
234 LOG_ERR, return -EXFULL);
235
236 easy_setopt(curl, CURLOPT_READDATA, data,
237 LOG_ERR, return -EXFULL);
238
239 /* use our special own mime type and chunked transfer */
240 easy_setopt(curl, CURLOPT_HTTPHEADER, u->header,
241 LOG_ERR, return -EXFULL);
242
243 if (DEBUG_LOGGING)
244 /* enable verbose for easier tracing */
245 easy_setopt(curl, CURLOPT_VERBOSE, 1L, LOG_WARNING, );
246
247 easy_setopt(curl, CURLOPT_USERAGENT,
248 "systemd-journal-upload " GIT_VERSION,
249 LOG_WARNING, );
250
251 if (!streq_ptr(arg_key, "-") && (arg_key || startswith(u->url, "https://"))) {
252 easy_setopt(curl, CURLOPT_SSLKEY, arg_key ?: PRIV_KEY_FILE,
253 LOG_ERR, return -EXFULL);
254 easy_setopt(curl, CURLOPT_SSLCERT, arg_cert ?: CERT_FILE,
255 LOG_ERR, return -EXFULL);
256 }
257
258 if (STRPTR_IN_SET(arg_trust, "-", "all"))
259 easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0,
260 LOG_ERR, return -EUCLEAN);
261 else if (arg_trust || startswith(u->url, "https://"))
262 easy_setopt(curl, CURLOPT_CAINFO, arg_trust ?: TRUST_FILE,
263 LOG_ERR, return -EXFULL);
264
265 if (arg_key || arg_trust)
266 easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1,
267 LOG_WARNING, );
268
269 u->easy = TAKE_PTR(curl);
270 } else {
271 /* truncate the potential old error message */
272 u->error[0] = '\0';
273
274 free(u->answer);
275 u->answer = 0;
276 }
277
278 /* upload to this place */
279 code = curl_easy_setopt(u->easy, CURLOPT_URL, u->url);
280 if (code)
281 return log_error_errno(SYNTHETIC_ERRNO(EXFULL),
282 "curl_easy_setopt CURLOPT_URL failed: %s",
283 curl_easy_strerror(code));
284
285 u->uploading = true;
286
287 return 0;
288 }
289
290 static size_t fd_input_callback(void *buf, size_t size, size_t nmemb, void *userp) {
291 Uploader *u = ASSERT_PTR(userp);
292 ssize_t n;
293
294 assert(nmemb < SSIZE_MAX / size);
295
296 if (u->input < 0)
297 return 0;
298
299 assert(!size_multiply_overflow(size, nmemb));
300
301 n = read(u->input, buf, size * nmemb);
302 log_debug("%s: allowed %zu, read %zd", __func__, size*nmemb, n);
303 if (n > 0)
304 return n;
305
306 u->uploading = false;
307 if (n < 0) {
308 log_error_errno(errno, "Aborting transfer after read error on input: %m.");
309 return CURL_READFUNC_ABORT;
310 }
311
312 log_debug("Reached EOF");
313 close_fd_input(u);
314 return 0;
315 }
316
317 static void close_fd_input(Uploader *u) {
318 assert(u);
319
320 u->input = safe_close(u->input);
321 u->timeout = 0;
322 }
323
324 static int dispatch_fd_input(sd_event_source *event,
325 int fd,
326 uint32_t revents,
327 void *userp) {
328 Uploader *u = ASSERT_PTR(userp);
329
330 assert(fd >= 0);
331
332 if (revents & EPOLLHUP) {
333 log_debug("Received HUP");
334 close_fd_input(u);
335 return 0;
336 }
337
338 if (!(revents & EPOLLIN)) {
339 log_warning("Unexpected poll event %"PRIu32".", revents);
340 return -EINVAL;
341 }
342
343 if (u->uploading) {
344 log_warning("dispatch_fd_input called when uploading, ignoring.");
345 return 0;
346 }
347
348 return start_upload(u, fd_input_callback, u);
349 }
350
351 static int open_file_for_upload(Uploader *u, const char *filename) {
352 int fd, r = 0;
353
354 if (streq(filename, "-"))
355 fd = STDIN_FILENO;
356 else {
357 fd = open(filename, O_RDONLY|O_CLOEXEC|O_NOCTTY);
358 if (fd < 0)
359 return log_error_errno(errno, "Failed to open %s: %m", filename);
360 }
361
362 u->input = fd;
363
364 if (arg_follow != 0) {
365 r = sd_event_add_io(u->events, &u->input_event,
366 fd, EPOLLIN, dispatch_fd_input, u);
367 if (r < 0) {
368 if (r != -EPERM || arg_follow > 0)
369 return log_error_errno(r, "Failed to register input event: %m");
370
371 /* Normal files should just be consumed without polling. */
372 r = start_upload(u, fd_input_callback, u);
373 }
374 }
375
376 return r;
377 }
378
379 static int dispatch_sigterm(sd_event_source *event,
380 const struct signalfd_siginfo *si,
381 void *userdata) {
382 Uploader *u = ASSERT_PTR(userdata);
383
384 log_received_signal(LOG_INFO, si);
385
386 close_fd_input(u);
387 close_journal_input(u);
388
389 sd_event_exit(u->events, 0);
390 return 0;
391 }
392
393 static int setup_signals(Uploader *u) {
394 int r;
395
396 assert(u);
397
398 assert_se(sigprocmask_many(SIG_SETMASK, NULL, SIGINT, SIGTERM, -1) >= 0);
399
400 r = sd_event_add_signal(u->events, &u->sigterm_event, SIGTERM, dispatch_sigterm, u);
401 if (r < 0)
402 return r;
403
404 r = sd_event_add_signal(u->events, &u->sigint_event, SIGINT, dispatch_sigterm, u);
405 if (r < 0)
406 return r;
407
408 return 0;
409 }
410
411 static int setup_uploader(Uploader *u, const char *url, const char *state_file) {
412 int r;
413 const char *host, *proto = "";
414
415 assert(u);
416 assert(url);
417
418 *u = (Uploader) {
419 .input = -1,
420 };
421
422 host = STARTSWITH_SET(url, "http://", "https://");
423 if (!host) {
424 host = url;
425 proto = "https://";
426 }
427
428 if (strchr(host, ':'))
429 u->url = strjoin(proto, url, "/upload");
430 else {
431 char *t;
432 size_t x;
433
434 t = strdupa_safe(url);
435 x = strlen(t);
436 while (x > 0 && t[x - 1] == '/')
437 t[x - 1] = '\0';
438
439 u->url = strjoin(proto, t, ":" STRINGIFY(DEFAULT_PORT), "/upload");
440 }
441 if (!u->url)
442 return log_oom();
443
444 u->state_file = state_file;
445
446 r = sd_event_default(&u->events);
447 if (r < 0)
448 return log_error_errno(r, "sd_event_default failed: %m");
449
450 r = setup_signals(u);
451 if (r < 0)
452 return log_error_errno(r, "Failed to set up signals: %m");
453
454 (void) sd_watchdog_enabled(false, &u->watchdog_usec);
455
456 return load_cursor_state(u);
457 }
458
459 static void destroy_uploader(Uploader *u) {
460 assert(u);
461
462 curl_easy_cleanup(u->easy);
463 curl_slist_free_all(u->header);
464 free(u->answer);
465
466 free(u->last_cursor);
467 free(u->current_cursor);
468
469 free(u->url);
470
471 u->input_event = sd_event_source_unref(u->input_event);
472
473 close_fd_input(u);
474 close_journal_input(u);
475
476 sd_event_source_unref(u->sigterm_event);
477 sd_event_source_unref(u->sigint_event);
478 sd_event_unref(u->events);
479 }
480
481 static int perform_upload(Uploader *u) {
482 CURLcode code;
483 long status;
484
485 assert(u);
486
487 u->watchdog_timestamp = now(CLOCK_MONOTONIC);
488 code = curl_easy_perform(u->easy);
489 if (code) {
490 if (u->error[0])
491 log_error("Upload to %s failed: %.*s",
492 u->url, (int) sizeof(u->error), u->error);
493 else
494 log_error("Upload to %s failed: %s",
495 u->url, curl_easy_strerror(code));
496 return -EIO;
497 }
498
499 code = curl_easy_getinfo(u->easy, CURLINFO_RESPONSE_CODE, &status);
500 if (code)
501 return log_error_errno(SYNTHETIC_ERRNO(EUCLEAN),
502 "Failed to retrieve response code: %s",
503 curl_easy_strerror(code));
504
505 if (status >= 300)
506 return log_error_errno(SYNTHETIC_ERRNO(EIO),
507 "Upload to %s failed with code %ld: %s",
508 u->url, status, strna(u->answer));
509 else if (status < 200)
510 return log_error_errno(SYNTHETIC_ERRNO(EIO),
511 "Upload to %s finished with unexpected code %ld: %s",
512 u->url, status, strna(u->answer));
513 else
514 log_debug("Upload finished successfully with code %ld: %s",
515 status, strna(u->answer));
516
517 free_and_replace(u->last_cursor, u->current_cursor);
518
519 return update_cursor_state(u);
520 }
521
522 static int config_parse_path_or_ignore(
523 const char *unit,
524 const char *filename,
525 unsigned line,
526 const char *section,
527 unsigned section_line,
528 const char *lvalue,
529 int ltype,
530 const char *rvalue,
531 void *data,
532 void *userdata) {
533
534 _cleanup_free_ char *n = NULL;
535 bool fatal = ltype;
536 char **s = ASSERT_PTR(data);
537 int r;
538
539 assert(filename);
540 assert(lvalue);
541 assert(rvalue);
542
543 if (isempty(rvalue))
544 goto finalize;
545
546 n = strdup(rvalue);
547 if (!n)
548 return log_oom();
549
550 if (streq(n, "-"))
551 goto finalize;
552
553 r = path_simplify_and_warn(n, PATH_CHECK_ABSOLUTE | (fatal ? PATH_CHECK_FATAL : 0), unit, filename, line, lvalue);
554 if (r < 0)
555 return fatal ? -ENOEXEC : 0;
556
557 finalize:
558 return free_and_replace(*s, n);
559 }
560
561 static int parse_config(void) {
562 const ConfigTableItem items[] = {
563 { "Upload", "URL", config_parse_string, CONFIG_PARSE_STRING_SAFE, &arg_url },
564 { "Upload", "ServerKeyFile", config_parse_path_or_ignore, 0, &arg_key },
565 { "Upload", "ServerCertificateFile", config_parse_path_or_ignore, 0, &arg_cert },
566 { "Upload", "TrustedCertificateFile", config_parse_path_or_ignore, 0, &arg_trust },
567 { "Upload", "NetworkTimeoutSec", config_parse_sec, 0, &arg_network_timeout_usec },
568 {}
569 };
570
571 return config_parse_config_file("journal-upload.conf", "Upload\0",
572 config_item_table_lookup, items,
573 CONFIG_PARSE_WARN, NULL);
574 }
575
576 static int help(void) {
577 _cleanup_free_ char *link = NULL;
578 int r;
579
580 r = terminal_urlify_man("systemd-journal-upload.service", "8", &link);
581 if (r < 0)
582 return log_oom();
583
584 printf("%s -u URL {FILE|-}...\n\n"
585 "Upload journal events to a remote server.\n\n"
586 " -h --help Show this help\n"
587 " --version Show package version\n"
588 " -u --url=URL Upload to this address (default port "
589 STRINGIFY(DEFAULT_PORT) ")\n"
590 " --key=FILENAME Specify key in PEM format (default:\n"
591 " \"" PRIV_KEY_FILE "\")\n"
592 " --cert=FILENAME Specify certificate in PEM format (default:\n"
593 " \"" CERT_FILE "\")\n"
594 " --trust=FILENAME|all Specify CA certificate or disable checking (default:\n"
595 " \"" TRUST_FILE "\")\n"
596 " --system Use the system journal\n"
597 " --user Use the user journal for the current user\n"
598 " -m --merge Use all available journals\n"
599 " -M --machine=CONTAINER Operate on local container\n"
600 " -D --directory=PATH Use journal files from directory\n"
601 " --file=PATH Use this journal file\n"
602 " --cursor=CURSOR Start at the specified cursor\n"
603 " --after-cursor=CURSOR Start after the specified cursor\n"
604 " --follow[=BOOL] Do [not] wait for input\n"
605 " --save-state[=FILE] Save uploaded cursors (default \n"
606 " " STATE_FILE ")\n"
607 "\nSee the %s for details.\n",
608 program_invocation_short_name,
609 link);
610
611 return 0;
612 }
613
614 static int parse_argv(int argc, char *argv[]) {
615 enum {
616 ARG_VERSION = 0x100,
617 ARG_KEY,
618 ARG_CERT,
619 ARG_TRUST,
620 ARG_USER,
621 ARG_SYSTEM,
622 ARG_FILE,
623 ARG_CURSOR,
624 ARG_AFTER_CURSOR,
625 ARG_FOLLOW,
626 ARG_SAVE_STATE,
627 };
628
629 static const struct option options[] = {
630 { "help", no_argument, NULL, 'h' },
631 { "version", no_argument, NULL, ARG_VERSION },
632 { "url", required_argument, NULL, 'u' },
633 { "key", required_argument, NULL, ARG_KEY },
634 { "cert", required_argument, NULL, ARG_CERT },
635 { "trust", required_argument, NULL, ARG_TRUST },
636 { "system", no_argument, NULL, ARG_SYSTEM },
637 { "user", no_argument, NULL, ARG_USER },
638 { "merge", no_argument, NULL, 'm' },
639 { "machine", required_argument, NULL, 'M' },
640 { "directory", required_argument, NULL, 'D' },
641 { "file", required_argument, NULL, ARG_FILE },
642 { "cursor", required_argument, NULL, ARG_CURSOR },
643 { "after-cursor", required_argument, NULL, ARG_AFTER_CURSOR },
644 { "follow", optional_argument, NULL, ARG_FOLLOW },
645 { "save-state", optional_argument, NULL, ARG_SAVE_STATE },
646 {}
647 };
648
649 int c, r;
650
651 assert(argc >= 0);
652 assert(argv);
653
654 opterr = 0;
655
656 while ((c = getopt_long(argc, argv, "hu:mM:D:", options, NULL)) >= 0)
657 switch (c) {
658 case 'h':
659 return help();
660
661 case ARG_VERSION:
662 return version();
663
664 case 'u':
665 if (arg_url)
666 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
667 "cannot use more than one --url");
668
669 arg_url = optarg;
670 break;
671
672 case ARG_KEY:
673 if (arg_key)
674 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
675 "cannot use more than one --key");
676
677 arg_key = optarg;
678 break;
679
680 case ARG_CERT:
681 if (arg_cert)
682 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
683 "cannot use more than one --cert");
684
685 arg_cert = optarg;
686 break;
687
688 case ARG_TRUST:
689 if (arg_trust)
690 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
691 "cannot use more than one --trust");
692
693 arg_trust = optarg;
694 break;
695
696 case ARG_SYSTEM:
697 arg_journal_type |= SD_JOURNAL_SYSTEM;
698 break;
699
700 case ARG_USER:
701 arg_journal_type |= SD_JOURNAL_CURRENT_USER;
702 break;
703
704 case 'm':
705 arg_merge = true;
706 break;
707
708 case 'M':
709 if (arg_machine)
710 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
711 "cannot use more than one --machine/-M");
712
713 arg_machine = optarg;
714 break;
715
716 case 'D':
717 if (arg_directory)
718 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
719 "cannot use more than one --directory/-D");
720
721 arg_directory = optarg;
722 break;
723
724 case ARG_FILE:
725 r = glob_extend(&arg_file, optarg, GLOB_NOCHECK);
726 if (r < 0)
727 return log_error_errno(r, "Failed to add paths: %m");
728 break;
729
730 case ARG_CURSOR:
731 if (arg_cursor)
732 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
733 "cannot use more than one --cursor/--after-cursor");
734
735 arg_cursor = optarg;
736 break;
737
738 case ARG_AFTER_CURSOR:
739 if (arg_cursor)
740 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
741 "cannot use more than one --cursor/--after-cursor");
742
743 arg_cursor = optarg;
744 arg_after_cursor = true;
745 break;
746
747 case ARG_FOLLOW:
748 r = parse_boolean_argument("--follow", optarg, NULL);
749 if (r < 0)
750 return r;
751 arg_follow = r;
752 break;
753
754 case ARG_SAVE_STATE:
755 arg_save_state = optarg ?: STATE_FILE;
756 break;
757
758 case '?':
759 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
760 "Unknown option %s.",
761 argv[optind - 1]);
762
763 case ':':
764 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
765 "Missing argument to %s.",
766 argv[optind - 1]);
767
768 default:
769 assert_not_reached();
770 }
771
772 if (!arg_url)
773 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
774 "Required --url/-u option missing.");
775
776 if (!!arg_key != !!arg_cert)
777 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
778 "Options --key and --cert must be used together.");
779
780 if (optind < argc && (arg_directory || arg_file || arg_machine || arg_journal_type))
781 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
782 "Input arguments make no sense with journal input.");
783
784 return 1;
785 }
786
787 static int open_journal(sd_journal **j) {
788 int r;
789
790 if (arg_directory)
791 r = sd_journal_open_directory(j, arg_directory, arg_journal_type);
792 else if (arg_file)
793 r = sd_journal_open_files(j, (const char**) arg_file, 0);
794 else if (arg_machine) {
795 #pragma GCC diagnostic push
796 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
797 /* FIXME: replace with D-Bus call OpenMachineRootDirectory() so that things also work with raw disk images */
798 r = sd_journal_open_container(j, arg_machine, 0);
799 #pragma GCC diagnostic pop
800 } else
801 r = sd_journal_open(j, (arg_merge ? 0 : SD_JOURNAL_LOCAL_ONLY) | arg_journal_type);
802 if (r < 0)
803 log_error_errno(r, "Failed to open %s: %m",
804 arg_directory ?: (arg_file ? "files" : "journal"));
805 return r;
806 }
807
808 static int run(int argc, char **argv) {
809 _cleanup_(destroy_uploader) Uploader u = {};
810 _unused_ _cleanup_(notify_on_cleanup) const char *notify_message = NULL;
811 bool use_journal;
812 int r;
813
814 log_show_color(true);
815 log_parse_environment();
816
817 /* The journal merging logic potentially needs a lot of fds. */
818 (void) rlimit_nofile_bump(HIGH_RLIMIT_NOFILE);
819
820 r = parse_config();
821 if (r < 0)
822 return r;
823
824 r = parse_argv(argc, argv);
825 if (r <= 0)
826 return r;
827
828 sigbus_install();
829
830 r = setup_uploader(&u, arg_url, arg_save_state);
831 if (r < 0)
832 return r;
833
834 sd_event_set_watchdog(u.events, true);
835
836 r = check_cursor_updating(&u);
837 if (r < 0)
838 return r;
839
840 log_debug("%s running as pid "PID_FMT,
841 program_invocation_short_name, getpid_cached());
842
843 use_journal = optind >= argc;
844 if (use_journal) {
845 sd_journal *j;
846 r = open_journal(&j);
847 if (r < 0)
848 return r;
849 r = open_journal_for_upload(&u, j,
850 arg_cursor ?: u.last_cursor,
851 arg_cursor ? arg_after_cursor : true,
852 arg_follow != 0);
853 if (r < 0)
854 return r;
855 }
856
857 notify_message = notify_start("READY=1\n"
858 "STATUS=Processing input...",
859 NOTIFY_STOPPING);
860
861 for (;;) {
862 r = sd_event_get_state(u.events);
863 if (r < 0)
864 return r;
865 if (r == SD_EVENT_FINISHED)
866 return 0;
867
868 if (use_journal) {
869 if (!u.journal)
870 return 0;
871
872 r = check_journal_input(&u);
873 } else if (u.input < 0 && !use_journal) {
874 if (optind >= argc)
875 return 0;
876
877 log_debug("Using %s as input.", argv[optind]);
878 r = open_file_for_upload(&u, argv[optind++]);
879 }
880 if (r < 0)
881 return r;
882
883 if (u.uploading) {
884 r = perform_upload(&u);
885 if (r < 0)
886 return r;
887 }
888
889 r = sd_event_run(u.events, u.timeout);
890 if (r < 0)
891 return log_error_errno(r, "Failed to run event loop: %m");
892 }
893 }
894
895 DEFINE_MAIN_FUNCTION(run);