1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
11 #include "alloc-util.h"
13 #include "conf-parser.h"
14 #include "daemon-util.h"
16 #include "extract-word.h"
19 #include "format-util.h"
21 #include "glob-util.h"
23 #include "journal-header-util.h"
24 #include "journal-upload.h"
25 #include "journal-util.h"
27 #include "logs-show.h"
28 #include "main-func.h"
30 #include "parse-argument.h"
31 #include "parse-helpers.h"
32 #include "pretty-print.h"
33 #include "process-util.h"
34 #include "string-util.h"
36 #include "time-util.h"
37 #include "tmpfile-util.h"
40 #define PRIV_KEY_FILE CERTIFICATE_ROOT "/private/journal-upload.pem"
41 #define CERT_FILE CERTIFICATE_ROOT "/certs/journal-upload.pem"
42 #define TRUST_FILE CERTIFICATE_ROOT "/ca/trusted.pem"
43 #define DEFAULT_PORT 19532
45 static char *arg_url
= NULL
;
46 static char *arg_key
= NULL
;
47 static char *arg_cert
= NULL
;
48 static char *arg_trust
= NULL
;
49 static char *arg_directory
= NULL
;
50 static char **arg_file
= NULL
;
51 static char *arg_cursor
= NULL
;
52 static bool arg_after_cursor
= false;
53 static int arg_journal_type
= 0;
54 static int arg_namespace_flags
= 0;
55 static char *arg_machine
= NULL
;
56 static char *arg_namespace
= NULL
;
57 static bool arg_merge
= false;
58 static int arg_follow
= -1;
59 static char *arg_save_state
= NULL
;
60 static usec_t arg_network_timeout_usec
= USEC_INFINITY
;
61 static OrderedHashmap
*arg_compression
= NULL
;
62 static OrderedHashmap
*arg_headers
= NULL
;
63 static bool arg_force_compression
= false;
65 STATIC_DESTRUCTOR_REGISTER(arg_url
, freep
);
66 STATIC_DESTRUCTOR_REGISTER(arg_key
, freep
);
67 STATIC_DESTRUCTOR_REGISTER(arg_cert
, freep
);
68 STATIC_DESTRUCTOR_REGISTER(arg_trust
, freep
);
69 STATIC_DESTRUCTOR_REGISTER(arg_directory
, freep
);
70 STATIC_DESTRUCTOR_REGISTER(arg_file
, strv_freep
);
71 STATIC_DESTRUCTOR_REGISTER(arg_cursor
, freep
);
72 STATIC_DESTRUCTOR_REGISTER(arg_machine
, freep
);
73 STATIC_DESTRUCTOR_REGISTER(arg_namespace
, freep
);
74 STATIC_DESTRUCTOR_REGISTER(arg_save_state
, freep
);
75 STATIC_DESTRUCTOR_REGISTER(arg_compression
, ordered_hashmap_freep
);
76 STATIC_DESTRUCTOR_REGISTER(arg_headers
, ordered_hashmap_freep
);
78 static void close_fd_input(Uploader
*u
);
80 #define SERVER_ANSWER_KEEP 2048
82 #define STATE_FILE "/var/lib/systemd/journal-upload/state"
84 #define easy_setopt(curl, opt, value, level, cmd) \
86 code = curl_easy_setopt(curl, opt, value); \
89 "curl_easy_setopt " #opt " failed: %s", \
90 curl_easy_strerror(code)); \
95 DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(CURL
*, curl_easy_cleanup
, NULL
);
96 DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct curl_slist
*, curl_slist_free_all
, NULL
);
98 static size_t output_callback(char *buf
,
102 Uploader
*u
= ASSERT_PTR(userp
);
104 log_debug("The server answers (%zu bytes): %.*s",
105 size
*nmemb
, (int)(size
*nmemb
), buf
);
107 if (nmemb
&& !u
->answer
) {
108 u
->answer
= strndup(buf
, size
*nmemb
);
110 log_warning("Failed to store server answer (%zu bytes): out of memory", size
*nmemb
);
116 static int check_cursor_updating(Uploader
*u
) {
117 _cleanup_free_
char *temp_path
= NULL
;
118 _cleanup_fclose_
FILE *f
= NULL
;
124 r
= mkdir_parents(u
->state_file
, 0755);
126 return log_error_errno(r
, "Cannot create parent directory of state file %s: %m",
129 r
= fopen_temporary(u
->state_file
, &f
, &temp_path
);
131 return log_error_errno(r
, "Cannot save state to %s: %m",
133 (void) unlink(temp_path
);
138 static int update_cursor_state(Uploader
*u
) {
139 _cleanup_(unlink_and_freep
) char *temp_path
= NULL
;
140 _cleanup_fclose_
FILE *f
= NULL
;
143 if (!u
->state_file
|| !u
->last_cursor
)
146 r
= fopen_temporary(u
->state_file
, &f
, &temp_path
);
151 "# This is private data. Do not parse.\n"
155 r
= fflush_and_check(f
);
159 if (rename(temp_path
, u
->state_file
) < 0) {
164 temp_path
= mfree(temp_path
);
168 (void) unlink(u
->state_file
);
170 return log_error_errno(r
, "Failed to save state %s: %m", u
->state_file
);
173 static int load_cursor_state(Uploader
*u
) {
179 r
= parse_env_file(NULL
, u
->state_file
, "LAST_CURSOR", &u
->last_cursor
);
181 log_debug("State file %s is not present.", u
->state_file
);
183 return log_error_errno(r
, "Failed to read state file %s: %m",
186 log_debug("Last cursor was %s", u
->last_cursor
);
191 int start_upload(Uploader
*u
,
192 size_t (*input_callback
)(void *ptr
,
200 assert(input_callback
);
203 _cleanup_(curl_slist_free_allp
) struct curl_slist
*h
= NULL
;
204 struct curl_slist
*l
;
206 h
= curl_slist_append(NULL
, "Content-Type: application/vnd.fdo.journal");
210 l
= curl_slist_append(h
, "Transfer-Encoding: chunked");
215 l
= curl_slist_append(h
, "Accept: text/plain");
220 if (u
->compression
) {
221 _cleanup_free_
char *header
= strjoin("Content-Encoding: ", compression_lowercase_to_string(u
->compression
->algorithm
));
225 l
= curl_slist_append(h
, header
);
233 ORDERED_HASHMAP_FOREACH_KEY(values
, name
, arg_headers
) {
234 _cleanup_free_
char *joined
= strv_join(values
, ", ");
238 if (!header_value_is_valid(joined
)) {
239 log_warning("Concatenated header value for %s is invalid, ignoring", name
);
243 _cleanup_free_
char *header
= strjoin(name
, ": ", joined
);
247 l
= curl_slist_append(h
, header
);
253 u
->header
= TAKE_PTR(h
);
257 _cleanup_(curl_easy_cleanupp
) CURL
*curl
= NULL
;
259 curl
= curl_easy_init();
261 return log_error_errno(SYNTHETIC_ERRNO(ENOSR
),
262 "Call to curl_easy_init failed.");
264 /* If configured, set a timeout for the curl operation. */
265 if (arg_network_timeout_usec
!= USEC_INFINITY
)
266 easy_setopt(curl
, CURLOPT_TIMEOUT
,
267 (long) DIV_ROUND_UP(arg_network_timeout_usec
, USEC_PER_SEC
),
268 LOG_ERR
, return -EXFULL
);
270 /* tell it to POST to the URL */
271 easy_setopt(curl
, CURLOPT_POST
, 1L,
272 LOG_ERR
, return -EXFULL
);
274 easy_setopt(curl
, CURLOPT_ERRORBUFFER
, u
->error
,
275 LOG_ERR
, return -EXFULL
);
277 /* set where to write to */
278 easy_setopt(curl
, CURLOPT_WRITEFUNCTION
, output_callback
,
279 LOG_ERR
, return -EXFULL
);
281 easy_setopt(curl
, CURLOPT_WRITEDATA
, data
,
282 LOG_ERR
, return -EXFULL
);
284 /* set where to read from */
285 easy_setopt(curl
, CURLOPT_READFUNCTION
, input_callback
,
286 LOG_ERR
, return -EXFULL
);
288 easy_setopt(curl
, CURLOPT_READDATA
, data
,
289 LOG_ERR
, return -EXFULL
);
291 /* use our special own mime type and chunked transfer */
292 easy_setopt(curl
, CURLOPT_HTTPHEADER
, u
->header
,
293 LOG_ERR
, return -EXFULL
);
296 /* enable verbose for easier tracing */
297 easy_setopt(curl
, CURLOPT_VERBOSE
, 1L, LOG_WARNING
, );
299 easy_setopt(curl
, CURLOPT_USERAGENT
,
300 "systemd-journal-upload " GIT_VERSION
,
303 if (!streq_ptr(arg_key
, "-") && (arg_key
|| startswith(u
->url
, "https://"))) {
304 easy_setopt(curl
, CURLOPT_SSLKEY
, arg_key
?: PRIV_KEY_FILE
,
305 LOG_ERR
, return -EXFULL
);
306 easy_setopt(curl
, CURLOPT_SSLCERT
, arg_cert
?: CERT_FILE
,
307 LOG_ERR
, return -EXFULL
);
310 if (STRPTR_IN_SET(arg_trust
, "-", "all"))
311 easy_setopt(curl
, CURLOPT_SSL_VERIFYPEER
, 0,
312 LOG_ERR
, return -EUCLEAN
);
313 else if (arg_trust
|| startswith(u
->url
, "https://"))
314 easy_setopt(curl
, CURLOPT_CAINFO
, arg_trust
?: TRUST_FILE
,
315 LOG_ERR
, return -EXFULL
);
317 if (arg_key
|| arg_trust
)
318 easy_setopt(curl
, CURLOPT_SSLVERSION
, CURL_SSLVERSION_TLSv1
,
321 u
->easy
= TAKE_PTR(curl
);
323 /* truncate the potential old error message */
326 u
->answer
= mfree(u
->answer
);
329 /* upload to this place */
330 code
= curl_easy_setopt(u
->easy
, CURLOPT_URL
, u
->url
);
332 return log_error_errno(SYNTHETIC_ERRNO(EXFULL
),
333 "curl_easy_setopt CURLOPT_URL failed: %s",
334 curl_easy_strerror(code
));
341 static size_t fd_input_callback(void *buf
, size_t size
, size_t nmemb
, void *userp
) {
342 _cleanup_free_
char *compression_buffer
= NULL
;
343 Uploader
*u
= ASSERT_PTR(userp
);
347 assert(nmemb
< SSIZE_MAX
/ size
);
352 assert(!size_multiply_overflow(size
, nmemb
));
354 if (u
->compression
) {
355 compression_buffer
= malloc_multiply(nmemb
, size
);
356 if (!compression_buffer
) {
358 return CURL_READFUNC_ABORT
;
362 n
= read(u
->input
, compression_buffer
?: buf
, size
* nmemb
);
364 log_debug("%s: allowed %zu, read %zd", __func__
, size
* nmemb
, n
);
368 size_t compressed_size
;
369 r
= compress_blob(u
->compression
->algorithm
, compression_buffer
, n
, buf
, size
* nmemb
, &compressed_size
, u
->compression
->level
);
371 log_error_errno(r
, "Failed to compress %zd bytes by %s with level %i: %m",
372 n
, compression_lowercase_to_string(u
->compression
->algorithm
), u
->compression
->level
);
373 return CURL_READFUNC_ABORT
;
375 assert(compressed_size
<= size
* nmemb
);
376 return compressed_size
;
378 log_error_errno(errno
, "Aborting transfer after read error on input: %m.");
379 return CURL_READFUNC_ABORT
;
382 u
->uploading
= false;
383 log_debug("Reached EOF");
388 static void close_fd_input(Uploader
*u
) {
391 u
->input
= safe_close(u
->input
);
395 static int dispatch_fd_input(sd_event_source
*event
,
399 Uploader
*u
= ASSERT_PTR(userp
);
403 if (revents
& EPOLLHUP
) {
404 log_debug("Received HUP");
409 if (!(revents
& EPOLLIN
)) {
410 log_warning("Unexpected poll event %"PRIu32
".", revents
);
415 log_warning("dispatch_fd_input called when uploading, ignoring.");
419 return start_upload(u
, fd_input_callback
, u
);
422 static int open_file_for_upload(Uploader
*u
, const char *filename
) {
425 if (streq(filename
, "-"))
428 fd
= open(filename
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
430 return log_error_errno(errno
, "Failed to open %s: %m", filename
);
435 if (arg_follow
!= 0) {
436 r
= sd_event_add_io(u
->event
, &u
->input_event
,
437 fd
, EPOLLIN
, dispatch_fd_input
, u
);
439 if (r
!= -EPERM
|| arg_follow
> 0)
440 return log_error_errno(r
, "Failed to register input event: %m");
442 /* Normal files should just be consumed without polling. */
443 r
= start_upload(u
, fd_input_callback
, u
);
450 static int setup_uploader(Uploader
*u
, const char *url
, const char *state_file
) {
452 const char *host
, *proto
= "";
461 if (arg_force_compression
)
462 u
->compression
= ordered_hashmap_first(arg_compression
);
464 host
= STARTSWITH_SET(url
, "http://", "https://");
470 if (strchr(host
, ':'))
471 u
->url
= strjoin(proto
, url
, "/upload");
476 t
= strdupa_safe(url
);
478 while (x
> 0 && t
[x
- 1] == '/')
481 u
->url
= strjoin(proto
, t
, ":" STRINGIFY(DEFAULT_PORT
), "/upload");
486 u
->state_file
= state_file
;
488 r
= sd_event_default(&u
->event
);
490 return log_error_errno(r
, "sd_event_default failed: %m");
492 r
= sd_event_set_signal_exit(u
->event
, true);
494 return log_error_errno(r
, "Failed to install SIGINT/SIGTERM handlers: %m");
496 (void) sd_watchdog_enabled(false, &u
->watchdog_usec
);
498 return load_cursor_state(u
);
501 static void destroy_uploader(Uploader
*u
) {
504 curl_easy_cleanup(u
->easy
);
505 curl_slist_free_all(u
->header
);
508 free(u
->last_cursor
);
509 free(u
->current_cursor
);
513 u
->input_event
= sd_event_source_unref(u
->input_event
);
516 close_journal_input(u
);
518 sd_event_unref(u
->event
);
521 #if LIBCURL_VERSION_NUM >= 0x075300
522 static int update_content_encoding_header(Uploader
*u
, const CompressionConfig
*cc
) {
523 bool update_header
= false;
527 if (cc
== u
->compression
)
528 return 0; /* Already picked the algorithm. Let's shortcut. */
531 _cleanup_free_
char *header
= strjoin("Content-Encoding: ", compression_lowercase_to_string(cc
->algorithm
));
535 /* First, try to update existing Content-Encoding header. */
537 for (struct curl_slist
*l
= u
->header
; l
; l
= l
->next
)
538 if (startswith(l
->data
, "Content-Encoding:")) {
539 free_and_replace(l
->data
, header
);
544 /* If Content-Encoding header is not found, append new one. */
546 struct curl_slist
*l
= curl_slist_append(u
->header
, header
);
552 update_header
= true;
554 /* Remove Content-Encoding header. */
555 for (struct curl_slist
*l
= u
->header
, *prev
= NULL
; l
; prev
= l
, l
= l
->next
)
556 if (startswith(l
->data
, "Content-Encoding:")) {
558 prev
->next
= TAKE_PTR(l
->next
);
560 u
->header
= TAKE_PTR(l
->next
);
562 curl_slist_free_all(l
);
563 update_header
= true;
569 easy_setopt(u
->easy
, CURLOPT_HTTPHEADER
, u
->header
, LOG_WARNING
, return -EXFULL
);
575 log_debug("Using compression algorithm %s with compression level %i.", compression_lowercase_to_string(cc
->algorithm
), cc
->level
);
577 log_debug("Disabled compression algorithm.");
582 static int parse_accept_encoding_header(Uploader
*u
) {
583 #if LIBCURL_VERSION_NUM >= 0x075300
588 if (ordered_hashmap_isempty(arg_compression
))
589 return update_content_encoding_header(u
, NULL
);
591 struct curl_header
*header
;
592 CURLHcode hcode
= curl_easy_header(u
->easy
, "Accept-Encoding", 0, CURLH_HEADER
, -1, &header
);
593 if (hcode
!= CURLHE_OK
)
596 for (const char *p
= header
->value
;;) {
597 _cleanup_free_
char *word
= NULL
;
599 r
= extract_first_word(&p
, &word
, ",", 0);
601 return log_warning_errno(r
, "Failed to parse Accept-Encoding header value, ignoring: %m");
605 /* Cut the quality value waiting. */
606 char *q
= strchr(word
, ';');
610 if (streq(word
, "*"))
611 return update_content_encoding_header(u
, ordered_hashmap_first(arg_compression
));
613 Compression c
= compression_lowercase_from_string(word
);
614 if (c
<= 0 || !compression_supported(c
))
615 continue; /* unsupported or invalid algorithm. */
617 const CompressionConfig
*cc
= ordered_hashmap_get(arg_compression
, INT_TO_PTR(c
));
619 continue; /* The specified algorithm is not enabled. */
621 return update_content_encoding_header(u
, cc
);
625 if (arg_force_compression
)
626 return update_content_encoding_header(u
, ordered_hashmap_first(arg_compression
));
628 return update_content_encoding_header(u
, NULL
);
634 static int perform_upload(Uploader
*u
) {
640 u
->watchdog_timestamp
= now(CLOCK_MONOTONIC
);
641 code
= curl_easy_perform(u
->easy
);
644 log_error("Upload to %s failed: %.*s",
645 u
->url
, (int) sizeof(u
->error
), u
->error
);
647 log_error("Upload to %s failed: %s",
648 u
->url
, curl_easy_strerror(code
));
652 code
= curl_easy_getinfo(u
->easy
, CURLINFO_RESPONSE_CODE
, &status
);
654 return log_error_errno(SYNTHETIC_ERRNO(EUCLEAN
),
655 "Failed to retrieve response code: %s",
656 curl_easy_strerror(code
));
659 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
660 "Upload to %s failed with code %ld: %s",
661 u
->url
, status
, strna(u
->answer
));
663 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
664 "Upload to %s finished with unexpected code %ld: %s",
665 u
->url
, status
, strna(u
->answer
));
667 (void) parse_accept_encoding_header(u
);
669 log_debug("Upload finished successfully with code %ld: %s",
670 status
, strna(u
->answer
));
672 free_and_replace(u
->last_cursor
, u
->current_cursor
);
674 return update_cursor_state(u
);
677 static int parse_config(void) {
678 const ConfigTableItem items
[] = {
679 { "Upload", "URL", config_parse_string
, CONFIG_PARSE_STRING_SAFE
, &arg_url
},
680 { "Upload", "ServerKeyFile", config_parse_path_or_ignore
, 0, &arg_key
},
681 { "Upload", "ServerCertificateFile", config_parse_path_or_ignore
, 0, &arg_cert
},
682 { "Upload", "TrustedCertificateFile", config_parse_path_or_ignore
, 0, &arg_trust
},
683 { "Upload", "NetworkTimeoutSec", config_parse_sec
, 0, &arg_network_timeout_usec
},
684 { "Upload", "Header", config_parse_header
, 0, &arg_headers
},
685 { "Upload", "Compression", config_parse_compression
, /* with_level */ true, &arg_compression
},
686 { "Upload", "ForceCompression", config_parse_bool
, 0, &arg_force_compression
},
690 return config_parse_standard_file_with_dropins(
691 "systemd/journal-upload.conf",
693 config_item_table_lookup
, items
,
695 /* userdata= */ NULL
);
698 static int help(void) {
699 _cleanup_free_
char *link
= NULL
;
702 r
= terminal_urlify_man("systemd-journal-upload.service", "8", &link
);
706 printf("%s -u URL {FILE|-}...\n\n"
707 "Upload journal events to a remote server.\n\n"
708 " -h --help Show this help\n"
709 " --version Show package version\n"
710 " -u --url=URL Upload to this address (default port "
711 STRINGIFY(DEFAULT_PORT
) ")\n"
712 " --key=FILENAME Specify key in PEM format (default:\n"
713 " \"" PRIV_KEY_FILE
"\")\n"
714 " --cert=FILENAME Specify certificate in PEM format (default:\n"
715 " \"" CERT_FILE
"\")\n"
716 " --trust=FILENAME|all Specify CA certificate or disable checking (default:\n"
717 " \"" TRUST_FILE
"\")\n"
718 " --system Use the system journal\n"
719 " --user Use the user journal for the current user\n"
720 " -m --merge Use all available journals\n"
721 " -M --machine=CONTAINER Operate on local container\n"
722 " --namespace=NAMESPACE Use journal files from namespace\n"
723 " -D --directory=PATH Use journal files from directory\n"
724 " --file=PATH Use this journal file\n"
725 " --cursor=CURSOR Start at the specified cursor\n"
726 " --after-cursor=CURSOR Start after the specified cursor\n"
727 " --follow[=BOOL] Do [not] wait for input\n"
728 " --save-state[=FILE] Save uploaded cursors (default \n"
730 "\nSee the %s for details.\n",
731 program_invocation_short_name
,
737 static int parse_argv(int argc
, char *argv
[]) {
753 static const struct option options
[] = {
754 { "help", no_argument
, NULL
, 'h' },
755 { "version", no_argument
, NULL
, ARG_VERSION
},
756 { "url", required_argument
, NULL
, 'u' },
757 { "key", required_argument
, NULL
, ARG_KEY
},
758 { "cert", required_argument
, NULL
, ARG_CERT
},
759 { "trust", required_argument
, NULL
, ARG_TRUST
},
760 { "system", no_argument
, NULL
, ARG_SYSTEM
},
761 { "user", no_argument
, NULL
, ARG_USER
},
762 { "merge", no_argument
, NULL
, 'm' },
763 { "machine", required_argument
, NULL
, 'M' },
764 { "namespace", required_argument
, NULL
, ARG_NAMESPACE
},
765 { "directory", required_argument
, NULL
, 'D' },
766 { "file", required_argument
, NULL
, ARG_FILE
},
767 { "cursor", required_argument
, NULL
, ARG_CURSOR
},
768 { "after-cursor", required_argument
, NULL
, ARG_AFTER_CURSOR
},
769 { "follow", optional_argument
, NULL
, ARG_FOLLOW
},
770 { "save-state", optional_argument
, NULL
, ARG_SAVE_STATE
},
781 while ((c
= getopt_long(argc
, argv
, "hu:mM:D:", options
, NULL
)) >= 0)
790 r
= free_and_strdup_warn(&arg_url
, optarg
);
796 r
= free_and_strdup_warn(&arg_key
, optarg
);
802 r
= free_and_strdup_warn(&arg_cert
, optarg
);
808 r
= free_and_strdup_warn(&arg_trust
, optarg
);
814 arg_journal_type
|= SD_JOURNAL_SYSTEM
;
818 arg_journal_type
|= SD_JOURNAL_CURRENT_USER
;
826 r
= free_and_strdup_warn(&arg_machine
, optarg
);
832 if (streq(optarg
, "*")) {
833 arg_namespace_flags
= SD_JOURNAL_ALL_NAMESPACES
;
834 arg_namespace
= mfree(arg_namespace
);
836 } else if (startswith(optarg
, "+")) {
837 arg_namespace_flags
= SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE
;
838 r
= free_and_strdup_warn(&arg_namespace
, optarg
+ 1);
839 } else if (isempty(optarg
)) {
840 arg_namespace_flags
= 0;
841 arg_namespace
= mfree(arg_namespace
);
844 arg_namespace_flags
= 0;
845 r
= free_and_strdup_warn(&arg_namespace
, optarg
);
852 r
= free_and_strdup_warn(&arg_directory
, optarg
);
858 r
= glob_extend(&arg_file
, optarg
, GLOB_NOCHECK
);
860 return log_error_errno(r
, "Failed to add paths: %m");
864 case ARG_AFTER_CURSOR
:
865 r
= free_and_strdup_warn(&arg_cursor
, optarg
);
868 arg_after_cursor
= c
== ARG_AFTER_CURSOR
;
872 r
= parse_boolean_argument("--follow", optarg
, NULL
);
879 r
= free_and_strdup_warn(&arg_save_state
, optarg
?: STATE_FILE
);
885 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
886 "Unknown option %s.",
890 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
891 "Missing argument to %s.",
895 assert_not_reached();
899 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
900 "Required --url=/-u option missing.");
902 if (!!arg_key
!= !!arg_cert
)
903 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
904 "Options --key= and --cert= must be used together.");
906 if (optind
< argc
&& (arg_directory
|| arg_file
|| arg_machine
|| arg_journal_type
))
907 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
908 "Input arguments make no sense with journal input.");
913 static int open_journal(sd_journal
**j
) {
919 r
= sd_journal_open_directory(j
, arg_directory
, arg_journal_type
);
921 r
= sd_journal_open_files(j
, (const char**) arg_file
, 0);
922 else if (arg_machine
)
923 r
= journal_open_machine(j
, arg_machine
, 0);
925 r
= sd_journal_open_namespace(j
, arg_namespace
,
926 (arg_merge
? 0 : SD_JOURNAL_LOCAL_ONLY
) | arg_namespace_flags
| arg_journal_type
);
928 log_error_errno(r
, "Failed to open %s: %m",
929 arg_directory
?: (arg_file
? "files" : "journal"));
933 static int run(int argc
, char **argv
) {
934 _cleanup_(destroy_uploader
) Uploader u
= {};
935 _unused_
_cleanup_(notify_on_cleanup
) const char *notify_message
= NULL
;
945 r
= parse_argv(argc
, argv
);
949 r
= compression_configs_mangle(&arg_compression
);
953 journal_browse_prepare();
955 r
= setup_uploader(&u
, arg_url
, arg_save_state
);
959 sd_event_set_watchdog(u
.event
, true);
961 r
= check_cursor_updating(&u
);
965 log_debug("%s running as pid "PID_FMT
,
966 program_invocation_short_name
, getpid_cached());
968 use_journal
= optind
>= argc
;
971 r
= open_journal(&j
);
974 r
= open_journal_for_upload(&u
, j
,
975 arg_cursor
?: u
.last_cursor
,
976 arg_cursor
? arg_after_cursor
: true,
982 notify_message
= notify_start("READY=1\n"
983 "STATUS=Processing input...",
984 NOTIFY_STOPPING_MESSAGE
);
987 r
= sd_event_get_state(u
.event
);
990 if (r
== SD_EVENT_FINISHED
)
997 r
= check_journal_input(&u
);
998 } else if (u
.input
< 0 && !use_journal
) {
1002 log_debug("Using %s as input.", argv
[optind
]);
1003 r
= open_file_for_upload(&u
, argv
[optind
++]);
1009 r
= perform_upload(&u
);
1014 r
= sd_event_run(u
.event
, u
.timeout
);
1016 return log_error_errno(r
, "Failed to run event loop: %m");
1020 DEFINE_MAIN_FUNCTION(run
);