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