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