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