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