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