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