LCOV - code coverage report
Current view: top level - journal-remote - journal-upload.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 51 399 12.8 %
Date: 2019-08-22 15:41:25 Functions: 7 20 35.0 %

          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);

Generated by: LCOV version 1.14