]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal-remote/journal-upload.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / journal-remote / journal-upload.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
3d090cc6
ZJS
2/***
3 This file is part of systemd.
4
5 Copyright 2014 Zbigniew Jędrzejewski-Szmek
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
07630cea 21#include <curl/curl.h>
3d090cc6
ZJS
22#include <fcntl.h>
23#include <getopt.h>
3f6fd1ba
LP
24#include <stdio.h>
25#include <sys/stat.h>
3d090cc6
ZJS
26
27#include "sd-daemon.h"
3f6fd1ba 28
b5efdb8a 29#include "alloc-util.h"
3f6fd1ba 30#include "conf-parser.h"
a0f29c76 31#include "def.h"
3ffd4af2 32#include "fd-util.h"
722b6795 33#include "fileio.h"
f97b34a6 34#include "format-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 } \
9ed794a3 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, ':'))
605405c6 442 u->url = strjoin(proto, url, "/upload");
50a0b071
ZJS
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
605405c6 452 u->url = strjoin(proto, t, ":" STRINGIFY(DEFAULT_PORT), "/upload");
50a0b071 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
0aa176a7
ZJS
467 (void) sd_watchdog_enabled(false, &u->watchdog_usec);
468
722b6795 469 return load_cursor_state(u);
3d090cc6
ZJS
470}
471
472static void destroy_uploader(Uploader *u) {
473 assert(u);
474
475 curl_easy_cleanup(u->easy);
476 curl_slist_free_all(u->header);
eacbb4d3
ZJS
477 free(u->answer);
478
479 free(u->last_cursor);
722b6795 480 free(u->current_cursor);
3d090cc6 481
5bc89120
ZJS
482 free(u->url);
483
3d090cc6
ZJS
484 u->input_event = sd_event_source_unref(u->input_event);
485
486 close_fd_input(u);
eacbb4d3 487 close_journal_input(u);
3d090cc6 488
a3152e76
ZJS
489 sd_event_source_unref(u->sigterm_event);
490 sd_event_source_unref(u->sigint_event);
3d090cc6
ZJS
491 sd_event_unref(u->events);
492}
493
eacbb4d3
ZJS
494static int perform_upload(Uploader *u) {
495 CURLcode code;
496 long status;
497
498 assert(u);
499
0aa176a7 500 u->watchdog_timestamp = now(CLOCK_MONOTONIC);
eacbb4d3
ZJS
501 code = curl_easy_perform(u->easy);
502 if (code) {
30776485
ZJS
503 if (u->error[0])
504 log_error("Upload to %s failed: %.*s",
505 u->url, (int) sizeof(u->error), u->error);
506 else
507 log_error("Upload to %s failed: %s",
508 u->url, curl_easy_strerror(code));
eacbb4d3
ZJS
509 return -EIO;
510 }
511
512 code = curl_easy_getinfo(u->easy, CURLINFO_RESPONSE_CODE, &status);
513 if (code) {
514 log_error("Failed to retrieve response code: %s",
515 curl_easy_strerror(code));
516 return -EUCLEAN;
517 }
518
519 if (status >= 300) {
1fa2f38f 520 log_error("Upload to %s failed with code %ld: %s",
eacbb4d3
ZJS
521 u->url, status, strna(u->answer));
522 return -EIO;
523 } else if (status < 200) {
1fa2f38f 524 log_error("Upload to %s finished with unexpected code %ld: %s",
eacbb4d3
ZJS
525 u->url, status, strna(u->answer));
526 return -EIO;
527 } else
1fa2f38f 528 log_debug("Upload finished successfully with code %ld: %s",
eacbb4d3 529 status, strna(u->answer));
722b6795 530
3b319885 531 free_and_replace(u->last_cursor, u->current_cursor);
722b6795
ZJS
532
533 return update_cursor_state(u);
eacbb4d3
ZJS
534}
535
29fc0ddc
ZJS
536static int parse_config(void) {
537 const ConfigTableItem items[] = {
538 { "Upload", "URL", config_parse_string, 0, &arg_url },
539 { "Upload", "ServerKeyFile", config_parse_path, 0, &arg_key },
540 { "Upload", "ServerCertificateFile", config_parse_path, 0, &arg_cert },
541 { "Upload", "TrustedCertificateFile", config_parse_path, 0, &arg_trust },
542 {}};
29fc0ddc 543
43688c49 544 return config_parse_many_nulstr(PKGSYSCONFDIR "/journal-upload.conf",
da412854
YW
545 CONF_PATHS_NULSTR("systemd/journal-upload.conf.d"),
546 "Upload\0", config_item_table_lookup, items,
bcde742e 547 CONFIG_PARSE_WARN, NULL);
29fc0ddc
ZJS
548}
549
3d090cc6
ZJS
550static void help(void) {
551 printf("%s -u URL {FILE|-}...\n\n"
552 "Upload journal events to a remote server.\n\n"
dad29dff
LP
553 " -h --help Show this help\n"
554 " --version Show package version\n"
50a0b071
ZJS
555 " -u --url=URL Upload to this address (default port "
556 STRINGIFY(DEFAULT_PORT) ")\n"
1af719ed
ZJS
557 " --key=FILENAME Specify key in PEM format (default:\n"
558 " \"" PRIV_KEY_FILE "\")\n"
559 " --cert=FILENAME Specify certificate in PEM format (default:\n"
560 " \"" CERT_FILE "\")\n"
561 " --trust=FILENAME|all Specify CA certificate or disable checking (default:\n"
562 " \"" TRUST_FILE "\")\n"
dad29dff
LP
563 " --system Use the system journal\n"
564 " --user Use the user journal for the current user\n"
565 " -m --merge Use all available journals\n"
566 " -M --machine=CONTAINER Operate on local container\n"
567 " -D --directory=PATH Use journal files from directory\n"
568 " --file=PATH Use this journal file\n"
569 " --cursor=CURSOR Start at the specified cursor\n"
570 " --after-cursor=CURSOR Start after the specified cursor\n"
571 " --follow[=BOOL] Do [not] wait for input\n"
572 " --save-state[=FILE] Save uploaded cursors (default \n"
573 " " STATE_FILE ")\n"
574 " -h --help Show this help and exit\n"
575 " --version Print version string and exit\n"
3d090cc6
ZJS
576 , program_invocation_short_name);
577}
578
579static int parse_argv(int argc, char *argv[]) {
580 enum {
581 ARG_VERSION = 0x100,
7449bc1f
ZJS
582 ARG_KEY,
583 ARG_CERT,
584 ARG_TRUST,
eacbb4d3
ZJS
585 ARG_USER,
586 ARG_SYSTEM,
587 ARG_FILE,
588 ARG_CURSOR,
589 ARG_AFTER_CURSOR,
590 ARG_FOLLOW,
722b6795 591 ARG_SAVE_STATE,
3d090cc6
ZJS
592 };
593
594 static const struct option options[] = {
595 { "help", no_argument, NULL, 'h' },
596 { "version", no_argument, NULL, ARG_VERSION },
597 { "url", required_argument, NULL, 'u' },
7449bc1f
ZJS
598 { "key", required_argument, NULL, ARG_KEY },
599 { "cert", required_argument, NULL, ARG_CERT },
600 { "trust", required_argument, NULL, ARG_TRUST },
eacbb4d3
ZJS
601 { "system", no_argument, NULL, ARG_SYSTEM },
602 { "user", no_argument, NULL, ARG_USER },
603 { "merge", no_argument, NULL, 'm' },
604 { "machine", required_argument, NULL, 'M' },
605 { "directory", required_argument, NULL, 'D' },
606 { "file", required_argument, NULL, ARG_FILE },
607 { "cursor", required_argument, NULL, ARG_CURSOR },
608 { "after-cursor", required_argument, NULL, ARG_AFTER_CURSOR },
dad29dff 609 { "follow", optional_argument, NULL, ARG_FOLLOW },
722b6795 610 { "save-state", optional_argument, NULL, ARG_SAVE_STATE },
3d090cc6
ZJS
611 {}
612 };
613
eacbb4d3 614 int c, r;
3d090cc6
ZJS
615
616 assert(argc >= 0);
617 assert(argv);
618
619 opterr = 0;
620
eacbb4d3 621 while ((c = getopt_long(argc, argv, "hu:mM:D:", options, NULL)) >= 0)
3d090cc6
ZJS
622 switch(c) {
623 case 'h':
624 help();
625 return 0 /* done */;
626
627 case ARG_VERSION:
3f6fd1ba 628 return version();
3d090cc6
ZJS
629
630 case 'u':
631 if (arg_url) {
632 log_error("cannot use more than one --url");
633 return -EINVAL;
634 }
635
636 arg_url = optarg;
637 break;
638
7449bc1f
ZJS
639 case ARG_KEY:
640 if (arg_key) {
641 log_error("cannot use more than one --key");
642 return -EINVAL;
643 }
644
645 arg_key = optarg;
646 break;
647
648 case ARG_CERT:
649 if (arg_cert) {
650 log_error("cannot use more than one --cert");
651 return -EINVAL;
652 }
653
654 arg_cert = optarg;
655 break;
656
657 case ARG_TRUST:
658 if (arg_trust) {
659 log_error("cannot use more than one --trust");
660 return -EINVAL;
661 }
662
663 arg_trust = optarg;
664 break;
665
eacbb4d3
ZJS
666 case ARG_SYSTEM:
667 arg_journal_type |= SD_JOURNAL_SYSTEM;
668 break;
669
670 case ARG_USER:
671 arg_journal_type |= SD_JOURNAL_CURRENT_USER;
672 break;
673
674 case 'm':
675 arg_merge = true;
676 break;
677
678 case 'M':
679 if (arg_machine) {
680 log_error("cannot use more than one --machine/-M");
681 return -EINVAL;
682 }
683
684 arg_machine = optarg;
685 break;
686
687 case 'D':
688 if (arg_directory) {
689 log_error("cannot use more than one --directory/-D");
690 return -EINVAL;
691 }
692
693 arg_directory = optarg;
694 break;
695
696 case ARG_FILE:
697 r = glob_extend(&arg_file, optarg);
eb56eb9b
MS
698 if (r < 0)
699 return log_error_errno(r, "Failed to add paths: %m");
eacbb4d3
ZJS
700 break;
701
702 case ARG_CURSOR:
703 if (arg_cursor) {
704 log_error("cannot use more than one --cursor/--after-cursor");
705 return -EINVAL;
706 }
707
708 arg_cursor = optarg;
709 break;
710
711 case ARG_AFTER_CURSOR:
712 if (arg_cursor) {
713 log_error("cannot use more than one --cursor/--after-cursor");
714 return -EINVAL;
715 }
716
717 arg_cursor = optarg;
718 arg_after_cursor = true;
719 break;
720
721 case ARG_FOLLOW:
dad29dff
LP
722 if (optarg) {
723 r = parse_boolean(optarg);
724 if (r < 0) {
725 log_error("Failed to parse --follow= parameter.");
726 return -EINVAL;
727 }
728
729 arg_follow = !!r;
730 } else
731 arg_follow = true;
eacbb4d3 732
eacbb4d3
ZJS
733 break;
734
722b6795
ZJS
735 case ARG_SAVE_STATE:
736 arg_save_state = optarg ?: STATE_FILE;
737 break;
738
3d090cc6
ZJS
739 case '?':
740 log_error("Unknown option %s.", argv[optind-1]);
741 return -EINVAL;
742
743 case ':':
744 log_error("Missing argument to %s.", argv[optind-1]);
745 return -EINVAL;
746
747 default:
748 assert_not_reached("Unhandled option code.");
749 }
750
751 if (!arg_url) {
752 log_error("Required --url/-u option missing.");
753 return -EINVAL;
754 }
755
7449bc1f
ZJS
756 if (!!arg_key != !!arg_cert) {
757 log_error("Options --key and --cert must be used together.");
758 return -EINVAL;
759 }
760
eacbb4d3
ZJS
761 if (optind < argc && (arg_directory || arg_file || arg_machine || arg_journal_type)) {
762 log_error("Input arguments make no sense with journal input.");
3d090cc6
ZJS
763 return -EINVAL;
764 }
765
766 return 1;
767}
768
eacbb4d3
ZJS
769static int open_journal(sd_journal **j) {
770 int r;
771
772 if (arg_directory)
773 r = sd_journal_open_directory(j, arg_directory, arg_journal_type);
774 else if (arg_file)
775 r = sd_journal_open_files(j, (const char**) arg_file, 0);
776 else if (arg_machine)
777 r = sd_journal_open_container(j, arg_machine, 0);
778 else
779 r = sd_journal_open(j, !arg_merge*SD_JOURNAL_LOCAL_ONLY + arg_journal_type);
780 if (r < 0)
c33b3297
MS
781 log_error_errno(r, "Failed to open %s: %m",
782 arg_directory ? arg_directory : arg_file ? "files" : "journal");
eacbb4d3
ZJS
783 return r;
784}
3d090cc6
ZJS
785
786int main(int argc, char **argv) {
787 Uploader u;
788 int r;
eacbb4d3 789 bool use_journal;
3d090cc6
ZJS
790
791 log_show_color(true);
792 log_parse_environment();
793
29fc0ddc 794 r = parse_config();
4015ac5c 795 if (r < 0)
29fc0ddc
ZJS
796 goto finish;
797
3d090cc6
ZJS
798 r = parse_argv(argc, argv);
799 if (r <= 0)
800 goto finish;
801
2cf4172a
LP
802 sigbus_install();
803
722b6795 804 r = setup_uploader(&u, arg_url, arg_save_state);
3d090cc6
ZJS
805 if (r < 0)
806 goto cleanup;
807
a3152e76
ZJS
808 sd_event_set_watchdog(u.events, true);
809
d71839af
ZJS
810 r = check_cursor_updating(&u);
811 if (r < 0)
812 goto cleanup;
813
3d090cc6 814 log_debug("%s running as pid "PID_FMT,
df0ff127 815 program_invocation_short_name, getpid_cached());
eacbb4d3
ZJS
816
817 use_journal = optind >= argc;
818 if (use_journal) {
819 sd_journal *j;
820 r = open_journal(&j);
821 if (r < 0)
822 goto finish;
823 r = open_journal_for_upload(&u, j,
722b6795
ZJS
824 arg_cursor ?: u.last_cursor,
825 arg_cursor ? arg_after_cursor : true,
eacbb4d3
ZJS
826 !!arg_follow);
827 if (r < 0)
828 goto finish;
829 }
830
3d090cc6
ZJS
831 sd_notify(false,
832 "READY=1\n"
833 "STATUS=Processing input...");
834
57255510 835 for (;;) {
36d4739a
ZJS
836 r = sd_event_get_state(u.events);
837 if (r < 0)
838 break;
839 if (r == SD_EVENT_FINISHED)
840 break;
841
eacbb4d3
ZJS
842 if (use_journal) {
843 if (!u.journal)
844 break;
845
846 r = check_journal_input(&u);
847 } else if (u.input < 0 && !use_journal) {
3d090cc6
ZJS
848 if (optind >= argc)
849 break;
850
851 log_debug("Using %s as input.", argv[optind]);
3d090cc6 852 r = open_file_for_upload(&u, argv[optind++]);
3d090cc6 853 }
eacbb4d3
ZJS
854 if (r < 0)
855 goto cleanup;
3d090cc6 856
3d090cc6 857 if (u.uploading) {
eacbb4d3
ZJS
858 r = perform_upload(&u);
859 if (r < 0)
3d090cc6 860 break;
3d090cc6
ZJS
861 }
862
eacbb4d3 863 r = sd_event_run(u.events, u.timeout);
3d090cc6 864 if (r < 0) {
da927ba9 865 log_error_errno(r, "Failed to run event loop: %m");
3d090cc6
ZJS
866 break;
867 }
868 }
869
870cleanup:
af4ec430
LP
871 sd_notify(false,
872 "STOPPING=1\n"
873 "STATUS=Shutting down...");
874
3d090cc6
ZJS
875 destroy_uploader(&u);
876
877finish:
36d4739a 878 return r >= 0 ? EXIT_SUCCESS : EXIT_FAILURE;
3d090cc6 879}