LCOV - code coverage report
Current view: top level - journal-remote - journal-upload.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 51 399 12.8 %
Date: 2019-08-23 13:36:53 Functions: 7 20 35.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 11 421 2.6 %

           Branch data     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                 :         16 : static void close_fd_input(Uploader *u) {
     311         [ -  + ]:         16 :         assert(u);
     312                 :            : 
     313                 :         16 :         u->input = safe_close(u->input);
     314                 :         16 :         u->timeout = 0;
     315                 :         16 : }
     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                 :         16 : static void destroy_uploader(Uploader *u) {
     456         [ -  + ]:         16 :         assert(u);
     457                 :            : 
     458                 :         16 :         curl_easy_cleanup(u->easy);
     459                 :         16 :         curl_slist_free_all(u->header);
     460                 :         16 :         free(u->answer);
     461                 :            : 
     462                 :         16 :         free(u->last_cursor);
     463                 :         16 :         free(u->current_cursor);
     464                 :            : 
     465                 :         16 :         free(u->url);
     466                 :            : 
     467                 :         16 :         u->input_event = sd_event_source_unref(u->input_event);
     468                 :            : 
     469                 :         16 :         close_fd_input(u);
     470                 :         16 :         close_journal_input(u);
     471                 :            : 
     472                 :         16 :         sd_event_source_unref(u->sigterm_event);
     473                 :         16 :         sd_event_source_unref(u->sigint_event);
     474                 :         16 :         sd_event_unref(u->events);
     475                 :         16 : }
     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                 :         16 : static int parse_config(void) {
     519                 :         16 :         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                 :         16 :         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                 :         12 : static int help(void) {
     533                 :         12 :         _cleanup_free_ char *link = NULL;
     534                 :            :         int r;
     535                 :            : 
     536                 :         12 :         r = terminal_urlify_man("systemd-journal-upload.service", "8", &link);
     537         [ -  + ]:         12 :         if (r < 0)
     538                 :          0 :                 return log_oom();
     539                 :            : 
     540                 :         12 :         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                 :         12 :         return 0;
     569                 :            : }
     570                 :            : 
     571                 :         16 : 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         [ -  + ]:         16 :         assert(argc >= 0);
     609         [ -  + ]:         16 :         assert(argv);
     610                 :            : 
     611                 :         16 :         opterr = 0;
     612                 :            : 
     613         [ +  - ]:         16 :         while ((c = getopt_long(argc, argv, "hu:mM:D:", options, NULL)) >= 0)
     614   [ +  -  -  -  :         16 :                 switch(c) {
          -  -  -  -  -  
          -  -  -  -  -  
             -  -  +  -  
                      - ]
     615                 :         12 :                 case 'h':
     616                 :         12 :                         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                 :          4 :                 case '?':
     722         [ +  - ]:          4 :                         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                 :         16 : static int run(int argc, char **argv) {
     768                 :         16 :         _cleanup_(notify_on_cleanup) const char *notify_message = NULL;
     769                 :         16 :         _cleanup_(destroy_uploader) Uploader u = {};
     770                 :            :         bool use_journal;
     771                 :            :         int r;
     772                 :            : 
     773                 :         16 :         log_show_color(true);
     774                 :         16 :         log_parse_environment();
     775                 :            : 
     776                 :            :         /* The journal merging logic potentially needs a lot of fds. */
     777                 :         16 :         (void) rlimit_nofile_bump(HIGH_RLIMIT_NOFILE);
     778                 :            : 
     779                 :         16 :         r = parse_config();
     780         [ -  + ]:         16 :         if (r < 0)
     781                 :          0 :                 return r;
     782                 :            : 
     783                 :         16 :         r = parse_argv(argc, argv);
     784         [ +  - ]:         16 :         if (r <= 0)
     785                 :         16 :                 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                 :         16 : DEFINE_MAIN_FUNCTION(run);

Generated by: LCOV version 1.14