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