]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal-remote/journal-upload.c
logind: make sure we don't trip up on half-initialized session devices
[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"
dccca82b 40#include "process-util.h"
2cf4172a 41#include "sigbus.h"
24882e06 42#include "signal-util.h"
07630cea 43#include "string-util.h"
3f6fd1ba 44#include "util.h"
3d090cc6 45
799a8f39
ZJS
46#define PRIV_KEY_FILE CERTIFICATE_ROOT "/private/journal-upload.pem"
47#define CERT_FILE CERTIFICATE_ROOT "/certs/journal-upload.pem"
48#define TRUST_FILE CERTIFICATE_ROOT "/ca/trusted.pem"
50a0b071 49#define DEFAULT_PORT 19532
29fc0ddc 50
2cf4172a 51static const char* arg_url = NULL;
7449bc1f
ZJS
52static const char *arg_key = NULL;
53static const char *arg_cert = NULL;
54static const char *arg_trust = NULL;
eacbb4d3
ZJS
55static const char *arg_directory = NULL;
56static char **arg_file = NULL;
57static const char *arg_cursor = NULL;
58static bool arg_after_cursor = false;
59static int arg_journal_type = 0;
60static const char *arg_machine = NULL;
61static bool arg_merge = false;
62static int arg_follow = -1;
722b6795 63static const char *arg_save_state = NULL;
eacbb4d3 64
2cf4172a
LP
65static void close_fd_input(Uploader *u);
66
eacbb4d3
ZJS
67#define SERVER_ANSWER_KEEP 2048
68
722b6795
ZJS
69#define STATE_FILE "/var/lib/systemd/journal-upload/state"
70
3d090cc6 71#define easy_setopt(curl, opt, value, level, cmd) \
8847551b 72 do { \
3d090cc6
ZJS
73 code = curl_easy_setopt(curl, opt, value); \
74 if (code) { \
75 log_full(level, \
76 "curl_easy_setopt " #opt " failed: %s", \
77 curl_easy_strerror(code)); \
78 cmd; \
79 } \
9ed794a3 80 } while (0)
3d090cc6 81
eacbb4d3
ZJS
82static size_t output_callback(char *buf,
83 size_t size,
84 size_t nmemb,
85 void *userp) {
86 Uploader *u = userp;
87
88 assert(u);
89
90 log_debug("The server answers (%zu bytes): %.*s",
91 size*nmemb, (int)(size*nmemb), buf);
92
93 if (nmemb && !u->answer) {
94 u->answer = strndup(buf, size*nmemb);
95 if (!u->answer)
c33b3297
MS
96 log_warning_errno(ENOMEM, "Failed to store server answer (%zu bytes): %m",
97 size*nmemb);
eacbb4d3
ZJS
98 }
99
100 return size * nmemb;
101}
102
d71839af
ZJS
103static int check_cursor_updating(Uploader *u) {
104 _cleanup_free_ char *temp_path = NULL;
105 _cleanup_fclose_ FILE *f = NULL;
106 int r;
107
108 if (!u->state_file)
109 return 0;
110
111 r = mkdir_parents(u->state_file, 0755);
eb56eb9b
MS
112 if (r < 0)
113 return log_error_errno(r, "Cannot create parent directory of state file %s: %m",
114 u->state_file);
d71839af
ZJS
115
116 r = fopen_temporary(u->state_file, &f, &temp_path);
eb56eb9b
MS
117 if (r < 0)
118 return log_error_errno(r, "Cannot save state to %s: %m",
119 u->state_file);
d71839af
ZJS
120 unlink(temp_path);
121
122 return 0;
123}
124
722b6795
ZJS
125static int update_cursor_state(Uploader *u) {
126 _cleanup_free_ char *temp_path = NULL;
127 _cleanup_fclose_ FILE *f = NULL;
128 int r;
129
130 if (!u->state_file || !u->last_cursor)
131 return 0;
132
133 r = fopen_temporary(u->state_file, &f, &temp_path);
134 if (r < 0)
dacd6cee 135 goto fail;
722b6795
ZJS
136
137 fprintf(f,
138 "# This is private data. Do not parse.\n"
139 "LAST_CURSOR=%s\n",
140 u->last_cursor);
141
dacd6cee
LP
142 r = fflush_and_check(f);
143 if (r < 0)
144 goto fail;
722b6795 145
dacd6cee 146 if (rename(temp_path, u->state_file) < 0) {
722b6795 147 r = -errno;
dacd6cee 148 goto fail;
722b6795
ZJS
149 }
150
dacd6cee
LP
151 return 0;
152
153fail:
154 if (temp_path)
155 (void) unlink(temp_path);
156
157 (void) unlink(u->state_file);
722b6795 158
dacd6cee 159 return log_error_errno(r, "Failed to save state %s: %m", u->state_file);
722b6795
ZJS
160}
161
162static int load_cursor_state(Uploader *u) {
163 int r;
164
165 if (!u->state_file)
166 return 0;
167
168 r = parse_env_file(u->state_file, NEWLINE,
169 "LAST_CURSOR", &u->last_cursor,
170 NULL);
171
36d4739a
ZJS
172 if (r == -ENOENT)
173 log_debug("State file %s is not present.", u->state_file);
eb56eb9b
MS
174 else if (r < 0)
175 return log_error_errno(r, "Failed to read state file %s: %m",
176 u->state_file);
177 else
36d4739a 178 log_debug("Last cursor was %s", u->last_cursor);
722b6795
ZJS
179
180 return 0;
181}
182
183
184
3d090cc6
ZJS
185int start_upload(Uploader *u,
186 size_t (*input_callback)(void *ptr,
187 size_t size,
188 size_t nmemb,
189 void *userdata),
190 void *data) {
191 CURLcode code;
192
193 assert(u);
194 assert(input_callback);
195
196 if (!u->header) {
197 struct curl_slist *h;
198
199 h = curl_slist_append(NULL, "Content-Type: application/vnd.fdo.journal");
200 if (!h)
201 return log_oom();
202
203 h = curl_slist_append(h, "Transfer-Encoding: chunked");
204 if (!h) {
205 curl_slist_free_all(h);
206 return log_oom();
207 }
208
209 h = curl_slist_append(h, "Accept: text/plain");
210 if (!h) {
211 curl_slist_free_all(h);
212 return log_oom();
213 }
214
215 u->header = h;
216 }
217
218 if (!u->easy) {
219 CURL *curl;
220
221 curl = curl_easy_init();
222 if (!curl) {
223 log_error("Call to curl_easy_init failed.");
224 return -ENOSR;
225 }
226
227 /* tell it to POST to the URL */
228 easy_setopt(curl, CURLOPT_POST, 1L,
229 LOG_ERR, return -EXFULL);
230
b88a40a7 231 easy_setopt(curl, CURLOPT_ERRORBUFFER, u->error,
eacbb4d3
ZJS
232 LOG_ERR, return -EXFULL);
233
234 /* set where to write to */
235 easy_setopt(curl, CURLOPT_WRITEFUNCTION, output_callback,
236 LOG_ERR, return -EXFULL);
237
238 easy_setopt(curl, CURLOPT_WRITEDATA, data,
239 LOG_ERR, return -EXFULL);
240
3d090cc6
ZJS
241 /* set where to read from */
242 easy_setopt(curl, CURLOPT_READFUNCTION, input_callback,
243 LOG_ERR, return -EXFULL);
244
245 easy_setopt(curl, CURLOPT_READDATA, data,
246 LOG_ERR, return -EXFULL);
247
248 /* use our special own mime type and chunked transfer */
249 easy_setopt(curl, CURLOPT_HTTPHEADER, u->header,
250 LOG_ERR, return -EXFULL);
251
f1d34068 252 if (DEBUG_LOGGING)
5dabb1e0
ZJS
253 /* enable verbose for easier tracing */
254 easy_setopt(curl, CURLOPT_VERBOSE, 1L, LOG_WARNING, );
3d090cc6
ZJS
255
256 easy_setopt(curl, CURLOPT_USERAGENT,
257 "systemd-journal-upload " PACKAGE_STRING,
258 LOG_WARNING, );
259
29fc0ddc 260 if (arg_key || startswith(u->url, "https://")) {
799a8f39 261 easy_setopt(curl, CURLOPT_SSLKEY, arg_key ?: PRIV_KEY_FILE,
7449bc1f 262 LOG_ERR, return -EXFULL);
29fc0ddc 263 easy_setopt(curl, CURLOPT_SSLCERT, arg_cert ?: CERT_FILE,
7449bc1f
ZJS
264 LOG_ERR, return -EXFULL);
265 }
266
8847551b
ZJS
267 if (streq_ptr(arg_trust, "all"))
268 easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0,
269 LOG_ERR, return -EUCLEAN);
270 else if (arg_trust || startswith(u->url, "https://"))
29fc0ddc 271 easy_setopt(curl, CURLOPT_CAINFO, arg_trust ?: TRUST_FILE,
7449bc1f
ZJS
272 LOG_ERR, return -EXFULL);
273
274 if (arg_key || arg_trust)
275 easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1,
276 LOG_WARNING, );
277
3d090cc6 278 u->easy = curl;
eacbb4d3
ZJS
279 } else {
280 /* truncate the potential old error message */
281 u->error[0] = '\0';
282
283 free(u->answer);
284 u->answer = 0;
3d090cc6
ZJS
285 }
286
287 /* upload to this place */
288 code = curl_easy_setopt(u->easy, CURLOPT_URL, u->url);
289 if (code) {
290 log_error("curl_easy_setopt CURLOPT_URL failed: %s",
291 curl_easy_strerror(code));
292 return -EXFULL;
293 }
294
295 u->uploading = true;
296
297 return 0;
298}
299
300static size_t fd_input_callback(void *buf, size_t size, size_t nmemb, void *userp) {
301 Uploader *u = userp;
302
303 ssize_t r;
304
305 assert(u);
306 assert(nmemb <= SSIZE_MAX / size);
307
308 if (u->input < 0)
309 return 0;
310
311 r = read(u->input, buf, size * nmemb);
1fa2f38f 312 log_debug("%s: allowed %zu, read %zd", __func__, size*nmemb, r);
3d090cc6
ZJS
313
314 if (r > 0)
315 return r;
316
317 u->uploading = false;
318 if (r == 0) {
319 log_debug("Reached EOF");
320 close_fd_input(u);
321 return 0;
322 } else {
56f64d95 323 log_error_errno(errno, "Aborting transfer after read error on input: %m.");
3d090cc6
ZJS
324 return CURL_READFUNC_ABORT;
325 }
326}
327
328static void close_fd_input(Uploader *u) {
329 assert(u);
330
331 if (u->input >= 0)
332 close_nointr(u->input);
333 u->input = -1;
eacbb4d3 334 u->timeout = 0;
3d090cc6
ZJS
335}
336
337static int dispatch_fd_input(sd_event_source *event,
338 int fd,
339 uint32_t revents,
340 void *userp) {
341 Uploader *u = userp;
342
343 assert(u);
3d090cc6
ZJS
344 assert(fd >= 0);
345
8201af08
ZJS
346 if (revents & EPOLLHUP) {
347 log_debug("Received HUP");
348 close_fd_input(u);
349 return 0;
350 }
351
352 if (!(revents & EPOLLIN)) {
353 log_warning("Unexpected poll event %"PRIu32".", revents);
354 return -EINVAL;
355 }
356
3d090cc6
ZJS
357 if (u->uploading) {
358 log_warning("dispatch_fd_input called when uploading, ignoring.");
359 return 0;
360 }
361
362 return start_upload(u, fd_input_callback, u);
363}
364
365static int open_file_for_upload(Uploader *u, const char *filename) {
e1ad6e24 366 int fd, r = 0;
3d090cc6
ZJS
367
368 if (streq(filename, "-"))
369 fd = STDIN_FILENO;
370 else {
371 fd = open(filename, O_RDONLY|O_CLOEXEC|O_NOCTTY);
4a62c710
MS
372 if (fd < 0)
373 return log_error_errno(errno, "Failed to open %s: %m", filename);
3d090cc6
ZJS
374 }
375
376 u->input = fd;
377
eacbb4d3
ZJS
378 if (arg_follow) {
379 r = sd_event_add_io(u->events, &u->input_event,
380 fd, EPOLLIN, dispatch_fd_input, u);
381 if (r < 0) {
eb56eb9b
MS
382 if (r != -EPERM || arg_follow > 0)
383 return log_error_errno(r, "Failed to register input event: %m");
3d090cc6 384
eacbb4d3
ZJS
385 /* Normal files should just be consumed without polling. */
386 r = start_upload(u, fd_input_callback, u);
387 }
3d090cc6 388 }
eacbb4d3 389
3d090cc6
ZJS
390 return r;
391}
392
a3152e76
ZJS
393static int dispatch_sigterm(sd_event_source *event,
394 const struct signalfd_siginfo *si,
395 void *userdata) {
396 Uploader *u = userdata;
397
398 assert(u);
399
400 log_received_signal(LOG_INFO, si);
401
402 close_fd_input(u);
403 close_journal_input(u);
404
405 sd_event_exit(u->events, 0);
406 return 0;
407}
408
409static int setup_signals(Uploader *u) {
a3152e76
ZJS
410 int r;
411
412 assert(u);
413
72c0a2c2 414 assert_se(sigprocmask_many(SIG_SETMASK, NULL, SIGINT, SIGTERM, -1) >= 0);
a3152e76
ZJS
415
416 r = sd_event_add_signal(u->events, &u->sigterm_event, SIGTERM, dispatch_sigterm, u);
417 if (r < 0)
418 return r;
419
420 r = sd_event_add_signal(u->events, &u->sigint_event, SIGINT, dispatch_sigterm, u);
421 if (r < 0)
422 return r;
423
424 return 0;
425}
426
722b6795 427static int setup_uploader(Uploader *u, const char *url, const char *state_file) {
3d090cc6 428 int r;
50a0b071 429 const char *host, *proto = "";
3d090cc6
ZJS
430
431 assert(u);
432 assert(url);
433
434 memzero(u, sizeof(Uploader));
435 u->input = -1;
436
50a0b071
ZJS
437 if (!(host = startswith(url, "http://")) && !(host = startswith(url, "https://"))) {
438 host = url;
439 proto = "https://";
440 }
441
442 if (strchr(host, ':'))
605405c6 443 u->url = strjoin(proto, url, "/upload");
50a0b071
ZJS
444 else {
445 char *t;
446 size_t x;
5bc89120 447
50a0b071
ZJS
448 t = strdupa(url);
449 x = strlen(t);
450 while (x > 0 && t[x - 1] == '/')
451 t[x - 1] = '\0';
452
605405c6 453 u->url = strjoin(proto, t, ":" STRINGIFY(DEFAULT_PORT), "/upload");
50a0b071 454 }
5bc89120
ZJS
455 if (!u->url)
456 return log_oom();
457
722b6795 458 u->state_file = state_file;
3d090cc6
ZJS
459
460 r = sd_event_default(&u->events);
eb56eb9b
MS
461 if (r < 0)
462 return log_error_errno(r, "sd_event_default failed: %m");
3d090cc6 463
a3152e76 464 r = setup_signals(u);
eb56eb9b
MS
465 if (r < 0)
466 return log_error_errno(r, "Failed to set up signals: %m");
a3152e76 467
0aa176a7
ZJS
468 (void) sd_watchdog_enabled(false, &u->watchdog_usec);
469
722b6795 470 return load_cursor_state(u);
3d090cc6
ZJS
471}
472
473static void destroy_uploader(Uploader *u) {
474 assert(u);
475
476 curl_easy_cleanup(u->easy);
477 curl_slist_free_all(u->header);
eacbb4d3
ZJS
478 free(u->answer);
479
480 free(u->last_cursor);
722b6795 481 free(u->current_cursor);
3d090cc6 482
5bc89120
ZJS
483 free(u->url);
484
3d090cc6
ZJS
485 u->input_event = sd_event_source_unref(u->input_event);
486
487 close_fd_input(u);
eacbb4d3 488 close_journal_input(u);
3d090cc6 489
a3152e76
ZJS
490 sd_event_source_unref(u->sigterm_event);
491 sd_event_source_unref(u->sigint_event);
3d090cc6
ZJS
492 sd_event_unref(u->events);
493}
494
eacbb4d3
ZJS
495static int perform_upload(Uploader *u) {
496 CURLcode code;
497 long status;
498
499 assert(u);
500
0aa176a7 501 u->watchdog_timestamp = now(CLOCK_MONOTONIC);
eacbb4d3
ZJS
502 code = curl_easy_perform(u->easy);
503 if (code) {
30776485
ZJS
504 if (u->error[0])
505 log_error("Upload to %s failed: %.*s",
506 u->url, (int) sizeof(u->error), u->error);
507 else
508 log_error("Upload to %s failed: %s",
509 u->url, curl_easy_strerror(code));
eacbb4d3
ZJS
510 return -EIO;
511 }
512
513 code = curl_easy_getinfo(u->easy, CURLINFO_RESPONSE_CODE, &status);
514 if (code) {
515 log_error("Failed to retrieve response code: %s",
516 curl_easy_strerror(code));
517 return -EUCLEAN;
518 }
519
520 if (status >= 300) {
1fa2f38f 521 log_error("Upload to %s failed with code %ld: %s",
eacbb4d3
ZJS
522 u->url, status, strna(u->answer));
523 return -EIO;
524 } else if (status < 200) {
1fa2f38f 525 log_error("Upload to %s finished with unexpected code %ld: %s",
eacbb4d3
ZJS
526 u->url, status, strna(u->answer));
527 return -EIO;
528 } else
1fa2f38f 529 log_debug("Upload finished successfully with code %ld: %s",
eacbb4d3 530 status, strna(u->answer));
722b6795 531
3b319885 532 free_and_replace(u->last_cursor, u->current_cursor);
722b6795
ZJS
533
534 return update_cursor_state(u);
eacbb4d3
ZJS
535}
536
29fc0ddc
ZJS
537static int parse_config(void) {
538 const ConfigTableItem items[] = {
539 { "Upload", "URL", config_parse_string, 0, &arg_url },
540 { "Upload", "ServerKeyFile", config_parse_path, 0, &arg_key },
541 { "Upload", "ServerCertificateFile", config_parse_path, 0, &arg_cert },
542 { "Upload", "TrustedCertificateFile", config_parse_path, 0, &arg_trust },
543 {}};
29fc0ddc 544
43688c49 545 return config_parse_many_nulstr(PKGSYSCONFDIR "/journal-upload.conf",
da412854
YW
546 CONF_PATHS_NULSTR("systemd/journal-upload.conf.d"),
547 "Upload\0", config_item_table_lookup, items,
bcde742e 548 CONFIG_PARSE_WARN, NULL);
29fc0ddc
ZJS
549}
550
3d090cc6
ZJS
551static void help(void) {
552 printf("%s -u URL {FILE|-}...\n\n"
553 "Upload journal events to a remote server.\n\n"
dad29dff
LP
554 " -h --help Show this help\n"
555 " --version Show package version\n"
50a0b071
ZJS
556 " -u --url=URL Upload to this address (default port "
557 STRINGIFY(DEFAULT_PORT) ")\n"
1af719ed
ZJS
558 " --key=FILENAME Specify key in PEM format (default:\n"
559 " \"" PRIV_KEY_FILE "\")\n"
560 " --cert=FILENAME Specify certificate in PEM format (default:\n"
561 " \"" CERT_FILE "\")\n"
562 " --trust=FILENAME|all Specify CA certificate or disable checking (default:\n"
563 " \"" TRUST_FILE "\")\n"
dad29dff
LP
564 " --system Use the system journal\n"
565 " --user Use the user journal for the current user\n"
566 " -m --merge Use all available journals\n"
567 " -M --machine=CONTAINER Operate on local container\n"
568 " -D --directory=PATH Use journal files from directory\n"
569 " --file=PATH Use this journal file\n"
570 " --cursor=CURSOR Start at the specified cursor\n"
571 " --after-cursor=CURSOR Start after the specified cursor\n"
572 " --follow[=BOOL] Do [not] wait for input\n"
573 " --save-state[=FILE] Save uploaded cursors (default \n"
574 " " STATE_FILE ")\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 813 log_debug("%s running as pid "PID_FMT,
df0ff127 814 program_invocation_short_name, getpid_cached());
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}