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