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