LCOV - code coverage report
Current view: top level - journal-remote - journal-gatewayd.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 30 534 5.6 %
Date: 2019-08-22 15:41:25 Functions: 7 25 28.0 %

          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           4 : STATIC_DESTRUCTOR_REGISTER(arg_key_pem, freep);
      41           4 : STATIC_DESTRUCTOR_REGISTER(arg_cert_pem, freep);
      42           4 : 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           3 : static int help(void) {
     851           3 :         _cleanup_free_ char *link = NULL;
     852             :         int r;
     853             : 
     854           3 :         r = terminal_urlify_man("systemd-journal-gatewayd.service", "8", &link);
     855           3 :         if (r < 0)
     856           0 :                 return log_oom();
     857             : 
     858           3 :         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           3 :         return 0;
     872             : }
     873             : 
     874           4 : 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           4 :         assert(argc >= 0);
     895           4 :         assert(argv);
     896             : 
     897           4 :         while ((c = getopt_long(argc, argv, "hD:", options, NULL)) >= 0)
     898             : 
     899           4 :                 switch(c) {
     900             : 
     901           3 :                 case 'h':
     902           3 :                         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           1 :                 case '?':
     946           1 :                         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           4 : static int run(int argc, char *argv[]) {
     968           4 :         _cleanup_(MHD_stop_daemonp) struct MHD_Daemon *d = NULL;
     969          12 :         struct MHD_OptionItem opts[] = {
     970             :                 { MHD_OPTION_NOTIFY_COMPLETED,
     971           4 :                   (intptr_t) request_meta_free, NULL },
     972             :                 { MHD_OPTION_EXTERNAL_LOGGER,
     973           4 :                   (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           4 :         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           4 :         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           4 :         log_setup_service();
     999             : 
    1000           4 :         r = parse_argv(argc, argv);
    1001           4 :         if (r <= 0)
    1002           4 :                 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           4 : DEFINE_MAIN_FUNCTION(run);

Generated by: LCOV version 1.14