2012-09-28 02:46:32 +04:00
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd .
Copyright 2012 Lennart Poettering
systemd is free software ; you can redistribute it and / or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation ; either version 2.1 of the License , or
( at your option ) any later version .
systemd is distributed in the hope that it will be useful , but
WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
Lesser General Public License for more details .
You should have received a copy of the GNU Lesser General Public License
along with systemd ; If not , see < http : //www.gnu.org/licenses/>.
* * */
# include <stdlib.h>
# include <string.h>
# include <unistd.h>
# include <fcntl.h>
2012-11-26 02:26:15 +04:00
# include <getopt.h>
2012-09-28 02:46:32 +04:00
# include <microhttpd.h>
2012-12-01 14:12:05 +04:00
# ifdef HAVE_GNUTLS
# include <gnutls/gnutls.h>
# endif
2012-09-28 02:46:32 +04:00
# include "sd-journal.h"
# include "sd-daemon.h"
2013-03-31 20:15:59 +04:00
# include "sd-bus.h"
2015-09-23 04:01:06 +03:00
2013-10-16 08:10:04 +04:00
# include "bus-util.h"
2015-09-23 04:01:06 +03:00
# include "fileio.h"
# include "hostname-util.h"
# include "log.h"
2012-09-28 02:46:32 +04:00
# include "logs-show.h"
2012-11-26 02:54:31 +04:00
# include "microhttpd-util.h"
2015-01-05 02:52:47 +03:00
# include "sigbus.h"
2015-09-23 04:01:06 +03:00
# include "util.h"
2012-09-28 02:46:32 +04:00
2015-01-05 02:52:47 +03:00
static char * arg_key_pem = NULL ;
static char * arg_cert_pem = NULL ;
static char * arg_trust_pem = NULL ;
2012-12-01 14:12:05 +04:00
2012-09-28 02:46:32 +04: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 03:17:29 +04:00
int argument_parse_error ;
2012-10-09 03:31:27 +04:00
bool follow ;
2012-10-11 00:39:45 +04:00
bool discrete ;
2012-10-19 00:31:27 +04:00
uint64_t n_fields ;
bool n_fields_set ;
2012-09-28 02:46:32 +04:00
} RequestMeta ;
static const char * const mime_types [ _OUTPUT_MODE_MAX ] = {
[ OUTPUT_SHORT ] = " text/plain " ,
[ OUTPUT_JSON ] = " application/json " ,
2012-10-11 04:37:10 +04:00
[ OUTPUT_JSON_SSE ] = " text/event-stream " ,
[ OUTPUT_EXPORT ] = " application/vnd.fdo.journal " ,
2012-09-28 02:46:32 +04:00
} ;
static RequestMeta * request_meta ( void * * connection_cls ) {
RequestMeta * m ;
2012-11-02 02:08:03 +04:00
assert ( connection_cls ) ;
2012-09-28 02:46:32 +04: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 11:45:30 +03:00
sd_journal_close ( m - > journal ) ;
2012-09-28 02:46:32 +04:00
2015-09-09 16:20:10 +03:00
safe_fclose ( m - > tmp ) ;
2012-09-28 02:46:32 +04:00
free ( m - > cursor ) ;
free ( m ) ;
}
static int open_journal ( RequestMeta * m ) {
assert ( m ) ;
if ( m - > journal )
return 0 ;
2013-06-05 06:31:05 +04:00
return sd_journal_open ( & m - > journal , SD_JOURNAL_LOCAL_ONLY | SD_JOURNAL_SYSTEM ) ;
2012-09-28 02:46:32 +04:00
}
2015-03-16 00:13:43 +03:00
static int request_meta_ensure_tmp ( RequestMeta * m ) {
if ( m - > tmp )
rewind ( m - > tmp ) ;
else {
int fd ;
fd = open_tmpfile ( " /tmp " , O_RDWR | O_CLOEXEC ) ;
if ( fd < 0 )
return fd ;
2015-07-08 00:21:03 +03:00
m - > tmp = fdopen ( fd , " w+ " ) ;
2015-03-16 00:13:43 +03:00
if ( ! m - > tmp ) {
safe_close ( fd ) ;
return - errno ;
}
}
return 0 ;
}
2012-09-28 02:46:32 +04: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 03:57:06 +04: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 02:46:32 +04: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 15:19:16 +03:00
log_error_errno ( r , " Failed to advance journal pointer: %m " ) ;
2012-09-28 02:46:32 +04:00
return MHD_CONTENT_READER_END_WITH_ERROR ;
2012-10-09 03:31:27 +04:00
} else if ( r = = 0 ) {
if ( m - > follow ) {
r = sd_journal_wait ( m - > journal , ( uint64_t ) - 1 ) ;
if ( r < 0 ) {
2014-11-28 15:19:16 +03:00
log_error_errno ( r , " Couldn't wait for journal event: %m " ) ;
2012-10-09 03:31:27 +04:00
return MHD_CONTENT_READER_END_WITH_ERROR ;
}
continue ;
}
2012-09-28 02:46:32 +04:00
return MHD_CONTENT_READER_END_OF_STREAM ;
2012-10-09 03:31:27 +04:00
}
2012-09-28 02:46:32 +04:00
2012-10-11 00:39:45 +04:00
if ( m - > discrete ) {
assert ( m - > cursor ) ;
r = sd_journal_test_cursor ( m - > journal , m - > cursor ) ;
if ( r < 0 ) {
2014-11-28 15:19:16 +03:00
log_error_errno ( r , " Failed to test cursor: %m " ) ;
2012-10-11 00:39:45 +04:00
return MHD_CONTENT_READER_END_WITH_ERROR ;
}
if ( r = = 0 )
return MHD_CONTENT_READER_END_OF_STREAM ;
}
2012-09-28 02:46:32 +04:00
pos - = m - > size ;
m - > delta + = m - > size ;
if ( m - > n_entries_set )
m - > n_entries - = 1 ;
m - > n_skip = 0 ;
2015-03-16 00:13:43 +03: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 02:46:32 +04:00
}
2013-08-04 03:38:13 +04:00
r = output_journal ( m - > tmp , m - > journal , m - > mode , 0 , OUTPUT_FULL_WIDTH , NULL ) ;
2012-09-28 02:46:32 +04:00
if ( r < 0 ) {
2014-11-28 15:19:16 +03:00
log_error_errno ( r , " Failed to serialize item: %m " ) ;
2012-09-28 02:46:32 +04:00
return MHD_CONTENT_READER_END_WITH_ERROR ;
}
sz = ftello ( m - > tmp ) ;
if ( sz = = ( off_t ) - 1 ) {
2014-11-28 21:29:59 +03:00
log_error_errno ( errno , " Failed to retrieve file position: %m " ) ;
2012-09-28 02:46:32 +04:00
return MHD_CONTENT_READER_END_WITH_ERROR ;
}
m - > size = ( uint64_t ) sz ;
}
if ( fseeko ( m - > tmp , pos , SEEK_SET ) < 0 ) {
2014-11-28 21:29:59 +03:00
log_error_errno ( errno , " Failed to seek to position: %m " ) ;
2012-09-28 02:46:32 +04: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 ) {
log_error ( " Failed to read from file: %s " , errno ? strerror ( errno ) : " Premature EOF " ) ;
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 15:08:17 +04:00
const char * header ;
2012-09-28 02:46:32 +04:00
assert ( m ) ;
assert ( connection ) ;
2012-10-13 15:08:17 +04:00
header = MHD_lookup_connection_value ( connection , MHD_HEADER_KIND , " Accept " ) ;
if ( ! header )
2012-09-28 02:46:32 +04:00
return 0 ;
2012-10-13 15:08:17 +04:00
if ( streq ( header , mime_types [ OUTPUT_JSON ] ) )
2012-09-28 02:46:32 +04:00
m - > mode = OUTPUT_JSON ;
2012-10-13 15:08:17 +04:00
else if ( streq ( header , mime_types [ OUTPUT_JSON_SSE ] ) )
2012-10-11 04:37:10 +04:00
m - > mode = OUTPUT_JSON_SSE ;
2012-10-13 15:08:17 +04:00
else if ( streq ( header , mime_types [ OUTPUT_EXPORT ] ) )
2012-09-28 02:46:32 +04: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 11:11:22 +04:00
_cleanup_free_ char * t ;
2012-09-28 02:46:32 +04: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-09 00:03:38 +03:00
if ( isempty ( m - > cursor ) )
2015-09-08 19:43:11 +03:00
m - > cursor = mfree ( m - > cursor ) ;
2012-09-28 02:46:32 +04:00
return 0 ;
}
2012-10-09 03:17:29 +04:00
static int request_parse_arguments_iterator (
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 03:31:27 +04: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-11 00:39:45 +04: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-19 00:31:58 +04: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 15:19:16 +03:00
log_error_errno ( r , " Failed to get boot ID: %m " ) ;
2012-10-19 00:31:58 +04: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 ;
}
2012-10-09 03:17:29 +04:00
p = strjoin ( key , " = " , strempty ( value ) , NULL ) ;
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 02:46:32 +04: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 21:32:01 +04:00
void * connection_cls ) {
2012-09-28 02:46:32 +04:00
struct MHD_Response * response ;
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 21:32:01 +04:00
RequestMeta * m = connection_cls ;
2012-09-28 02:46:32 +04: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 21:32:01 +04:00
assert ( m ) ;
2012-09-28 02:46:32 +04:00
r = open_journal ( m ) ;
if ( r < 0 )
2014-03-17 04:05:50 +04:00
return mhd_respondf ( connection , MHD_HTTP_INTERNAL_SERVER_ERROR , " Failed to open journal: %s \n " , strerror ( - r ) ) ;
2012-09-28 02:46:32 +04:00
if ( request_parse_accept ( m , connection ) < 0 )
2014-03-17 04:05:50 +04:00
return mhd_respond ( connection , MHD_HTTP_BAD_REQUEST , " Failed to parse Accept header. \n " ) ;
2012-09-28 02:46:32 +04:00
if ( request_parse_range ( m , connection ) < 0 )
2014-03-17 04:05:50 +04:00
return mhd_respond ( connection , MHD_HTTP_BAD_REQUEST , " Failed to parse Range header. \n " ) ;
2012-09-28 02:46:32 +04:00
2012-10-09 03:17:29 +04:00
if ( request_parse_arguments ( m , connection ) < 0 )
2014-03-17 04:05:50 +04:00
return mhd_respond ( connection , MHD_HTTP_BAD_REQUEST , " Failed to parse URL arguments. \n " ) ;
2012-10-09 03:17:29 +04:00
2012-10-11 00:39:45 +04:00
if ( m - > discrete ) {
if ( ! m - > cursor )
2014-03-17 04:05:50 +04:00
return mhd_respond ( connection , MHD_HTTP_BAD_REQUEST , " Discrete seeks require a cursor specification. \n " ) ;
2012-10-11 00:39:45 +04:00
m - > n_entries = 1 ;
m - > n_entries_set = true ;
}
2012-09-28 02:46:32 +04: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 )
2014-03-17 04:05:50 +04:00
return mhd_respond ( connection , MHD_HTTP_BAD_REQUEST , " Failed to seek in journal. \n " ) ;
2012-09-28 02:46:32 +04: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 ] ) ;
r = MHD_queue_response ( connection , MHD_HTTP_OK , response ) ;
MHD_destroy_response ( response ) ;
return r ;
}
2012-10-19 00:31:27 +04: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 */
if ( m - > n_fields_set & &
m - > n_fields < = 0 )
return MHD_CONTENT_READER_END_OF_STREAM ;
r = sd_journal_enumerate_unique ( m - > journal , & d , & l ) ;
if ( r < 0 ) {
2014-11-28 15:19:16 +03:00
log_error_errno ( r , " Failed to advance field index: %m " ) ;
2012-10-19 00:31:27 +04: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 ;
if ( m - > n_fields_set )
m - > n_fields - = 1 ;
2015-03-16 00:13:43 +03: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-19 00:31:27 +04:00
}
r = output_field ( m - > tmp , m - > mode , d , l ) ;
if ( r < 0 ) {
2014-11-28 15:19:16 +03:00
log_error_errno ( r , " Failed to serialize item: %m " ) ;
2012-10-19 00:31:27 +04:00
return MHD_CONTENT_READER_END_WITH_ERROR ;
}
sz = ftello ( m - > tmp ) ;
if ( sz = = ( off_t ) - 1 ) {
2014-11-28 21:29:59 +03:00
log_error_errno ( errno , " Failed to retrieve file position: %m " ) ;
2012-10-19 00:31:27 +04:00
return MHD_CONTENT_READER_END_WITH_ERROR ;
}
m - > size = ( uint64_t ) sz ;
}
if ( fseeko ( m - > tmp , pos , SEEK_SET ) < 0 ) {
2014-11-28 21:29:59 +03:00
log_error_errno ( errno , " Failed to seek to position: %m " ) ;
2012-10-19 00:31:27 +04: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 ) {
log_error ( " Failed to read from file: %s " , errno ? strerror ( errno ) : " Premature EOF " ) ;
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 ) {
struct MHD_Response * response ;
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 21:32:01 +04:00
RequestMeta * m = connection_cls ;
2012-10-19 00:31:27 +04: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 21:32:01 +04:00
assert ( m ) ;
2012-10-19 00:31:27 +04:00
r = open_journal ( m ) ;
if ( r < 0 )
2014-03-17 04:05:50 +04:00
return mhd_respondf ( connection , MHD_HTTP_INTERNAL_SERVER_ERROR , " Failed to open journal: %s \n " , strerror ( - r ) ) ;
2012-10-19 00:31:27 +04:00
if ( request_parse_accept ( m , connection ) < 0 )
2014-03-17 04:05:50 +04:00
return mhd_respond ( connection , MHD_HTTP_BAD_REQUEST , " Failed to parse Accept header. \n " ) ;
2012-10-19 00:31:27 +04:00
r = sd_journal_query_unique ( m - > journal , field ) ;
if ( r < 0 )
2014-03-17 04:05:50 +04:00
return mhd_respond ( connection , MHD_HTTP_BAD_REQUEST , " Failed to query unique fields. \n " ) ;
2012-10-19 00:31:27 +04: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 ] ) ;
r = MHD_queue_response ( connection , MHD_HTTP_OK , response ) ;
MHD_destroy_response ( response ) ;
return r ;
}
2012-09-28 02:46:32 +04:00
static int request_handler_redirect (
struct MHD_Connection * connection ,
const char * target ) {
char * page ;
struct MHD_Response * response ;
int ret ;
assert ( connection ) ;
2012-10-01 11:53:33 +04:00
assert ( target ) ;
2012-09-28 02:46:32 +04: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 ) ;
ret = MHD_queue_response ( connection , MHD_HTTP_MOVED_PERMANENTLY , response ) ;
MHD_destroy_response ( response ) ;
return ret ;
}
static int request_handler_file (
struct MHD_Connection * connection ,
const char * path ,
const char * mime_type ) {
struct MHD_Response * response ;
int ret ;
_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 )
2014-03-17 04:05:50 +04:00
return mhd_respondf ( connection , MHD_HTTP_NOT_FOUND , " Failed to open file %s: %m \n " , path ) ;
2012-09-28 02:46:32 +04:00
if ( fstat ( fd , & st ) < 0 )
2014-03-17 04:05:50 +04:00
return mhd_respondf ( connection , MHD_HTTP_INTERNAL_SERVER_ERROR , " Failed to stat file: %m \n " ) ;
2012-09-28 02:46:32 +04:00
response = MHD_create_response_from_fd_at_offset ( st . st_size , fd , 0 ) ;
if ( ! response )
return respond_oom ( connection ) ;
fd = - 1 ;
MHD_add_response_header ( response , " Content-Type " , mime_type ) ;
ret = MHD_queue_response ( connection , MHD_HTTP_OK , response ) ;
MHD_destroy_response ( response ) ;
return ret ;
}
2013-03-31 20:15:59 +04:00
static int get_virtualization ( char * * v ) {
_cleanup_bus_unref_ sd_bus * bus = NULL ;
2014-02-19 20:47:11 +04:00
char * b = NULL ;
2013-03-31 20:15:59 +04:00
int r ;
2013-11-12 01:00:48 +04:00
r = sd_bus_default_system ( & bus ) ;
2013-03-31 20:15:59 +04:00
if ( r < 0 )
return r ;
2013-11-08 01:17:19 +04:00
r = sd_bus_get_property_string (
2013-03-31 20:15:59 +04:00
bus ,
" org.freedesktop.systemd1 " ,
" /org/freedesktop/systemd1 " ,
2013-04-05 06:15:39 +04:00
" org.freedesktop.systemd1.Manager " ,
2013-11-08 01:17:19 +04:00
" Virtualization " ,
NULL ,
& b ) ;
2013-03-31 20:15:59 +04:00
if ( r < 0 )
return r ;
2013-11-08 01:17:19 +04:00
if ( isempty ( b ) ) {
free ( b ) ;
2013-03-31 20:15:59 +04:00
* v = NULL ;
return 0 ;
}
* v = b ;
return 1 ;
}
2012-09-28 02:46:32 +04: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 21:32:01 +04:00
void * connection_cls ) {
2012-09-28 02:46:32 +04:00
struct MHD_Response * response ;
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 21:32:01 +04:00
RequestMeta * m = connection_cls ;
2012-09-28 02:46:32 +04:00
int r ;
_cleanup_free_ char * hostname = NULL , * os_name = NULL ;
2015-03-27 14:02:49 +03:00
uint64_t cutoff_from = 0 , cutoff_to = 0 , usage = 0 ;
2012-09-28 02:46:32 +04:00
char * json ;
sd_id128_t mid , bid ;
2013-03-31 20:15:59 +04:00
_cleanup_free_ char * v = NULL ;
2012-09-28 02:46:32 +04: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 21:32:01 +04:00
assert ( m ) ;
2012-09-28 02:46:32 +04:00
r = open_journal ( m ) ;
if ( r < 0 )
2014-03-17 04:05:50 +04:00
return mhd_respondf ( connection , MHD_HTTP_INTERNAL_SERVER_ERROR , " Failed to open journal: %s \n " , strerror ( - r ) ) ;
2012-09-28 02:46:32 +04:00
r = sd_id128_get_machine ( & mid ) ;
if ( r < 0 )
2014-03-17 04:05:50 +04:00
return mhd_respondf ( connection , MHD_HTTP_INTERNAL_SERVER_ERROR , " Failed to determine machine ID: %s \n " , strerror ( - r ) ) ;
2012-09-28 02:46:32 +04:00
r = sd_id128_get_boot ( & bid ) ;
if ( r < 0 )
2014-03-17 04:05:50 +04:00
return mhd_respondf ( connection , MHD_HTTP_INTERNAL_SERVER_ERROR , " Failed to determine boot ID: %s \n " , strerror ( - r ) ) ;
2012-09-28 02:46:32 +04:00
hostname = gethostname_malloc ( ) ;
if ( ! hostname )
return respond_oom ( connection ) ;
r = sd_journal_get_usage ( m - > journal , & usage ) ;
if ( r < 0 )
2014-03-17 04:05:50 +04:00
return mhd_respondf ( connection , MHD_HTTP_INTERNAL_SERVER_ERROR , " Failed to determine disk usage: %s \n " , strerror ( - r ) ) ;
2012-09-28 02:46:32 +04:00
r = sd_journal_get_cutoff_realtime_usec ( m - > journal , & cutoff_from , & cutoff_to ) ;
if ( r < 0 )
2014-03-17 04:05:50 +04:00
return mhd_respondf ( connection , MHD_HTTP_INTERNAL_SERVER_ERROR , " Failed to determine disk usage: %s \n " , strerror ( - r ) ) ;
2012-09-28 02:46:32 +04:00
2014-06-13 21:45:52 +04:00
if ( parse_env_file ( " /etc/os-release " , NEWLINE , " PRETTY_NAME " , & os_name , NULL ) = = - ENOENT )
2015-04-13 05:46:37 +03:00
( void ) parse_env_file ( " /usr/lib/os-release " , NEWLINE , " PRETTY_NAME " , & os_name , NULL ) ;
2012-09-28 02:46:32 +04:00
2013-03-31 20:15:59 +04:00
get_virtualization ( & v ) ;
2012-09-28 02:46:32 +04: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 02:44:16 +04:00
" \" usage \" : \" % " PRIu64 " \" , "
" \" cutoff_from_realtime \" : \" % " PRIu64 " \" , "
" \" cutoff_to_realtime \" : \" % " PRIu64 " \" } \n " ,
2012-09-28 02:46:32 +04:00
SD_ID128_FORMAT_VAL ( mid ) ,
SD_ID128_FORMAT_VAL ( bid ) ,
2015-07-28 05:36:36 +03:00
hostname_cleanup ( hostname ) ,
2012-09-28 02:46:32 +04:00
os_name ? os_name : " Linux " ,
2013-03-31 20:15:59 +04:00
v ? v : " bare " ,
2013-06-06 02:44:16 +04:00
usage ,
cutoff_from ,
cutoff_to ) ;
2012-09-28 02:46:32 +04:00
if ( r < 0 )
return respond_oom ( connection ) ;
response = MHD_create_response_from_buffer ( strlen ( json ) , json , MHD_RESPMEM_MUST_FREE ) ;
if ( ! response ) {
free ( json ) ;
return respond_oom ( connection ) ;
}
MHD_add_response_header ( response , " Content-Type " , " application/json " ) ;
r = MHD_queue_response ( connection , MHD_HTTP_OK , response ) ;
MHD_destroy_response ( response ) ;
return r ;
}
static int request_handler (
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 14:12:05 +04:00
int r , code ;
2012-09-28 02:46:32 +04: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 21:32:01 +04:00
assert ( connection_cls ) ;
2012-09-28 02:46:32 +04:00
assert ( url ) ;
assert ( method ) ;
if ( ! streq ( method , " GET " ) )
2014-03-17 04:05:50 +04:00
return mhd_respond ( connection , MHD_HTTP_METHOD_NOT_ACCEPTABLE ,
" Unsupported method. \n " ) ;
2013-01-17 10:53:01 +04:00
2012-09-28 02:46:32 +04: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 21:32:01 +04:00
if ( ! * connection_cls ) {
if ( ! request_meta ( connection_cls ) )
return respond_oom ( connection ) ;
return MHD_YES ;
}
2015-01-05 02:52:47 +03:00
if ( arg_trust_pem ) {
2014-06-22 21:36:31 +04:00
r = check_permissions ( connection , & code , NULL ) ;
2012-12-01 14:12:05 +04:00
if ( r < 0 )
return code ;
}
2012-09-28 02:46:32 +04: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 21:32:01 +04:00
return request_handler_entries ( connection , * connection_cls ) ;
2012-09-28 02:46:32 +04:00
2012-10-19 00:31:27 +04: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 21:32:01 +04:00
return request_handler_fields ( connection , url + 8 , * connection_cls ) ;
2012-10-19 00:31:27 +04:00
2012-09-28 02:46:32 +04: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 21:32:01 +04:00
return request_handler_machine ( connection , * connection_cls ) ;
2012-09-28 02:46:32 +04:00
2014-03-17 04:05:50 +04:00
return mhd_respond ( connection , MHD_HTTP_NOT_FOUND , " Not found. \n " ) ;
2012-09-28 02:46:32 +04:00
}
2014-08-02 19:12:21 +04:00
static void help ( void ) {
2013-01-18 10:41:01 +04: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-27 02:02:14 +04:00
" --cert=CERT.PEM Server certificate in PEM format \n "
" --key=KEY.PEM Server key in PEM format \n "
" --trust=CERT.PEM Certificat authority certificate in PEM format \n " ,
2013-01-18 10:41:01 +04:00
program_invocation_short_name ) ;
}
2012-11-26 02:26:15 +04:00
static int parse_argv ( int argc , char * argv [ ] ) {
enum {
ARG_VERSION = 0x100 ,
ARG_KEY ,
ARG_CERT ,
2012-11-27 02:02:14 +04:00
ARG_TRUST ,
2012-11-26 02:26:15 +04:00
} ;
int r , c ;
static const struct option options [ ] = {
2013-01-18 10:41:01 +04:00
{ " help " , no_argument , NULL , ' h ' } ,
2012-11-26 02:26:15 +04:00
{ " version " , no_argument , NULL , ARG_VERSION } ,
{ " key " , required_argument , NULL , ARG_KEY } ,
{ " cert " , required_argument , NULL , ARG_CERT } ,
2012-11-27 02:02:14 +04:00
{ " trust " , required_argument , NULL , ARG_TRUST } ,
2013-11-06 21:28:39 +04:00
{ }
2012-11-26 02:26:15 +04:00
} ;
assert ( argc > = 0 ) ;
assert ( argv ) ;
2013-01-18 10:41:01 +04:00
while ( ( c = getopt_long ( argc , argv , " h " , options , NULL ) ) > = 0 )
2013-11-06 21:28:39 +04:00
2012-11-26 02:26:15 +04:00
switch ( c ) {
2013-11-06 21:28:39 +04:00
case ' h ' :
2014-08-02 19:12:21 +04:00
help ( ) ;
return 0 ;
2013-11-06 21:28:39 +04:00
2012-11-26 02:26:15 +04:00
case ARG_VERSION :
2015-09-23 04:01:06 +03:00
return version ( ) ;
2012-11-26 02:26:15 +04:00
case ARG_KEY :
2015-01-05 02:52:47 +03:00
if ( arg_key_pem ) {
2012-11-26 02:26:15 +04:00
log_error ( " Key file specified twice " ) ;
return - EINVAL ;
}
2015-01-05 02:52:47 +03:00
r = read_full_file ( optarg , & arg_key_pem , NULL ) ;
2014-11-28 20:23:20 +03:00
if ( r < 0 )
return log_error_errno ( r , " Failed to read key file: %m " ) ;
2015-01-05 02:52:47 +03:00
assert ( arg_key_pem ) ;
2012-11-26 02:26:15 +04:00
break ;
2012-09-28 02:46:32 +04:00
2012-11-26 02:26:15 +04:00
case ARG_CERT :
2015-01-05 02:52:47 +03:00
if ( arg_cert_pem ) {
2012-11-26 02:26:15 +04:00
log_error ( " Certificate file specified twice " ) ;
return - EINVAL ;
}
2015-01-05 02:52:47 +03:00
r = read_full_file ( optarg , & arg_cert_pem , NULL ) ;
2014-11-28 20:23:20 +03:00
if ( r < 0 )
return log_error_errno ( r , " Failed to read certificate file: %m " ) ;
2015-01-05 02:52:47 +03:00
assert ( arg_cert_pem ) ;
2012-11-26 02:26:15 +04:00
break ;
2012-11-27 02:02:14 +04:00
case ARG_TRUST :
2012-12-01 14:12:05 +04:00
# ifdef HAVE_GNUTLS
2015-01-05 02:52:47 +03:00
if ( arg_trust_pem ) {
2012-11-27 02:02:14 +04:00
log_error ( " CA certificate file specified twice " ) ;
return - EINVAL ;
}
2015-01-05 02:52:47 +03:00
r = read_full_file ( optarg , & arg_trust_pem , NULL ) ;
2014-11-28 20:23:20 +03:00
if ( r < 0 )
return log_error_errno ( r , " Failed to read CA certificate file: %m " ) ;
2015-01-05 02:52:47 +03:00
assert ( arg_trust_pem ) ;
2012-11-27 02:02:14 +04:00
break ;
2012-12-01 14:12:05 +04:00
# else
log_error ( " Option --trust is not available. " ) ;
# endif
2012-11-27 02:02:14 +04:00
2012-11-26 02:26:15 +04:00
case ' ? ' :
return - EINVAL ;
default :
2013-11-06 21:28:39 +04:00
assert_not_reached ( " Unhandled option " ) ;
2012-11-26 02:26:15 +04:00
}
if ( optind < argc ) {
2012-09-28 02:46:32 +04:00
log_error ( " This program does not take arguments. " ) ;
2012-11-26 02:26:15 +04:00
return - EINVAL ;
}
2015-01-05 02:52:47 +03:00
if ( ! ! arg_key_pem ! = ! ! arg_cert_pem ) {
2012-11-26 02:26:15 +04:00
log_error ( " Certificate and key files must be specified together " ) ;
return - EINVAL ;
2012-09-28 02:46:32 +04:00
}
2015-01-05 02:52:47 +03:00
if ( arg_trust_pem & & ! arg_key_pem ) {
2012-11-27 02:02:14 +04:00
log_error ( " CA certificate can only be used with certificate file " ) ;
return - EINVAL ;
}
2012-11-26 02:26:15 +04:00
return 1 ;
}
int main ( int argc , char * argv [ ] ) {
struct MHD_Daemon * d = NULL ;
int r , n ;
2012-10-10 03:57:06 +04:00
log_set_target ( LOG_TARGET_AUTO ) ;
2012-09-28 02:46:32 +04:00
log_parse_environment ( ) ;
log_open ( ) ;
2012-11-26 02:26:15 +04:00
r = parse_argv ( argc , argv ) ;
if ( r < 0 )
return EXIT_FAILURE ;
if ( r = = 0 )
return EXIT_SUCCESS ;
2015-01-05 02:52:47 +03:00
sigbus_install ( ) ;
2015-03-14 05:07:45 +03:00
r = setup_gnutls_logger ( NULL ) ;
if ( r < 0 )
return EXIT_FAILURE ;
2012-11-29 02:08:35 +04:00
2012-09-28 02:46:32 +04:00
n = sd_listen_fds ( 1 ) ;
if ( n < 0 ) {
2014-11-28 15:19:16 +03:00
log_error_errno ( n , " Failed to determine passed sockets: %m " ) ;
2012-09-28 02:46:32 +04:00
goto finish ;
} else if ( n > 1 ) {
log_error ( " Can't listen on more than one socket. " ) ;
goto finish ;
} else {
2012-11-26 02:21:22 +04:00
struct MHD_OptionItem opts [ ] = {
{ MHD_OPTION_NOTIFY_COMPLETED ,
( intptr_t ) request_meta_free , NULL } ,
2012-11-26 02:54:31 +04:00
{ MHD_OPTION_EXTERNAL_LOGGER ,
( intptr_t ) microhttpd_logger , NULL } ,
2012-11-26 02:21:22 +04:00
{ MHD_OPTION_END , 0 , NULL } ,
2012-11-26 02:26:15 +04:00
{ MHD_OPTION_END , 0 , NULL } ,
{ MHD_OPTION_END , 0 , NULL } ,
2012-11-27 02:02:14 +04:00
{ MHD_OPTION_END , 0 , NULL } ,
2012-11-26 02:21:22 +04:00
{ MHD_OPTION_END , 0 , NULL } } ;
2012-11-26 02:54:31 +04:00
int opts_pos = 2 ;
2012-11-26 02:26:15 +04:00
int flags = MHD_USE_THREAD_PER_CONNECTION | MHD_USE_POLL | MHD_USE_DEBUG ;
2012-11-26 02:21:22 +04:00
if ( n > 0 )
2012-11-26 02:26:15 +04:00
opts [ opts_pos + + ] = ( struct MHD_OptionItem )
{ MHD_OPTION_LISTEN_SOCKET , SD_LISTEN_FDS_START } ;
2015-01-05 02:52:47 +03:00
if ( arg_key_pem ) {
assert ( arg_cert_pem ) ;
2012-11-26 02:26:15 +04:00
opts [ opts_pos + + ] = ( struct MHD_OptionItem )
2015-01-05 02:52:47 +03:00
{ MHD_OPTION_HTTPS_MEM_KEY , 0 , arg_key_pem } ;
2012-11-26 02:26:15 +04:00
opts [ opts_pos + + ] = ( struct MHD_OptionItem )
2015-01-05 02:52:47 +03:00
{ MHD_OPTION_HTTPS_MEM_CERT , 0 , arg_cert_pem } ;
2012-11-26 02:26:15 +04:00
flags | = MHD_USE_SSL ;
}
2015-01-05 02:52:47 +03:00
if ( arg_trust_pem ) {
2012-11-27 02:02:14 +04:00
assert ( flags & MHD_USE_SSL ) ;
opts [ opts_pos + + ] = ( struct MHD_OptionItem )
2015-01-05 02:52:47 +03:00
{ MHD_OPTION_HTTPS_MEM_TRUST , 0 , arg_trust_pem } ;
2012-11-27 02:02:14 +04:00
}
2012-11-26 02:26:15 +04:00
d = MHD_start_daemon ( flags , 19531 ,
NULL , NULL ,
request_handler , NULL ,
MHD_OPTION_ARRAY , opts ,
MHD_OPTION_END ) ;
2012-09-28 02:46:32 +04:00
}
2012-10-13 15:08:17 +04:00
if ( ! d ) {
2012-09-28 02:46:32 +04:00
log_error ( " Failed to start daemon! " ) ;
goto finish ;
}
pause ( ) ;
r = EXIT_SUCCESS ;
finish :
2012-10-13 15:08:17 +04:00
if ( d )
MHD_stop_daemon ( d ) ;
2012-09-28 02:46:32 +04:00
return r ;
}