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