]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal-remote/journal-upload.c
util-lib: move CONF_DIRS_NULSTR definition to def.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"
a0f29c76 32#include "def.h"
3ffd4af2 33#include "fd-util.h"
722b6795 34#include "fileio.h"
3f6fd1ba 35#include "formats-util.h"
7d50b32a 36#include "glob-util.h"
3ffd4af2 37#include "journal-upload.h"
3f6fd1ba 38#include "log.h"
d71839af 39#include "mkdir.h"
6bedfcbb 40#include "parse-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 } \
8847551b 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
5dabb1e0
ZJS
252 if (_unlikely_(log_get_max_level() >= LOG_DEBUG))
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, ':'))
443 u->url = strjoin(proto, url, "/upload", NULL);
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
453 u->url = strjoin(proto, t, ":" STRINGIFY(DEFAULT_PORT), "/upload", NULL);
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
722b6795 468 return load_cursor_state(u);
3d090cc6
ZJS
469}
470
471static void destroy_uploader(Uploader *u) {
472 assert(u);
473
474 curl_easy_cleanup(u->easy);
475 curl_slist_free_all(u->header);
eacbb4d3
ZJS
476 free(u->answer);
477
478 free(u->last_cursor);
722b6795 479 free(u->current_cursor);
3d090cc6 480
5bc89120
ZJS
481 free(u->url);
482
3d090cc6
ZJS
483 u->input_event = sd_event_source_unref(u->input_event);
484
485 close_fd_input(u);
eacbb4d3 486 close_journal_input(u);
3d090cc6 487
a3152e76
ZJS
488 sd_event_source_unref(u->sigterm_event);
489 sd_event_source_unref(u->sigint_event);
3d090cc6
ZJS
490 sd_event_unref(u->events);
491}
492
eacbb4d3
ZJS
493static int perform_upload(Uploader *u) {
494 CURLcode code;
495 long status;
496
497 assert(u);
498
499 code = curl_easy_perform(u->easy);
500 if (code) {
30776485
ZJS
501 if (u->error[0])
502 log_error("Upload to %s failed: %.*s",
503 u->url, (int) sizeof(u->error), u->error);
504 else
505 log_error("Upload to %s failed: %s",
506 u->url, curl_easy_strerror(code));
eacbb4d3
ZJS
507 return -EIO;
508 }
509
510 code = curl_easy_getinfo(u->easy, CURLINFO_RESPONSE_CODE, &status);
511 if (code) {
512 log_error("Failed to retrieve response code: %s",
513 curl_easy_strerror(code));
514 return -EUCLEAN;
515 }
516
517 if (status >= 300) {
1fa2f38f 518 log_error("Upload to %s failed with code %ld: %s",
eacbb4d3
ZJS
519 u->url, status, strna(u->answer));
520 return -EIO;
521 } else if (status < 200) {
1fa2f38f 522 log_error("Upload to %s finished with unexpected code %ld: %s",
eacbb4d3
ZJS
523 u->url, status, strna(u->answer));
524 return -EIO;
525 } else
1fa2f38f 526 log_debug("Upload finished successfully with code %ld: %s",
eacbb4d3 527 status, strna(u->answer));
722b6795
ZJS
528
529 free(u->last_cursor);
530 u->last_cursor = u->current_cursor;
531 u->current_cursor = NULL;
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
bf257aed
JT
544 return config_parse_many(PKGSYSCONFDIR "/journal-upload.conf",
545 CONF_DIRS_NULSTR("systemd/journal-upload.conf"),
546 "Upload\0", config_item_table_lookup, items,
547 false, 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
ZJS
814 log_debug("%s running as pid "PID_FMT,
815 program_invocation_short_name, getpid());
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}