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