]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal-remote/journal-upload.c
Fix some format strings for enums, they are signed
[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"
3d090cc6
ZJS
29#include "log.h"
30#include "util.h"
31#include "build.h"
722b6795 32#include "fileio.h"
d71839af 33#include "mkdir.h"
29fc0ddc 34#include "conf-parser.h"
2cf4172a 35#include "sigbus.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
2cf4172a 43static const char* arg_url = NULL;
7449bc1f
ZJS
44static const char *arg_key = NULL;
45static const char *arg_cert = NULL;
46static const char *arg_trust = NULL;
eacbb4d3
ZJS
47static const char *arg_directory = NULL;
48static char **arg_file = NULL;
49static const char *arg_cursor = NULL;
50static bool arg_after_cursor = false;
51static int arg_journal_type = 0;
52static const char *arg_machine = NULL;
53static bool arg_merge = false;
54static int arg_follow = -1;
722b6795 55static const char *arg_save_state = NULL;
eacbb4d3 56
2cf4172a
LP
57static void close_fd_input(Uploader *u);
58
eacbb4d3
ZJS
59#define SERVER_ANSWER_KEEP 2048
60
722b6795
ZJS
61#define STATE_FILE "/var/lib/systemd/journal-upload/state"
62
3d090cc6 63#define easy_setopt(curl, opt, value, level, cmd) \
8847551b 64 do { \
3d090cc6
ZJS
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 } \
8847551b 72 } while(0)
3d090cc6 73
eacbb4d3
ZJS
74static 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)
c33b3297
MS
88 log_warning_errno(ENOMEM, "Failed to store server answer (%zu bytes): %m",
89 size*nmemb);
eacbb4d3
ZJS
90 }
91
92 return size * nmemb;
93}
94
d71839af
ZJS
95static 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);
eb56eb9b
MS
104 if (r < 0)
105 return log_error_errno(r, "Cannot create parent directory of state file %s: %m",
106 u->state_file);
d71839af
ZJS
107
108 r = fopen_temporary(u->state_file, &f, &temp_path);
eb56eb9b
MS
109 if (r < 0)
110 return log_error_errno(r, "Cannot save state to %s: %m",
111 u->state_file);
d71839af
ZJS
112 unlink(temp_path);
113
114 return 0;
115}
116
722b6795
ZJS
117static 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
142finish:
143 if (r < 0)
da927ba9 144 log_error_errno(r, "Failed to save state %s: %m", u->state_file);
722b6795
ZJS
145
146 return r;
147}
148
149static 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
36d4739a
ZJS
159 if (r == -ENOENT)
160 log_debug("State file %s is not present.", u->state_file);
eb56eb9b
MS
161 else if (r < 0)
162 return log_error_errno(r, "Failed to read state file %s: %m",
163 u->state_file);
164 else
36d4739a 165 log_debug("Last cursor was %s", u->last_cursor);
722b6795
ZJS
166
167 return 0;
168}
169
170
171
3d090cc6
ZJS
172int 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
b88a40a7 218 easy_setopt(curl, CURLOPT_ERRORBUFFER, u->error,
eacbb4d3
ZJS
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
3d090cc6
ZJS
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
5dabb1e0
ZJS
239 if (_unlikely_(log_get_max_level() >= LOG_DEBUG))
240 /* enable verbose for easier tracing */
241 easy_setopt(curl, CURLOPT_VERBOSE, 1L, LOG_WARNING, );
3d090cc6
ZJS
242
243 easy_setopt(curl, CURLOPT_USERAGENT,
244 "systemd-journal-upload " PACKAGE_STRING,
245 LOG_WARNING, );
246
29fc0ddc 247 if (arg_key || startswith(u->url, "https://")) {
799a8f39 248 easy_setopt(curl, CURLOPT_SSLKEY, arg_key ?: PRIV_KEY_FILE,
7449bc1f 249 LOG_ERR, return -EXFULL);
29fc0ddc 250 easy_setopt(curl, CURLOPT_SSLCERT, arg_cert ?: CERT_FILE,
7449bc1f
ZJS
251 LOG_ERR, return -EXFULL);
252 }
253
8847551b
ZJS
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://"))
29fc0ddc 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 {
56f64d95 310 log_error_errno(errno, "Aborting transfer after read error on input: %m.");
3d090cc6
ZJS
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);
4a62c710
MS
359 if (fd < 0)
360 return log_error_errno(errno, "Failed to open %s: %m", filename);
3d090cc6
ZJS
361 }
362
363 u->input = fd;
364
eacbb4d3
ZJS
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) {
eb56eb9b
MS
369 if (r != -EPERM || arg_follow > 0)
370 return log_error_errno(r, "Failed to register input event: %m");
3d090cc6 371
eacbb4d3
ZJS
372 /* Normal files should just be consumed without polling. */
373 r = start_upload(u, fd_input_callback, u);
374 }
3d090cc6 375 }
eacbb4d3 376
3d090cc6
ZJS
377 return r;
378}
379
a3152e76
ZJS
380static 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
396static 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
722b6795 417static int setup_uploader(Uploader *u, const char *url, const char *state_file) {
3d090cc6 418 int r;
50a0b071 419 const char *host, *proto = "";
3d090cc6
ZJS
420
421 assert(u);
422 assert(url);
423
424 memzero(u, sizeof(Uploader));
425 u->input = -1;
426
50a0b071
ZJS
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;
5bc89120 437
50a0b071
ZJS
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 }
5bc89120
ZJS
445 if (!u->url)
446 return log_oom();
447
722b6795 448 u->state_file = state_file;
3d090cc6
ZJS
449
450 r = sd_event_default(&u->events);
eb56eb9b
MS
451 if (r < 0)
452 return log_error_errno(r, "sd_event_default failed: %m");
3d090cc6 453
a3152e76 454 r = setup_signals(u);
eb56eb9b
MS
455 if (r < 0)
456 return log_error_errno(r, "Failed to set up signals: %m");
a3152e76 457
722b6795 458 return load_cursor_state(u);
3d090cc6
ZJS
459}
460
461static void destroy_uploader(Uploader *u) {
462 assert(u);
463
464 curl_easy_cleanup(u->easy);
465 curl_slist_free_all(u->header);
eacbb4d3
ZJS
466 free(u->answer);
467
468 free(u->last_cursor);
722b6795 469 free(u->current_cursor);
3d090cc6 470
5bc89120
ZJS
471 free(u->url);
472
3d090cc6
ZJS
473 u->input_event = sd_event_source_unref(u->input_event);
474
475 close_fd_input(u);
eacbb4d3 476 close_journal_input(u);
3d090cc6 477
a3152e76
ZJS
478 sd_event_source_unref(u->sigterm_event);
479 sd_event_source_unref(u->sigint_event);
3d090cc6
ZJS
480 sd_event_unref(u->events);
481}
482
eacbb4d3
ZJS
483static 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) {
30776485
ZJS
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));
eacbb4d3
ZJS
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 %lu: %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 %lu: %s",
513 u->url, status, strna(u->answer));
514 return -EIO;
515 } else
516 log_debug("Upload finished successfully with code %lu: %s",
517 status, strna(u->answer));
722b6795
ZJS
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);
eacbb4d3
ZJS
524}
525
29fc0ddc
ZJS
526static 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 {}};
29fc0ddc 533
bf257aed
JT
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);
29fc0ddc
ZJS
538}
539
3d090cc6
ZJS
540static void help(void) {
541 printf("%s -u URL {FILE|-}...\n\n"
542 "Upload journal events to a remote server.\n\n"
dad29dff
LP
543 " -h --help Show this help\n"
544 " --version Show package version\n"
50a0b071
ZJS
545 " -u --url=URL Upload to this address (default port "
546 STRINGIFY(DEFAULT_PORT) ")\n"
1af719ed
ZJS
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"
dad29dff
LP
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"
3d090cc6
ZJS
566 , program_invocation_short_name);
567}
568
569static int parse_argv(int argc, char *argv[]) {
570 enum {
571 ARG_VERSION = 0x100,
7449bc1f
ZJS
572 ARG_KEY,
573 ARG_CERT,
574 ARG_TRUST,
eacbb4d3
ZJS
575 ARG_USER,
576 ARG_SYSTEM,
577 ARG_FILE,
578 ARG_CURSOR,
579 ARG_AFTER_CURSOR,
580 ARG_FOLLOW,
722b6795 581 ARG_SAVE_STATE,
3d090cc6
ZJS
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' },
7449bc1f
ZJS
588 { "key", required_argument, NULL, ARG_KEY },
589 { "cert", required_argument, NULL, ARG_CERT },
590 { "trust", required_argument, NULL, ARG_TRUST },
eacbb4d3
ZJS
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 },
dad29dff 599 { "follow", optional_argument, NULL, ARG_FOLLOW },
722b6795 600 { "save-state", optional_argument, NULL, ARG_SAVE_STATE },
3d090cc6
ZJS
601 {}
602 };
603
eacbb4d3 604 int c, r;
3d090cc6
ZJS
605
606 assert(argc >= 0);
607 assert(argv);
608
609 opterr = 0;
610
eacbb4d3 611 while ((c = getopt_long(argc, argv, "hu:mM:D:", options, NULL)) >= 0)
3d090cc6
ZJS
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
7449bc1f
ZJS
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
eacbb4d3
ZJS
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);
eb56eb9b
MS
690 if (r < 0)
691 return log_error_errno(r, "Failed to add paths: %m");
eacbb4d3
ZJS
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:
dad29dff
LP
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;
eacbb4d3 724
eacbb4d3
ZJS
725 break;
726
722b6795
ZJS
727 case ARG_SAVE_STATE:
728 arg_save_state = optarg ?: STATE_FILE;
729 break;
730
3d090cc6
ZJS
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
7449bc1f
ZJS
748 if (!!arg_key != !!arg_cert) {
749 log_error("Options --key and --cert must be used together.");
750 return -EINVAL;
751 }
752
eacbb4d3
ZJS
753 if (optind < argc && (arg_directory || arg_file || arg_machine || arg_journal_type)) {
754 log_error("Input arguments make no sense with journal input.");
3d090cc6
ZJS
755 return -EINVAL;
756 }
757
758 return 1;
759}
760
eacbb4d3
ZJS
761static 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)
c33b3297
MS
773 log_error_errno(r, "Failed to open %s: %m",
774 arg_directory ? arg_directory : arg_file ? "files" : "journal");
eacbb4d3
ZJS
775 return r;
776}
3d090cc6
ZJS
777
778int main(int argc, char **argv) {
779 Uploader u;
780 int r;
eacbb4d3 781 bool use_journal;
3d090cc6
ZJS
782
783 log_show_color(true);
784 log_parse_environment();
785
29fc0ddc 786 r = parse_config();
4015ac5c 787 if (r < 0)
29fc0ddc
ZJS
788 goto finish;
789
3d090cc6
ZJS
790 r = parse_argv(argc, argv);
791 if (r <= 0)
792 goto finish;
793
2cf4172a
LP
794 sigbus_install();
795
722b6795 796 r = setup_uploader(&u, arg_url, arg_save_state);
3d090cc6
ZJS
797 if (r < 0)
798 goto cleanup;
799
a3152e76
ZJS
800 sd_event_set_watchdog(u.events, true);
801
d71839af
ZJS
802 r = check_cursor_updating(&u);
803 if (r < 0)
804 goto cleanup;
805
3d090cc6
ZJS
806 log_debug("%s running as pid "PID_FMT,
807 program_invocation_short_name, getpid());
eacbb4d3
ZJS
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,
722b6795
ZJS
816 arg_cursor ?: u.last_cursor,
817 arg_cursor ? arg_after_cursor : true,
eacbb4d3
ZJS
818 !!arg_follow);
819 if (r < 0)
820 goto finish;
821 }
822
3d090cc6
ZJS
823 sd_notify(false,
824 "READY=1\n"
825 "STATUS=Processing input...");
826
827 while (true) {
36d4739a
ZJS
828 r = sd_event_get_state(u.events);
829 if (r < 0)
830 break;
831 if (r == SD_EVENT_FINISHED)
832 break;
833
eacbb4d3
ZJS
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) {
3d090cc6
ZJS
840 if (optind >= argc)
841 break;
842
843 log_debug("Using %s as input.", argv[optind]);
3d090cc6 844 r = open_file_for_upload(&u, argv[optind++]);
3d090cc6 845 }
eacbb4d3
ZJS
846 if (r < 0)
847 goto cleanup;
3d090cc6 848
3d090cc6 849 if (u.uploading) {
eacbb4d3
ZJS
850 r = perform_upload(&u);
851 if (r < 0)
3d090cc6 852 break;
3d090cc6
ZJS
853 }
854
eacbb4d3 855 r = sd_event_run(u.events, u.timeout);
3d090cc6 856 if (r < 0) {
da927ba9 857 log_error_errno(r, "Failed to run event loop: %m");
3d090cc6
ZJS
858 break;
859 }
860 }
861
862cleanup:
af4ec430
LP
863 sd_notify(false,
864 "STOPPING=1\n"
865 "STATUS=Shutting down...");
866
3d090cc6
ZJS
867 destroy_uploader(&u);
868
869finish:
36d4739a 870 return r >= 0 ? EXIT_SUCCESS : EXIT_FAILURE;
3d090cc6 871}