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