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