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