2020-11-09 05:23:58 +01:00
|
|
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
2012-09-28 00:46:32 +02:00
|
|
|
|
|
|
|
#include <fcntl.h>
|
2012-11-25 23:26:15 +01:00
|
|
|
#include <getopt.h>
|
2015-10-25 13:14:12 +01:00
|
|
|
#include <microhttpd.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2019-03-27 11:32:41 +01:00
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/types.h>
|
2015-10-25 13:14:12 +01:00
|
|
|
#include <unistd.h>
|
2012-12-01 11:12:05 +01:00
|
|
|
|
2013-03-31 18:15:59 +02:00
|
|
|
#include "sd-bus.h"
|
2015-10-25 13:14:12 +01:00
|
|
|
#include "sd-daemon.h"
|
|
|
|
#include "sd-journal.h"
|
2015-09-23 03:01:06 +02:00
|
|
|
|
2015-10-27 03:01:06 +01:00
|
|
|
#include "alloc-util.h"
|
2013-10-16 06:10:04 +02:00
|
|
|
#include "bus-util.h"
|
2019-07-03 16:56:17 +02:00
|
|
|
#include "errno-util.h"
|
2015-10-25 13:14:12 +01:00
|
|
|
#include "fd-util.h"
|
2015-09-23 03:01:06 +02:00
|
|
|
#include "fileio.h"
|
|
|
|
#include "hostname-util.h"
|
|
|
|
#include "log.h"
|
2012-09-28 00:46:32 +02:00
|
|
|
#include "logs-show.h"
|
2018-11-25 21:21:50 +01:00
|
|
|
#include "main-func.h"
|
2020-11-04 16:13:09 +01:00
|
|
|
#include "memory-util.h"
|
2012-11-25 23:54:31 +01:00
|
|
|
#include "microhttpd-util.h"
|
2018-03-26 16:32:40 +02:00
|
|
|
#include "os-util.h"
|
2015-10-26 16:18:16 +01:00
|
|
|
#include "parse-util.h"
|
2018-11-20 15:42:57 +01:00
|
|
|
#include "pretty-print.h"
|
2015-01-05 00:52:47 +01:00
|
|
|
#include "sigbus.h"
|
2018-11-30 21:05:27 +01:00
|
|
|
#include "tmpfile-util.h"
|
2015-09-23 03:01:06 +02:00
|
|
|
#include "util.h"
|
2012-09-28 00:46:32 +02:00
|
|
|
|
2015-12-10 22:13:24 +01:00
|
|
|
#define JOURNAL_WAIT_TIMEOUT (10*USEC_PER_SEC)
|
|
|
|
|
2015-01-05 00:52:47 +01:00
|
|
|
static char *arg_key_pem = NULL;
|
|
|
|
static char *arg_cert_pem = NULL;
|
|
|
|
static char *arg_trust_pem = NULL;
|
2018-11-25 21:21:50 +01:00
|
|
|
static const char *arg_directory = NULL;
|
|
|
|
|
2020-11-04 16:13:09 +01:00
|
|
|
STATIC_DESTRUCTOR_REGISTER(arg_key_pem, erase_and_freep);
|
2018-11-25 21:21:50 +01:00
|
|
|
STATIC_DESTRUCTOR_REGISTER(arg_cert_pem, freep);
|
|
|
|
STATIC_DESTRUCTOR_REGISTER(arg_trust_pem, freep);
|
2012-12-01 11:12:05 +01:00
|
|
|
|
2012-09-28 00:46:32 +02:00
|
|
|
typedef struct RequestMeta {
|
|
|
|
sd_journal *journal;
|
|
|
|
|
|
|
|
OutputMode mode;
|
|
|
|
|
|
|
|
char *cursor;
|
|
|
|
int64_t n_skip;
|
|
|
|
uint64_t n_entries;
|
|
|
|
bool n_entries_set;
|
|
|
|
|
|
|
|
FILE *tmp;
|
|
|
|
uint64_t delta, size;
|
2012-10-09 01:17:29 +02:00
|
|
|
|
|
|
|
int argument_parse_error;
|
2012-10-09 01:31:27 +02:00
|
|
|
|
|
|
|
bool follow;
|
2012-10-10 22:39:45 +02:00
|
|
|
bool discrete;
|
2012-09-28 00:46:32 +02:00
|
|
|
} RequestMeta;
|
|
|
|
|
|
|
|
static const char* const mime_types[_OUTPUT_MODE_MAX] = {
|
|
|
|
[OUTPUT_SHORT] = "text/plain",
|
|
|
|
[OUTPUT_JSON] = "application/json",
|
2012-10-11 02:37:10 +02:00
|
|
|
[OUTPUT_JSON_SSE] = "text/event-stream",
|
2018-07-23 20:22:30 +02:00
|
|
|
[OUTPUT_JSON_SEQ] = "application/json-seq",
|
2012-10-11 02:37:10 +02:00
|
|
|
[OUTPUT_EXPORT] = "application/vnd.fdo.journal",
|
2012-09-28 00:46:32 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
static RequestMeta *request_meta(void **connection_cls) {
|
|
|
|
RequestMeta *m;
|
|
|
|
|
2012-11-01 23:08:03 +01:00
|
|
|
assert(connection_cls);
|
2012-09-28 00:46:32 +02:00
|
|
|
if (*connection_cls)
|
|
|
|
return *connection_cls;
|
|
|
|
|
|
|
|
m = new0(RequestMeta, 1);
|
|
|
|
if (!m)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
*connection_cls = m;
|
|
|
|
return m;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void request_meta_free(
|
|
|
|
void *cls,
|
|
|
|
struct MHD_Connection *connection,
|
|
|
|
void **connection_cls,
|
|
|
|
enum MHD_RequestTerminationCode toe) {
|
|
|
|
|
|
|
|
RequestMeta *m = *connection_cls;
|
|
|
|
|
|
|
|
if (!m)
|
|
|
|
return;
|
|
|
|
|
2015-08-17 10:45:30 +02:00
|
|
|
sd_journal_close(m->journal);
|
2012-09-28 00:46:32 +02:00
|
|
|
|
2015-09-09 15:20:10 +02:00
|
|
|
safe_fclose(m->tmp);
|
2012-09-28 00:46:32 +02:00
|
|
|
|
|
|
|
free(m->cursor);
|
|
|
|
free(m);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int open_journal(RequestMeta *m) {
|
|
|
|
assert(m);
|
|
|
|
|
|
|
|
if (m->journal)
|
|
|
|
return 0;
|
|
|
|
|
2016-08-06 19:00:31 +02:00
|
|
|
if (arg_directory)
|
|
|
|
return sd_journal_open_directory(&m->journal, arg_directory, 0);
|
|
|
|
else
|
|
|
|
return sd_journal_open(&m->journal, SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM);
|
2012-09-28 00:46:32 +02:00
|
|
|
}
|
|
|
|
|
2015-03-15 22:13:43 +01:00
|
|
|
static int request_meta_ensure_tmp(RequestMeta *m) {
|
2016-04-20 19:27:32 +02:00
|
|
|
assert(m);
|
|
|
|
|
2015-03-15 22:13:43 +01:00
|
|
|
if (m->tmp)
|
|
|
|
rewind(m->tmp);
|
|
|
|
else {
|
2020-03-31 11:29:37 +02:00
|
|
|
_cleanup_close_ int fd = -1;
|
2015-03-15 22:13:43 +01:00
|
|
|
|
2016-04-20 19:27:32 +02:00
|
|
|
fd = open_tmpfile_unlinkable("/tmp", O_RDWR|O_CLOEXEC);
|
2015-03-15 22:13:43 +01:00
|
|
|
if (fd < 0)
|
|
|
|
return fd;
|
|
|
|
|
2020-03-31 11:29:37 +02:00
|
|
|
m->tmp = take_fdopen(&fd, "w+");
|
|
|
|
if (!m->tmp)
|
2015-03-15 22:13:43 +01:00
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-09-28 00:46:32 +02:00
|
|
|
static ssize_t request_reader_entries(
|
|
|
|
void *cls,
|
|
|
|
uint64_t pos,
|
|
|
|
char *buf,
|
|
|
|
size_t max) {
|
|
|
|
|
|
|
|
RequestMeta *m = cls;
|
|
|
|
int r;
|
|
|
|
size_t n, k;
|
|
|
|
|
|
|
|
assert(m);
|
|
|
|
assert(buf);
|
|
|
|
assert(max > 0);
|
|
|
|
assert(pos >= m->delta);
|
|
|
|
|
|
|
|
pos -= m->delta;
|
|
|
|
|
|
|
|
while (pos >= m->size) {
|
|
|
|
off_t sz;
|
|
|
|
|
|
|
|
/* End of this entry, so let's serialize the next
|
|
|
|
* one */
|
|
|
|
|
|
|
|
if (m->n_entries_set &&
|
|
|
|
m->n_entries <= 0)
|
|
|
|
return MHD_CONTENT_READER_END_OF_STREAM;
|
|
|
|
|
2012-10-10 01:57:06 +02:00
|
|
|
if (m->n_skip < 0)
|
|
|
|
r = sd_journal_previous_skip(m->journal, (uint64_t) -m->n_skip + 1);
|
|
|
|
else if (m->n_skip > 0)
|
2012-09-28 00:46:32 +02:00
|
|
|
r = sd_journal_next_skip(m->journal, (uint64_t) m->n_skip + 1);
|
|
|
|
else
|
|
|
|
r = sd_journal_next(m->journal);
|
|
|
|
|
|
|
|
if (r < 0) {
|
2014-11-28 13:19:16 +01:00
|
|
|
log_error_errno(r, "Failed to advance journal pointer: %m");
|
2012-09-28 00:46:32 +02:00
|
|
|
return MHD_CONTENT_READER_END_WITH_ERROR;
|
2012-10-09 01:31:27 +02:00
|
|
|
} else if (r == 0) {
|
|
|
|
|
|
|
|
if (m->follow) {
|
2015-12-10 22:13:24 +01:00
|
|
|
r = sd_journal_wait(m->journal, (uint64_t) JOURNAL_WAIT_TIMEOUT);
|
2012-10-09 01:31:27 +02:00
|
|
|
if (r < 0) {
|
2014-11-28 13:19:16 +01:00
|
|
|
log_error_errno(r, "Couldn't wait for journal event: %m");
|
2012-10-09 01:31:27 +02:00
|
|
|
return MHD_CONTENT_READER_END_WITH_ERROR;
|
|
|
|
}
|
2015-12-10 22:13:24 +01:00
|
|
|
if (r == SD_JOURNAL_NOP)
|
|
|
|
break;
|
2012-10-09 01:31:27 +02:00
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2012-09-28 00:46:32 +02:00
|
|
|
return MHD_CONTENT_READER_END_OF_STREAM;
|
2012-10-09 01:31:27 +02:00
|
|
|
}
|
2012-09-28 00:46:32 +02:00
|
|
|
|
2012-10-10 22:39:45 +02:00
|
|
|
if (m->discrete) {
|
|
|
|
assert(m->cursor);
|
|
|
|
|
|
|
|
r = sd_journal_test_cursor(m->journal, m->cursor);
|
|
|
|
if (r < 0) {
|
2014-11-28 13:19:16 +01:00
|
|
|
log_error_errno(r, "Failed to test cursor: %m");
|
2012-10-10 22:39:45 +02:00
|
|
|
return MHD_CONTENT_READER_END_WITH_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (r == 0)
|
|
|
|
return MHD_CONTENT_READER_END_OF_STREAM;
|
|
|
|
}
|
|
|
|
|
2012-09-28 00:46:32 +02:00
|
|
|
pos -= m->size;
|
|
|
|
m->delta += m->size;
|
|
|
|
|
|
|
|
if (m->n_entries_set)
|
|
|
|
m->n_entries -= 1;
|
|
|
|
|
|
|
|
m->n_skip = 0;
|
|
|
|
|
2015-03-15 22:13:43 +01:00
|
|
|
r = request_meta_ensure_tmp(m);
|
|
|
|
if (r < 0) {
|
|
|
|
log_error_errno(r, "Failed to create temporary file: %m");
|
|
|
|
return MHD_CONTENT_READER_END_WITH_ERROR;
|
2012-09-28 00:46:32 +02:00
|
|
|
}
|
|
|
|
|
2018-05-16 17:12:53 +02:00
|
|
|
r = show_journal_entry(m->tmp, m->journal, m->mode, 0, OUTPUT_FULL_WIDTH,
|
2018-01-27 13:00:09 +01:00
|
|
|
NULL, NULL, NULL);
|
2012-09-28 00:46:32 +02:00
|
|
|
if (r < 0) {
|
2014-11-28 13:19:16 +01:00
|
|
|
log_error_errno(r, "Failed to serialize item: %m");
|
2012-09-28 00:46:32 +02:00
|
|
|
return MHD_CONTENT_READER_END_WITH_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
sz = ftello(m->tmp);
|
|
|
|
if (sz == (off_t) -1) {
|
2014-11-28 19:29:59 +01:00
|
|
|
log_error_errno(errno, "Failed to retrieve file position: %m");
|
2012-09-28 00:46:32 +02:00
|
|
|
return MHD_CONTENT_READER_END_WITH_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
m->size = (uint64_t) sz;
|
|
|
|
}
|
|
|
|
|
2016-08-06 22:39:13 +02:00
|
|
|
if (m->tmp == NULL && m->follow)
|
|
|
|
return 0;
|
|
|
|
|
2012-09-28 00:46:32 +02:00
|
|
|
if (fseeko(m->tmp, pos, SEEK_SET) < 0) {
|
2014-11-28 19:29:59 +01:00
|
|
|
log_error_errno(errno, "Failed to seek to position: %m");
|
2012-09-28 00:46:32 +02:00
|
|
|
return MHD_CONTENT_READER_END_WITH_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
n = m->size - pos;
|
2015-12-10 22:13:24 +01:00
|
|
|
if (n < 1)
|
|
|
|
return 0;
|
2012-09-28 00:46:32 +02:00
|
|
|
if (n > max)
|
|
|
|
n = max;
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
k = fread(buf, 1, n, m->tmp);
|
|
|
|
if (k != n) {
|
2019-07-03 16:56:17 +02:00
|
|
|
log_error("Failed to read from file: %s", errno != 0 ? strerror_safe(errno) : "Premature EOF");
|
2012-09-28 00:46:32 +02:00
|
|
|
return MHD_CONTENT_READER_END_WITH_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (ssize_t) k;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int request_parse_accept(
|
|
|
|
RequestMeta *m,
|
|
|
|
struct MHD_Connection *connection) {
|
|
|
|
|
2012-10-13 13:08:17 +02:00
|
|
|
const char *header;
|
2012-09-28 00:46:32 +02:00
|
|
|
|
|
|
|
assert(m);
|
|
|
|
assert(connection);
|
|
|
|
|
2012-10-13 13:08:17 +02:00
|
|
|
header = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "Accept");
|
|
|
|
if (!header)
|
2012-09-28 00:46:32 +02:00
|
|
|
return 0;
|
|
|
|
|
2012-10-13 13:08:17 +02:00
|
|
|
if (streq(header, mime_types[OUTPUT_JSON]))
|
2012-09-28 00:46:32 +02:00
|
|
|
m->mode = OUTPUT_JSON;
|
2012-10-13 13:08:17 +02:00
|
|
|
else if (streq(header, mime_types[OUTPUT_JSON_SSE]))
|
2012-10-11 02:37:10 +02:00
|
|
|
m->mode = OUTPUT_JSON_SSE;
|
2018-07-23 20:22:30 +02:00
|
|
|
else if (streq(header, mime_types[OUTPUT_JSON_SEQ]))
|
|
|
|
m->mode = OUTPUT_JSON_SEQ;
|
2012-10-13 13:08:17 +02:00
|
|
|
else if (streq(header, mime_types[OUTPUT_EXPORT]))
|
2012-09-28 00:46:32 +02:00
|
|
|
m->mode = OUTPUT_EXPORT;
|
|
|
|
else
|
|
|
|
m->mode = OUTPUT_SHORT;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int request_parse_range(
|
|
|
|
RequestMeta *m,
|
|
|
|
struct MHD_Connection *connection) {
|
|
|
|
|
|
|
|
const char *range, *colon, *colon2;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(m);
|
|
|
|
assert(connection);
|
|
|
|
|
|
|
|
range = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "Range");
|
|
|
|
if (!range)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (!startswith(range, "entries="))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
range += 8;
|
|
|
|
range += strspn(range, WHITESPACE);
|
|
|
|
|
|
|
|
colon = strchr(range, ':');
|
|
|
|
if (!colon)
|
|
|
|
m->cursor = strdup(range);
|
|
|
|
else {
|
|
|
|
const char *p;
|
|
|
|
|
|
|
|
colon2 = strchr(colon + 1, ':');
|
|
|
|
if (colon2) {
|
2013-04-18 09:11:22 +02:00
|
|
|
_cleanup_free_ char *t;
|
2012-09-28 00:46:32 +02:00
|
|
|
|
|
|
|
t = strndup(colon + 1, colon2 - colon - 1);
|
|
|
|
if (!t)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
r = safe_atoi64(t, &m->n_skip);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
p = (colon2 ? colon2 : colon) + 1;
|
|
|
|
if (*p) {
|
|
|
|
r = safe_atou64(p, &m->n_entries);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
if (m->n_entries <= 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
m->n_entries_set = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
m->cursor = strndup(range, colon - range);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!m->cursor)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
m->cursor[strcspn(m->cursor, WHITESPACE)] = 0;
|
2015-09-08 23:03:38 +02:00
|
|
|
if (isempty(m->cursor))
|
2015-09-08 18:43:11 +02:00
|
|
|
m->cursor = mfree(m->cursor);
|
2012-09-28 00:46:32 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-06-30 09:56:10 +02:00
|
|
|
static mhd_result request_parse_arguments_iterator(
|
2012-10-09 01:17:29 +02:00
|
|
|
void *cls,
|
|
|
|
enum MHD_ValueKind kind,
|
|
|
|
const char *key,
|
|
|
|
const char *value) {
|
|
|
|
|
|
|
|
RequestMeta *m = cls;
|
|
|
|
_cleanup_free_ char *p = NULL;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(m);
|
|
|
|
|
|
|
|
if (isempty(key)) {
|
|
|
|
m->argument_parse_error = -EINVAL;
|
|
|
|
return MHD_NO;
|
|
|
|
}
|
|
|
|
|
2012-10-09 01:31:27 +02:00
|
|
|
if (streq(key, "follow")) {
|
|
|
|
if (isempty(value)) {
|
|
|
|
m->follow = true;
|
|
|
|
return MHD_YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = parse_boolean(value);
|
|
|
|
if (r < 0) {
|
|
|
|
m->argument_parse_error = r;
|
|
|
|
return MHD_NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
m->follow = r;
|
|
|
|
return MHD_YES;
|
|
|
|
}
|
|
|
|
|
2012-10-10 22:39:45 +02:00
|
|
|
if (streq(key, "discrete")) {
|
|
|
|
if (isempty(value)) {
|
|
|
|
m->discrete = true;
|
|
|
|
return MHD_YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = parse_boolean(value);
|
|
|
|
if (r < 0) {
|
|
|
|
m->argument_parse_error = r;
|
|
|
|
return MHD_NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
m->discrete = r;
|
|
|
|
return MHD_YES;
|
|
|
|
}
|
|
|
|
|
2012-10-18 22:31:58 +02:00
|
|
|
if (streq(key, "boot")) {
|
|
|
|
if (isempty(value))
|
|
|
|
r = true;
|
|
|
|
else {
|
|
|
|
r = parse_boolean(value);
|
|
|
|
if (r < 0) {
|
|
|
|
m->argument_parse_error = r;
|
|
|
|
return MHD_NO;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (r) {
|
|
|
|
char match[9 + 32 + 1] = "_BOOT_ID=";
|
|
|
|
sd_id128_t bid;
|
|
|
|
|
|
|
|
r = sd_id128_get_boot(&bid);
|
|
|
|
if (r < 0) {
|
2014-11-28 13:19:16 +01:00
|
|
|
log_error_errno(r, "Failed to get boot ID: %m");
|
2012-10-18 22:31:58 +02:00
|
|
|
return MHD_NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
sd_id128_to_string(bid, match + 9);
|
|
|
|
r = sd_journal_add_match(m->journal, match, sizeof(match)-1);
|
|
|
|
if (r < 0) {
|
|
|
|
m->argument_parse_error = r;
|
|
|
|
return MHD_NO;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return MHD_YES;
|
|
|
|
}
|
|
|
|
|
2016-10-23 17:43:27 +02:00
|
|
|
p = strjoin(key, "=", strempty(value));
|
2012-10-09 01:17:29 +02:00
|
|
|
if (!p) {
|
|
|
|
m->argument_parse_error = log_oom();
|
|
|
|
return MHD_NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = sd_journal_add_match(m->journal, p, 0);
|
|
|
|
if (r < 0) {
|
|
|
|
m->argument_parse_error = r;
|
|
|
|
return MHD_NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
return MHD_YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int request_parse_arguments(
|
|
|
|
RequestMeta *m,
|
|
|
|
struct MHD_Connection *connection) {
|
|
|
|
|
|
|
|
assert(m);
|
|
|
|
assert(connection);
|
|
|
|
|
|
|
|
m->argument_parse_error = 0;
|
|
|
|
MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND, request_parse_arguments_iterator, m);
|
|
|
|
|
|
|
|
return m->argument_parse_error;
|
|
|
|
}
|
|
|
|
|
2012-09-28 00:46:32 +02:00
|
|
|
static int request_handler_entries(
|
|
|
|
struct MHD_Connection *connection,
|
journal-gatewayd: allow pipelining
The request must not be answered immediately (at first call to
response_handler()), but on the second. This is also important
for authentication, which cannot be performed on the first call.
Before:
% wget -O/dev/null -S https://localhost:19531/
--2012-11-28 18:29:43-- https://localhost:19531/
Resolving localhost (localhost)... 127.0.0.1
Connecting to localhost (localhost)|127.0.0.1|:19531... connected.
HTTP request sent, awaiting response...
HTTP/1.1 301 Moved Permanently
Connection: close
Content-Length: 87
Location: /browse
Content-Type: text/html
Date: Wed, 28 Nov 2012 17:29:44 GMT
Location: /browse [following]
--2012-11-28 18:29:43-- https://localhost:19531/browse
Connecting to localhost (localhost)|127.0.0.1|:19531... connected.
HTTP request sent, awaiting response...
HTTP/1.1 200 OK
Connection: close
Content-Length: 23260
Content-Type: text/html
Date: Wed, 28 Nov 2012 17:29:44 GMT
Length: 23260 (23K) [text/html]
After:
% wget --no-check-certificate -O/dev/null -S https://localhost:19531/
--2012-11-28 18:30:05-- https://localhost:19531/
Resolving localhost (localhost)... 127.0.0.1
Connecting to localhost (localhost)|127.0.0.1|:19531... connected.
HTTP request sent, awaiting response...
HTTP/1.1 301 Moved Permanently
Content-Length: 87
Location: /browse
Content-Type: text/html
Date: Wed, 28 Nov 2012 17:30:05 GMT
Location: /browse [following]
--2012-11-28 18:30:05-- https://localhost:19531/browse
Reusing existing connection to localhost:19531.
HTTP request sent, awaiting response...
HTTP/1.1 200 OK
Content-Length: 23260
Content-Type: text/html
Date: Wed, 28 Nov 2012 17:30:06 GMT
Length: 23260 (23K) [text/html]
2012-11-28 18:32:01 +01:00
|
|
|
void *connection_cls) {
|
2012-09-28 00:46:32 +02:00
|
|
|
|
2018-12-07 12:13:10 +01:00
|
|
|
_cleanup_(MHD_destroy_responsep) struct MHD_Response *response = NULL;
|
journal-gatewayd: allow pipelining
The request must not be answered immediately (at first call to
response_handler()), but on the second. This is also important
for authentication, which cannot be performed on the first call.
Before:
% wget -O/dev/null -S https://localhost:19531/
--2012-11-28 18:29:43-- https://localhost:19531/
Resolving localhost (localhost)... 127.0.0.1
Connecting to localhost (localhost)|127.0.0.1|:19531... connected.
HTTP request sent, awaiting response...
HTTP/1.1 301 Moved Permanently
Connection: close
Content-Length: 87
Location: /browse
Content-Type: text/html
Date: Wed, 28 Nov 2012 17:29:44 GMT
Location: /browse [following]
--2012-11-28 18:29:43-- https://localhost:19531/browse
Connecting to localhost (localhost)|127.0.0.1|:19531... connected.
HTTP request sent, awaiting response...
HTTP/1.1 200 OK
Connection: close
Content-Length: 23260
Content-Type: text/html
Date: Wed, 28 Nov 2012 17:29:44 GMT
Length: 23260 (23K) [text/html]
After:
% wget --no-check-certificate -O/dev/null -S https://localhost:19531/
--2012-11-28 18:30:05-- https://localhost:19531/
Resolving localhost (localhost)... 127.0.0.1
Connecting to localhost (localhost)|127.0.0.1|:19531... connected.
HTTP request sent, awaiting response...
HTTP/1.1 301 Moved Permanently
Content-Length: 87
Location: /browse
Content-Type: text/html
Date: Wed, 28 Nov 2012 17:30:05 GMT
Location: /browse [following]
--2012-11-28 18:30:05-- https://localhost:19531/browse
Reusing existing connection to localhost:19531.
HTTP request sent, awaiting response...
HTTP/1.1 200 OK
Content-Length: 23260
Content-Type: text/html
Date: Wed, 28 Nov 2012 17:30:06 GMT
Length: 23260 (23K) [text/html]
2012-11-28 18:32:01 +01:00
|
|
|
RequestMeta *m = connection_cls;
|
2012-09-28 00:46:32 +02:00
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(connection);
|
journal-gatewayd: allow pipelining
The request must not be answered immediately (at first call to
response_handler()), but on the second. This is also important
for authentication, which cannot be performed on the first call.
Before:
% wget -O/dev/null -S https://localhost:19531/
--2012-11-28 18:29:43-- https://localhost:19531/
Resolving localhost (localhost)... 127.0.0.1
Connecting to localhost (localhost)|127.0.0.1|:19531... connected.
HTTP request sent, awaiting response...
HTTP/1.1 301 Moved Permanently
Connection: close
Content-Length: 87
Location: /browse
Content-Type: text/html
Date: Wed, 28 Nov 2012 17:29:44 GMT
Location: /browse [following]
--2012-11-28 18:29:43-- https://localhost:19531/browse
Connecting to localhost (localhost)|127.0.0.1|:19531... connected.
HTTP request sent, awaiting response...
HTTP/1.1 200 OK
Connection: close
Content-Length: 23260
Content-Type: text/html
Date: Wed, 28 Nov 2012 17:29:44 GMT
Length: 23260 (23K) [text/html]
After:
% wget --no-check-certificate -O/dev/null -S https://localhost:19531/
--2012-11-28 18:30:05-- https://localhost:19531/
Resolving localhost (localhost)... 127.0.0.1
Connecting to localhost (localhost)|127.0.0.1|:19531... connected.
HTTP request sent, awaiting response...
HTTP/1.1 301 Moved Permanently
Content-Length: 87
Location: /browse
Content-Type: text/html
Date: Wed, 28 Nov 2012 17:30:05 GMT
Location: /browse [following]
--2012-11-28 18:30:05-- https://localhost:19531/browse
Reusing existing connection to localhost:19531.
HTTP request sent, awaiting response...
HTTP/1.1 200 OK
Content-Length: 23260
Content-Type: text/html
Date: Wed, 28 Nov 2012 17:30:06 GMT
Length: 23260 (23K) [text/html]
2012-11-28 18:32:01 +01:00
|
|
|
assert(m);
|
2012-09-28 00:46:32 +02:00
|
|
|
|
|
|
|
r = open_journal(m);
|
|
|
|
if (r < 0)
|
2016-09-12 20:33:37 +02:00
|
|
|
return mhd_respondf(connection, r, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to open journal: %m");
|
2012-09-28 00:46:32 +02:00
|
|
|
|
|
|
|
if (request_parse_accept(m, connection) < 0)
|
2016-09-12 20:33:37 +02:00
|
|
|
return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to parse Accept header.");
|
2012-09-28 00:46:32 +02:00
|
|
|
|
|
|
|
if (request_parse_range(m, connection) < 0)
|
2016-09-12 20:33:37 +02:00
|
|
|
return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to parse Range header.");
|
2012-09-28 00:46:32 +02:00
|
|
|
|
2012-10-09 01:17:29 +02:00
|
|
|
if (request_parse_arguments(m, connection) < 0)
|
2016-09-12 20:33:37 +02:00
|
|
|
return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to parse URL arguments.");
|
2012-10-09 01:17:29 +02:00
|
|
|
|
2012-10-10 22:39:45 +02:00
|
|
|
if (m->discrete) {
|
|
|
|
if (!m->cursor)
|
2016-09-12 20:33:37 +02:00
|
|
|
return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Discrete seeks require a cursor specification.");
|
2012-10-10 22:39:45 +02:00
|
|
|
|
|
|
|
m->n_entries = 1;
|
|
|
|
m->n_entries_set = true;
|
|
|
|
}
|
|
|
|
|
2012-09-28 00:46:32 +02:00
|
|
|
if (m->cursor)
|
|
|
|
r = sd_journal_seek_cursor(m->journal, m->cursor);
|
|
|
|
else if (m->n_skip >= 0)
|
|
|
|
r = sd_journal_seek_head(m->journal);
|
|
|
|
else if (m->n_skip < 0)
|
|
|
|
r = sd_journal_seek_tail(m->journal);
|
|
|
|
if (r < 0)
|
2016-09-12 20:33:37 +02:00
|
|
|
return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to seek in journal.");
|
2012-09-28 00:46:32 +02:00
|
|
|
|
|
|
|
response = MHD_create_response_from_callback(MHD_SIZE_UNKNOWN, 4*1024, request_reader_entries, m, NULL);
|
|
|
|
if (!response)
|
|
|
|
return respond_oom(connection);
|
|
|
|
|
|
|
|
MHD_add_response_header(response, "Content-Type", mime_types[m->mode]);
|
2018-12-07 12:13:10 +01:00
|
|
|
return MHD_queue_response(connection, MHD_HTTP_OK, response);
|
2012-09-28 00:46:32 +02:00
|
|
|
}
|
|
|
|
|
2012-10-18 22:31:27 +02:00
|
|
|
static int output_field(FILE *f, OutputMode m, const char *d, size_t l) {
|
|
|
|
const char *eq;
|
|
|
|
size_t j;
|
|
|
|
|
|
|
|
eq = memchr(d, '=', l);
|
|
|
|
if (!eq)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
j = l - (eq - d + 1);
|
|
|
|
|
|
|
|
if (m == OUTPUT_JSON) {
|
|
|
|
fprintf(f, "{ \"%.*s\" : ", (int) (eq - d), d);
|
|
|
|
json_escape(f, eq+1, j, OUTPUT_FULL_WIDTH);
|
|
|
|
fputs(" }\n", f);
|
|
|
|
} else {
|
|
|
|
fwrite(eq+1, 1, j, f);
|
|
|
|
fputc('\n', f);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t request_reader_fields(
|
|
|
|
void *cls,
|
|
|
|
uint64_t pos,
|
|
|
|
char *buf,
|
|
|
|
size_t max) {
|
|
|
|
|
|
|
|
RequestMeta *m = cls;
|
|
|
|
int r;
|
|
|
|
size_t n, k;
|
|
|
|
|
|
|
|
assert(m);
|
|
|
|
assert(buf);
|
|
|
|
assert(max > 0);
|
|
|
|
assert(pos >= m->delta);
|
|
|
|
|
|
|
|
pos -= m->delta;
|
|
|
|
|
|
|
|
while (pos >= m->size) {
|
|
|
|
off_t sz;
|
|
|
|
const void *d;
|
|
|
|
size_t l;
|
|
|
|
|
|
|
|
/* End of this field, so let's serialize the next
|
|
|
|
* one */
|
|
|
|
|
|
|
|
r = sd_journal_enumerate_unique(m->journal, &d, &l);
|
|
|
|
if (r < 0) {
|
2014-11-28 13:19:16 +01:00
|
|
|
log_error_errno(r, "Failed to advance field index: %m");
|
2012-10-18 22:31:27 +02:00
|
|
|
return MHD_CONTENT_READER_END_WITH_ERROR;
|
|
|
|
} else if (r == 0)
|
|
|
|
return MHD_CONTENT_READER_END_OF_STREAM;
|
|
|
|
|
|
|
|
pos -= m->size;
|
|
|
|
m->delta += m->size;
|
|
|
|
|
2015-03-15 22:13:43 +01:00
|
|
|
r = request_meta_ensure_tmp(m);
|
|
|
|
if (r < 0) {
|
|
|
|
log_error_errno(r, "Failed to create temporary file: %m");
|
|
|
|
return MHD_CONTENT_READER_END_WITH_ERROR;
|
2012-10-18 22:31:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
r = output_field(m->tmp, m->mode, d, l);
|
|
|
|
if (r < 0) {
|
2014-11-28 13:19:16 +01:00
|
|
|
log_error_errno(r, "Failed to serialize item: %m");
|
2012-10-18 22:31:27 +02:00
|
|
|
return MHD_CONTENT_READER_END_WITH_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
sz = ftello(m->tmp);
|
|
|
|
if (sz == (off_t) -1) {
|
2014-11-28 19:29:59 +01:00
|
|
|
log_error_errno(errno, "Failed to retrieve file position: %m");
|
2012-10-18 22:31:27 +02:00
|
|
|
return MHD_CONTENT_READER_END_WITH_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
m->size = (uint64_t) sz;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fseeko(m->tmp, pos, SEEK_SET) < 0) {
|
2014-11-28 19:29:59 +01:00
|
|
|
log_error_errno(errno, "Failed to seek to position: %m");
|
2012-10-18 22:31:27 +02:00
|
|
|
return MHD_CONTENT_READER_END_WITH_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
n = m->size - pos;
|
|
|
|
if (n > max)
|
|
|
|
n = max;
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
k = fread(buf, 1, n, m->tmp);
|
|
|
|
if (k != n) {
|
2019-07-03 16:56:17 +02:00
|
|
|
log_error("Failed to read from file: %s", errno != 0 ? strerror_safe(errno) : "Premature EOF");
|
2012-10-18 22:31:27 +02:00
|
|
|
return MHD_CONTENT_READER_END_WITH_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (ssize_t) k;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int request_handler_fields(
|
|
|
|
struct MHD_Connection *connection,
|
|
|
|
const char *field,
|
|
|
|
void *connection_cls) {
|
|
|
|
|
2018-12-07 12:13:10 +01:00
|
|
|
_cleanup_(MHD_destroy_responsep) struct MHD_Response *response = NULL;
|
journal-gatewayd: allow pipelining
The request must not be answered immediately (at first call to
response_handler()), but on the second. This is also important
for authentication, which cannot be performed on the first call.
Before:
% wget -O/dev/null -S https://localhost:19531/
--2012-11-28 18:29:43-- https://localhost:19531/
Resolving localhost (localhost)... 127.0.0.1
Connecting to localhost (localhost)|127.0.0.1|:19531... connected.
HTTP request sent, awaiting response...
HTTP/1.1 301 Moved Permanently
Connection: close
Content-Length: 87
Location: /browse
Content-Type: text/html
Date: Wed, 28 Nov 2012 17:29:44 GMT
Location: /browse [following]
--2012-11-28 18:29:43-- https://localhost:19531/browse
Connecting to localhost (localhost)|127.0.0.1|:19531... connected.
HTTP request sent, awaiting response...
HTTP/1.1 200 OK
Connection: close
Content-Length: 23260
Content-Type: text/html
Date: Wed, 28 Nov 2012 17:29:44 GMT
Length: 23260 (23K) [text/html]
After:
% wget --no-check-certificate -O/dev/null -S https://localhost:19531/
--2012-11-28 18:30:05-- https://localhost:19531/
Resolving localhost (localhost)... 127.0.0.1
Connecting to localhost (localhost)|127.0.0.1|:19531... connected.
HTTP request sent, awaiting response...
HTTP/1.1 301 Moved Permanently
Content-Length: 87
Location: /browse
Content-Type: text/html
Date: Wed, 28 Nov 2012 17:30:05 GMT
Location: /browse [following]
--2012-11-28 18:30:05-- https://localhost:19531/browse
Reusing existing connection to localhost:19531.
HTTP request sent, awaiting response...
HTTP/1.1 200 OK
Content-Length: 23260
Content-Type: text/html
Date: Wed, 28 Nov 2012 17:30:06 GMT
Length: 23260 (23K) [text/html]
2012-11-28 18:32:01 +01:00
|
|
|
RequestMeta *m = connection_cls;
|
2012-10-18 22:31:27 +02:00
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(connection);
|
journal-gatewayd: allow pipelining
The request must not be answered immediately (at first call to
response_handler()), but on the second. This is also important
for authentication, which cannot be performed on the first call.
Before:
% wget -O/dev/null -S https://localhost:19531/
--2012-11-28 18:29:43-- https://localhost:19531/
Resolving localhost (localhost)... 127.0.0.1
Connecting to localhost (localhost)|127.0.0.1|:19531... connected.
HTTP request sent, awaiting response...
HTTP/1.1 301 Moved Permanently
Connection: close
Content-Length: 87
Location: /browse
Content-Type: text/html
Date: Wed, 28 Nov 2012 17:29:44 GMT
Location: /browse [following]
--2012-11-28 18:29:43-- https://localhost:19531/browse
Connecting to localhost (localhost)|127.0.0.1|:19531... connected.
HTTP request sent, awaiting response...
HTTP/1.1 200 OK
Connection: close
Content-Length: 23260
Content-Type: text/html
Date: Wed, 28 Nov 2012 17:29:44 GMT
Length: 23260 (23K) [text/html]
After:
% wget --no-check-certificate -O/dev/null -S https://localhost:19531/
--2012-11-28 18:30:05-- https://localhost:19531/
Resolving localhost (localhost)... 127.0.0.1
Connecting to localhost (localhost)|127.0.0.1|:19531... connected.
HTTP request sent, awaiting response...
HTTP/1.1 301 Moved Permanently
Content-Length: 87
Location: /browse
Content-Type: text/html
Date: Wed, 28 Nov 2012 17:30:05 GMT
Location: /browse [following]
--2012-11-28 18:30:05-- https://localhost:19531/browse
Reusing existing connection to localhost:19531.
HTTP request sent, awaiting response...
HTTP/1.1 200 OK
Content-Length: 23260
Content-Type: text/html
Date: Wed, 28 Nov 2012 17:30:06 GMT
Length: 23260 (23K) [text/html]
2012-11-28 18:32:01 +01:00
|
|
|
assert(m);
|
2012-10-18 22:31:27 +02:00
|
|
|
|
|
|
|
r = open_journal(m);
|
|
|
|
if (r < 0)
|
2016-09-12 20:33:37 +02:00
|
|
|
return mhd_respondf(connection, r, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to open journal: %m");
|
2012-10-18 22:31:27 +02:00
|
|
|
|
|
|
|
if (request_parse_accept(m, connection) < 0)
|
2016-09-12 20:33:37 +02:00
|
|
|
return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to parse Accept header.");
|
2012-10-18 22:31:27 +02:00
|
|
|
|
|
|
|
r = sd_journal_query_unique(m->journal, field);
|
|
|
|
if (r < 0)
|
2016-09-12 20:33:37 +02:00
|
|
|
return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, "Failed to query unique fields.");
|
2012-10-18 22:31:27 +02:00
|
|
|
|
|
|
|
response = MHD_create_response_from_callback(MHD_SIZE_UNKNOWN, 4*1024, request_reader_fields, m, NULL);
|
|
|
|
if (!response)
|
|
|
|
return respond_oom(connection);
|
|
|
|
|
|
|
|
MHD_add_response_header(response, "Content-Type", mime_types[m->mode == OUTPUT_JSON ? OUTPUT_JSON : OUTPUT_SHORT]);
|
2018-12-07 12:13:10 +01:00
|
|
|
return MHD_queue_response(connection, MHD_HTTP_OK, response);
|
2012-10-18 22:31:27 +02:00
|
|
|
}
|
|
|
|
|
2012-09-28 00:46:32 +02:00
|
|
|
static int request_handler_redirect(
|
|
|
|
struct MHD_Connection *connection,
|
|
|
|
const char *target) {
|
|
|
|
|
|
|
|
char *page;
|
2018-12-07 12:13:10 +01:00
|
|
|
_cleanup_(MHD_destroy_responsep) struct MHD_Response *response = NULL;
|
2012-09-28 00:46:32 +02:00
|
|
|
|
|
|
|
assert(connection);
|
2012-10-01 09:53:33 +02:00
|
|
|
assert(target);
|
2012-09-28 00:46:32 +02:00
|
|
|
|
|
|
|
if (asprintf(&page, "<html><body>Please continue to the <a href=\"%s\">journal browser</a>.</body></html>", target) < 0)
|
|
|
|
return respond_oom(connection);
|
|
|
|
|
|
|
|
response = MHD_create_response_from_buffer(strlen(page), page, MHD_RESPMEM_MUST_FREE);
|
|
|
|
if (!response) {
|
|
|
|
free(page);
|
|
|
|
return respond_oom(connection);
|
|
|
|
}
|
|
|
|
|
|
|
|
MHD_add_response_header(response, "Content-Type", "text/html");
|
|
|
|
MHD_add_response_header(response, "Location", target);
|
2018-12-07 12:13:10 +01:00
|
|
|
return MHD_queue_response(connection, MHD_HTTP_MOVED_PERMANENTLY, response);
|
2012-09-28 00:46:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int request_handler_file(
|
|
|
|
struct MHD_Connection *connection,
|
|
|
|
const char *path,
|
|
|
|
const char *mime_type) {
|
|
|
|
|
2018-12-07 12:13:10 +01:00
|
|
|
_cleanup_(MHD_destroy_responsep) struct MHD_Response *response = NULL;
|
2012-09-28 00:46:32 +02:00
|
|
|
_cleanup_close_ int fd = -1;
|
|
|
|
struct stat st;
|
|
|
|
|
|
|
|
assert(connection);
|
|
|
|
assert(path);
|
|
|
|
assert(mime_type);
|
|
|
|
|
|
|
|
fd = open(path, O_RDONLY|O_CLOEXEC);
|
|
|
|
if (fd < 0)
|
2016-09-12 20:33:37 +02:00
|
|
|
return mhd_respondf(connection, errno, MHD_HTTP_NOT_FOUND, "Failed to open file %s: %m", path);
|
2012-09-28 00:46:32 +02:00
|
|
|
|
|
|
|
if (fstat(fd, &st) < 0)
|
2016-09-12 20:33:37 +02:00
|
|
|
return mhd_respondf(connection, errno, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to stat file: %m");
|
2012-09-28 00:46:32 +02:00
|
|
|
|
2016-01-20 16:12:18 +01:00
|
|
|
response = MHD_create_response_from_fd_at_offset64(st.st_size, fd, 0);
|
2012-09-28 00:46:32 +02:00
|
|
|
if (!response)
|
|
|
|
return respond_oom(connection);
|
2018-12-07 12:13:10 +01:00
|
|
|
TAKE_FD(fd);
|
2012-09-28 00:46:32 +02:00
|
|
|
|
|
|
|
MHD_add_response_header(response, "Content-Type", mime_type);
|
2018-12-07 12:13:10 +01:00
|
|
|
return MHD_queue_response(connection, MHD_HTTP_OK, response);
|
2012-09-28 00:46:32 +02:00
|
|
|
}
|
|
|
|
|
2013-03-31 18:15:59 +02:00
|
|
|
static int get_virtualization(char **v) {
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
|
2014-02-19 17:47:11 +01:00
|
|
|
char *b = NULL;
|
2013-03-31 18:15:59 +02:00
|
|
|
int r;
|
|
|
|
|
2013-11-11 22:00:48 +01:00
|
|
|
r = sd_bus_default_system(&bus);
|
2013-03-31 18:15:59 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2013-11-07 22:17:19 +01:00
|
|
|
r = sd_bus_get_property_string(
|
2013-03-31 18:15:59 +02:00
|
|
|
bus,
|
|
|
|
"org.freedesktop.systemd1",
|
|
|
|
"/org/freedesktop/systemd1",
|
2013-04-05 04:15:39 +02:00
|
|
|
"org.freedesktop.systemd1.Manager",
|
2013-11-07 22:17:19 +01:00
|
|
|
"Virtualization",
|
|
|
|
NULL,
|
|
|
|
&b);
|
2013-03-31 18:15:59 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2013-11-07 22:17:19 +01:00
|
|
|
if (isempty(b)) {
|
|
|
|
free(b);
|
2013-03-31 18:15:59 +02:00
|
|
|
*v = NULL;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
*v = b;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2012-09-28 00:46:32 +02:00
|
|
|
static int request_handler_machine(
|
|
|
|
struct MHD_Connection *connection,
|
journal-gatewayd: allow pipelining
The request must not be answered immediately (at first call to
response_handler()), but on the second. This is also important
for authentication, which cannot be performed on the first call.
Before:
% wget -O/dev/null -S https://localhost:19531/
--2012-11-28 18:29:43-- https://localhost:19531/
Resolving localhost (localhost)... 127.0.0.1
Connecting to localhost (localhost)|127.0.0.1|:19531... connected.
HTTP request sent, awaiting response...
HTTP/1.1 301 Moved Permanently
Connection: close
Content-Length: 87
Location: /browse
Content-Type: text/html
Date: Wed, 28 Nov 2012 17:29:44 GMT
Location: /browse [following]
--2012-11-28 18:29:43-- https://localhost:19531/browse
Connecting to localhost (localhost)|127.0.0.1|:19531... connected.
HTTP request sent, awaiting response...
HTTP/1.1 200 OK
Connection: close
Content-Length: 23260
Content-Type: text/html
Date: Wed, 28 Nov 2012 17:29:44 GMT
Length: 23260 (23K) [text/html]
After:
% wget --no-check-certificate -O/dev/null -S https://localhost:19531/
--2012-11-28 18:30:05-- https://localhost:19531/
Resolving localhost (localhost)... 127.0.0.1
Connecting to localhost (localhost)|127.0.0.1|:19531... connected.
HTTP request sent, awaiting response...
HTTP/1.1 301 Moved Permanently
Content-Length: 87
Location: /browse
Content-Type: text/html
Date: Wed, 28 Nov 2012 17:30:05 GMT
Location: /browse [following]
--2012-11-28 18:30:05-- https://localhost:19531/browse
Reusing existing connection to localhost:19531.
HTTP request sent, awaiting response...
HTTP/1.1 200 OK
Content-Length: 23260
Content-Type: text/html
Date: Wed, 28 Nov 2012 17:30:06 GMT
Length: 23260 (23K) [text/html]
2012-11-28 18:32:01 +01:00
|
|
|
void *connection_cls) {
|
2012-09-28 00:46:32 +02:00
|
|
|
|
2018-12-07 12:13:10 +01:00
|
|
|
_cleanup_(MHD_destroy_responsep) struct MHD_Response *response = NULL;
|
journal-gatewayd: allow pipelining
The request must not be answered immediately (at first call to
response_handler()), but on the second. This is also important
for authentication, which cannot be performed on the first call.
Before:
% wget -O/dev/null -S https://localhost:19531/
--2012-11-28 18:29:43-- https://localhost:19531/
Resolving localhost (localhost)... 127.0.0.1
Connecting to localhost (localhost)|127.0.0.1|:19531... connected.
HTTP request sent, awaiting response...
HTTP/1.1 301 Moved Permanently
Connection: close
Content-Length: 87
Location: /browse
Content-Type: text/html
Date: Wed, 28 Nov 2012 17:29:44 GMT
Location: /browse [following]
--2012-11-28 18:29:43-- https://localhost:19531/browse
Connecting to localhost (localhost)|127.0.0.1|:19531... connected.
HTTP request sent, awaiting response...
HTTP/1.1 200 OK
Connection: close
Content-Length: 23260
Content-Type: text/html
Date: Wed, 28 Nov 2012 17:29:44 GMT
Length: 23260 (23K) [text/html]
After:
% wget --no-check-certificate -O/dev/null -S https://localhost:19531/
--2012-11-28 18:30:05-- https://localhost:19531/
Resolving localhost (localhost)... 127.0.0.1
Connecting to localhost (localhost)|127.0.0.1|:19531... connected.
HTTP request sent, awaiting response...
HTTP/1.1 301 Moved Permanently
Content-Length: 87
Location: /browse
Content-Type: text/html
Date: Wed, 28 Nov 2012 17:30:05 GMT
Location: /browse [following]
--2012-11-28 18:30:05-- https://localhost:19531/browse
Reusing existing connection to localhost:19531.
HTTP request sent, awaiting response...
HTTP/1.1 200 OK
Content-Length: 23260
Content-Type: text/html
Date: Wed, 28 Nov 2012 17:30:06 GMT
Length: 23260 (23K) [text/html]
2012-11-28 18:32:01 +01:00
|
|
|
RequestMeta *m = connection_cls;
|
2012-09-28 00:46:32 +02:00
|
|
|
int r;
|
|
|
|
_cleanup_free_ char* hostname = NULL, *os_name = NULL;
|
2015-03-27 12:02:49 +01:00
|
|
|
uint64_t cutoff_from = 0, cutoff_to = 0, usage = 0;
|
2012-09-28 00:46:32 +02:00
|
|
|
sd_id128_t mid, bid;
|
2018-12-07 12:13:10 +01:00
|
|
|
_cleanup_free_ char *v = NULL, *json = NULL;
|
2012-09-28 00:46:32 +02:00
|
|
|
|
|
|
|
assert(connection);
|
journal-gatewayd: allow pipelining
The request must not be answered immediately (at first call to
response_handler()), but on the second. This is also important
for authentication, which cannot be performed on the first call.
Before:
% wget -O/dev/null -S https://localhost:19531/
--2012-11-28 18:29:43-- https://localhost:19531/
Resolving localhost (localhost)... 127.0.0.1
Connecting to localhost (localhost)|127.0.0.1|:19531... connected.
HTTP request sent, awaiting response...
HTTP/1.1 301 Moved Permanently
Connection: close
Content-Length: 87
Location: /browse
Content-Type: text/html
Date: Wed, 28 Nov 2012 17:29:44 GMT
Location: /browse [following]
--2012-11-28 18:29:43-- https://localhost:19531/browse
Connecting to localhost (localhost)|127.0.0.1|:19531... connected.
HTTP request sent, awaiting response...
HTTP/1.1 200 OK
Connection: close
Content-Length: 23260
Content-Type: text/html
Date: Wed, 28 Nov 2012 17:29:44 GMT
Length: 23260 (23K) [text/html]
After:
% wget --no-check-certificate -O/dev/null -S https://localhost:19531/
--2012-11-28 18:30:05-- https://localhost:19531/
Resolving localhost (localhost)... 127.0.0.1
Connecting to localhost (localhost)|127.0.0.1|:19531... connected.
HTTP request sent, awaiting response...
HTTP/1.1 301 Moved Permanently
Content-Length: 87
Location: /browse
Content-Type: text/html
Date: Wed, 28 Nov 2012 17:30:05 GMT
Location: /browse [following]
--2012-11-28 18:30:05-- https://localhost:19531/browse
Reusing existing connection to localhost:19531.
HTTP request sent, awaiting response...
HTTP/1.1 200 OK
Content-Length: 23260
Content-Type: text/html
Date: Wed, 28 Nov 2012 17:30:06 GMT
Length: 23260 (23K) [text/html]
2012-11-28 18:32:01 +01:00
|
|
|
assert(m);
|
2012-09-28 00:46:32 +02:00
|
|
|
|
|
|
|
r = open_journal(m);
|
|
|
|
if (r < 0)
|
2016-09-12 20:33:37 +02:00
|
|
|
return mhd_respondf(connection, r, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to open journal: %m");
|
2012-09-28 00:46:32 +02:00
|
|
|
|
|
|
|
r = sd_id128_get_machine(&mid);
|
|
|
|
if (r < 0)
|
2016-09-12 20:33:37 +02:00
|
|
|
return mhd_respondf(connection, r, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to determine machine ID: %m");
|
2012-09-28 00:46:32 +02:00
|
|
|
|
|
|
|
r = sd_id128_get_boot(&bid);
|
|
|
|
if (r < 0)
|
2016-09-12 20:33:37 +02:00
|
|
|
return mhd_respondf(connection, r, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to determine boot ID: %m");
|
2012-09-28 00:46:32 +02:00
|
|
|
|
|
|
|
hostname = gethostname_malloc();
|
|
|
|
if (!hostname)
|
|
|
|
return respond_oom(connection);
|
|
|
|
|
|
|
|
r = sd_journal_get_usage(m->journal, &usage);
|
|
|
|
if (r < 0)
|
2016-09-25 03:46:48 +02:00
|
|
|
return mhd_respondf(connection, r, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to determine disk usage: %m");
|
2012-09-28 00:46:32 +02:00
|
|
|
|
|
|
|
r = sd_journal_get_cutoff_realtime_usec(m->journal, &cutoff_from, &cutoff_to);
|
|
|
|
if (r < 0)
|
2016-09-25 03:46:48 +02:00
|
|
|
return mhd_respondf(connection, r, MHD_HTTP_INTERNAL_SERVER_ERROR, "Failed to determine disk usage: %m");
|
2012-09-28 00:46:32 +02:00
|
|
|
|
2018-03-26 16:32:40 +02:00
|
|
|
(void) parse_os_release(NULL, "PRETTY_NAME", &os_name, NULL);
|
|
|
|
(void) get_virtualization(&v);
|
2012-09-28 00:46:32 +02:00
|
|
|
|
|
|
|
r = asprintf(&json,
|
|
|
|
"{ \"machine_id\" : \"" SD_ID128_FORMAT_STR "\","
|
|
|
|
"\"boot_id\" : \"" SD_ID128_FORMAT_STR "\","
|
|
|
|
"\"hostname\" : \"%s\","
|
|
|
|
"\"os_pretty_name\" : \"%s\","
|
|
|
|
"\"virtualization\" : \"%s\","
|
2013-06-06 00:44:16 +02:00
|
|
|
"\"usage\" : \"%"PRIu64"\","
|
|
|
|
"\"cutoff_from_realtime\" : \"%"PRIu64"\","
|
|
|
|
"\"cutoff_to_realtime\" : \"%"PRIu64"\" }\n",
|
2012-09-28 00:46:32 +02:00
|
|
|
SD_ID128_FORMAT_VAL(mid),
|
|
|
|
SD_ID128_FORMAT_VAL(bid),
|
2015-07-28 04:36:36 +02:00
|
|
|
hostname_cleanup(hostname),
|
2012-09-28 00:46:32 +02:00
|
|
|
os_name ? os_name : "Linux",
|
2013-03-31 18:15:59 +02:00
|
|
|
v ? v : "bare",
|
2013-06-06 00:44:16 +02:00
|
|
|
usage,
|
|
|
|
cutoff_from,
|
|
|
|
cutoff_to);
|
2012-09-28 00:46:32 +02:00
|
|
|
if (r < 0)
|
|
|
|
return respond_oom(connection);
|
|
|
|
|
|
|
|
response = MHD_create_response_from_buffer(strlen(json), json, MHD_RESPMEM_MUST_FREE);
|
2018-12-07 12:13:10 +01:00
|
|
|
if (!response)
|
2012-09-28 00:46:32 +02:00
|
|
|
return respond_oom(connection);
|
2018-12-07 12:13:10 +01:00
|
|
|
TAKE_PTR(json);
|
2012-09-28 00:46:32 +02:00
|
|
|
|
|
|
|
MHD_add_response_header(response, "Content-Type", "application/json");
|
2018-12-07 12:13:10 +01:00
|
|
|
return MHD_queue_response(connection, MHD_HTTP_OK, response);
|
2012-09-28 00:46:32 +02:00
|
|
|
}
|
|
|
|
|
2020-06-30 09:56:10 +02:00
|
|
|
static mhd_result request_handler(
|
2012-09-28 00:46:32 +02:00
|
|
|
void *cls,
|
|
|
|
struct MHD_Connection *connection,
|
|
|
|
const char *url,
|
|
|
|
const char *method,
|
|
|
|
const char *version,
|
|
|
|
const char *upload_data,
|
|
|
|
size_t *upload_data_size,
|
|
|
|
void **connection_cls) {
|
2012-12-01 11:12:05 +01:00
|
|
|
int r, code;
|
2012-09-28 00:46:32 +02:00
|
|
|
|
|
|
|
assert(connection);
|
journal-gatewayd: allow pipelining
The request must not be answered immediately (at first call to
response_handler()), but on the second. This is also important
for authentication, which cannot be performed on the first call.
Before:
% wget -O/dev/null -S https://localhost:19531/
--2012-11-28 18:29:43-- https://localhost:19531/
Resolving localhost (localhost)... 127.0.0.1
Connecting to localhost (localhost)|127.0.0.1|:19531... connected.
HTTP request sent, awaiting response...
HTTP/1.1 301 Moved Permanently
Connection: close
Content-Length: 87
Location: /browse
Content-Type: text/html
Date: Wed, 28 Nov 2012 17:29:44 GMT
Location: /browse [following]
--2012-11-28 18:29:43-- https://localhost:19531/browse
Connecting to localhost (localhost)|127.0.0.1|:19531... connected.
HTTP request sent, awaiting response...
HTTP/1.1 200 OK
Connection: close
Content-Length: 23260
Content-Type: text/html
Date: Wed, 28 Nov 2012 17:29:44 GMT
Length: 23260 (23K) [text/html]
After:
% wget --no-check-certificate -O/dev/null -S https://localhost:19531/
--2012-11-28 18:30:05-- https://localhost:19531/
Resolving localhost (localhost)... 127.0.0.1
Connecting to localhost (localhost)|127.0.0.1|:19531... connected.
HTTP request sent, awaiting response...
HTTP/1.1 301 Moved Permanently
Content-Length: 87
Location: /browse
Content-Type: text/html
Date: Wed, 28 Nov 2012 17:30:05 GMT
Location: /browse [following]
--2012-11-28 18:30:05-- https://localhost:19531/browse
Reusing existing connection to localhost:19531.
HTTP request sent, awaiting response...
HTTP/1.1 200 OK
Content-Length: 23260
Content-Type: text/html
Date: Wed, 28 Nov 2012 17:30:06 GMT
Length: 23260 (23K) [text/html]
2012-11-28 18:32:01 +01:00
|
|
|
assert(connection_cls);
|
2012-09-28 00:46:32 +02:00
|
|
|
assert(url);
|
|
|
|
assert(method);
|
|
|
|
|
|
|
|
if (!streq(method, "GET"))
|
2016-09-12 20:33:37 +02:00
|
|
|
return mhd_respond(connection, MHD_HTTP_NOT_ACCEPTABLE, "Unsupported method.");
|
2013-01-17 07:53:01 +01:00
|
|
|
|
journal-gatewayd: allow pipelining
The request must not be answered immediately (at first call to
response_handler()), but on the second. This is also important
for authentication, which cannot be performed on the first call.
Before:
% wget -O/dev/null -S https://localhost:19531/
--2012-11-28 18:29:43-- https://localhost:19531/
Resolving localhost (localhost)... 127.0.0.1
Connecting to localhost (localhost)|127.0.0.1|:19531... connected.
HTTP request sent, awaiting response...
HTTP/1.1 301 Moved Permanently
Connection: close
Content-Length: 87
Location: /browse
Content-Type: text/html
Date: Wed, 28 Nov 2012 17:29:44 GMT
Location: /browse [following]
--2012-11-28 18:29:43-- https://localhost:19531/browse
Connecting to localhost (localhost)|127.0.0.1|:19531... connected.
HTTP request sent, awaiting response...
HTTP/1.1 200 OK
Connection: close
Content-Length: 23260
Content-Type: text/html
Date: Wed, 28 Nov 2012 17:29:44 GMT
Length: 23260 (23K) [text/html]
After:
% wget --no-check-certificate -O/dev/null -S https://localhost:19531/
--2012-11-28 18:30:05-- https://localhost:19531/
Resolving localhost (localhost)... 127.0.0.1
Connecting to localhost (localhost)|127.0.0.1|:19531... connected.
HTTP request sent, awaiting response...
HTTP/1.1 301 Moved Permanently
Content-Length: 87
Location: /browse
Content-Type: text/html
Date: Wed, 28 Nov 2012 17:30:05 GMT
Location: /browse [following]
--2012-11-28 18:30:05-- https://localhost:19531/browse
Reusing existing connection to localhost:19531.
HTTP request sent, awaiting response...
HTTP/1.1 200 OK
Content-Length: 23260
Content-Type: text/html
Date: Wed, 28 Nov 2012 17:30:06 GMT
Length: 23260 (23K) [text/html]
2012-11-28 18:32:01 +01:00
|
|
|
if (!*connection_cls) {
|
|
|
|
if (!request_meta(connection_cls))
|
|
|
|
return respond_oom(connection);
|
|
|
|
return MHD_YES;
|
|
|
|
}
|
|
|
|
|
2015-01-05 00:52:47 +01:00
|
|
|
if (arg_trust_pem) {
|
2014-06-22 19:36:31 +02:00
|
|
|
r = check_permissions(connection, &code, NULL);
|
2012-12-01 11:12:05 +01:00
|
|
|
if (r < 0)
|
|
|
|
return code;
|
|
|
|
}
|
|
|
|
|
2012-09-28 00:46:32 +02:00
|
|
|
if (streq(url, "/"))
|
|
|
|
return request_handler_redirect(connection, "/browse");
|
|
|
|
|
|
|
|
if (streq(url, "/entries"))
|
journal-gatewayd: allow pipelining
The request must not be answered immediately (at first call to
response_handler()), but on the second. This is also important
for authentication, which cannot be performed on the first call.
Before:
% wget -O/dev/null -S https://localhost:19531/
--2012-11-28 18:29:43-- https://localhost:19531/
Resolving localhost (localhost)... 127.0.0.1
Connecting to localhost (localhost)|127.0.0.1|:19531... connected.
HTTP request sent, awaiting response...
HTTP/1.1 301 Moved Permanently
Connection: close
Content-Length: 87
Location: /browse
Content-Type: text/html
Date: Wed, 28 Nov 2012 17:29:44 GMT
Location: /browse [following]
--2012-11-28 18:29:43-- https://localhost:19531/browse
Connecting to localhost (localhost)|127.0.0.1|:19531... connected.
HTTP request sent, awaiting response...
HTTP/1.1 200 OK
Connection: close
Content-Length: 23260
Content-Type: text/html
Date: Wed, 28 Nov 2012 17:29:44 GMT
Length: 23260 (23K) [text/html]
After:
% wget --no-check-certificate -O/dev/null -S https://localhost:19531/
--2012-11-28 18:30:05-- https://localhost:19531/
Resolving localhost (localhost)... 127.0.0.1
Connecting to localhost (localhost)|127.0.0.1|:19531... connected.
HTTP request sent, awaiting response...
HTTP/1.1 301 Moved Permanently
Content-Length: 87
Location: /browse
Content-Type: text/html
Date: Wed, 28 Nov 2012 17:30:05 GMT
Location: /browse [following]
--2012-11-28 18:30:05-- https://localhost:19531/browse
Reusing existing connection to localhost:19531.
HTTP request sent, awaiting response...
HTTP/1.1 200 OK
Content-Length: 23260
Content-Type: text/html
Date: Wed, 28 Nov 2012 17:30:06 GMT
Length: 23260 (23K) [text/html]
2012-11-28 18:32:01 +01:00
|
|
|
return request_handler_entries(connection, *connection_cls);
|
2012-09-28 00:46:32 +02:00
|
|
|
|
2012-10-18 22:31:27 +02:00
|
|
|
if (startswith(url, "/fields/"))
|
journal-gatewayd: allow pipelining
The request must not be answered immediately (at first call to
response_handler()), but on the second. This is also important
for authentication, which cannot be performed on the first call.
Before:
% wget -O/dev/null -S https://localhost:19531/
--2012-11-28 18:29:43-- https://localhost:19531/
Resolving localhost (localhost)... 127.0.0.1
Connecting to localhost (localhost)|127.0.0.1|:19531... connected.
HTTP request sent, awaiting response...
HTTP/1.1 301 Moved Permanently
Connection: close
Content-Length: 87
Location: /browse
Content-Type: text/html
Date: Wed, 28 Nov 2012 17:29:44 GMT
Location: /browse [following]
--2012-11-28 18:29:43-- https://localhost:19531/browse
Connecting to localhost (localhost)|127.0.0.1|:19531... connected.
HTTP request sent, awaiting response...
HTTP/1.1 200 OK
Connection: close
Content-Length: 23260
Content-Type: text/html
Date: Wed, 28 Nov 2012 17:29:44 GMT
Length: 23260 (23K) [text/html]
After:
% wget --no-check-certificate -O/dev/null -S https://localhost:19531/
--2012-11-28 18:30:05-- https://localhost:19531/
Resolving localhost (localhost)... 127.0.0.1
Connecting to localhost (localhost)|127.0.0.1|:19531... connected.
HTTP request sent, awaiting response...
HTTP/1.1 301 Moved Permanently
Content-Length: 87
Location: /browse
Content-Type: text/html
Date: Wed, 28 Nov 2012 17:30:05 GMT
Location: /browse [following]
--2012-11-28 18:30:05-- https://localhost:19531/browse
Reusing existing connection to localhost:19531.
HTTP request sent, awaiting response...
HTTP/1.1 200 OK
Content-Length: 23260
Content-Type: text/html
Date: Wed, 28 Nov 2012 17:30:06 GMT
Length: 23260 (23K) [text/html]
2012-11-28 18:32:01 +01:00
|
|
|
return request_handler_fields(connection, url + 8, *connection_cls);
|
2012-10-18 22:31:27 +02:00
|
|
|
|
2012-09-28 00:46:32 +02:00
|
|
|
if (streq(url, "/browse"))
|
|
|
|
return request_handler_file(connection, DOCUMENT_ROOT "/browse.html", "text/html");
|
|
|
|
|
|
|
|
if (streq(url, "/machine"))
|
journal-gatewayd: allow pipelining
The request must not be answered immediately (at first call to
response_handler()), but on the second. This is also important
for authentication, which cannot be performed on the first call.
Before:
% wget -O/dev/null -S https://localhost:19531/
--2012-11-28 18:29:43-- https://localhost:19531/
Resolving localhost (localhost)... 127.0.0.1
Connecting to localhost (localhost)|127.0.0.1|:19531... connected.
HTTP request sent, awaiting response...
HTTP/1.1 301 Moved Permanently
Connection: close
Content-Length: 87
Location: /browse
Content-Type: text/html
Date: Wed, 28 Nov 2012 17:29:44 GMT
Location: /browse [following]
--2012-11-28 18:29:43-- https://localhost:19531/browse
Connecting to localhost (localhost)|127.0.0.1|:19531... connected.
HTTP request sent, awaiting response...
HTTP/1.1 200 OK
Connection: close
Content-Length: 23260
Content-Type: text/html
Date: Wed, 28 Nov 2012 17:29:44 GMT
Length: 23260 (23K) [text/html]
After:
% wget --no-check-certificate -O/dev/null -S https://localhost:19531/
--2012-11-28 18:30:05-- https://localhost:19531/
Resolving localhost (localhost)... 127.0.0.1
Connecting to localhost (localhost)|127.0.0.1|:19531... connected.
HTTP request sent, awaiting response...
HTTP/1.1 301 Moved Permanently
Content-Length: 87
Location: /browse
Content-Type: text/html
Date: Wed, 28 Nov 2012 17:30:05 GMT
Location: /browse [following]
--2012-11-28 18:30:05-- https://localhost:19531/browse
Reusing existing connection to localhost:19531.
HTTP request sent, awaiting response...
HTTP/1.1 200 OK
Content-Length: 23260
Content-Type: text/html
Date: Wed, 28 Nov 2012 17:30:06 GMT
Length: 23260 (23K) [text/html]
2012-11-28 18:32:01 +01:00
|
|
|
return request_handler_machine(connection, *connection_cls);
|
2012-09-28 00:46:32 +02:00
|
|
|
|
2016-09-12 20:33:37 +02:00
|
|
|
return mhd_respond(connection, MHD_HTTP_NOT_FOUND, "Not found.");
|
2012-09-28 00:46:32 +02:00
|
|
|
}
|
|
|
|
|
2018-08-09 10:32:31 +02:00
|
|
|
static int help(void) {
|
|
|
|
_cleanup_free_ char *link = NULL;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
r = terminal_urlify_man("systemd-journal-gatewayd.service", "8", &link);
|
|
|
|
if (r < 0)
|
|
|
|
return log_oom();
|
|
|
|
|
2013-01-18 07:41:01 +01:00
|
|
|
printf("%s [OPTIONS...] ...\n\n"
|
|
|
|
"HTTP server for journal events.\n\n"
|
|
|
|
" -h --help Show this help\n"
|
|
|
|
" --version Show package version\n"
|
2012-11-26 23:02:14 +01:00
|
|
|
" --cert=CERT.PEM Server certificate in PEM format\n"
|
|
|
|
" --key=KEY.PEM Server key in PEM format\n"
|
2016-08-09 06:35:07 +02:00
|
|
|
" --trust=CERT.PEM Certificate authority certificate in PEM format\n"
|
2018-08-09 10:32:31 +02:00
|
|
|
" -D --directory=PATH Serve journal files in directory\n"
|
|
|
|
"\nSee the %s for details.\n"
|
|
|
|
, program_invocation_short_name
|
|
|
|
, link
|
|
|
|
);
|
|
|
|
|
|
|
|
return 0;
|
2013-01-18 07:41:01 +01:00
|
|
|
}
|
|
|
|
|
2012-11-25 23:26:15 +01:00
|
|
|
static int parse_argv(int argc, char *argv[]) {
|
|
|
|
enum {
|
|
|
|
ARG_VERSION = 0x100,
|
|
|
|
ARG_KEY,
|
|
|
|
ARG_CERT,
|
2012-11-26 23:02:14 +01:00
|
|
|
ARG_TRUST,
|
2012-11-25 23:26:15 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
int r, c;
|
|
|
|
|
|
|
|
static const struct option options[] = {
|
2016-08-06 19:00:31 +02:00
|
|
|
{ "help", no_argument, NULL, 'h' },
|
|
|
|
{ "version", no_argument, NULL, ARG_VERSION },
|
|
|
|
{ "key", required_argument, NULL, ARG_KEY },
|
|
|
|
{ "cert", required_argument, NULL, ARG_CERT },
|
|
|
|
{ "trust", required_argument, NULL, ARG_TRUST },
|
2017-01-11 10:50:58 +01:00
|
|
|
{ "directory", required_argument, NULL, 'D' },
|
2013-11-06 18:28:39 +01:00
|
|
|
{}
|
2012-11-25 23:26:15 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
assert(argc >= 0);
|
|
|
|
assert(argv);
|
|
|
|
|
2017-01-29 20:33:37 +01:00
|
|
|
while ((c = getopt_long(argc, argv, "hD:", options, NULL)) >= 0)
|
2013-11-06 18:28:39 +01:00
|
|
|
|
2012-11-25 23:26:15 +01:00
|
|
|
switch(c) {
|
2013-11-06 18:28:39 +01:00
|
|
|
|
|
|
|
case 'h':
|
2018-08-09 10:32:31 +02:00
|
|
|
return help();
|
2013-11-06 18:28:39 +01:00
|
|
|
|
2012-11-25 23:26:15 +01:00
|
|
|
case ARG_VERSION:
|
2015-09-23 03:01:06 +02:00
|
|
|
return version();
|
2012-11-25 23:26:15 +01:00
|
|
|
|
|
|
|
case ARG_KEY:
|
2018-11-20 23:40:44 +01:00
|
|
|
if (arg_key_pem)
|
|
|
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
|
|
|
"Key file specified twice");
|
2020-11-04 16:21:26 +01:00
|
|
|
r = read_full_file_full(
|
2020-11-04 20:25:06 +01:00
|
|
|
AT_FDCWD, optarg, UINT64_MAX, SIZE_MAX,
|
2020-11-04 16:21:26 +01:00
|
|
|
READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET,
|
|
|
|
NULL,
|
|
|
|
&arg_key_pem, NULL);
|
2014-11-28 18:23:20 +01:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to read key file: %m");
|
2015-01-05 00:52:47 +01:00
|
|
|
assert(arg_key_pem);
|
2012-11-25 23:26:15 +01:00
|
|
|
break;
|
2012-09-28 00:46:32 +02:00
|
|
|
|
2012-11-25 23:26:15 +01:00
|
|
|
case ARG_CERT:
|
2018-11-20 23:40:44 +01:00
|
|
|
if (arg_cert_pem)
|
|
|
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
|
|
|
"Certificate file specified twice");
|
2020-11-04 20:25:06 +01:00
|
|
|
r = read_full_file_full(
|
|
|
|
AT_FDCWD, optarg, UINT64_MAX, SIZE_MAX,
|
|
|
|
READ_FULL_FILE_CONNECT_SOCKET,
|
|
|
|
NULL,
|
|
|
|
&arg_cert_pem, NULL);
|
2014-11-28 18:23:20 +01:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to read certificate file: %m");
|
2015-01-05 00:52:47 +01:00
|
|
|
assert(arg_cert_pem);
|
2012-11-25 23:26:15 +01:00
|
|
|
break;
|
|
|
|
|
2012-11-26 23:02:14 +01:00
|
|
|
case ARG_TRUST:
|
2017-10-03 10:41:51 +02:00
|
|
|
#if HAVE_GNUTLS
|
2018-11-20 23:40:44 +01:00
|
|
|
if (arg_trust_pem)
|
|
|
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
|
|
|
"CA certificate file specified twice");
|
2020-11-04 20:25:06 +01:00
|
|
|
r = read_full_file_full(
|
|
|
|
AT_FDCWD, optarg, UINT64_MAX, SIZE_MAX,
|
|
|
|
READ_FULL_FILE_CONNECT_SOCKET,
|
|
|
|
NULL,
|
|
|
|
&arg_trust_pem, NULL);
|
2014-11-28 18:23:20 +01:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to read CA certificate file: %m");
|
2015-01-05 00:52:47 +01:00
|
|
|
assert(arg_trust_pem);
|
2012-11-26 23:02:14 +01:00
|
|
|
break;
|
2012-12-01 11:12:05 +01:00
|
|
|
#else
|
2018-11-20 23:40:44 +01:00
|
|
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
2020-11-04 20:24:57 +01:00
|
|
|
"Option --trust= is not available.");
|
2012-12-01 11:12:05 +01:00
|
|
|
#endif
|
2016-08-06 19:00:31 +02:00
|
|
|
case 'D':
|
|
|
|
arg_directory = optarg;
|
|
|
|
break;
|
2012-11-26 23:02:14 +01:00
|
|
|
|
2012-11-25 23:26:15 +01:00
|
|
|
case '?':
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
default:
|
2013-11-06 18:28:39 +01:00
|
|
|
assert_not_reached("Unhandled option");
|
2012-11-25 23:26:15 +01:00
|
|
|
}
|
|
|
|
|
2018-11-20 23:40:44 +01:00
|
|
|
if (optind < argc)
|
|
|
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
|
|
|
"This program does not take arguments.");
|
2012-11-25 23:26:15 +01:00
|
|
|
|
2018-11-20 23:40:44 +01:00
|
|
|
if (!!arg_key_pem != !!arg_cert_pem)
|
|
|
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
|
|
|
"Certificate and key files must be specified together");
|
2012-09-28 00:46:32 +02:00
|
|
|
|
2018-11-20 23:40:44 +01:00
|
|
|
if (arg_trust_pem && !arg_key_pem)
|
|
|
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
|
|
|
"CA certificate can only be used with certificate file");
|
2012-11-26 23:02:14 +01:00
|
|
|
|
2012-11-25 23:26:15 +01:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2018-11-25 21:21:50 +01:00
|
|
|
static int run(int argc, char *argv[]) {
|
2018-11-25 21:13:00 +01:00
|
|
|
_cleanup_(MHD_stop_daemonp) struct MHD_Daemon *d = NULL;
|
2018-11-25 21:21:50 +01:00
|
|
|
struct MHD_OptionItem opts[] = {
|
|
|
|
{ MHD_OPTION_NOTIFY_COMPLETED,
|
|
|
|
(intptr_t) request_meta_free, NULL },
|
|
|
|
{ MHD_OPTION_EXTERNAL_LOGGER,
|
|
|
|
(intptr_t) microhttpd_logger, NULL },
|
|
|
|
{ MHD_OPTION_END, 0, NULL },
|
|
|
|
{ MHD_OPTION_END, 0, NULL },
|
|
|
|
{ MHD_OPTION_END, 0, NULL },
|
|
|
|
{ MHD_OPTION_END, 0, NULL },
|
|
|
|
{ MHD_OPTION_END, 0, NULL },
|
|
|
|
};
|
|
|
|
int opts_pos = 2;
|
|
|
|
|
|
|
|
/* We force MHD_USE_ITC here, in order to make sure
|
|
|
|
* libmicrohttpd doesn't use shutdown() on our listening
|
|
|
|
* socket, which would break socket re-activation. See
|
|
|
|
*
|
|
|
|
* https://lists.gnu.org/archive/html/libmicrohttpd/2015-09/msg00014.html
|
|
|
|
* https://github.com/systemd/systemd/pull/1286
|
|
|
|
*/
|
|
|
|
|
|
|
|
int flags =
|
|
|
|
MHD_USE_DEBUG |
|
|
|
|
MHD_USE_DUAL_STACK |
|
|
|
|
MHD_USE_ITC |
|
|
|
|
MHD_USE_POLL_INTERNAL_THREAD |
|
|
|
|
MHD_USE_THREAD_PER_CONNECTION;
|
2012-11-25 23:26:15 +01:00
|
|
|
int r, n;
|
|
|
|
|
2018-11-20 11:18:22 +01:00
|
|
|
log_setup_service();
|
2012-09-28 00:46:32 +02:00
|
|
|
|
2012-11-25 23:26:15 +01:00
|
|
|
r = parse_argv(argc, argv);
|
2018-11-25 21:21:50 +01:00
|
|
|
if (r <= 0)
|
|
|
|
return r;
|
2012-11-25 23:26:15 +01:00
|
|
|
|
2015-01-05 00:52:47 +01:00
|
|
|
sigbus_install();
|
|
|
|
|
2015-03-14 03:07:45 +01:00
|
|
|
r = setup_gnutls_logger(NULL);
|
|
|
|
if (r < 0)
|
2018-11-25 21:21:50 +01:00
|
|
|
return r;
|
2012-11-28 23:08:35 +01:00
|
|
|
|
2012-09-28 00:46:32 +02:00
|
|
|
n = sd_listen_fds(1);
|
2018-11-25 21:21:50 +01:00
|
|
|
if (n < 0)
|
|
|
|
return log_error_errno(n, "Failed to determine passed sockets: %m");
|
|
|
|
if (n > 1)
|
|
|
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Can't listen on more than one socket.");
|
|
|
|
|
|
|
|
if (n == 1)
|
|
|
|
opts[opts_pos++] = (struct MHD_OptionItem)
|
|
|
|
{ MHD_OPTION_LISTEN_SOCKET, SD_LISTEN_FDS_START };
|
|
|
|
|
|
|
|
if (arg_key_pem) {
|
|
|
|
assert(arg_cert_pem);
|
|
|
|
opts[opts_pos++] = (struct MHD_OptionItem)
|
|
|
|
{ MHD_OPTION_HTTPS_MEM_KEY, 0, arg_key_pem };
|
|
|
|
opts[opts_pos++] = (struct MHD_OptionItem)
|
|
|
|
{ MHD_OPTION_HTTPS_MEM_CERT, 0, arg_cert_pem };
|
|
|
|
flags |= MHD_USE_TLS;
|
2012-09-28 00:46:32 +02:00
|
|
|
}
|
|
|
|
|
2018-11-25 21:21:50 +01:00
|
|
|
if (arg_trust_pem) {
|
|
|
|
assert(flags & MHD_USE_TLS);
|
|
|
|
opts[opts_pos++] = (struct MHD_OptionItem)
|
|
|
|
{ MHD_OPTION_HTTPS_MEM_TRUST, 0, arg_trust_pem };
|
2012-09-28 00:46:32 +02:00
|
|
|
}
|
|
|
|
|
2018-11-25 21:21:50 +01:00
|
|
|
d = MHD_start_daemon(flags, 19531,
|
|
|
|
NULL, NULL,
|
|
|
|
request_handler, NULL,
|
|
|
|
MHD_OPTION_ARRAY, opts,
|
|
|
|
MHD_OPTION_END);
|
|
|
|
if (!d)
|
|
|
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to start daemon!");
|
2012-09-28 00:46:32 +02:00
|
|
|
|
2018-11-25 21:21:50 +01:00
|
|
|
pause();
|
2012-09-28 00:46:32 +02:00
|
|
|
|
2018-11-25 21:21:50 +01:00
|
|
|
return 0;
|
2012-09-28 00:46:32 +02:00
|
|
|
}
|
2018-11-25 21:21:50 +01:00
|
|
|
|
|
|
|
DEFINE_MAIN_FUNCTION(run);
|