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