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