]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/journal-gatewayd.c
journal: add ability to list unique fields to gatewayd
[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");
240 return MHD_CONTENT_READER_END_WITH_ERROR;;
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) {
331 char *t;
332
333 t = strndup(colon + 1, colon2 - colon - 1);
334 if (!t)
335 return -ENOMEM;
336
337 r = safe_atoi64(t, &m->n_skip);
338 free(t);
339 if (r < 0)
340 return r;
341 }
342
343 p = (colon2 ? colon2 : colon) + 1;
344 if (*p) {
345 r = safe_atou64(p, &m->n_entries);
346 if (r < 0)
347 return r;
348
349 if (m->n_entries <= 0)
350 return -EINVAL;
351
352 m->n_entries_set = true;
353 }
354
355 m->cursor = strndup(range, colon - range);
356 }
357
358 if (!m->cursor)
359 return -ENOMEM;
360
361 m->cursor[strcspn(m->cursor, WHITESPACE)] = 0;
362 if (isempty(m->cursor)) {
363 free(m->cursor);
364 m->cursor = NULL;
365 }
366
367 return 0;
368}
369
98206c93
LP
370static int request_parse_arguments_iterator(
371 void *cls,
372 enum MHD_ValueKind kind,
373 const char *key,
374 const char *value) {
375
376 RequestMeta *m = cls;
377 _cleanup_free_ char *p = NULL;
378 int r;
379
380 assert(m);
381
382 if (isempty(key)) {
383 m->argument_parse_error = -EINVAL;
384 return MHD_NO;
385 }
386
7a69007a
LP
387 if (streq(key, "follow")) {
388 if (isempty(value)) {
389 m->follow = true;
390 return MHD_YES;
391 }
392
393 r = parse_boolean(value);
394 if (r < 0) {
395 m->argument_parse_error = r;
396 return MHD_NO;
397 }
398
399 m->follow = r;
400 return MHD_YES;
401 }
402
c6511e85
LP
403 if (streq(key, "discrete")) {
404 if (isempty(value)) {
405 m->discrete = true;
406 return MHD_YES;
407 }
408
409 r = parse_boolean(value);
410 if (r < 0) {
411 m->argument_parse_error = r;
412 return MHD_NO;
413 }
414
415 m->discrete = r;
416 return MHD_YES;
417 }
418
98206c93
LP
419 p = strjoin(key, "=", strempty(value), NULL);
420 if (!p) {
421 m->argument_parse_error = log_oom();
422 return MHD_NO;
423 }
424
425 r = sd_journal_add_match(m->journal, p, 0);
426 if (r < 0) {
427 m->argument_parse_error = r;
428 return MHD_NO;
429 }
430
431 return MHD_YES;
432}
433
434static int request_parse_arguments(
435 RequestMeta *m,
436 struct MHD_Connection *connection) {
437
438 assert(m);
439 assert(connection);
440
441 m->argument_parse_error = 0;
442 MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND, request_parse_arguments_iterator, m);
443
444 return m->argument_parse_error;
445}
446
7b17a7d7
LP
447static int request_handler_entries(
448 struct MHD_Connection *connection,
449 void **connection_cls) {
450
451 struct MHD_Response *response;
452 RequestMeta *m;
453 int r;
454
455 assert(connection);
456 assert(connection_cls);
457
458 m = request_meta(connection_cls);
459 if (!m)
460 return respond_oom(connection);
461
462 r = open_journal(m);
463 if (r < 0)
464 return respond_error(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to open journal: %s\n", strerror(-r));
465
466 if (request_parse_accept(m, connection) < 0)
467 return respond_error(connection, MHD_HTTP_BAD_REQUEST, "Failed to parse Accept header.\n");
468
469 if (request_parse_range(m, connection) < 0)
470 return respond_error(connection, MHD_HTTP_BAD_REQUEST, "Failed to parse Range header.\n");
471
98206c93
LP
472 if (request_parse_arguments(m, connection) < 0)
473 return respond_error(connection, MHD_HTTP_BAD_REQUEST, "Failed to parse URL arguments.\n");
474
c6511e85
LP
475 if (m->discrete) {
476 if (!m->cursor)
477 return respond_error(connection, MHD_HTTP_BAD_REQUEST, "Discrete seeks require a cursor specification.\n");
478
479 m->n_entries = 1;
480 m->n_entries_set = true;
481 }
482
7b17a7d7
LP
483 if (m->cursor)
484 r = sd_journal_seek_cursor(m->journal, m->cursor);
485 else if (m->n_skip >= 0)
486 r = sd_journal_seek_head(m->journal);
487 else if (m->n_skip < 0)
488 r = sd_journal_seek_tail(m->journal);
489 if (r < 0)
490 return respond_error(connection, MHD_HTTP_BAD_REQUEST, "Failed to seek in journal.\n");
491
492 response = MHD_create_response_from_callback(MHD_SIZE_UNKNOWN, 4*1024, request_reader_entries, m, NULL);
493 if (!response)
494 return respond_oom(connection);
495
496 MHD_add_response_header(response, "Content-Type", mime_types[m->mode]);
497
498 r = MHD_queue_response(connection, MHD_HTTP_OK, response);
499 MHD_destroy_response(response);
500
501 return r;
502}
503
240a5fe8
LP
504static int output_field(FILE *f, OutputMode m, const char *d, size_t l) {
505 const char *eq;
506 size_t j;
507
508 eq = memchr(d, '=', l);
509 if (!eq)
510 return -EINVAL;
511
512 j = l - (eq - d + 1);
513
514 if (m == OUTPUT_JSON) {
515 fprintf(f, "{ \"%.*s\" : ", (int) (eq - d), d);
516 json_escape(f, eq+1, j, OUTPUT_FULL_WIDTH);
517 fputs(" }\n", f);
518 } else {
519 fwrite(eq+1, 1, j, f);
520 fputc('\n', f);
521 }
522
523 return 0;
524}
525
526static ssize_t request_reader_fields(
527 void *cls,
528 uint64_t pos,
529 char *buf,
530 size_t max) {
531
532 RequestMeta *m = cls;
533 int r;
534 size_t n, k;
535
536 assert(m);
537 assert(buf);
538 assert(max > 0);
539 assert(pos >= m->delta);
540
541 pos -= m->delta;
542
543 while (pos >= m->size) {
544 off_t sz;
545 const void *d;
546 size_t l;
547
548 /* End of this field, so let's serialize the next
549 * one */
550
551 if (m->n_fields_set &&
552 m->n_fields <= 0)
553 return MHD_CONTENT_READER_END_OF_STREAM;
554
555 r = sd_journal_enumerate_unique(m->journal, &d, &l);
556 if (r < 0) {
557 log_error("Failed to advance field index: %s", strerror(-r));
558 return MHD_CONTENT_READER_END_WITH_ERROR;
559 } else if (r == 0)
560 return MHD_CONTENT_READER_END_OF_STREAM;
561
562 pos -= m->size;
563 m->delta += m->size;
564
565 if (m->n_fields_set)
566 m->n_fields -= 1;
567
568 if (m->tmp)
569 rewind(m->tmp);
570 else {
571 m->tmp = tmpfile();
572 if (!m->tmp) {
573 log_error("Failed to create temporary file: %m");
574 return MHD_CONTENT_READER_END_WITH_ERROR;;
575 }
576 }
577
578 r = output_field(m->tmp, m->mode, d, l);
579 if (r < 0) {
580 log_error("Failed to serialize item: %s", strerror(-r));
581 return MHD_CONTENT_READER_END_WITH_ERROR;
582 }
583
584 sz = ftello(m->tmp);
585 if (sz == (off_t) -1) {
586 log_error("Failed to retrieve file position: %m");
587 return MHD_CONTENT_READER_END_WITH_ERROR;
588 }
589
590 m->size = (uint64_t) sz;
591 }
592
593 if (fseeko(m->tmp, pos, SEEK_SET) < 0) {
594 log_error("Failed to seek to position: %m");
595 return MHD_CONTENT_READER_END_WITH_ERROR;
596 }
597
598 n = m->size - pos;
599 if (n > max)
600 n = max;
601
602 errno = 0;
603 k = fread(buf, 1, n, m->tmp);
604 if (k != n) {
605 log_error("Failed to read from file: %s", errno ? strerror(errno) : "Premature EOF");
606 return MHD_CONTENT_READER_END_WITH_ERROR;
607 }
608
609 return (ssize_t) k;
610}
611
612static int request_handler_fields(
613 struct MHD_Connection *connection,
614 const char *field,
615 void *connection_cls) {
616
617 struct MHD_Response *response;
618 RequestMeta *m;
619 int r;
620
621 assert(connection);
622 assert(connection_cls);
623
624 m = request_meta(connection_cls);
625 if (!m)
626 return respond_oom(connection);
627
628 r = open_journal(m);
629 if (r < 0)
630 return respond_error(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to open journal: %s\n", strerror(-r));
631
632 if (request_parse_accept(m, connection) < 0)
633 return respond_error(connection, MHD_HTTP_BAD_REQUEST, "Failed to parse Accept header.\n");
634
635 r = sd_journal_query_unique(m->journal, field);
636 if (r < 0)
637 return respond_error(connection, MHD_HTTP_BAD_REQUEST, "Failed to query unique fields.\n");
638
639 response = MHD_create_response_from_callback(MHD_SIZE_UNKNOWN, 4*1024, request_reader_fields, m, NULL);
640 if (!response)
641 return respond_oom(connection);
642
643 MHD_add_response_header(response, "Content-Type", mime_types[m->mode == OUTPUT_JSON ? OUTPUT_JSON : OUTPUT_SHORT]);
644
645 r = MHD_queue_response(connection, MHD_HTTP_OK, response);
646 MHD_destroy_response(response);
647
648 return r;
649}
650
7b17a7d7
LP
651static int request_handler_redirect(
652 struct MHD_Connection *connection,
653 const char *target) {
654
655 char *page;
656 struct MHD_Response *response;
657 int ret;
658
659 assert(connection);
fadd79d2 660 assert(target);
7b17a7d7
LP
661
662 if (asprintf(&page, "<html><body>Please continue to the <a href=\"%s\">journal browser</a>.</body></html>", target) < 0)
663 return respond_oom(connection);
664
665 response = MHD_create_response_from_buffer(strlen(page), page, MHD_RESPMEM_MUST_FREE);
666 if (!response) {
667 free(page);
668 return respond_oom(connection);
669 }
670
671 MHD_add_response_header(response, "Content-Type", "text/html");
672 MHD_add_response_header(response, "Location", target);
673
674 ret = MHD_queue_response(connection, MHD_HTTP_MOVED_PERMANENTLY, response);
675 MHD_destroy_response(response);
676
677 return ret;
678}
679
680static int request_handler_file(
681 struct MHD_Connection *connection,
682 const char *path,
683 const char *mime_type) {
684
685 struct MHD_Response *response;
686 int ret;
687 _cleanup_close_ int fd = -1;
688 struct stat st;
689
690 assert(connection);
691 assert(path);
692 assert(mime_type);
693
694 fd = open(path, O_RDONLY|O_CLOEXEC);
695 if (fd < 0)
696 return respond_error(connection, MHD_HTTP_NOT_FOUND, "Failed to open file %s: %m\n", path);
697
698 if (fstat(fd, &st) < 0)
699 return respond_error(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to stat file: %m\n");
700
701 response = MHD_create_response_from_fd_at_offset(st.st_size, fd, 0);
702 if (!response)
703 return respond_oom(connection);
704
705 fd = -1;
706
707 MHD_add_response_header(response, "Content-Type", mime_type);
708
709 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
710 MHD_destroy_response(response);
711
712 return ret;
713}
714
715static int request_handler_machine(
716 struct MHD_Connection *connection,
717 void **connection_cls) {
718
719 struct MHD_Response *response;
720 RequestMeta *m;
721 int r;
722 _cleanup_free_ char* hostname = NULL, *os_name = NULL;
723 uint64_t cutoff_from, cutoff_to, usage;
724 char *json;
725 sd_id128_t mid, bid;
726 const char *v = "bare";
727
728 assert(connection);
729
730 m = request_meta(connection_cls);
731 if (!m)
732 return respond_oom(connection);
733
734 r = open_journal(m);
735 if (r < 0)
736 return respond_error(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to open journal: %s\n", strerror(-r));
737
738 r = sd_id128_get_machine(&mid);
739 if (r < 0)
740 return respond_error(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to determine machine ID: %s\n", strerror(-r));
741
742 r = sd_id128_get_boot(&bid);
743 if (r < 0)
744 return respond_error(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to determine boot ID: %s\n", strerror(-r));
745
746 hostname = gethostname_malloc();
747 if (!hostname)
748 return respond_oom(connection);
749
750 r = sd_journal_get_usage(m->journal, &usage);
751 if (r < 0)
752 return respond_error(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to determine disk usage: %s\n", strerror(-r));
753
754 r = sd_journal_get_cutoff_realtime_usec(m->journal, &cutoff_from, &cutoff_to);
755 if (r < 0)
756 return respond_error(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to determine disk usage: %s\n", strerror(-r));
757
758 parse_env_file("/etc/os-release", NEWLINE, "PRETTY_NAME", &os_name, NULL);
759
760 detect_virtualization(&v);
761
762 r = asprintf(&json,
763 "{ \"machine_id\" : \"" SD_ID128_FORMAT_STR "\","
764 "\"boot_id\" : \"" SD_ID128_FORMAT_STR "\","
765 "\"hostname\" : \"%s\","
766 "\"os_pretty_name\" : \"%s\","
767 "\"virtualization\" : \"%s\","
768 "\"usage\" : \"%llu\","
769 "\"cutoff_from_realtime\" : \"%llu\","
770 "\"cutoff_to_realtime\" : \"%llu\" }\n",
771 SD_ID128_FORMAT_VAL(mid),
772 SD_ID128_FORMAT_VAL(bid),
773 hostname_cleanup(hostname),
774 os_name ? os_name : "Linux",
775 v,
776 (unsigned long long) usage,
777 (unsigned long long) cutoff_from,
778 (unsigned long long) cutoff_to);
779
780 if (r < 0)
781 return respond_oom(connection);
782
783 response = MHD_create_response_from_buffer(strlen(json), json, MHD_RESPMEM_MUST_FREE);
784 if (!response) {
785 free(json);
786 return respond_oom(connection);
787 }
788
789 MHD_add_response_header(response, "Content-Type", "application/json");
790 r = MHD_queue_response(connection, MHD_HTTP_OK, response);
791 MHD_destroy_response(response);
792
793 return r;
794}
795
796static int request_handler(
797 void *cls,
798 struct MHD_Connection *connection,
799 const char *url,
800 const char *method,
801 const char *version,
802 const char *upload_data,
803 size_t *upload_data_size,
804 void **connection_cls) {
805
806 assert(connection);
807 assert(url);
808 assert(method);
809
810 if (!streq(method, "GET"))
811 return MHD_NO;
812
813 if (streq(url, "/"))
814 return request_handler_redirect(connection, "/browse");
815
816 if (streq(url, "/entries"))
817 return request_handler_entries(connection, connection_cls);
818
240a5fe8
LP
819 if (startswith(url, "/fields/"))
820 return request_handler_fields(connection, url + 8, connection_cls);
821
7b17a7d7
LP
822 if (streq(url, "/browse"))
823 return request_handler_file(connection, DOCUMENT_ROOT "/browse.html", "text/html");
824
825 if (streq(url, "/machine"))
826 return request_handler_machine(connection, connection_cls);
827
828 return respond_error(connection, MHD_HTTP_NOT_FOUND, "Not found.\n");
829}
830
831int main(int argc, char *argv[]) {
6374a73b 832 struct MHD_Daemon *d = NULL;
7b17a7d7
LP
833 int r = EXIT_FAILURE, n;
834
835 if (argc > 1) {
836 log_error("This program does not take arguments.");
837 goto finish;
838 }
839
77ad3b93 840 log_set_target(LOG_TARGET_AUTO);
7b17a7d7
LP
841 log_parse_environment();
842 log_open();
843
844 n = sd_listen_fds(1);
845 if (n < 0) {
846 log_error("Failed to determine passed sockets: %s", strerror(-n));
847 goto finish;
848 } else if (n > 1) {
849 log_error("Can't listen on more than one socket.");
850 goto finish;
851 } else if (n > 0) {
6374a73b 852 d = MHD_start_daemon(
7b17a7d7
LP
853 MHD_USE_THREAD_PER_CONNECTION|MHD_USE_POLL|MHD_USE_DEBUG,
854 19531,
855 NULL, NULL,
856 request_handler, NULL,
857 MHD_OPTION_LISTEN_SOCKET, SD_LISTEN_FDS_START,
858 MHD_OPTION_NOTIFY_COMPLETED, request_meta_free, NULL,
859 MHD_OPTION_END);
860 } else {
6374a73b 861 d = MHD_start_daemon(
7b17a7d7
LP
862 MHD_USE_DEBUG|MHD_USE_THREAD_PER_CONNECTION|MHD_USE_POLL,
863 19531,
864 NULL, NULL,
865 request_handler, NULL,
866 MHD_OPTION_NOTIFY_COMPLETED, request_meta_free, NULL,
867 MHD_OPTION_END);
868 }
869
6374a73b 870 if (!d) {
7b17a7d7
LP
871 log_error("Failed to start daemon!");
872 goto finish;
873 }
874
875 pause();
876
877 r = EXIT_SUCCESS;
878
879finish:
6374a73b
ZJS
880 if (d)
881 MHD_stop_daemon(d);
7b17a7d7
LP
882
883 return r;
884}