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