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