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