chiark / gitweb /
journal-upload: fix socket activation
[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\n"
510                "     --cert=FILENAME        Specify certificate in PEM format\n"
511                "     --trust=FILENAME       Specify CA certificate in PEM format\n"
512                "     --system               Use the system journal\n"
513                "     --user                 Use the user journal for the current user\n"
514                "  -m --merge                Use  all available journals\n"
515                "  -M --machine=CONTAINER    Operate on local container\n"
516                "  -D --directory=PATH       Use journal files from directory\n"
517                "     --file=PATH            Use this journal file\n"
518                "     --cursor=CURSOR        Start at the specified cursor\n"
519                "     --after-cursor=CURSOR  Start after the specified cursor\n"
520                "     --follow[=BOOL]        Do [not] wait for input\n"
521                "     --save-state[=FILE]    Save uploaded cursors (default \n"
522                "                            " STATE_FILE ")\n"
523                "  -h --help                 Show this help and exit\n"
524                "     --version              Print version string and exit\n"
525                , program_invocation_short_name);
526 }
527
528 static int parse_argv(int argc, char *argv[]) {
529         enum {
530                 ARG_VERSION = 0x100,
531                 ARG_KEY,
532                 ARG_CERT,
533                 ARG_TRUST,
534                 ARG_USER,
535                 ARG_SYSTEM,
536                 ARG_FILE,
537                 ARG_CURSOR,
538                 ARG_AFTER_CURSOR,
539                 ARG_FOLLOW,
540                 ARG_SAVE_STATE,
541         };
542
543         static const struct option options[] = {
544                 { "help",         no_argument,       NULL, 'h'                },
545                 { "version",      no_argument,       NULL, ARG_VERSION        },
546                 { "url",          required_argument, NULL, 'u'                },
547                 { "key",          required_argument, NULL, ARG_KEY            },
548                 { "cert",         required_argument, NULL, ARG_CERT           },
549                 { "trust",        required_argument, NULL, ARG_TRUST          },
550                 { "system",       no_argument,       NULL, ARG_SYSTEM         },
551                 { "user",         no_argument,       NULL, ARG_USER           },
552                 { "merge",        no_argument,       NULL, 'm'                },
553                 { "machine",      required_argument, NULL, 'M'                },
554                 { "directory",    required_argument, NULL, 'D'                },
555                 { "file",         required_argument, NULL, ARG_FILE           },
556                 { "cursor",       required_argument, NULL, ARG_CURSOR         },
557                 { "after-cursor", required_argument, NULL, ARG_AFTER_CURSOR   },
558                 { "follow",       optional_argument, NULL, ARG_FOLLOW         },
559                 { "save-state",   optional_argument, NULL, ARG_SAVE_STATE     },
560                 {}
561         };
562
563         int c, r;
564
565         assert(argc >= 0);
566         assert(argv);
567
568         opterr = 0;
569
570         while ((c = getopt_long(argc, argv, "hu:mM:D:", options, NULL)) >= 0)
571                 switch(c) {
572                 case 'h':
573                         help();
574                         return 0 /* done */;
575
576                 case ARG_VERSION:
577                         puts(PACKAGE_STRING);
578                         puts(SYSTEMD_FEATURES);
579                         return 0 /* done */;
580
581                 case 'u':
582                         if (arg_url) {
583                                 log_error("cannot use more than one --url");
584                                 return -EINVAL;
585                         }
586
587                         arg_url = optarg;
588                         break;
589
590                 case ARG_KEY:
591                         if (arg_key) {
592                                 log_error("cannot use more than one --key");
593                                 return -EINVAL;
594                         }
595
596                         arg_key = optarg;
597                         break;
598
599                 case ARG_CERT:
600                         if (arg_cert) {
601                                 log_error("cannot use more than one --cert");
602                                 return -EINVAL;
603                         }
604
605                         arg_cert = optarg;
606                         break;
607
608                 case ARG_TRUST:
609                         if (arg_trust) {
610                                 log_error("cannot use more than one --trust");
611                                 return -EINVAL;
612                         }
613
614                         arg_trust = optarg;
615                         break;
616
617                 case ARG_SYSTEM:
618                         arg_journal_type |= SD_JOURNAL_SYSTEM;
619                         break;
620
621                 case ARG_USER:
622                         arg_journal_type |= SD_JOURNAL_CURRENT_USER;
623                         break;
624
625                 case 'm':
626                         arg_merge = true;
627                         break;
628
629                 case 'M':
630                         if (arg_machine) {
631                                 log_error("cannot use more than one --machine/-M");
632                                 return -EINVAL;
633                         }
634
635                         arg_machine = optarg;
636                         break;
637
638                 case 'D':
639                         if (arg_directory) {
640                                 log_error("cannot use more than one --directory/-D");
641                                 return -EINVAL;
642                         }
643
644                         arg_directory = optarg;
645                         break;
646
647                 case ARG_FILE:
648                         r = glob_extend(&arg_file, optarg);
649                         if (r < 0) {
650                                 log_error("Failed to add paths: %s", strerror(-r));
651                                 return r;
652                         };
653                         break;
654
655                 case ARG_CURSOR:
656                         if (arg_cursor) {
657                                 log_error("cannot use more than one --cursor/--after-cursor");
658                                 return -EINVAL;
659                         }
660
661                         arg_cursor = optarg;
662                         break;
663
664                 case ARG_AFTER_CURSOR:
665                         if (arg_cursor) {
666                                 log_error("cannot use more than one --cursor/--after-cursor");
667                                 return -EINVAL;
668                         }
669
670                         arg_cursor = optarg;
671                         arg_after_cursor = true;
672                         break;
673
674                 case ARG_FOLLOW:
675                         if (optarg) {
676                                 r = parse_boolean(optarg);
677                                 if (r < 0) {
678                                         log_error("Failed to parse --follow= parameter.");
679                                         return -EINVAL;
680                                 }
681
682                                 arg_follow = !!r;
683                         } else
684                                 arg_follow = true;
685
686                         break;
687
688                 case ARG_SAVE_STATE:
689                         arg_save_state = optarg ?: STATE_FILE;
690                         break;
691
692                 case '?':
693                         log_error("Unknown option %s.", argv[optind-1]);
694                         return -EINVAL;
695
696                 case ':':
697                         log_error("Missing argument to %s.", argv[optind-1]);
698                         return -EINVAL;
699
700                 default:
701                         assert_not_reached("Unhandled option code.");
702                 }
703
704         if (!arg_url) {
705                 log_error("Required --url/-u option missing.");
706                 return -EINVAL;
707         }
708
709         if (!!arg_key != !!arg_cert) {
710                 log_error("Options --key and --cert must be used together.");
711                 return -EINVAL;
712         }
713
714         if (optind < argc && (arg_directory || arg_file || arg_machine || arg_journal_type)) {
715                 log_error("Input arguments make no sense with journal input.");
716                 return -EINVAL;
717         }
718
719         return 1;
720 }
721
722 static int open_journal(sd_journal **j) {
723         int r;
724
725         if (arg_directory)
726                 r = sd_journal_open_directory(j, arg_directory, arg_journal_type);
727         else if (arg_file)
728                 r = sd_journal_open_files(j, (const char**) arg_file, 0);
729         else if (arg_machine)
730                 r = sd_journal_open_container(j, arg_machine, 0);
731         else
732                 r = sd_journal_open(j, !arg_merge*SD_JOURNAL_LOCAL_ONLY + arg_journal_type);
733         if (r < 0)
734                 log_error("Failed to open %s: %s",
735                           arg_directory ? arg_directory : arg_file ? "files" : "journal",
736                           strerror(-r));
737         return r;
738 }
739
740 int main(int argc, char **argv) {
741         Uploader u;
742         int r;
743         bool use_journal;
744
745         log_show_color(true);
746         log_parse_environment();
747
748         r = parse_config();
749         if (r < 0)
750                 goto finish;
751
752         r = parse_argv(argc, argv);
753         if (r <= 0)
754                 goto finish;
755
756         r = setup_uploader(&u, arg_url, arg_save_state);
757         if (r < 0)
758                 goto cleanup;
759
760         sd_event_set_watchdog(u.events, true);
761
762         log_debug("%s running as pid "PID_FMT,
763                   program_invocation_short_name, getpid());
764
765         use_journal = optind >= argc;
766         if (use_journal) {
767                 sd_journal *j;
768                 r = open_journal(&j);
769                 if (r < 0)
770                         goto finish;
771                 r = open_journal_for_upload(&u, j,
772                                             arg_cursor ?: u.last_cursor,
773                                             arg_cursor ? arg_after_cursor : true,
774                                             !!arg_follow);
775                 if (r < 0)
776                         goto finish;
777         }
778
779         sd_notify(false,
780                   "READY=1\n"
781                   "STATUS=Processing input...");
782
783         while (true) {
784                 if (use_journal) {
785                         if (!u.journal)
786                                 break;
787
788                         r = check_journal_input(&u);
789                 } else if (u.input < 0 && !use_journal) {
790                         if (optind >= argc)
791                                 break;
792
793                         log_debug("Using %s as input.", argv[optind]);
794                         r = open_file_for_upload(&u, argv[optind++]);
795                 }
796                 if (r < 0)
797                         goto cleanup;
798
799                 r = sd_event_get_state(u.events);
800                 if (r < 0)
801                         break;
802                 if (r == SD_EVENT_FINISHED)
803                         break;
804
805                 if (u.uploading) {
806                         r = perform_upload(&u);
807                         if (r < 0)
808                                 break;
809                 }
810
811                 r = sd_event_run(u.events, u.timeout);
812                 if (r < 0) {
813                         log_error("Failed to run event loop: %s", strerror(-r));
814                         break;
815                 }
816         }
817
818 cleanup:
819         sd_notify(false,
820                   "STOPPING=1\n"
821                   "STATUS=Shutting down...");
822
823         destroy_uploader(&u);
824
825 finish:
826         return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
827 }