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