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