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