LCOV - code coverage report
Current view: top level - journal-remote - journal-gatewayd.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 30 534 5.6 %
Date: 2019-08-23 13:36:53 Functions: 7 25 28.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 7 432 1.6 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: LGPL-2.1+ */
       2                 :            : 
       3                 :            : #include <fcntl.h>
       4                 :            : #include <getopt.h>
       5                 :            : #include <microhttpd.h>
       6                 :            : #include <stdlib.h>
       7                 :            : #include <string.h>
       8                 :            : #include <sys/stat.h>
       9                 :            : #include <sys/types.h>
      10                 :            : #include <unistd.h>
      11                 :            : 
      12                 :            : #include "sd-bus.h"
      13                 :            : #include "sd-daemon.h"
      14                 :            : #include "sd-journal.h"
      15                 :            : 
      16                 :            : #include "alloc-util.h"
      17                 :            : #include "bus-util.h"
      18                 :            : #include "errno-util.h"
      19                 :            : #include "fd-util.h"
      20                 :            : #include "fileio.h"
      21                 :            : #include "hostname-util.h"
      22                 :            : #include "log.h"
      23                 :            : #include "logs-show.h"
      24                 :            : #include "main-func.h"
      25                 :            : #include "microhttpd-util.h"
      26                 :            : #include "os-util.h"
      27                 :            : #include "parse-util.h"
      28                 :            : #include "pretty-print.h"
      29                 :            : #include "sigbus.h"
      30                 :            : #include "tmpfile-util.h"
      31                 :            : #include "util.h"
      32                 :            : 
      33                 :            : #define JOURNAL_WAIT_TIMEOUT (10*USEC_PER_SEC)
      34                 :            : 
      35                 :            : static char *arg_key_pem = NULL;
      36                 :            : static char *arg_cert_pem = NULL;
      37                 :            : static char *arg_trust_pem = NULL;
      38                 :            : static const char *arg_directory = NULL;
      39                 :            : 
      40                 :         16 : STATIC_DESTRUCTOR_REGISTER(arg_key_pem, freep);
      41                 :         16 : STATIC_DESTRUCTOR_REGISTER(arg_cert_pem, freep);
      42                 :         16 : STATIC_DESTRUCTOR_REGISTER(arg_trust_pem, freep);
      43                 :            : 
      44                 :            : typedef struct RequestMeta {
      45                 :            :         sd_journal *journal;
      46                 :            : 
      47                 :            :         OutputMode mode;
      48                 :            : 
      49                 :            :         char *cursor;
      50                 :            :         int64_t n_skip;
      51                 :            :         uint64_t n_entries;
      52                 :            :         bool n_entries_set;
      53                 :            : 
      54                 :            :         FILE *tmp;
      55                 :            :         uint64_t delta, size;
      56                 :            : 
      57                 :            :         int argument_parse_error;
      58                 :            : 
      59                 :            :         bool follow;
      60                 :            :         bool discrete;
      61                 :            : 
      62                 :            :         uint64_t n_fields;
      63                 :            :         bool n_fields_set;
      64                 :            : } RequestMeta;
      65                 :            : 
      66                 :            : static const char* const mime_types[_OUTPUT_MODE_MAX] = {
      67                 :            :         [OUTPUT_SHORT] = "text/plain",
      68                 :            :         [OUTPUT_JSON] = "application/json",
      69                 :            :         [OUTPUT_JSON_SSE] = "text/event-stream",
      70                 :            :         [OUTPUT_JSON_SEQ] = "application/json-seq",
      71                 :            :         [OUTPUT_EXPORT] = "application/vnd.fdo.journal",
      72                 :            : };
      73                 :            : 
      74                 :          0 : static RequestMeta *request_meta(void **connection_cls) {
      75                 :            :         RequestMeta *m;
      76                 :            : 
      77         [ #  # ]:          0 :         assert(connection_cls);
      78         [ #  # ]:          0 :         if (*connection_cls)
      79                 :          0 :                 return *connection_cls;
      80                 :            : 
      81                 :          0 :         m = new0(RequestMeta, 1);
      82         [ #  # ]:          0 :         if (!m)
      83                 :          0 :                 return NULL;
      84                 :            : 
      85                 :          0 :         *connection_cls = m;
      86                 :          0 :         return m;
      87                 :            : }
      88                 :            : 
      89                 :          0 : static void request_meta_free(
      90                 :            :                 void *cls,
      91                 :            :                 struct MHD_Connection *connection,
      92                 :            :                 void **connection_cls,
      93                 :            :                 enum MHD_RequestTerminationCode toe) {
      94                 :            : 
      95                 :          0 :         RequestMeta *m = *connection_cls;
      96                 :            : 
      97         [ #  # ]:          0 :         if (!m)
      98                 :          0 :                 return;
      99                 :            : 
     100                 :          0 :         sd_journal_close(m->journal);
     101                 :            : 
     102                 :          0 :         safe_fclose(m->tmp);
     103                 :            : 
     104                 :          0 :         free(m->cursor);
     105                 :          0 :         free(m);
     106                 :            : }
     107                 :            : 
     108                 :          0 : static int open_journal(RequestMeta *m) {
     109         [ #  # ]:          0 :         assert(m);
     110                 :            : 
     111         [ #  # ]:          0 :         if (m->journal)
     112                 :          0 :                 return 0;
     113                 :            : 
     114         [ #  # ]:          0 :         if (arg_directory)
     115                 :          0 :                 return sd_journal_open_directory(&m->journal, arg_directory, 0);
     116                 :            :         else
     117                 :          0 :                 return sd_journal_open(&m->journal, SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM);
     118                 :            : }
     119                 :            : 
     120                 :          0 : static int request_meta_ensure_tmp(RequestMeta *m) {
     121         [ #  # ]:          0 :         assert(m);
     122                 :            : 
     123         [ #  # ]:          0 :         if (m->tmp)
     124                 :          0 :                 rewind(m->tmp);
     125                 :            :         else {
     126                 :            :                 int fd;
     127                 :            : 
     128                 :          0 :                 fd = open_tmpfile_unlinkable("/tmp", O_RDWR|O_CLOEXEC);
     129         [ #  # ]:          0 :                 if (fd < 0)
     130                 :          0 :                         return fd;
     131                 :            : 
     132                 :          0 :                 m->tmp = fdopen(fd, "w+");
     133         [ #  # ]:          0 :                 if (!m->tmp) {
     134                 :          0 :                         safe_close(fd);
     135                 :          0 :                         return -errno;
     136                 :            :                 }
     137                 :            :         }
     138                 :            : 
     139                 :          0 :         return 0;
     140                 :            : }
     141                 :            : 
     142                 :          0 : static ssize_t request_reader_entries(
     143                 :            :                 void *cls,
     144                 :            :                 uint64_t pos,
     145                 :            :                 char *buf,
     146                 :            :                 size_t max) {
     147                 :            : 
     148                 :          0 :         RequestMeta *m = cls;
     149                 :            :         int r;
     150                 :            :         size_t n, k;
     151                 :            : 
     152         [ #  # ]:          0 :         assert(m);
     153         [ #  # ]:          0 :         assert(buf);
     154         [ #  # ]:          0 :         assert(max > 0);
     155         [ #  # ]:          0 :         assert(pos >= m->delta);
     156                 :            : 
     157                 :          0 :         pos -= m->delta;
     158                 :            : 
     159         [ #  # ]:          0 :         while (pos >= m->size) {
     160                 :            :                 off_t sz;
     161                 :            : 
     162                 :            :                 /* End of this entry, so let's serialize the next
     163                 :            :                  * one */
     164                 :            : 
     165         [ #  # ]:          0 :                 if (m->n_entries_set &&
     166         [ #  # ]:          0 :                     m->n_entries <= 0)
     167                 :          0 :                         return MHD_CONTENT_READER_END_OF_STREAM;
     168                 :            : 
     169         [ #  # ]:          0 :                 if (m->n_skip < 0)
     170                 :          0 :                         r = sd_journal_previous_skip(m->journal, (uint64_t) -m->n_skip + 1);
     171         [ #  # ]:          0 :                 else if (m->n_skip > 0)
     172                 :          0 :                         r = sd_journal_next_skip(m->journal, (uint64_t) m->n_skip + 1);
     173                 :            :                 else
     174                 :          0 :                         r = sd_journal_next(m->journal);
     175                 :            : 
     176         [ #  # ]:          0 :                 if (r < 0) {
     177         [ #  # ]:          0 :                         log_error_errno(r, "Failed to advance journal pointer: %m");
     178                 :          0 :                         return MHD_CONTENT_READER_END_WITH_ERROR;
     179         [ #  # ]:          0 :                 } else if (r == 0) {
     180                 :            : 
     181         [ #  # ]:          0 :                         if (m->follow) {
     182                 :          0 :                                 r = sd_journal_wait(m->journal, (uint64_t) JOURNAL_WAIT_TIMEOUT);
     183         [ #  # ]:          0 :                                 if (r < 0) {
     184         [ #  # ]:          0 :                                         log_error_errno(r, "Couldn't wait for journal event: %m");
     185                 :          0 :                                         return MHD_CONTENT_READER_END_WITH_ERROR;
     186                 :            :                                 }
     187         [ #  # ]:          0 :                                 if (r == SD_JOURNAL_NOP)
     188                 :          0 :                                         break;
     189                 :            : 
     190                 :          0 :                                 continue;
     191                 :            :                         }
     192                 :            : 
     193                 :          0 :                         return MHD_CONTENT_READER_END_OF_STREAM;
     194                 :            :                 }
     195                 :            : 
     196         [ #  # ]:          0 :                 if (m->discrete) {
     197         [ #  # ]:          0 :                         assert(m->cursor);
     198                 :            : 
     199                 :          0 :                         r = sd_journal_test_cursor(m->journal, m->cursor);
     200         [ #  # ]:          0 :                         if (r < 0) {
     201         [ #  # ]:          0 :                                 log_error_errno(r, "Failed to test cursor: %m");
     202                 :          0 :                                 return MHD_CONTENT_READER_END_WITH_ERROR;
     203                 :            :                         }
     204                 :            : 
     205         [ #  # ]:          0 :                         if (r == 0)
     206                 :          0 :                                 return MHD_CONTENT_READER_END_OF_STREAM;
     207                 :            :                 }
     208                 :            : 
     209                 :          0 :                 pos -= m->size;
     210                 :          0 :                 m->delta += m->size;
     211                 :            : 
     212         [ #  # ]:          0 :                 if (m->n_entries_set)
     213                 :          0 :                         m->n_entries -= 1;
     214                 :            : 
     215                 :          0 :                 m->n_skip = 0;
     216                 :            : 
     217                 :          0 :                 r = request_meta_ensure_tmp(m);
     218         [ #  # ]:          0 :                 if (r < 0) {
     219         [ #  # ]:          0 :                         log_error_errno(r, "Failed to create temporary file: %m");
     220                 :          0 :                         return MHD_CONTENT_READER_END_WITH_ERROR;
     221                 :            :                 }
     222                 :            : 
     223                 :          0 :                 r = show_journal_entry(m->tmp, m->journal, m->mode, 0, OUTPUT_FULL_WIDTH,
     224                 :            :                                    NULL, NULL, NULL);
     225         [ #  # ]:          0 :                 if (r < 0) {
     226         [ #  # ]:          0 :                         log_error_errno(r, "Failed to serialize item: %m");
     227                 :          0 :                         return MHD_CONTENT_READER_END_WITH_ERROR;
     228                 :            :                 }
     229                 :            : 
     230                 :          0 :                 sz = ftello(m->tmp);
     231         [ #  # ]:          0 :                 if (sz == (off_t) -1) {
     232         [ #  # ]:          0 :                         log_error_errno(errno, "Failed to retrieve file position: %m");
     233                 :          0 :                         return MHD_CONTENT_READER_END_WITH_ERROR;
     234                 :            :                 }
     235                 :            : 
     236                 :          0 :                 m->size = (uint64_t) sz;
     237                 :            :         }
     238                 :            : 
     239   [ #  #  #  # ]:          0 :         if (m->tmp == NULL && m->follow)
     240                 :          0 :                 return 0;
     241                 :            : 
     242         [ #  # ]:          0 :         if (fseeko(m->tmp, pos, SEEK_SET) < 0) {
     243         [ #  # ]:          0 :                 log_error_errno(errno, "Failed to seek to position: %m");
     244                 :          0 :                 return MHD_CONTENT_READER_END_WITH_ERROR;
     245                 :            :         }
     246                 :            : 
     247                 :          0 :         n = m->size - pos;
     248         [ #  # ]:          0 :         if (n < 1)
     249                 :          0 :                 return 0;
     250         [ #  # ]:          0 :         if (n > max)
     251                 :          0 :                 n = max;
     252                 :            : 
     253                 :          0 :         errno = 0;
     254                 :          0 :         k = fread(buf, 1, n, m->tmp);
     255         [ #  # ]:          0 :         if (k != n) {
     256   [ #  #  #  # ]:          0 :                 log_error("Failed to read from file: %s", errno != 0 ? strerror_safe(errno) : "Premature EOF");
     257                 :          0 :                 return MHD_CONTENT_READER_END_WITH_ERROR;
     258                 :            :         }
     259                 :            : 
     260                 :          0 :         return (ssize_t) k;
     261                 :            : }
     262                 :            : 
     263                 :          0 : static int request_parse_accept(
     264                 :            :                 RequestMeta *m,
     265                 :            :                 struct MHD_Connection *connection) {
     266                 :            : 
     267                 :            :         const char *header;
     268                 :            : 
     269         [ #  # ]:          0 :         assert(m);
     270         [ #  # ]:          0 :         assert(connection);
     271                 :            : 
     272                 :          0 :         header = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "Accept");
     273         [ #  # ]:          0 :         if (!header)
     274                 :          0 :                 return 0;
     275                 :            : 
     276         [ #  # ]:          0 :         if (streq(header, mime_types[OUTPUT_JSON]))
     277                 :          0 :                 m->mode = OUTPUT_JSON;
     278         [ #  # ]:          0 :         else if (streq(header, mime_types[OUTPUT_JSON_SSE]))
     279                 :          0 :                 m->mode = OUTPUT_JSON_SSE;
     280         [ #  # ]:          0 :         else if (streq(header, mime_types[OUTPUT_JSON_SEQ]))
     281                 :          0 :                 m->mode = OUTPUT_JSON_SEQ;
     282         [ #  # ]:          0 :         else if (streq(header, mime_types[OUTPUT_EXPORT]))
     283                 :          0 :                 m->mode = OUTPUT_EXPORT;
     284                 :            :         else
     285                 :          0 :                 m->mode = OUTPUT_SHORT;
     286                 :            : 
     287                 :          0 :         return 0;
     288                 :            : }
     289                 :            : 
     290                 :          0 : static int request_parse_range(
     291                 :            :                 RequestMeta *m,
     292                 :            :                 struct MHD_Connection *connection) {
     293                 :            : 
     294                 :            :         const char *range, *colon, *colon2;
     295                 :            :         int r;
     296                 :            : 
     297         [ #  # ]:          0 :         assert(m);
     298         [ #  # ]:          0 :         assert(connection);
     299                 :            : 
     300                 :          0 :         range = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "Range");
     301         [ #  # ]:          0 :         if (!range)
     302                 :          0 :                 return 0;
     303                 :            : 
     304         [ #  # ]:          0 :         if (!startswith(range, "entries="))
     305                 :          0 :                 return 0;
     306                 :            : 
     307                 :          0 :         range += 8;
     308                 :          0 :         range += strspn(range, WHITESPACE);
     309                 :            : 
     310                 :          0 :         colon = strchr(range, ':');
     311         [ #  # ]:          0 :         if (!colon)
     312                 :          0 :                 m->cursor = strdup(range);
     313                 :            :         else {
     314                 :            :                 const char *p;
     315                 :            : 
     316                 :          0 :                 colon2 = strchr(colon + 1, ':');
     317         [ #  # ]:          0 :                 if (colon2) {
     318         [ #  # ]:          0 :                         _cleanup_free_ char *t;
     319                 :            : 
     320                 :          0 :                         t = strndup(colon + 1, colon2 - colon - 1);
     321         [ #  # ]:          0 :                         if (!t)
     322                 :          0 :                                 return -ENOMEM;
     323                 :            : 
     324                 :          0 :                         r = safe_atoi64(t, &m->n_skip);
     325         [ #  # ]:          0 :                         if (r < 0)
     326                 :          0 :                                 return r;
     327                 :            :                 }
     328                 :            : 
     329         [ #  # ]:          0 :                 p = (colon2 ? colon2 : colon) + 1;
     330         [ #  # ]:          0 :                 if (*p) {
     331                 :          0 :                         r = safe_atou64(p, &m->n_entries);
     332         [ #  # ]:          0 :                         if (r < 0)
     333                 :          0 :                                 return r;
     334                 :            : 
     335         [ #  # ]:          0 :                         if (m->n_entries <= 0)
     336                 :          0 :                                 return -EINVAL;
     337                 :            : 
     338                 :          0 :                         m->n_entries_set = true;
     339                 :            :                 }
     340                 :            : 
     341                 :          0 :                 m->cursor = strndup(range, colon - range);
     342                 :            :         }
     343                 :            : 
     344         [ #  # ]:          0 :         if (!m->cursor)
     345                 :          0 :                 return -ENOMEM;
     346                 :            : 
     347                 :          0 :         m->cursor[strcspn(m->cursor, WHITESPACE)] = 0;
     348         [ #  # ]:          0 :         if (isempty(m->cursor))
     349                 :          0 :                 m->cursor = mfree(m->cursor);
     350                 :            : 
     351                 :          0 :         return 0;
     352                 :            : }
     353                 :            : 
     354                 :          0 : static int request_parse_arguments_iterator(
     355                 :            :                 void *cls,
     356                 :            :                 enum MHD_ValueKind kind,
     357                 :            :                 const char *key,
     358                 :            :                 const char *value) {
     359                 :            : 
     360                 :          0 :         RequestMeta *m = cls;
     361                 :          0 :         _cleanup_free_ char *p = NULL;
     362                 :            :         int r;
     363                 :            : 
     364         [ #  # ]:          0 :         assert(m);
     365                 :            : 
     366         [ #  # ]:          0 :         if (isempty(key)) {
     367                 :          0 :                 m->argument_parse_error = -EINVAL;
     368                 :          0 :                 return MHD_NO;
     369                 :            :         }
     370                 :            : 
     371         [ #  # ]:          0 :         if (streq(key, "follow")) {
     372         [ #  # ]:          0 :                 if (isempty(value)) {
     373                 :          0 :                         m->follow = true;
     374                 :          0 :                         return MHD_YES;
     375                 :            :                 }
     376                 :            : 
     377                 :          0 :                 r = parse_boolean(value);
     378         [ #  # ]:          0 :                 if (r < 0) {
     379                 :          0 :                         m->argument_parse_error = r;
     380                 :          0 :                         return MHD_NO;
     381                 :            :                 }
     382                 :            : 
     383                 :          0 :                 m->follow = r;
     384                 :          0 :                 return MHD_YES;
     385                 :            :         }
     386                 :            : 
     387         [ #  # ]:          0 :         if (streq(key, "discrete")) {
     388         [ #  # ]:          0 :                 if (isempty(value)) {
     389                 :          0 :                         m->discrete = true;
     390                 :          0 :                         return MHD_YES;
     391                 :            :                 }
     392                 :            : 
     393                 :          0 :                 r = parse_boolean(value);
     394         [ #  # ]:          0 :                 if (r < 0) {
     395                 :          0 :                         m->argument_parse_error = r;
     396                 :          0 :                         return MHD_NO;
     397                 :            :                 }
     398                 :            : 
     399                 :          0 :                 m->discrete = r;
     400                 :          0 :                 return MHD_YES;
     401                 :            :         }
     402                 :            : 
     403         [ #  # ]:          0 :         if (streq(key, "boot")) {
     404         [ #  # ]:          0 :                 if (isempty(value))
     405                 :          0 :                         r = true;
     406                 :            :                 else {
     407                 :          0 :                         r = parse_boolean(value);
     408         [ #  # ]:          0 :                         if (r < 0) {
     409                 :          0 :                                 m->argument_parse_error = r;
     410                 :          0 :                                 return MHD_NO;
     411                 :            :                         }
     412                 :            :                 }
     413                 :            : 
     414         [ #  # ]:          0 :                 if (r) {
     415                 :          0 :                         char match[9 + 32 + 1] = "_BOOT_ID=";
     416                 :            :                         sd_id128_t bid;
     417                 :            : 
     418                 :          0 :                         r = sd_id128_get_boot(&bid);
     419         [ #  # ]:          0 :                         if (r < 0) {
     420         [ #  # ]:          0 :                                 log_error_errno(r, "Failed to get boot ID: %m");
     421                 :          0 :                                 return MHD_NO;
     422                 :            :                         }
     423                 :            : 
     424                 :          0 :                         sd_id128_to_string(bid, match + 9);
     425                 :          0 :                         r = sd_journal_add_match(m->journal, match, sizeof(match)-1);
     426         [ #  # ]:          0 :                         if (r < 0) {
     427                 :          0 :                                 m->argument_parse_error = r;
     428                 :          0 :                                 return MHD_NO;
     429                 :            :                         }
     430                 :            :                 }
     431                 :            : 
     432                 :          0 :                 return MHD_YES;
     433                 :            :         }
     434                 :            : 
     435                 :          0 :         p = strjoin(key, "=", strempty(value));
     436         [ #  # ]:          0 :         if (!p) {
     437                 :          0 :                 m->argument_parse_error = log_oom();
     438                 :          0 :                 return MHD_NO;
     439                 :            :         }
     440                 :            : 
     441                 :          0 :         r = sd_journal_add_match(m->journal, p, 0);
     442         [ #  # ]:          0 :         if (r < 0) {
     443                 :          0 :                 m->argument_parse_error = r;
     444                 :          0 :                 return MHD_NO;
     445                 :            :         }
     446                 :            : 
     447                 :          0 :         return MHD_YES;
     448                 :            : }
     449                 :            : 
     450                 :          0 : static int request_parse_arguments(
     451                 :            :                 RequestMeta *m,
     452                 :            :                 struct MHD_Connection *connection) {
     453                 :            : 
     454         [ #  # ]:          0 :         assert(m);
     455         [ #  # ]:          0 :         assert(connection);
     456                 :            : 
     457                 :          0 :         m->argument_parse_error = 0;
     458                 :          0 :         MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND, request_parse_arguments_iterator, m);
     459                 :            : 
     460                 :          0 :         return m->argument_parse_error;
     461                 :            : }
     462                 :            : 
     463                 :          0 : static int request_handler_entries(
     464                 :            :                 struct MHD_Connection *connection,
     465                 :            :                 void *connection_cls) {
     466                 :            : 
     467                 :          0 :         _cleanup_(MHD_destroy_responsep) struct MHD_Response *response = NULL;
     468                 :          0 :         RequestMeta *m = connection_cls;
     469                 :            :         int r;
     470                 :            : 
     471         [ #  # ]:          0 :         assert(connection);
     472         [ #  # ]:          0 :         assert(m);
     473                 :            : 
     474                 :          0 :         r = open_journal(m);
     475         [ #  # ]:          0 :         if (r < 0)
     476                 :          0 :                 return mhd_respondf(connection, r, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to open journal: %m");
     477                 :            : 
     478         [ #  # ]:          0 :         if (request_parse_accept(m, connection) < 0)
     479                 :          0 :                 return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to parse Accept header.");
     480                 :            : 
     481         [ #  # ]:          0 :         if (request_parse_range(m, connection) < 0)
     482                 :          0 :                 return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to parse Range header.");
     483                 :            : 
     484         [ #  # ]:          0 :         if (request_parse_arguments(m, connection) < 0)
     485                 :          0 :                 return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to parse URL arguments.");
     486                 :            : 
     487         [ #  # ]:          0 :         if (m->discrete) {
     488         [ #  # ]:          0 :                 if (!m->cursor)
     489                 :          0 :                         return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Discrete seeks require a cursor specification.");
     490                 :            : 
     491                 :          0 :                 m->n_entries = 1;
     492                 :          0 :                 m->n_entries_set = true;
     493                 :            :         }
     494                 :            : 
     495         [ #  # ]:          0 :         if (m->cursor)
     496                 :          0 :                 r = sd_journal_seek_cursor(m->journal, m->cursor);
     497         [ #  # ]:          0 :         else if (m->n_skip >= 0)
     498                 :          0 :                 r = sd_journal_seek_head(m->journal);
     499         [ #  # ]:          0 :         else if (m->n_skip < 0)
     500                 :          0 :                 r = sd_journal_seek_tail(m->journal);
     501         [ #  # ]:          0 :         if (r < 0)
     502                 :          0 :                 return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to seek in journal.");
     503                 :            : 
     504                 :          0 :         response = MHD_create_response_from_callback(MHD_SIZE_UNKNOWN, 4*1024, request_reader_entries, m, NULL);
     505         [ #  # ]:          0 :         if (!response)
     506                 :          0 :                 return respond_oom(connection);
     507                 :            : 
     508                 :          0 :         MHD_add_response_header(response, "Content-Type", mime_types[m->mode]);
     509                 :          0 :         return MHD_queue_response(connection, MHD_HTTP_OK, response);
     510                 :            : }
     511                 :            : 
     512                 :          0 : static int output_field(FILE *f, OutputMode m, const char *d, size_t l) {
     513                 :            :         const char *eq;
     514                 :            :         size_t j;
     515                 :            : 
     516                 :          0 :         eq = memchr(d, '=', l);
     517         [ #  # ]:          0 :         if (!eq)
     518                 :          0 :                 return -EINVAL;
     519                 :            : 
     520                 :          0 :         j = l - (eq - d + 1);
     521                 :            : 
     522         [ #  # ]:          0 :         if (m == OUTPUT_JSON) {
     523                 :          0 :                 fprintf(f, "{ \"%.*s\" : ", (int) (eq - d), d);
     524                 :          0 :                 json_escape(f, eq+1, j, OUTPUT_FULL_WIDTH);
     525                 :          0 :                 fputs(" }\n", f);
     526                 :            :         } else {
     527                 :          0 :                 fwrite(eq+1, 1, j, f);
     528                 :          0 :                 fputc('\n', f);
     529                 :            :         }
     530                 :            : 
     531                 :          0 :         return 0;
     532                 :            : }
     533                 :            : 
     534                 :          0 : static ssize_t request_reader_fields(
     535                 :            :                 void *cls,
     536                 :            :                 uint64_t pos,
     537                 :            :                 char *buf,
     538                 :            :                 size_t max) {
     539                 :            : 
     540                 :          0 :         RequestMeta *m = cls;
     541                 :            :         int r;
     542                 :            :         size_t n, k;
     543                 :            : 
     544         [ #  # ]:          0 :         assert(m);
     545         [ #  # ]:          0 :         assert(buf);
     546         [ #  # ]:          0 :         assert(max > 0);
     547         [ #  # ]:          0 :         assert(pos >= m->delta);
     548                 :            : 
     549                 :          0 :         pos -= m->delta;
     550                 :            : 
     551         [ #  # ]:          0 :         while (pos >= m->size) {
     552                 :            :                 off_t sz;
     553                 :            :                 const void *d;
     554                 :            :                 size_t l;
     555                 :            : 
     556                 :            :                 /* End of this field, so let's serialize the next
     557                 :            :                  * one */
     558                 :            : 
     559         [ #  # ]:          0 :                 if (m->n_fields_set &&
     560         [ #  # ]:          0 :                     m->n_fields <= 0)
     561                 :          0 :                         return MHD_CONTENT_READER_END_OF_STREAM;
     562                 :            : 
     563                 :          0 :                 r = sd_journal_enumerate_unique(m->journal, &d, &l);
     564         [ #  # ]:          0 :                 if (r < 0) {
     565         [ #  # ]:          0 :                         log_error_errno(r, "Failed to advance field index: %m");
     566                 :          0 :                         return MHD_CONTENT_READER_END_WITH_ERROR;
     567         [ #  # ]:          0 :                 } else if (r == 0)
     568                 :          0 :                         return MHD_CONTENT_READER_END_OF_STREAM;
     569                 :            : 
     570                 :          0 :                 pos -= m->size;
     571                 :          0 :                 m->delta += m->size;
     572                 :            : 
     573         [ #  # ]:          0 :                 if (m->n_fields_set)
     574                 :          0 :                         m->n_fields -= 1;
     575                 :            : 
     576                 :          0 :                 r = request_meta_ensure_tmp(m);
     577         [ #  # ]:          0 :                 if (r < 0) {
     578         [ #  # ]:          0 :                         log_error_errno(r, "Failed to create temporary file: %m");
     579                 :          0 :                         return MHD_CONTENT_READER_END_WITH_ERROR;
     580                 :            :                 }
     581                 :            : 
     582                 :          0 :                 r = output_field(m->tmp, m->mode, d, l);
     583         [ #  # ]:          0 :                 if (r < 0) {
     584         [ #  # ]:          0 :                         log_error_errno(r, "Failed to serialize item: %m");
     585                 :          0 :                         return MHD_CONTENT_READER_END_WITH_ERROR;
     586                 :            :                 }
     587                 :            : 
     588                 :          0 :                 sz = ftello(m->tmp);
     589         [ #  # ]:          0 :                 if (sz == (off_t) -1) {
     590         [ #  # ]:          0 :                         log_error_errno(errno, "Failed to retrieve file position: %m");
     591                 :          0 :                         return MHD_CONTENT_READER_END_WITH_ERROR;
     592                 :            :                 }
     593                 :            : 
     594                 :          0 :                 m->size = (uint64_t) sz;
     595                 :            :         }
     596                 :            : 
     597         [ #  # ]:          0 :         if (fseeko(m->tmp, pos, SEEK_SET) < 0) {
     598         [ #  # ]:          0 :                 log_error_errno(errno, "Failed to seek to position: %m");
     599                 :          0 :                 return MHD_CONTENT_READER_END_WITH_ERROR;
     600                 :            :         }
     601                 :            : 
     602                 :          0 :         n = m->size - pos;
     603         [ #  # ]:          0 :         if (n > max)
     604                 :          0 :                 n = max;
     605                 :            : 
     606                 :          0 :         errno = 0;
     607                 :          0 :         k = fread(buf, 1, n, m->tmp);
     608         [ #  # ]:          0 :         if (k != n) {
     609   [ #  #  #  # ]:          0 :                 log_error("Failed to read from file: %s", errno != 0 ? strerror_safe(errno) : "Premature EOF");
     610                 :          0 :                 return MHD_CONTENT_READER_END_WITH_ERROR;
     611                 :            :         }
     612                 :            : 
     613                 :          0 :         return (ssize_t) k;
     614                 :            : }
     615                 :            : 
     616                 :          0 : static int request_handler_fields(
     617                 :            :                 struct MHD_Connection *connection,
     618                 :            :                 const char *field,
     619                 :            :                 void *connection_cls) {
     620                 :            : 
     621                 :          0 :         _cleanup_(MHD_destroy_responsep) struct MHD_Response *response = NULL;
     622                 :          0 :         RequestMeta *m = connection_cls;
     623                 :            :         int r;
     624                 :            : 
     625         [ #  # ]:          0 :         assert(connection);
     626         [ #  # ]:          0 :         assert(m);
     627                 :            : 
     628                 :          0 :         r = open_journal(m);
     629         [ #  # ]:          0 :         if (r < 0)
     630                 :          0 :                 return mhd_respondf(connection, r, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to open journal: %m");
     631                 :            : 
     632         [ #  # ]:          0 :         if (request_parse_accept(m, connection) < 0)
     633                 :          0 :                 return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to parse Accept header.");
     634                 :            : 
     635                 :          0 :         r = sd_journal_query_unique(m->journal, field);
     636         [ #  # ]:          0 :         if (r < 0)
     637                 :          0 :                 return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to query unique fields.");
     638                 :            : 
     639                 :          0 :         response = MHD_create_response_from_callback(MHD_SIZE_UNKNOWN, 4*1024, request_reader_fields, m, NULL);
     640         [ #  # ]:          0 :         if (!response)
     641                 :          0 :                 return respond_oom(connection);
     642                 :            : 
     643         [ #  # ]:          0 :         MHD_add_response_header(response, "Content-Type", mime_types[m->mode == OUTPUT_JSON ? OUTPUT_JSON : OUTPUT_SHORT]);
     644                 :          0 :         return MHD_queue_response(connection, MHD_HTTP_OK, response);
     645                 :            : }
     646                 :            : 
     647                 :          0 : static int request_handler_redirect(
     648                 :            :                 struct MHD_Connection *connection,
     649                 :            :                 const char *target) {
     650                 :            : 
     651                 :            :         char *page;
     652                 :          0 :         _cleanup_(MHD_destroy_responsep) struct MHD_Response *response = NULL;
     653                 :            : 
     654         [ #  # ]:          0 :         assert(connection);
     655         [ #  # ]:          0 :         assert(target);
     656                 :            : 
     657         [ #  # ]:          0 :         if (asprintf(&page, "<html><body>Please continue to the <a href=\"%s\">journal browser</a>.</body></html>", target) < 0)
     658                 :          0 :                 return respond_oom(connection);
     659                 :            : 
     660                 :          0 :         response = MHD_create_response_from_buffer(strlen(page), page, MHD_RESPMEM_MUST_FREE);
     661         [ #  # ]:          0 :         if (!response) {
     662                 :          0 :                 free(page);
     663                 :          0 :                 return respond_oom(connection);
     664                 :            :         }
     665                 :            : 
     666                 :          0 :         MHD_add_response_header(response, "Content-Type", "text/html");
     667                 :          0 :         MHD_add_response_header(response, "Location", target);
     668                 :          0 :         return MHD_queue_response(connection, MHD_HTTP_MOVED_PERMANENTLY, response);
     669                 :            : }
     670                 :            : 
     671                 :          0 : static int request_handler_file(
     672                 :            :                 struct MHD_Connection *connection,
     673                 :            :                 const char *path,
     674                 :            :                 const char *mime_type) {
     675                 :            : 
     676                 :          0 :         _cleanup_(MHD_destroy_responsep) struct MHD_Response *response = NULL;
     677                 :          0 :         _cleanup_close_ int fd = -1;
     678                 :            :         struct stat st;
     679                 :            : 
     680         [ #  # ]:          0 :         assert(connection);
     681         [ #  # ]:          0 :         assert(path);
     682         [ #  # ]:          0 :         assert(mime_type);
     683                 :            : 
     684                 :          0 :         fd = open(path, O_RDONLY|O_CLOEXEC);
     685         [ #  # ]:          0 :         if (fd < 0)
     686                 :          0 :                 return mhd_respondf(connection, errno, MHD_HTTP_NOT_FOUND, "Failed to open file %s: %m", path);
     687                 :            : 
     688         [ #  # ]:          0 :         if (fstat(fd, &st) < 0)
     689                 :          0 :                 return mhd_respondf(connection, errno, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to stat file: %m");
     690                 :            : 
     691                 :          0 :         response = MHD_create_response_from_fd_at_offset64(st.st_size, fd, 0);
     692         [ #  # ]:          0 :         if (!response)
     693                 :          0 :                 return respond_oom(connection);
     694                 :          0 :         TAKE_FD(fd);
     695                 :            : 
     696                 :          0 :         MHD_add_response_header(response, "Content-Type", mime_type);
     697                 :          0 :         return MHD_queue_response(connection, MHD_HTTP_OK, response);
     698                 :            : }
     699                 :            : 
     700                 :          0 : static int get_virtualization(char **v) {
     701                 :          0 :         _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
     702                 :          0 :         char *b = NULL;
     703                 :            :         int r;
     704                 :            : 
     705                 :          0 :         r = sd_bus_default_system(&bus);
     706         [ #  # ]:          0 :         if (r < 0)
     707                 :          0 :                 return r;
     708                 :            : 
     709                 :          0 :         r = sd_bus_get_property_string(
     710                 :            :                         bus,
     711                 :            :                         "org.freedesktop.systemd1",
     712                 :            :                         "/org/freedesktop/systemd1",
     713                 :            :                         "org.freedesktop.systemd1.Manager",
     714                 :            :                         "Virtualization",
     715                 :            :                         NULL,
     716                 :            :                         &b);
     717         [ #  # ]:          0 :         if (r < 0)
     718                 :          0 :                 return r;
     719                 :            : 
     720         [ #  # ]:          0 :         if (isempty(b)) {
     721                 :          0 :                 free(b);
     722                 :          0 :                 *v = NULL;
     723                 :          0 :                 return 0;
     724                 :            :         }
     725                 :            : 
     726                 :          0 :         *v = b;
     727                 :          0 :         return 1;
     728                 :            : }
     729                 :            : 
     730                 :          0 : static int request_handler_machine(
     731                 :            :                 struct MHD_Connection *connection,
     732                 :            :                 void *connection_cls) {
     733                 :            : 
     734                 :          0 :         _cleanup_(MHD_destroy_responsep) struct MHD_Response *response = NULL;
     735                 :          0 :         RequestMeta *m = connection_cls;
     736                 :            :         int r;
     737                 :          0 :         _cleanup_free_ char* hostname = NULL, *os_name = NULL;
     738                 :          0 :         uint64_t cutoff_from = 0, cutoff_to = 0, usage = 0;
     739                 :            :         sd_id128_t mid, bid;
     740                 :          0 :         _cleanup_free_ char *v = NULL, *json = NULL;
     741                 :            : 
     742         [ #  # ]:          0 :         assert(connection);
     743         [ #  # ]:          0 :         assert(m);
     744                 :            : 
     745                 :          0 :         r = open_journal(m);
     746         [ #  # ]:          0 :         if (r < 0)
     747                 :          0 :                 return mhd_respondf(connection, r, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to open journal: %m");
     748                 :            : 
     749                 :          0 :         r = sd_id128_get_machine(&mid);
     750         [ #  # ]:          0 :         if (r < 0)
     751                 :          0 :                 return mhd_respondf(connection, r, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to determine machine ID: %m");
     752                 :            : 
     753                 :          0 :         r = sd_id128_get_boot(&bid);
     754         [ #  # ]:          0 :         if (r < 0)
     755                 :          0 :                 return mhd_respondf(connection, r, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to determine boot ID: %m");
     756                 :            : 
     757                 :          0 :         hostname = gethostname_malloc();
     758         [ #  # ]:          0 :         if (!hostname)
     759                 :          0 :                 return respond_oom(connection);
     760                 :            : 
     761                 :          0 :         r = sd_journal_get_usage(m->journal, &usage);
     762         [ #  # ]:          0 :         if (r < 0)
     763                 :          0 :                 return mhd_respondf(connection, r, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to determine disk usage: %m");
     764                 :            : 
     765                 :          0 :         r = sd_journal_get_cutoff_realtime_usec(m->journal, &cutoff_from, &cutoff_to);
     766         [ #  # ]:          0 :         if (r < 0)
     767                 :          0 :                 return mhd_respondf(connection, r, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to determine disk usage: %m");
     768                 :            : 
     769                 :          0 :         (void) parse_os_release(NULL, "PRETTY_NAME", &os_name, NULL);
     770                 :          0 :         (void) get_virtualization(&v);
     771                 :            : 
     772                 :          0 :         r = asprintf(&json,
     773                 :            :                      "{ \"machine_id\" : \"" SD_ID128_FORMAT_STR "\","
     774                 :            :                      "\"boot_id\" : \"" SD_ID128_FORMAT_STR "\","
     775                 :            :                      "\"hostname\" : \"%s\","
     776                 :            :                      "\"os_pretty_name\" : \"%s\","
     777                 :            :                      "\"virtualization\" : \"%s\","
     778                 :            :                      "\"usage\" : \"%"PRIu64"\","
     779                 :            :                      "\"cutoff_from_realtime\" : \"%"PRIu64"\","
     780                 :            :                      "\"cutoff_to_realtime\" : \"%"PRIu64"\" }\n",
     781                 :          0 :                      SD_ID128_FORMAT_VAL(mid),
     782                 :          0 :                      SD_ID128_FORMAT_VAL(bid),
     783                 :            :                      hostname_cleanup(hostname),
     784         [ #  # ]:          0 :                      os_name ? os_name : "Linux",
     785         [ #  # ]:          0 :                      v ? v : "bare",
     786                 :            :                      usage,
     787                 :            :                      cutoff_from,
     788                 :            :                      cutoff_to);
     789         [ #  # ]:          0 :         if (r < 0)
     790                 :          0 :                 return respond_oom(connection);
     791                 :            : 
     792                 :          0 :         response = MHD_create_response_from_buffer(strlen(json), json, MHD_RESPMEM_MUST_FREE);
     793         [ #  # ]:          0 :         if (!response)
     794                 :          0 :                 return respond_oom(connection);
     795                 :          0 :         TAKE_PTR(json);
     796                 :            : 
     797                 :          0 :         MHD_add_response_header(response, "Content-Type", "application/json");
     798                 :          0 :         return MHD_queue_response(connection, MHD_HTTP_OK, response);
     799                 :            : }
     800                 :            : 
     801                 :          0 : static int request_handler(
     802                 :            :                 void *cls,
     803                 :            :                 struct MHD_Connection *connection,
     804                 :            :                 const char *url,
     805                 :            :                 const char *method,
     806                 :            :                 const char *version,
     807                 :            :                 const char *upload_data,
     808                 :            :                 size_t *upload_data_size,
     809                 :            :                 void **connection_cls) {
     810                 :            :         int r, code;
     811                 :            : 
     812         [ #  # ]:          0 :         assert(connection);
     813         [ #  # ]:          0 :         assert(connection_cls);
     814         [ #  # ]:          0 :         assert(url);
     815         [ #  # ]:          0 :         assert(method);
     816                 :            : 
     817         [ #  # ]:          0 :         if (!streq(method, "GET"))
     818                 :          0 :                 return mhd_respond(connection, MHD_HTTP_NOT_ACCEPTABLE, "Unsupported method.");
     819                 :            : 
     820         [ #  # ]:          0 :         if (!*connection_cls) {
     821         [ #  # ]:          0 :                 if (!request_meta(connection_cls))
     822                 :          0 :                         return respond_oom(connection);
     823                 :          0 :                 return MHD_YES;
     824                 :            :         }
     825                 :            : 
     826         [ #  # ]:          0 :         if (arg_trust_pem) {
     827                 :          0 :                 r = check_permissions(connection, &code, NULL);
     828         [ #  # ]:          0 :                 if (r < 0)
     829                 :          0 :                         return code;
     830                 :            :         }
     831                 :            : 
     832         [ #  # ]:          0 :         if (streq(url, "/"))
     833                 :          0 :                 return request_handler_redirect(connection, "/browse");
     834                 :            : 
     835         [ #  # ]:          0 :         if (streq(url, "/entries"))
     836                 :          0 :                 return request_handler_entries(connection, *connection_cls);
     837                 :            : 
     838         [ #  # ]:          0 :         if (startswith(url, "/fields/"))
     839                 :          0 :                 return request_handler_fields(connection, url + 8, *connection_cls);
     840                 :            : 
     841         [ #  # ]:          0 :         if (streq(url, "/browse"))
     842                 :          0 :                 return request_handler_file(connection, DOCUMENT_ROOT "/browse.html", "text/html");
     843                 :            : 
     844         [ #  # ]:          0 :         if (streq(url, "/machine"))
     845                 :          0 :                 return request_handler_machine(connection, *connection_cls);
     846                 :            : 
     847                 :          0 :         return mhd_respond(connection, MHD_HTTP_NOT_FOUND, "Not found.");
     848                 :            : }
     849                 :            : 
     850                 :         12 : static int help(void) {
     851                 :         12 :         _cleanup_free_ char *link = NULL;
     852                 :            :         int r;
     853                 :            : 
     854                 :         12 :         r = terminal_urlify_man("systemd-journal-gatewayd.service", "8", &link);
     855         [ -  + ]:         12 :         if (r < 0)
     856                 :          0 :                 return log_oom();
     857                 :            : 
     858                 :         12 :         printf("%s [OPTIONS...] ...\n\n"
     859                 :            :                "HTTP server for journal events.\n\n"
     860                 :            :                "  -h --help           Show this help\n"
     861                 :            :                "     --version        Show package version\n"
     862                 :            :                "     --cert=CERT.PEM  Server certificate in PEM format\n"
     863                 :            :                "     --key=KEY.PEM    Server key in PEM format\n"
     864                 :            :                "     --trust=CERT.PEM Certificate authority certificate in PEM format\n"
     865                 :            :                "  -D --directory=PATH Serve journal files in directory\n"
     866                 :            :                "\nSee the %s for details.\n"
     867                 :            :                , program_invocation_short_name
     868                 :            :                , link
     869                 :            :         );
     870                 :            : 
     871                 :         12 :         return 0;
     872                 :            : }
     873                 :            : 
     874                 :         16 : static int parse_argv(int argc, char *argv[]) {
     875                 :            :         enum {
     876                 :            :                 ARG_VERSION = 0x100,
     877                 :            :                 ARG_KEY,
     878                 :            :                 ARG_CERT,
     879                 :            :                 ARG_TRUST,
     880                 :            :         };
     881                 :            : 
     882                 :            :         int r, c;
     883                 :            : 
     884                 :            :         static const struct option options[] = {
     885                 :            :                 { "help",      no_argument,       NULL, 'h'           },
     886                 :            :                 { "version",   no_argument,       NULL, ARG_VERSION   },
     887                 :            :                 { "key",       required_argument, NULL, ARG_KEY       },
     888                 :            :                 { "cert",      required_argument, NULL, ARG_CERT      },
     889                 :            :                 { "trust",     required_argument, NULL, ARG_TRUST     },
     890                 :            :                 { "directory", required_argument, NULL, 'D'           },
     891                 :            :                 {}
     892                 :            :         };
     893                 :            : 
     894         [ -  + ]:         16 :         assert(argc >= 0);
     895         [ -  + ]:         16 :         assert(argv);
     896                 :            : 
     897         [ +  - ]:         16 :         while ((c = getopt_long(argc, argv, "hD:", options, NULL)) >= 0)
     898                 :            : 
     899   [ +  -  -  -  :         16 :                 switch(c) {
             -  -  +  - ]
     900                 :            : 
     901                 :         12 :                 case 'h':
     902                 :         12 :                         return help();
     903                 :            : 
     904                 :          0 :                 case ARG_VERSION:
     905                 :          0 :                         return version();
     906                 :            : 
     907                 :          0 :                 case ARG_KEY:
     908         [ #  # ]:          0 :                         if (arg_key_pem)
     909         [ #  # ]:          0 :                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
     910                 :            :                                                        "Key file specified twice");
     911                 :          0 :                         r = read_full_file(optarg, &arg_key_pem, NULL);
     912         [ #  # ]:          0 :                         if (r < 0)
     913         [ #  # ]:          0 :                                 return log_error_errno(r, "Failed to read key file: %m");
     914         [ #  # ]:          0 :                         assert(arg_key_pem);
     915                 :          0 :                         break;
     916                 :            : 
     917                 :          0 :                 case ARG_CERT:
     918         [ #  # ]:          0 :                         if (arg_cert_pem)
     919         [ #  # ]:          0 :                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
     920                 :            :                                                        "Certificate file specified twice");
     921                 :          0 :                         r = read_full_file(optarg, &arg_cert_pem, NULL);
     922         [ #  # ]:          0 :                         if (r < 0)
     923         [ #  # ]:          0 :                                 return log_error_errno(r, "Failed to read certificate file: %m");
     924         [ #  # ]:          0 :                         assert(arg_cert_pem);
     925                 :          0 :                         break;
     926                 :            : 
     927                 :          0 :                 case ARG_TRUST:
     928                 :            : #if HAVE_GNUTLS
     929         [ #  # ]:          0 :                         if (arg_trust_pem)
     930         [ #  # ]:          0 :                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
     931                 :            :                                                        "CA certificate file specified twice");
     932                 :          0 :                         r = read_full_file(optarg, &arg_trust_pem, NULL);
     933         [ #  # ]:          0 :                         if (r < 0)
     934         [ #  # ]:          0 :                                 return log_error_errno(r, "Failed to read CA certificate file: %m");
     935         [ #  # ]:          0 :                         assert(arg_trust_pem);
     936                 :          0 :                         break;
     937                 :            : #else
     938                 :            :                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
     939                 :            :                                                "Option --trust is not available.");
     940                 :            : #endif
     941                 :          0 :                 case 'D':
     942                 :          0 :                         arg_directory = optarg;
     943                 :          0 :                         break;
     944                 :            : 
     945                 :          4 :                 case '?':
     946                 :          4 :                         return -EINVAL;
     947                 :            : 
     948                 :          0 :                 default:
     949                 :          0 :                         assert_not_reached("Unhandled option");
     950                 :            :                 }
     951                 :            : 
     952         [ #  # ]:          0 :         if (optind < argc)
     953         [ #  # ]:          0 :                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
     954                 :            :                                        "This program does not take arguments.");
     955                 :            : 
     956         [ #  # ]:          0 :         if (!!arg_key_pem != !!arg_cert_pem)
     957         [ #  # ]:          0 :                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
     958                 :            :                                        "Certificate and key files must be specified together");
     959                 :            : 
     960   [ #  #  #  # ]:          0 :         if (arg_trust_pem && !arg_key_pem)
     961         [ #  # ]:          0 :                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
     962                 :            :                                        "CA certificate can only be used with certificate file");
     963                 :            : 
     964                 :          0 :         return 1;
     965                 :            : }
     966                 :            : 
     967                 :         16 : static int run(int argc, char *argv[]) {
     968                 :         16 :         _cleanup_(MHD_stop_daemonp) struct MHD_Daemon *d = NULL;
     969                 :         48 :         struct MHD_OptionItem opts[] = {
     970                 :            :                 { MHD_OPTION_NOTIFY_COMPLETED,
     971                 :         16 :                   (intptr_t) request_meta_free, NULL },
     972                 :            :                 { MHD_OPTION_EXTERNAL_LOGGER,
     973                 :         16 :                   (intptr_t) microhttpd_logger, NULL },
     974                 :            :                 { MHD_OPTION_END, 0, NULL },
     975                 :            :                 { MHD_OPTION_END, 0, NULL },
     976                 :            :                 { MHD_OPTION_END, 0, NULL },
     977                 :            :                 { MHD_OPTION_END, 0, NULL },
     978                 :            :                 { MHD_OPTION_END, 0, NULL },
     979                 :            :         };
     980                 :         16 :         int opts_pos = 2;
     981                 :            : 
     982                 :            :         /* We force MHD_USE_ITC here, in order to make sure
     983                 :            :          * libmicrohttpd doesn't use shutdown() on our listening
     984                 :            :          * socket, which would break socket re-activation. See
     985                 :            :          *
     986                 :            :          * https://lists.gnu.org/archive/html/libmicrohttpd/2015-09/msg00014.html
     987                 :            :          * https://github.com/systemd/systemd/pull/1286
     988                 :            :          */
     989                 :            : 
     990                 :         16 :         int flags =
     991                 :            :                 MHD_USE_DEBUG |
     992                 :            :                 MHD_USE_DUAL_STACK |
     993                 :            :                 MHD_USE_ITC |
     994                 :            :                 MHD_USE_POLL_INTERNAL_THREAD |
     995                 :            :                 MHD_USE_THREAD_PER_CONNECTION;
     996                 :            :         int r, n;
     997                 :            : 
     998                 :         16 :         log_setup_service();
     999                 :            : 
    1000                 :         16 :         r = parse_argv(argc, argv);
    1001         [ +  - ]:         16 :         if (r <= 0)
    1002                 :         16 :                 return r;
    1003                 :            : 
    1004                 :          0 :         sigbus_install();
    1005                 :            : 
    1006                 :          0 :         r = setup_gnutls_logger(NULL);
    1007         [ #  # ]:          0 :         if (r < 0)
    1008                 :          0 :                 return r;
    1009                 :            : 
    1010                 :          0 :         n = sd_listen_fds(1);
    1011         [ #  # ]:          0 :         if (n < 0)
    1012         [ #  # ]:          0 :                 return log_error_errno(n, "Failed to determine passed sockets: %m");
    1013         [ #  # ]:          0 :         if (n > 1)
    1014         [ #  # ]:          0 :                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Can't listen on more than one socket.");
    1015                 :            : 
    1016         [ #  # ]:          0 :         if (n == 1)
    1017                 :          0 :                 opts[opts_pos++] = (struct MHD_OptionItem)
    1018                 :            :                         { MHD_OPTION_LISTEN_SOCKET, SD_LISTEN_FDS_START };
    1019                 :            : 
    1020         [ #  # ]:          0 :         if (arg_key_pem) {
    1021         [ #  # ]:          0 :                 assert(arg_cert_pem);
    1022                 :          0 :                 opts[opts_pos++] = (struct MHD_OptionItem)
    1023                 :            :                         { MHD_OPTION_HTTPS_MEM_KEY, 0, arg_key_pem };
    1024                 :          0 :                 opts[opts_pos++] = (struct MHD_OptionItem)
    1025                 :            :                         { MHD_OPTION_HTTPS_MEM_CERT, 0, arg_cert_pem };
    1026                 :          0 :                 flags |= MHD_USE_TLS;
    1027                 :            :         }
    1028                 :            : 
    1029         [ #  # ]:          0 :         if (arg_trust_pem) {
    1030         [ #  # ]:          0 :                 assert(flags & MHD_USE_TLS);
    1031                 :          0 :                 opts[opts_pos++] = (struct MHD_OptionItem)
    1032                 :            :                         { MHD_OPTION_HTTPS_MEM_TRUST, 0, arg_trust_pem };
    1033                 :            :         }
    1034                 :            : 
    1035                 :          0 :         d = MHD_start_daemon(flags, 19531,
    1036                 :            :                              NULL, NULL,
    1037                 :            :                              request_handler, NULL,
    1038                 :            :                              MHD_OPTION_ARRAY, opts,
    1039                 :            :                              MHD_OPTION_END);
    1040         [ #  # ]:          0 :         if (!d)
    1041         [ #  # ]:          0 :                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to start daemon!");
    1042                 :            : 
    1043                 :          0 :         pause();
    1044                 :            : 
    1045                 :          0 :         return 0;
    1046                 :            : }
    1047                 :            : 
    1048                 :         16 : DEFINE_MAIN_FUNCTION(run);

Generated by: LCOV version 1.14