]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal-remote/journal-upload.c
journal-upload: avoid calling printf with maximum precision
[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
ZJS
65#define easy_setopt(curl, opt, value, level, cmd) \
66 { \
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 }
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)
90 log_warning("Failed to store server answer (%zu bytes): %s",
91 size*nmemb, strerror(ENOMEM));
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);
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
722b6795
ZJS
123static 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
148finish:
149 if (r < 0)
150 log_error("Failed to save state %s: %s", u->state_file, strerror(-r));
151
152 return r;
153}
154
155static 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
3d090cc6
ZJS
176int 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
b88a40a7 222 easy_setopt(curl, CURLOPT_ERRORBUFFER, u->error,
eacbb4d3
ZJS
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
3d090cc6
ZJS
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
29fc0ddc 250 if (arg_key || startswith(u->url, "https://")) {
799a8f39 251 easy_setopt(curl, CURLOPT_SSLKEY, arg_key ?: PRIV_KEY_FILE,
7449bc1f 252 LOG_ERR, return -EXFULL);
29fc0ddc 253 easy_setopt(curl, CURLOPT_SSLCERT, arg_cert ?: CERT_FILE,
7449bc1f
ZJS
254 LOG_ERR, return -EXFULL);
255 }
256
29fc0ddc
ZJS
257 if (arg_trust || startswith(u->url, "https://"))
258 easy_setopt(curl, CURLOPT_CAINFO, arg_trust ?: TRUST_FILE,
7449bc1f
ZJS
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
3d090cc6 265 u->easy = curl;
eacbb4d3
ZJS
266 } else {
267 /* truncate the potential old error message */
268 u->error[0] = '\0';
269
270 free(u->answer);
271 u->answer = 0;
3d090cc6
ZJS
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
287static 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 %zu", __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("Aborting transfer after read error on input: %m.");
311 return CURL_READFUNC_ABORT;
312 }
313}
314
315static void close_fd_input(Uploader *u) {
316 assert(u);
317
318 if (u->input >= 0)
319 close_nointr(u->input);
320 u->input = -1;
eacbb4d3 321 u->timeout = 0;
3d090cc6
ZJS
322}
323
324static 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);
3d090cc6
ZJS
331 assert(fd >= 0);
332
8201af08
ZJS
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
3d090cc6
ZJS
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
352static int open_file_for_upload(Uploader *u, const char *filename) {
e1ad6e24 353 int fd, r = 0;
3d090cc6
ZJS
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 log_error("Failed to open %s: %m", filename);
361 return -errno;
362 }
363 }
364
365 u->input = fd;
366
eacbb4d3
ZJS
367 if (arg_follow) {
368 r = sd_event_add_io(u->events, &u->input_event,
369 fd, EPOLLIN, dispatch_fd_input, u);
370 if (r < 0) {
371 if (r != -EPERM || arg_follow > 0) {
372 log_error("Failed to register input event: %s", strerror(-r));
373 return r;
374 }
3d090cc6 375
eacbb4d3
ZJS
376 /* Normal files should just be consumed without polling. */
377 r = start_upload(u, fd_input_callback, u);
378 }
3d090cc6 379 }
eacbb4d3 380
3d090cc6
ZJS
381 return r;
382}
383
a3152e76
ZJS
384static int dispatch_sigterm(sd_event_source *event,
385 const struct signalfd_siginfo *si,
386 void *userdata) {
387 Uploader *u = userdata;
388
389 assert(u);
390
391 log_received_signal(LOG_INFO, si);
392
393 close_fd_input(u);
394 close_journal_input(u);
395
396 sd_event_exit(u->events, 0);
397 return 0;
398}
399
400static int setup_signals(Uploader *u) {
401 sigset_t mask;
402 int r;
403
404 assert(u);
405
406 assert_se(sigemptyset(&mask) == 0);
407 sigset_add_many(&mask, SIGINT, SIGTERM, -1);
408 assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
409
410 r = sd_event_add_signal(u->events, &u->sigterm_event, SIGTERM, dispatch_sigterm, u);
411 if (r < 0)
412 return r;
413
414 r = sd_event_add_signal(u->events, &u->sigint_event, SIGINT, dispatch_sigterm, u);
415 if (r < 0)
416 return r;
417
418 return 0;
419}
420
722b6795 421static int setup_uploader(Uploader *u, const char *url, const char *state_file) {
3d090cc6 422 int r;
50a0b071 423 const char *host, *proto = "";
3d090cc6
ZJS
424
425 assert(u);
426 assert(url);
427
428 memzero(u, sizeof(Uploader));
429 u->input = -1;
430
50a0b071
ZJS
431 if (!(host = startswith(url, "http://")) && !(host = startswith(url, "https://"))) {
432 host = url;
433 proto = "https://";
434 }
435
436 if (strchr(host, ':'))
437 u->url = strjoin(proto, url, "/upload", NULL);
438 else {
439 char *t;
440 size_t x;
5bc89120 441
50a0b071
ZJS
442 t = strdupa(url);
443 x = strlen(t);
444 while (x > 0 && t[x - 1] == '/')
445 t[x - 1] = '\0';
446
447 u->url = strjoin(proto, t, ":" STRINGIFY(DEFAULT_PORT), "/upload", NULL);
448 }
5bc89120
ZJS
449 if (!u->url)
450 return log_oom();
451
722b6795 452 u->state_file = state_file;
3d090cc6
ZJS
453
454 r = sd_event_default(&u->events);
455 if (r < 0) {
456 log_error("sd_event_default failed: %s", strerror(-r));
457 return r;
458 }
459
a3152e76
ZJS
460 r = setup_signals(u);
461 if (r < 0) {
462 log_error("Failed to set up signals: %s", strerror(-r));
463 return r;
464 }
465
722b6795 466 return load_cursor_state(u);
3d090cc6
ZJS
467}
468
469static void destroy_uploader(Uploader *u) {
470 assert(u);
471
472 curl_easy_cleanup(u->easy);
473 curl_slist_free_all(u->header);
eacbb4d3
ZJS
474 free(u->answer);
475
476 free(u->last_cursor);
722b6795 477 free(u->current_cursor);
3d090cc6 478
5bc89120
ZJS
479 free(u->url);
480
3d090cc6
ZJS
481 u->input_event = sd_event_source_unref(u->input_event);
482
483 close_fd_input(u);
eacbb4d3 484 close_journal_input(u);
3d090cc6 485
a3152e76
ZJS
486 sd_event_source_unref(u->sigterm_event);
487 sd_event_source_unref(u->sigint_event);
3d090cc6
ZJS
488 sd_event_unref(u->events);
489}
490
eacbb4d3
ZJS
491static int perform_upload(Uploader *u) {
492 CURLcode code;
493 long status;
494
495 assert(u);
496
497 code = curl_easy_perform(u->easy);
498 if (code) {
30776485
ZJS
499 if (u->error[0])
500 log_error("Upload to %s failed: %.*s",
501 u->url, (int) sizeof(u->error), u->error);
502 else
503 log_error("Upload to %s failed: %s",
504 u->url, curl_easy_strerror(code));
eacbb4d3
ZJS
505 return -EIO;
506 }
507
508 code = curl_easy_getinfo(u->easy, CURLINFO_RESPONSE_CODE, &status);
509 if (code) {
510 log_error("Failed to retrieve response code: %s",
511 curl_easy_strerror(code));
512 return -EUCLEAN;
513 }
514
515 if (status >= 300) {
516 log_error("Upload to %s failed with code %lu: %s",
517 u->url, status, strna(u->answer));
518 return -EIO;
519 } else if (status < 200) {
520 log_error("Upload to %s finished with unexpected code %lu: %s",
521 u->url, status, strna(u->answer));
522 return -EIO;
523 } else
524 log_debug("Upload finished successfully with code %lu: %s",
525 status, strna(u->answer));
722b6795
ZJS
526
527 free(u->last_cursor);
528 u->last_cursor = u->current_cursor;
529 u->current_cursor = NULL;
530
531 return update_cursor_state(u);
eacbb4d3
ZJS
532}
533
29fc0ddc
ZJS
534static int parse_config(void) {
535 const ConfigTableItem items[] = {
536 { "Upload", "URL", config_parse_string, 0, &arg_url },
537 { "Upload", "ServerKeyFile", config_parse_path, 0, &arg_key },
538 { "Upload", "ServerCertificateFile", config_parse_path, 0, &arg_cert },
539 { "Upload", "TrustedCertificateFile", config_parse_path, 0, &arg_trust },
540 {}};
29fc0ddc 541
36f822c4
ZJS
542 return config_parse(NULL, PKGSYSCONFDIR "/journal-upload.conf", NULL,
543 "Upload\0",
544 config_item_table_lookup, items,
545 false, false, true, NULL);
29fc0ddc
ZJS
546}
547
3d090cc6
ZJS
548static void help(void) {
549 printf("%s -u URL {FILE|-}...\n\n"
550 "Upload journal events to a remote server.\n\n"
dad29dff
LP
551 " -h --help Show this help\n"
552 " --version Show package version\n"
50a0b071
ZJS
553 " -u --url=URL Upload to this address (default port "
554 STRINGIFY(DEFAULT_PORT) ")\n"
1af719ed
ZJS
555 " --key=FILENAME Specify key in PEM format (default:\n"
556 " \"" PRIV_KEY_FILE "\")\n"
557 " --cert=FILENAME Specify certificate in PEM format (default:\n"
558 " \"" CERT_FILE "\")\n"
559 " --trust=FILENAME|all Specify CA certificate or disable checking (default:\n"
560 " \"" TRUST_FILE "\")\n"
dad29dff
LP
561 " --system Use the system journal\n"
562 " --user Use the user journal for the current user\n"
563 " -m --merge Use all available journals\n"
564 " -M --machine=CONTAINER Operate on local container\n"
565 " -D --directory=PATH Use journal files from directory\n"
566 " --file=PATH Use this journal file\n"
567 " --cursor=CURSOR Start at the specified cursor\n"
568 " --after-cursor=CURSOR Start after the specified cursor\n"
569 " --follow[=BOOL] Do [not] wait for input\n"
570 " --save-state[=FILE] Save uploaded cursors (default \n"
571 " " STATE_FILE ")\n"
572 " -h --help Show this help and exit\n"
573 " --version Print version string and exit\n"
3d090cc6
ZJS
574 , program_invocation_short_name);
575}
576
577static int parse_argv(int argc, char *argv[]) {
578 enum {
579 ARG_VERSION = 0x100,
7449bc1f
ZJS
580 ARG_KEY,
581 ARG_CERT,
582 ARG_TRUST,
eacbb4d3
ZJS
583 ARG_USER,
584 ARG_SYSTEM,
585 ARG_FILE,
586 ARG_CURSOR,
587 ARG_AFTER_CURSOR,
588 ARG_FOLLOW,
722b6795 589 ARG_SAVE_STATE,
3d090cc6
ZJS
590 };
591
592 static const struct option options[] = {
593 { "help", no_argument, NULL, 'h' },
594 { "version", no_argument, NULL, ARG_VERSION },
595 { "url", required_argument, NULL, 'u' },
7449bc1f
ZJS
596 { "key", required_argument, NULL, ARG_KEY },
597 { "cert", required_argument, NULL, ARG_CERT },
598 { "trust", required_argument, NULL, ARG_TRUST },
eacbb4d3
ZJS
599 { "system", no_argument, NULL, ARG_SYSTEM },
600 { "user", no_argument, NULL, ARG_USER },
601 { "merge", no_argument, NULL, 'm' },
602 { "machine", required_argument, NULL, 'M' },
603 { "directory", required_argument, NULL, 'D' },
604 { "file", required_argument, NULL, ARG_FILE },
605 { "cursor", required_argument, NULL, ARG_CURSOR },
606 { "after-cursor", required_argument, NULL, ARG_AFTER_CURSOR },
dad29dff 607 { "follow", optional_argument, NULL, ARG_FOLLOW },
722b6795 608 { "save-state", optional_argument, NULL, ARG_SAVE_STATE },
3d090cc6
ZJS
609 {}
610 };
611
eacbb4d3 612 int c, r;
3d090cc6
ZJS
613
614 assert(argc >= 0);
615 assert(argv);
616
617 opterr = 0;
618
eacbb4d3 619 while ((c = getopt_long(argc, argv, "hu:mM:D:", options, NULL)) >= 0)
3d090cc6
ZJS
620 switch(c) {
621 case 'h':
622 help();
623 return 0 /* done */;
624
625 case ARG_VERSION:
626 puts(PACKAGE_STRING);
627 puts(SYSTEMD_FEATURES);
628 return 0 /* done */;
629
630 case 'u':
631 if (arg_url) {
632 log_error("cannot use more than one --url");
633 return -EINVAL;
634 }
635
636 arg_url = optarg;
637 break;
638
7449bc1f
ZJS
639 case ARG_KEY:
640 if (arg_key) {
641 log_error("cannot use more than one --key");
642 return -EINVAL;
643 }
644
645 arg_key = optarg;
646 break;
647
648 case ARG_CERT:
649 if (arg_cert) {
650 log_error("cannot use more than one --cert");
651 return -EINVAL;
652 }
653
654 arg_cert = optarg;
655 break;
656
657 case ARG_TRUST:
658 if (arg_trust) {
659 log_error("cannot use more than one --trust");
660 return -EINVAL;
661 }
662
663 arg_trust = optarg;
664 break;
665
eacbb4d3
ZJS
666 case ARG_SYSTEM:
667 arg_journal_type |= SD_JOURNAL_SYSTEM;
668 break;
669
670 case ARG_USER:
671 arg_journal_type |= SD_JOURNAL_CURRENT_USER;
672 break;
673
674 case 'm':
675 arg_merge = true;
676 break;
677
678 case 'M':
679 if (arg_machine) {
680 log_error("cannot use more than one --machine/-M");
681 return -EINVAL;
682 }
683
684 arg_machine = optarg;
685 break;
686
687 case 'D':
688 if (arg_directory) {
689 log_error("cannot use more than one --directory/-D");
690 return -EINVAL;
691 }
692
693 arg_directory = optarg;
694 break;
695
696 case ARG_FILE:
697 r = glob_extend(&arg_file, optarg);
698 if (r < 0) {
699 log_error("Failed to add paths: %s", strerror(-r));
700 return r;
701 };
702 break;
703
704 case ARG_CURSOR:
705 if (arg_cursor) {
706 log_error("cannot use more than one --cursor/--after-cursor");
707 return -EINVAL;
708 }
709
710 arg_cursor = optarg;
711 break;
712
713 case ARG_AFTER_CURSOR:
714 if (arg_cursor) {
715 log_error("cannot use more than one --cursor/--after-cursor");
716 return -EINVAL;
717 }
718
719 arg_cursor = optarg;
720 arg_after_cursor = true;
721 break;
722
723 case ARG_FOLLOW:
dad29dff
LP
724 if (optarg) {
725 r = parse_boolean(optarg);
726 if (r < 0) {
727 log_error("Failed to parse --follow= parameter.");
728 return -EINVAL;
729 }
730
731 arg_follow = !!r;
732 } else
733 arg_follow = true;
eacbb4d3 734
eacbb4d3
ZJS
735 break;
736
722b6795
ZJS
737 case ARG_SAVE_STATE:
738 arg_save_state = optarg ?: STATE_FILE;
739 break;
740
3d090cc6
ZJS
741 case '?':
742 log_error("Unknown option %s.", argv[optind-1]);
743 return -EINVAL;
744
745 case ':':
746 log_error("Missing argument to %s.", argv[optind-1]);
747 return -EINVAL;
748
749 default:
750 assert_not_reached("Unhandled option code.");
751 }
752
753 if (!arg_url) {
754 log_error("Required --url/-u option missing.");
755 return -EINVAL;
756 }
757
7449bc1f
ZJS
758 if (!!arg_key != !!arg_cert) {
759 log_error("Options --key and --cert must be used together.");
760 return -EINVAL;
761 }
762
eacbb4d3
ZJS
763 if (optind < argc && (arg_directory || arg_file || arg_machine || arg_journal_type)) {
764 log_error("Input arguments make no sense with journal input.");
3d090cc6
ZJS
765 return -EINVAL;
766 }
767
768 return 1;
769}
770
eacbb4d3
ZJS
771static int open_journal(sd_journal **j) {
772 int r;
773
774 if (arg_directory)
775 r = sd_journal_open_directory(j, arg_directory, arg_journal_type);
776 else if (arg_file)
777 r = sd_journal_open_files(j, (const char**) arg_file, 0);
778 else if (arg_machine)
779 r = sd_journal_open_container(j, arg_machine, 0);
780 else
781 r = sd_journal_open(j, !arg_merge*SD_JOURNAL_LOCAL_ONLY + arg_journal_type);
782 if (r < 0)
783 log_error("Failed to open %s: %s",
784 arg_directory ? arg_directory : arg_file ? "files" : "journal",
785 strerror(-r));
786 return r;
787}
3d090cc6
ZJS
788
789int main(int argc, char **argv) {
790 Uploader u;
791 int r;
eacbb4d3 792 bool use_journal;
3d090cc6
ZJS
793
794 log_show_color(true);
795 log_parse_environment();
796
29fc0ddc 797 r = parse_config();
4015ac5c 798 if (r < 0)
29fc0ddc
ZJS
799 goto finish;
800
3d090cc6
ZJS
801 r = parse_argv(argc, argv);
802 if (r <= 0)
803 goto finish;
804
722b6795 805 r = setup_uploader(&u, arg_url, arg_save_state);
3d090cc6
ZJS
806 if (r < 0)
807 goto cleanup;
808
a3152e76
ZJS
809 sd_event_set_watchdog(u.events, true);
810
d71839af
ZJS
811 r = check_cursor_updating(&u);
812 if (r < 0)
813 goto cleanup;
814
3d090cc6
ZJS
815 log_debug("%s running as pid "PID_FMT,
816 program_invocation_short_name, getpid());
eacbb4d3
ZJS
817
818 use_journal = optind >= argc;
819 if (use_journal) {
820 sd_journal *j;
821 r = open_journal(&j);
822 if (r < 0)
823 goto finish;
824 r = open_journal_for_upload(&u, j,
722b6795
ZJS
825 arg_cursor ?: u.last_cursor,
826 arg_cursor ? arg_after_cursor : true,
eacbb4d3
ZJS
827 !!arg_follow);
828 if (r < 0)
829 goto finish;
830 }
831
3d090cc6
ZJS
832 sd_notify(false,
833 "READY=1\n"
834 "STATUS=Processing input...");
835
836 while (true) {
eacbb4d3
ZJS
837 if (use_journal) {
838 if (!u.journal)
839 break;
840
841 r = check_journal_input(&u);
842 } else if (u.input < 0 && !use_journal) {
3d090cc6
ZJS
843 if (optind >= argc)
844 break;
845
846 log_debug("Using %s as input.", argv[optind]);
3d090cc6 847 r = open_file_for_upload(&u, argv[optind++]);
3d090cc6 848 }
eacbb4d3
ZJS
849 if (r < 0)
850 goto cleanup;
3d090cc6
ZJS
851
852 r = sd_event_get_state(u.events);
853 if (r < 0)
854 break;
855 if (r == SD_EVENT_FINISHED)
856 break;
857
858 if (u.uploading) {
eacbb4d3
ZJS
859 r = perform_upload(&u);
860 if (r < 0)
3d090cc6 861 break;
3d090cc6
ZJS
862 }
863
eacbb4d3 864 r = sd_event_run(u.events, u.timeout);
3d090cc6
ZJS
865 if (r < 0) {
866 log_error("Failed to run event loop: %s", strerror(-r));
867 break;
868 }
869 }
870
871cleanup:
af4ec430
LP
872 sd_notify(false,
873 "STOPPING=1\n"
874 "STATUS=Shutting down...");
875
3d090cc6
ZJS
876 destroy_uploader(&u);
877
878finish:
879 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
880}