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