]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal-remote/journal-upload.c
systemctl: fix waiting for jobs when using direct connections to PID 1 for dbus
[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 {
56f64d95 311 log_error_errno(errno, "Aborting transfer after read error on input: %m.");
3d090cc6
ZJS
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);
4a62c710
MS
360 if (fd < 0)
361 return log_error_errno(errno, "Failed to open %s: %m", filename);
3d090cc6
ZJS
362 }
363
364 u->input = fd;
365
eacbb4d3
ZJS
366 if (arg_follow) {
367 r = sd_event_add_io(u->events, &u->input_event,
368 fd, EPOLLIN, dispatch_fd_input, u);
369 if (r < 0) {
eb56eb9b
MS
370 if (r != -EPERM || arg_follow > 0)
371 return log_error_errno(r, "Failed to register input event: %m");
3d090cc6 372
eacbb4d3
ZJS
373 /* Normal files should just be consumed without polling. */
374 r = start_upload(u, fd_input_callback, u);
375 }
3d090cc6 376 }
eacbb4d3 377
3d090cc6
ZJS
378 return r;
379}
380
a3152e76
ZJS
381static int dispatch_sigterm(sd_event_source *event,
382 const struct signalfd_siginfo *si,
383 void *userdata) {
384 Uploader *u = userdata;
385
386 assert(u);
387
388 log_received_signal(LOG_INFO, si);
389
390 close_fd_input(u);
391 close_journal_input(u);
392
393 sd_event_exit(u->events, 0);
394 return 0;
395}
396
397static int setup_signals(Uploader *u) {
398 sigset_t mask;
399 int r;
400
401 assert(u);
402
403 assert_se(sigemptyset(&mask) == 0);
404 sigset_add_many(&mask, SIGINT, SIGTERM, -1);
405 assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
406
407 r = sd_event_add_signal(u->events, &u->sigterm_event, SIGTERM, dispatch_sigterm, u);
408 if (r < 0)
409 return r;
410
411 r = sd_event_add_signal(u->events, &u->sigint_event, SIGINT, dispatch_sigterm, u);
412 if (r < 0)
413 return r;
414
415 return 0;
416}
417
722b6795 418static int setup_uploader(Uploader *u, const char *url, const char *state_file) {
3d090cc6 419 int r;
50a0b071 420 const char *host, *proto = "";
3d090cc6
ZJS
421
422 assert(u);
423 assert(url);
424
425 memzero(u, sizeof(Uploader));
426 u->input = -1;
427
50a0b071
ZJS
428 if (!(host = startswith(url, "http://")) && !(host = startswith(url, "https://"))) {
429 host = url;
430 proto = "https://";
431 }
432
433 if (strchr(host, ':'))
434 u->url = strjoin(proto, url, "/upload", NULL);
435 else {
436 char *t;
437 size_t x;
5bc89120 438
50a0b071
ZJS
439 t = strdupa(url);
440 x = strlen(t);
441 while (x > 0 && t[x - 1] == '/')
442 t[x - 1] = '\0';
443
444 u->url = strjoin(proto, t, ":" STRINGIFY(DEFAULT_PORT), "/upload", NULL);
445 }
5bc89120
ZJS
446 if (!u->url)
447 return log_oom();
448
722b6795 449 u->state_file = state_file;
3d090cc6
ZJS
450
451 r = sd_event_default(&u->events);
eb56eb9b
MS
452 if (r < 0)
453 return log_error_errno(r, "sd_event_default failed: %m");
3d090cc6 454
a3152e76 455 r = setup_signals(u);
eb56eb9b
MS
456 if (r < 0)
457 return log_error_errno(r, "Failed to set up signals: %m");
a3152e76 458
722b6795 459 return load_cursor_state(u);
3d090cc6
ZJS
460}
461
462static void destroy_uploader(Uploader *u) {
463 assert(u);
464
465 curl_easy_cleanup(u->easy);
466 curl_slist_free_all(u->header);
eacbb4d3
ZJS
467 free(u->answer);
468
469 free(u->last_cursor);
722b6795 470 free(u->current_cursor);
3d090cc6 471
5bc89120
ZJS
472 free(u->url);
473
3d090cc6
ZJS
474 u->input_event = sd_event_source_unref(u->input_event);
475
476 close_fd_input(u);
eacbb4d3 477 close_journal_input(u);
3d090cc6 478
a3152e76
ZJS
479 sd_event_source_unref(u->sigterm_event);
480 sd_event_source_unref(u->sigint_event);
3d090cc6
ZJS
481 sd_event_unref(u->events);
482}
483
eacbb4d3
ZJS
484static int perform_upload(Uploader *u) {
485 CURLcode code;
486 long status;
487
488 assert(u);
489
490 code = curl_easy_perform(u->easy);
491 if (code) {
30776485
ZJS
492 if (u->error[0])
493 log_error("Upload to %s failed: %.*s",
494 u->url, (int) sizeof(u->error), u->error);
495 else
496 log_error("Upload to %s failed: %s",
497 u->url, curl_easy_strerror(code));
eacbb4d3
ZJS
498 return -EIO;
499 }
500
501 code = curl_easy_getinfo(u->easy, CURLINFO_RESPONSE_CODE, &status);
502 if (code) {
503 log_error("Failed to retrieve response code: %s",
504 curl_easy_strerror(code));
505 return -EUCLEAN;
506 }
507
508 if (status >= 300) {
509 log_error("Upload to %s failed with code %lu: %s",
510 u->url, status, strna(u->answer));
511 return -EIO;
512 } else if (status < 200) {
513 log_error("Upload to %s finished with unexpected code %lu: %s",
514 u->url, status, strna(u->answer));
515 return -EIO;
516 } else
517 log_debug("Upload finished successfully with code %lu: %s",
518 status, strna(u->answer));
722b6795
ZJS
519
520 free(u->last_cursor);
521 u->last_cursor = u->current_cursor;
522 u->current_cursor = NULL;
523
524 return update_cursor_state(u);
eacbb4d3
ZJS
525}
526
29fc0ddc
ZJS
527static int parse_config(void) {
528 const ConfigTableItem items[] = {
529 { "Upload", "URL", config_parse_string, 0, &arg_url },
530 { "Upload", "ServerKeyFile", config_parse_path, 0, &arg_key },
531 { "Upload", "ServerCertificateFile", config_parse_path, 0, &arg_cert },
532 { "Upload", "TrustedCertificateFile", config_parse_path, 0, &arg_trust },
533 {}};
29fc0ddc 534
bf257aed
JT
535 return config_parse_many(PKGSYSCONFDIR "/journal-upload.conf",
536 CONF_DIRS_NULSTR("systemd/journal-upload.conf"),
537 "Upload\0", config_item_table_lookup, items,
538 false, NULL);
29fc0ddc
ZJS
539}
540
3d090cc6
ZJS
541static void help(void) {
542 printf("%s -u URL {FILE|-}...\n\n"
543 "Upload journal events to a remote server.\n\n"
dad29dff
LP
544 " -h --help Show this help\n"
545 " --version Show package version\n"
50a0b071
ZJS
546 " -u --url=URL Upload to this address (default port "
547 STRINGIFY(DEFAULT_PORT) ")\n"
1af719ed
ZJS
548 " --key=FILENAME Specify key in PEM format (default:\n"
549 " \"" PRIV_KEY_FILE "\")\n"
550 " --cert=FILENAME Specify certificate in PEM format (default:\n"
551 " \"" CERT_FILE "\")\n"
552 " --trust=FILENAME|all Specify CA certificate or disable checking (default:\n"
553 " \"" TRUST_FILE "\")\n"
dad29dff
LP
554 " --system Use the system journal\n"
555 " --user Use the user journal for the current user\n"
556 " -m --merge Use all available journals\n"
557 " -M --machine=CONTAINER Operate on local container\n"
558 " -D --directory=PATH Use journal files from directory\n"
559 " --file=PATH Use this journal file\n"
560 " --cursor=CURSOR Start at the specified cursor\n"
561 " --after-cursor=CURSOR Start after the specified cursor\n"
562 " --follow[=BOOL] Do [not] wait for input\n"
563 " --save-state[=FILE] Save uploaded cursors (default \n"
564 " " STATE_FILE ")\n"
565 " -h --help Show this help and exit\n"
566 " --version Print version string and exit\n"
3d090cc6
ZJS
567 , program_invocation_short_name);
568}
569
570static int parse_argv(int argc, char *argv[]) {
571 enum {
572 ARG_VERSION = 0x100,
7449bc1f
ZJS
573 ARG_KEY,
574 ARG_CERT,
575 ARG_TRUST,
eacbb4d3
ZJS
576 ARG_USER,
577 ARG_SYSTEM,
578 ARG_FILE,
579 ARG_CURSOR,
580 ARG_AFTER_CURSOR,
581 ARG_FOLLOW,
722b6795 582 ARG_SAVE_STATE,
3d090cc6
ZJS
583 };
584
585 static const struct option options[] = {
586 { "help", no_argument, NULL, 'h' },
587 { "version", no_argument, NULL, ARG_VERSION },
588 { "url", required_argument, NULL, 'u' },
7449bc1f
ZJS
589 { "key", required_argument, NULL, ARG_KEY },
590 { "cert", required_argument, NULL, ARG_CERT },
591 { "trust", required_argument, NULL, ARG_TRUST },
eacbb4d3
ZJS
592 { "system", no_argument, NULL, ARG_SYSTEM },
593 { "user", no_argument, NULL, ARG_USER },
594 { "merge", no_argument, NULL, 'm' },
595 { "machine", required_argument, NULL, 'M' },
596 { "directory", required_argument, NULL, 'D' },
597 { "file", required_argument, NULL, ARG_FILE },
598 { "cursor", required_argument, NULL, ARG_CURSOR },
599 { "after-cursor", required_argument, NULL, ARG_AFTER_CURSOR },
dad29dff 600 { "follow", optional_argument, NULL, ARG_FOLLOW },
722b6795 601 { "save-state", optional_argument, NULL, ARG_SAVE_STATE },
3d090cc6
ZJS
602 {}
603 };
604
eacbb4d3 605 int c, r;
3d090cc6
ZJS
606
607 assert(argc >= 0);
608 assert(argv);
609
610 opterr = 0;
611
eacbb4d3 612 while ((c = getopt_long(argc, argv, "hu:mM:D:", options, NULL)) >= 0)
3d090cc6
ZJS
613 switch(c) {
614 case 'h':
615 help();
616 return 0 /* done */;
617
618 case ARG_VERSION:
619 puts(PACKAGE_STRING);
620 puts(SYSTEMD_FEATURES);
621 return 0 /* done */;
622
623 case 'u':
624 if (arg_url) {
625 log_error("cannot use more than one --url");
626 return -EINVAL;
627 }
628
629 arg_url = optarg;
630 break;
631
7449bc1f
ZJS
632 case ARG_KEY:
633 if (arg_key) {
634 log_error("cannot use more than one --key");
635 return -EINVAL;
636 }
637
638 arg_key = optarg;
639 break;
640
641 case ARG_CERT:
642 if (arg_cert) {
643 log_error("cannot use more than one --cert");
644 return -EINVAL;
645 }
646
647 arg_cert = optarg;
648 break;
649
650 case ARG_TRUST:
651 if (arg_trust) {
652 log_error("cannot use more than one --trust");
653 return -EINVAL;
654 }
655
656 arg_trust = optarg;
657 break;
658
eacbb4d3
ZJS
659 case ARG_SYSTEM:
660 arg_journal_type |= SD_JOURNAL_SYSTEM;
661 break;
662
663 case ARG_USER:
664 arg_journal_type |= SD_JOURNAL_CURRENT_USER;
665 break;
666
667 case 'm':
668 arg_merge = true;
669 break;
670
671 case 'M':
672 if (arg_machine) {
673 log_error("cannot use more than one --machine/-M");
674 return -EINVAL;
675 }
676
677 arg_machine = optarg;
678 break;
679
680 case 'D':
681 if (arg_directory) {
682 log_error("cannot use more than one --directory/-D");
683 return -EINVAL;
684 }
685
686 arg_directory = optarg;
687 break;
688
689 case ARG_FILE:
690 r = glob_extend(&arg_file, optarg);
eb56eb9b
MS
691 if (r < 0)
692 return log_error_errno(r, "Failed to add paths: %m");
eacbb4d3
ZJS
693 break;
694
695 case ARG_CURSOR:
696 if (arg_cursor) {
697 log_error("cannot use more than one --cursor/--after-cursor");
698 return -EINVAL;
699 }
700
701 arg_cursor = optarg;
702 break;
703
704 case ARG_AFTER_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 arg_after_cursor = true;
712 break;
713
714 case ARG_FOLLOW:
dad29dff
LP
715 if (optarg) {
716 r = parse_boolean(optarg);
717 if (r < 0) {
718 log_error("Failed to parse --follow= parameter.");
719 return -EINVAL;
720 }
721
722 arg_follow = !!r;
723 } else
724 arg_follow = true;
eacbb4d3 725
eacbb4d3
ZJS
726 break;
727
722b6795
ZJS
728 case ARG_SAVE_STATE:
729 arg_save_state = optarg ?: STATE_FILE;
730 break;
731
3d090cc6
ZJS
732 case '?':
733 log_error("Unknown option %s.", argv[optind-1]);
734 return -EINVAL;
735
736 case ':':
737 log_error("Missing argument to %s.", argv[optind-1]);
738 return -EINVAL;
739
740 default:
741 assert_not_reached("Unhandled option code.");
742 }
743
744 if (!arg_url) {
745 log_error("Required --url/-u option missing.");
746 return -EINVAL;
747 }
748
7449bc1f
ZJS
749 if (!!arg_key != !!arg_cert) {
750 log_error("Options --key and --cert must be used together.");
751 return -EINVAL;
752 }
753
eacbb4d3
ZJS
754 if (optind < argc && (arg_directory || arg_file || arg_machine || arg_journal_type)) {
755 log_error("Input arguments make no sense with journal input.");
3d090cc6
ZJS
756 return -EINVAL;
757 }
758
759 return 1;
760}
761
eacbb4d3
ZJS
762static int open_journal(sd_journal **j) {
763 int r;
764
765 if (arg_directory)
766 r = sd_journal_open_directory(j, arg_directory, arg_journal_type);
767 else if (arg_file)
768 r = sd_journal_open_files(j, (const char**) arg_file, 0);
769 else if (arg_machine)
770 r = sd_journal_open_container(j, arg_machine, 0);
771 else
772 r = sd_journal_open(j, !arg_merge*SD_JOURNAL_LOCAL_ONLY + arg_journal_type);
773 if (r < 0)
c33b3297
MS
774 log_error_errno(r, "Failed to open %s: %m",
775 arg_directory ? arg_directory : arg_file ? "files" : "journal");
eacbb4d3
ZJS
776 return r;
777}
3d090cc6
ZJS
778
779int main(int argc, char **argv) {
780 Uploader u;
781 int r;
eacbb4d3 782 bool use_journal;
3d090cc6
ZJS
783
784 log_show_color(true);
785 log_parse_environment();
786
29fc0ddc 787 r = parse_config();
4015ac5c 788 if (r < 0)
29fc0ddc
ZJS
789 goto finish;
790
3d090cc6
ZJS
791 r = parse_argv(argc, argv);
792 if (r <= 0)
793 goto finish;
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}