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