]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal-remote/journal-gatewayd.c
logind: check return value of session_release
[thirdparty/systemd.git] / src / journal-remote / journal-gatewayd.c
CommitLineData
7b17a7d7
LP
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>
858634ff 26#include <getopt.h>
7b17a7d7
LP
27
28#include <microhttpd.h>
29
f12be7e8
ZJS
30#ifdef HAVE_GNUTLS
31#include <gnutls/gnutls.h>
32#endif
33
7b17a7d7
LP
34#include "sd-journal.h"
35#include "sd-daemon.h"
a7edaadd 36#include "sd-bus.h"
2cf4172a
LP
37#include "log.h"
38#include "util.h"
40ca29a1 39#include "bus-util.h"
7b17a7d7 40#include "logs-show.h"
e64690a8 41#include "microhttpd-util.h"
858634ff 42#include "build.h"
a860325e 43#include "fileio.h"
2cf4172a 44#include "sigbus.h"
7b17a7d7 45
2cf4172a
LP
46static char *arg_key_pem = NULL;
47static char *arg_cert_pem = NULL;
48static char *arg_trust_pem = NULL;
f12be7e8 49
7b17a7d7
LP
50typedef 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;
98206c93
LP
62
63 int argument_parse_error;
7a69007a
LP
64
65 bool follow;
c6511e85 66 bool discrete;
240a5fe8
LP
67
68 uint64_t n_fields;
69 bool n_fields_set;
7b17a7d7
LP
70} RequestMeta;
71
72static const char* const mime_types[_OUTPUT_MODE_MAX] = {
73 [OUTPUT_SHORT] = "text/plain",
74 [OUTPUT_JSON] = "application/json",
48383c25
LP
75 [OUTPUT_JSON_SSE] = "text/event-stream",
76 [OUTPUT_EXPORT] = "application/vnd.fdo.journal",
7b17a7d7
LP
77};
78
79static RequestMeta *request_meta(void **connection_cls) {
80 RequestMeta *m;
81
fdfccdbc 82 assert(connection_cls);
7b17a7d7
LP
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
94static 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
115static int open_journal(RequestMeta *m) {
116 assert(m);
117
118 if (m->journal)
119 return 0;
120
a688baa8 121 return sd_journal_open(&m->journal, SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM);
7b17a7d7
LP
122}
123
7b17a7d7
LP
124static 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
77ad3b93
LP
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)
7b17a7d7
LP
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) {
da927ba9 159 log_error_errno(r, "Failed to advance journal pointer: %m");
7b17a7d7 160 return MHD_CONTENT_READER_END_WITH_ERROR;
7a69007a
LP
161 } else if (r == 0) {
162
163 if (m->follow) {
164 r = sd_journal_wait(m->journal, (uint64_t) -1);
165 if (r < 0) {
da927ba9 166 log_error_errno(r, "Couldn't wait for journal event: %m");
7a69007a
LP
167 return MHD_CONTENT_READER_END_WITH_ERROR;
168 }
169
170 continue;
171 }
172
7b17a7d7 173 return MHD_CONTENT_READER_END_OF_STREAM;
7a69007a 174 }
7b17a7d7 175
c6511e85
LP
176 if (m->discrete) {
177 assert(m->cursor);
178
179 r = sd_journal_test_cursor(m->journal, m->cursor);
180 if (r < 0) {
da927ba9 181 log_error_errno(r, "Failed to test cursor: %m");
c6511e85
LP
182 return MHD_CONTENT_READER_END_WITH_ERROR;
183 }
184
185 if (r == 0)
186 return MHD_CONTENT_READER_END_OF_STREAM;
187 }
188
7b17a7d7
LP
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) {
56f64d95 202 log_error_errno(errno, "Failed to create temporary file: %m");
33b40551 203 return MHD_CONTENT_READER_END_WITH_ERROR;
7b17a7d7
LP
204 }
205 }
206
94e0bd7d 207 r = output_journal(m->tmp, m->journal, m->mode, 0, OUTPUT_FULL_WIDTH, NULL);
7b17a7d7 208 if (r < 0) {
da927ba9 209 log_error_errno(r, "Failed to serialize item: %m");
7b17a7d7
LP
210 return MHD_CONTENT_READER_END_WITH_ERROR;
211 }
212
213 sz = ftello(m->tmp);
214 if (sz == (off_t) -1) {
56f64d95 215 log_error_errno(errno, "Failed to retrieve file position: %m");
7b17a7d7
LP
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) {
56f64d95 223 log_error_errno(errno, "Failed to seek to position: %m");
7b17a7d7
LP
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
241static int request_parse_accept(
242 RequestMeta *m,
243 struct MHD_Connection *connection) {
244
6374a73b 245 const char *header;
7b17a7d7
LP
246
247 assert(m);
248 assert(connection);
249
6374a73b
ZJS
250 header = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "Accept");
251 if (!header)
7b17a7d7
LP
252 return 0;
253
6374a73b 254 if (streq(header, mime_types[OUTPUT_JSON]))
7b17a7d7 255 m->mode = OUTPUT_JSON;
6374a73b 256 else if (streq(header, mime_types[OUTPUT_JSON_SSE]))
48383c25 257 m->mode = OUTPUT_JSON_SSE;
6374a73b 258 else if (streq(header, mime_types[OUTPUT_EXPORT]))
7b17a7d7
LP
259 m->mode = OUTPUT_EXPORT;
260 else
261 m->mode = OUTPUT_SHORT;
262
263 return 0;
264}
265
266static 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) {
7fd1b19b 294 _cleanup_free_ char *t;
7b17a7d7
LP
295
296 t = strndup(colon + 1, colon2 - colon - 1);
297 if (!t)
298 return -ENOMEM;
299
300 r = safe_atoi64(t, &m->n_skip);
7b17a7d7
LP
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
98206c93
LP
332static 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
7a69007a
LP
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
c6511e85
LP
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
082d0180
LP
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) {
da927ba9 398 log_error_errno(r, "Failed to get boot ID: %m");
082d0180
LP
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
98206c93
LP
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
428static 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
7b17a7d7
LP
441static int request_handler_entries(
442 struct MHD_Connection *connection,
8530a143 443 void *connection_cls) {
7b17a7d7
LP
444
445 struct MHD_Response *response;
8530a143 446 RequestMeta *m = connection_cls;
7b17a7d7
LP
447 int r;
448
449 assert(connection);
8530a143 450 assert(m);
7b17a7d7
LP
451
452 r = open_journal(m);
453 if (r < 0)
e7216d11 454 return mhd_respondf(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to open journal: %s\n", strerror(-r));
7b17a7d7
LP
455
456 if (request_parse_accept(m, connection) < 0)
e7216d11 457 return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to parse Accept header.\n");
7b17a7d7
LP
458
459 if (request_parse_range(m, connection) < 0)
e7216d11 460 return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to parse Range header.\n");
7b17a7d7 461
98206c93 462 if (request_parse_arguments(m, connection) < 0)
e7216d11 463 return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to parse URL arguments.\n");
98206c93 464
c6511e85
LP
465 if (m->discrete) {
466 if (!m->cursor)
e7216d11 467 return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Discrete seeks require a cursor specification.\n");
c6511e85
LP
468
469 m->n_entries = 1;
470 m->n_entries_set = true;
471 }
472
7b17a7d7
LP
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)
e7216d11 480 return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to seek in journal.\n");
7b17a7d7
LP
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
240a5fe8
LP
494static 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
516static 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) {
da927ba9 547 log_error_errno(r, "Failed to advance field index: %m");
240a5fe8
LP
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) {
56f64d95 563 log_error_errno(errno, "Failed to create temporary file: %m");
33b40551 564 return MHD_CONTENT_READER_END_WITH_ERROR;
240a5fe8
LP
565 }
566 }
567
568 r = output_field(m->tmp, m->mode, d, l);
569 if (r < 0) {
da927ba9 570 log_error_errno(r, "Failed to serialize item: %m");
240a5fe8
LP
571 return MHD_CONTENT_READER_END_WITH_ERROR;
572 }
573
574 sz = ftello(m->tmp);
575 if (sz == (off_t) -1) {
56f64d95 576 log_error_errno(errno, "Failed to retrieve file position: %m");
240a5fe8
LP
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) {
56f64d95 584 log_error_errno(errno, "Failed to seek to position: %m");
240a5fe8
LP
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
602static int request_handler_fields(
603 struct MHD_Connection *connection,
604 const char *field,
605 void *connection_cls) {
606
607 struct MHD_Response *response;
8530a143 608 RequestMeta *m = connection_cls;
240a5fe8
LP
609 int r;
610
611 assert(connection);
8530a143 612 assert(m);
240a5fe8
LP
613
614 r = open_journal(m);
615 if (r < 0)
e7216d11 616 return mhd_respondf(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to open journal: %s\n", strerror(-r));
240a5fe8
LP
617
618 if (request_parse_accept(m, connection) < 0)
e7216d11 619 return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to parse Accept header.\n");
240a5fe8
LP
620
621 r = sd_journal_query_unique(m->journal, field);
622 if (r < 0)
e7216d11 623 return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to query unique fields.\n");
240a5fe8
LP
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
7b17a7d7
LP
637static 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);
fadd79d2 646 assert(target);
7b17a7d7
LP
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
666static 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)
e7216d11 682 return mhd_respondf(connection, MHD_HTTP_NOT_FOUND, "Failed to open file %s: %m\n", path);
7b17a7d7
LP
683
684 if (fstat(fd, &st) < 0)
e7216d11 685 return mhd_respondf(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to stat file: %m\n");
7b17a7d7
LP
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
a7edaadd 701static int get_virtualization(char **v) {
a7edaadd 702 _cleanup_bus_unref_ sd_bus *bus = NULL;
39883f62 703 char *b = NULL;
a7edaadd
LP
704 int r;
705
76b54375 706 r = sd_bus_default_system(&bus);
a7edaadd
LP
707 if (r < 0)
708 return r;
709
47c649b5 710 r = sd_bus_get_property_string(
a7edaadd
LP
711 bus,
712 "org.freedesktop.systemd1",
713 "/org/freedesktop/systemd1",
917b5dc7 714 "org.freedesktop.systemd1.Manager",
47c649b5
LP
715 "Virtualization",
716 NULL,
717 &b);
a7edaadd
LP
718 if (r < 0)
719 return r;
720
47c649b5
LP
721 if (isempty(b)) {
722 free(b);
a7edaadd
LP
723 *v = NULL;
724 return 0;
725 }
726
a7edaadd
LP
727 *v = b;
728 return 1;
729}
730
7b17a7d7
LP
731static int request_handler_machine(
732 struct MHD_Connection *connection,
8530a143 733 void *connection_cls) {
7b17a7d7
LP
734
735 struct MHD_Response *response;
8530a143 736 RequestMeta *m = connection_cls;
7b17a7d7
LP
737 int r;
738 _cleanup_free_ char* hostname = NULL, *os_name = NULL;
39883f62 739 uint64_t cutoff_from = 0, cutoff_to = 0, usage;
7b17a7d7
LP
740 char *json;
741 sd_id128_t mid, bid;
a7edaadd 742 _cleanup_free_ char *v = NULL;
7b17a7d7
LP
743
744 assert(connection);
8530a143 745 assert(m);
7b17a7d7
LP
746
747 r = open_journal(m);
748 if (r < 0)
e7216d11 749 return mhd_respondf(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to open journal: %s\n", strerror(-r));
7b17a7d7
LP
750
751 r = sd_id128_get_machine(&mid);
752 if (r < 0)
e7216d11 753 return mhd_respondf(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to determine machine ID: %s\n", strerror(-r));
7b17a7d7
LP
754
755 r = sd_id128_get_boot(&bid);
756 if (r < 0)
e7216d11 757 return mhd_respondf(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to determine boot ID: %s\n", strerror(-r));
7b17a7d7
LP
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)
e7216d11 765 return mhd_respondf(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to determine disk usage: %s\n", strerror(-r));
7b17a7d7
LP
766
767 r = sd_journal_get_cutoff_realtime_usec(m->journal, &cutoff_from, &cutoff_to);
768 if (r < 0)
e7216d11 769 return mhd_respondf(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to determine disk usage: %s\n", strerror(-r));
7b17a7d7 770
5ae4d543
LP
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);
7b17a7d7 773
a7edaadd 774 get_virtualization(&v);
7b17a7d7
LP
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\","
507f22bd
ZJS
782 "\"usage\" : \"%"PRIu64"\","
783 "\"cutoff_from_realtime\" : \"%"PRIu64"\","
784 "\"cutoff_to_realtime\" : \"%"PRIu64"\" }\n",
7b17a7d7
LP
785 SD_ID128_FORMAT_VAL(mid),
786 SD_ID128_FORMAT_VAL(bid),
e724b063 787 hostname_cleanup(hostname, false),
7b17a7d7 788 os_name ? os_name : "Linux",
a7edaadd 789 v ? v : "bare",
507f22bd
ZJS
790 usage,
791 cutoff_from,
792 cutoff_to);
7b17a7d7
LP
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
810static 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) {
f12be7e8 819 int r, code;
7b17a7d7
LP
820
821 assert(connection);
8530a143 822 assert(connection_cls);
7b17a7d7
LP
823 assert(url);
824 assert(method);
825
826 if (!streq(method, "GET"))
e7216d11
ZJS
827 return mhd_respond(connection, MHD_HTTP_METHOD_NOT_ACCEPTABLE,
828 "Unsupported method.\n");
a93035ce 829
7b17a7d7 830
8530a143
ZJS
831 if (!*connection_cls) {
832 if (!request_meta(connection_cls))
833 return respond_oom(connection);
834 return MHD_YES;
835 }
836
2cf4172a 837 if (arg_trust_pem) {
8201af08 838 r = check_permissions(connection, &code, NULL);
f12be7e8
ZJS
839 if (r < 0)
840 return code;
841 }
842
7b17a7d7
LP
843 if (streq(url, "/"))
844 return request_handler_redirect(connection, "/browse");
845
846 if (streq(url, "/entries"))
8530a143 847 return request_handler_entries(connection, *connection_cls);
7b17a7d7 848
240a5fe8 849 if (startswith(url, "/fields/"))
8530a143 850 return request_handler_fields(connection, url + 8, *connection_cls);
240a5fe8 851
7b17a7d7
LP
852 if (streq(url, "/browse"))
853 return request_handler_file(connection, DOCUMENT_ROOT "/browse.html", "text/html");
854
855 if (streq(url, "/machine"))
8530a143 856 return request_handler_machine(connection, *connection_cls);
7b17a7d7 857
e7216d11 858 return mhd_respond(connection, MHD_HTTP_NOT_FOUND, "Not found.\n");
7b17a7d7
LP
859}
860
601185b4 861static void help(void) {
c3a7cfb7
ZJS
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"
e5ebe12b
ZJS
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",
c3a7cfb7 869 program_invocation_short_name);
c3a7cfb7
ZJS
870}
871
858634ff
ZJS
872static int parse_argv(int argc, char *argv[]) {
873 enum {
874 ARG_VERSION = 0x100,
875 ARG_KEY,
876 ARG_CERT,
e5ebe12b 877 ARG_TRUST,
858634ff
ZJS
878 };
879
880 int r, c;
881
882 static const struct option options[] = {
c3a7cfb7 883 { "help", no_argument, NULL, 'h' },
858634ff
ZJS
884 { "version", no_argument, NULL, ARG_VERSION },
885 { "key", required_argument, NULL, ARG_KEY },
886 { "cert", required_argument, NULL, ARG_CERT },
e5ebe12b 887 { "trust", required_argument, NULL, ARG_TRUST },
eb9da376 888 {}
858634ff
ZJS
889 };
890
891 assert(argc >= 0);
892 assert(argv);
893
c3a7cfb7 894 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
eb9da376 895
858634ff 896 switch(c) {
eb9da376
LP
897
898 case 'h':
601185b4
ZJS
899 help();
900 return 0;
eb9da376 901
858634ff
ZJS
902 case ARG_VERSION:
903 puts(PACKAGE_STRING);
904 puts(SYSTEMD_FEATURES);
905 return 0;
906
907 case ARG_KEY:
2cf4172a 908 if (arg_key_pem) {
858634ff
ZJS
909 log_error("Key file specified twice");
910 return -EINVAL;
911 }
2cf4172a 912 r = read_full_file(optarg, &arg_key_pem, NULL);
23bbb0de
MS
913 if (r < 0)
914 return log_error_errno(r, "Failed to read key file: %m");
2cf4172a 915 assert(arg_key_pem);
858634ff 916 break;
7b17a7d7 917
858634ff 918 case ARG_CERT:
2cf4172a 919 if (arg_cert_pem) {
858634ff
ZJS
920 log_error("Certificate file specified twice");
921 return -EINVAL;
922 }
2cf4172a 923 r = read_full_file(optarg, &arg_cert_pem, NULL);
23bbb0de
MS
924 if (r < 0)
925 return log_error_errno(r, "Failed to read certificate file: %m");
2cf4172a 926 assert(arg_cert_pem);
858634ff
ZJS
927 break;
928
e5ebe12b 929 case ARG_TRUST:
f12be7e8 930#ifdef HAVE_GNUTLS
2cf4172a 931 if (arg_trust_pem) {
e5ebe12b
ZJS
932 log_error("CA certificate file specified twice");
933 return -EINVAL;
934 }
2cf4172a 935 r = read_full_file(optarg, &arg_trust_pem, NULL);
23bbb0de
MS
936 if (r < 0)
937 return log_error_errno(r, "Failed to read CA certificate file: %m");
2cf4172a 938 assert(arg_trust_pem);
e5ebe12b 939 break;
f12be7e8
ZJS
940#else
941 log_error("Option --trust is not available.");
942#endif
e5ebe12b 943
858634ff
ZJS
944 case '?':
945 return -EINVAL;
946
947 default:
eb9da376 948 assert_not_reached("Unhandled option");
858634ff
ZJS
949 }
950
951 if (optind < argc) {
7b17a7d7 952 log_error("This program does not take arguments.");
858634ff
ZJS
953 return -EINVAL;
954 }
955
2cf4172a 956 if (!!arg_key_pem != !!arg_cert_pem) {
858634ff
ZJS
957 log_error("Certificate and key files must be specified together");
958 return -EINVAL;
7b17a7d7
LP
959 }
960
2cf4172a 961 if (arg_trust_pem && !arg_key_pem) {
e5ebe12b
ZJS
962 log_error("CA certificate can only be used with certificate file");
963 return -EINVAL;
964 }
965
858634ff
ZJS
966 return 1;
967}
968
969int main(int argc, char *argv[]) {
970 struct MHD_Daemon *d = NULL;
971 int r, n;
972
77ad3b93 973 log_set_target(LOG_TARGET_AUTO);
7b17a7d7
LP
974 log_parse_environment();
975 log_open();
976
858634ff
ZJS
977 r = parse_argv(argc, argv);
978 if (r < 0)
979 return EXIT_FAILURE;
980 if (r == 0)
981 return EXIT_SUCCESS;
982
2cf4172a
LP
983 sigbus_install();
984
d357562c
ZJS
985 r = setup_gnutls_logger(NULL);
986 if (r < 0)
987 return EXIT_FAILURE;
cafc7f91 988
7b17a7d7
LP
989 n = sd_listen_fds(1);
990 if (n < 0) {
da927ba9 991 log_error_errno(n, "Failed to determine passed sockets: %m");
7b17a7d7
LP
992 goto finish;
993 } else if (n > 1) {
994 log_error("Can't listen on more than one socket.");
995 goto finish;
7b17a7d7 996 } else {
c54ff8e3
ZJS
997 struct MHD_OptionItem opts[] = {
998 { MHD_OPTION_NOTIFY_COMPLETED,
999 (intptr_t) request_meta_free, NULL },
e64690a8
ZJS
1000 { MHD_OPTION_EXTERNAL_LOGGER,
1001 (intptr_t) microhttpd_logger, NULL },
c54ff8e3 1002 { MHD_OPTION_END, 0, NULL },
858634ff
ZJS
1003 { MHD_OPTION_END, 0, NULL },
1004 { MHD_OPTION_END, 0, NULL },
e5ebe12b 1005 { MHD_OPTION_END, 0, NULL },
c54ff8e3 1006 { MHD_OPTION_END, 0, NULL }};
e64690a8 1007 int opts_pos = 2;
858634ff
ZJS
1008 int flags = MHD_USE_THREAD_PER_CONNECTION|MHD_USE_POLL|MHD_USE_DEBUG;
1009
c54ff8e3 1010 if (n > 0)
858634ff
ZJS
1011 opts[opts_pos++] = (struct MHD_OptionItem)
1012 {MHD_OPTION_LISTEN_SOCKET, SD_LISTEN_FDS_START};
2cf4172a
LP
1013 if (arg_key_pem) {
1014 assert(arg_cert_pem);
858634ff 1015 opts[opts_pos++] = (struct MHD_OptionItem)
2cf4172a 1016 {MHD_OPTION_HTTPS_MEM_KEY, 0, arg_key_pem};
858634ff 1017 opts[opts_pos++] = (struct MHD_OptionItem)
2cf4172a 1018 {MHD_OPTION_HTTPS_MEM_CERT, 0, arg_cert_pem};
858634ff
ZJS
1019 flags |= MHD_USE_SSL;
1020 }
2cf4172a 1021 if (arg_trust_pem) {
e5ebe12b
ZJS
1022 assert(flags & MHD_USE_SSL);
1023 opts[opts_pos++] = (struct MHD_OptionItem)
2cf4172a 1024 {MHD_OPTION_HTTPS_MEM_TRUST, 0, arg_trust_pem};
e5ebe12b 1025 }
858634ff
ZJS
1026
1027 d = MHD_start_daemon(flags, 19531,
1028 NULL, NULL,
1029 request_handler, NULL,
1030 MHD_OPTION_ARRAY, opts,
1031 MHD_OPTION_END);
7b17a7d7
LP
1032 }
1033
6374a73b 1034 if (!d) {
7b17a7d7
LP
1035 log_error("Failed to start daemon!");
1036 goto finish;
1037 }
1038
1039 pause();
1040
1041 r = EXIT_SUCCESS;
1042
1043finish:
6374a73b
ZJS
1044 if (d)
1045 MHD_stop_daemon(d);
7b17a7d7
LP
1046
1047 return r;
1048}