From ac00d58af53dcc566e08e46f220f1e5993f2f55f Mon Sep 17 00:00:00 2001 From: Eduardo Silva Date: Wed, 9 Oct 2024 16:20:46 -0600 Subject: [PATCH] lib: monkey: upgrade to v1.8.1 Signed-off-by: Eduardo Silva --- lib/monkey/CMakeLists.txt | 4 +- lib/monkey/api/test.c | 6 +- lib/monkey/include/monkey/mk_http_parser.h | 85 ++++- lib/monkey/mk_core/mk_event_select.c | 4 +- lib/monkey/mk_server/mk_http.c | 3 + lib/monkey/mk_server/mk_http_parser.c | 364 ++++++++++++++++++++- lib/monkey/plugins/logger/logger.c | 4 +- 7 files changed, 451 insertions(+), 19 deletions(-) diff --git a/lib/monkey/CMakeLists.txt b/lib/monkey/CMakeLists.txt index 95d8cc1e618..028240bcc44 100644 --- a/lib/monkey/CMakeLists.txt +++ b/lib/monkey/CMakeLists.txt @@ -22,8 +22,8 @@ endif() # Monkey Version set(MK_VERSION_MAJOR 1) -set(MK_VERSION_MINOR 7) -set(MK_VERSION_PATCH 2) +set(MK_VERSION_MINOR 8) +set(MK_VERSION_PATCH 1) set(MK_VERSION_STR "${MK_VERSION_MAJOR}.${MK_VERSION_MINOR}.${MK_VERSION_PATCH}") # Output paths diff --git a/lib/monkey/api/test.c b/lib/monkey/api/test.c index ffb96f1352f..6ed0fe0e0a2 100644 --- a/lib/monkey/api/test.c +++ b/lib/monkey/api/test.c @@ -8,7 +8,7 @@ #include #define API_ADDR "127.0.0.1" -#define API_PORT "8080" +#define API_PORT "9080" /* Main context set as global so the signal handler can use it */ mk_ctx_t *ctx; @@ -153,9 +153,9 @@ int main() "Name", "monotop", NULL); - mk_vhost_handler(ctx, vid, "/api/v1/stream_processor/task/[A-Za-z_][0-9A-Za-z_\\-]*", + mk_vhost_handler(ctx, vid, "/api/v1/stream_processor/task/[A-Za-z_][0-9A-Za-z_\\-]*", cb_sp_test_task_detail, NULL); - + mk_vhost_handler(ctx, vid, "/api/v1/stream_processor/task", cb_sp_test_task_main, NULL); diff --git a/lib/monkey/include/monkey/mk_http_parser.h b/lib/monkey/include/monkey/mk_http_parser.h index 6d45c39414c..9e3b365eef7 100644 --- a/lib/monkey/include/monkey/mk_http_parser.h +++ b/lib/monkey/include/monkey/mk_http_parser.h @@ -45,6 +45,16 @@ #define MK_HTTP_PARSER_UPGRADE_H2 1 #define MK_HTTP_PARSER_UPGRADE_H2C 2 +/* Transfer encoding */ +#define MK_HTTP_PARSER_TRANSFER_ENCODING_NONE (0) +#define MK_HTTP_PARSER_TRANSFER_ENCODING_CHUNKED (1 << 0) +#define MK_HTTP_PARSER_TRANSFER_ENCODING_GZIP (1 << 1) + +/* Transfer encoding (almost not used) */ +#define MK_HTTP_PARSER_TRANSFER_ENCODING_COMPRESS (1 << 2) +#define MK_HTTP_PARSER_TRANSFER_ENCODING_DEFLATE (1 << 3) +#define MK_HTTP_PARSER_TRANSFER_ENCODING_IDENTITY (1 << 4) + #define MK_HEADER_EXTRA_SIZE 50 /* Request levels @@ -118,6 +128,7 @@ enum mk_request_headers { MK_HEADER_LAST_MODIFIED_SINCE , MK_HEADER_RANGE , MK_HEADER_REFERER , + MK_HEADER_TRANSFER_ENCODING , MK_HEADER_UPGRADE , MK_HEADER_USER_AGENT , MK_HEADER_SIZEOF , @@ -193,6 +204,21 @@ struct mk_http_parser { */ int header_upgrade; + + /* + * Transfer-Encoding + * ------------------ + * we support the following values (bitwise): + * + * - MK_HTTP_PARSER_TRANSFER_ENCODING_NONE + * - MK_HTTP_PARSER_TRANSFER_ENCODING_CHUNKED + * - MK_HTTP_PARSER_TRANSFER_ENCODING_GZIP + * - MK_HTTP_PARSER_TRANSFER_ENCODING_COMPRESS + * - MK_HTTP_PARSER_TRANSFER_ENCODING_DEFLATE + * - MK_HTTP_PARSER_TRANSFER_ENCODING_IDENTITY + */ + int header_transfer_encoding; + /* probable current header, fly parsing */ int header_key; int header_sep; @@ -210,6 +236,20 @@ struct mk_http_parser { /* Extra headers */ struct mk_http_header headers_extra[MK_HEADER_EXTRA_SIZE]; + + + /* + * total size of bytes received as chunked data; this don't count the + * hex strings + */ + size_t chunk_total_size_received; + + /* Transfer chunked encoding: state for active chunk being processed */ + char *chunk_expected_start; /* pointer to the expected very first chunk in the payload */ + + size_t chunk_expected_size; /* expected size of a chunk being read */ + char *chunk_processed_start; /* beginning of a chunk being read */ + char *chunk_processed_end; /* last position of a chunk that is complete */ }; @@ -333,6 +373,20 @@ static inline void mk_http_parser_init(struct mk_http_parser *p) mk_list_init(&p->header_list); } +int mk_http_parser(struct mk_http_request *req, struct mk_http_parser *p, + char *buffer, int buf_len, struct mk_server *server); + +size_t mk_http_parser_content_length(struct mk_http_parser *p); +int mk_http_parser_is_content_chunked(struct mk_http_parser *p); + +int mk_http_parser_chunked_decode(struct mk_http_parser *p, + char *buf_request, size_t buf_request_len, + char **out_buf, size_t *out_buf_size); + +int mk_http_parser_chunked_decode_buf(struct mk_http_parser *p, + char *buf_request, size_t buf_request_len, + char *out_buf, size_t out_buf_size, size_t *out_buf_len); + static inline int mk_http_parser_more(struct mk_http_parser *p, int len) { if (abs(len - p->i) - 1 > 0) { @@ -342,7 +396,34 @@ static inline int mk_http_parser_more(struct mk_http_parser *p, int len) return MK_FALSE; } -int mk_http_parser(struct mk_http_request *req, struct mk_http_parser *p, - char *buffer, int buf_len, struct mk_server *server); +/* Returns the full size of the HTTP request in bytes "If" mk_http_parser() has returned MK_HTTP_PARSER_OK */ +static inline size_t mk_http_parser_request_size(struct mk_http_parser *p, char *buf_request, size_t buf_request_len) +{ + size_t bytes; + + /* + * if the request is chunked encoded, p->i points to the beginning of the last chunk + * found, so we need to check if the last chunk is complete, if so we can return the + * size of the request + */ + if (mk_http_parser_is_content_chunked(p)) { + if (p->chunk_processed_start < buf_request) { + return -1; + } + + /* Look at the last chunk processed (0\r\n\r\n) */ + bytes = p->chunk_processed_start - buf_request + 5; + if (bytes > buf_request_len) { + return -1; + } + return bytes; + } + else if (p->header_content_length > 0) { + /* p->i points to the last byte after the content body */ + return p->i; + } + + return -1; +} #endif /* MK_HTTP_H */ diff --git a/lib/monkey/mk_core/mk_event_select.c b/lib/monkey/mk_core/mk_event_select.c index e326ba85b0b..f23dafc7352 100644 --- a/lib/monkey/mk_core/mk_event_select.c +++ b/lib/monkey/mk_core/mk_event_select.c @@ -367,7 +367,7 @@ static inline int _mk_event_inject(struct mk_event_loop *loop, if (prevent_duplication) { for (index = 0 ; index < loop->n_events ; index++) { - if (ctx->fired[index]->fd == event->fd) { + if (ctx->fired[index].data == event) { return 0; } } @@ -375,7 +375,7 @@ static inline int _mk_event_inject(struct mk_event_loop *loop, event->mask = mask; - ctx->fired[loop->n_events] = event; + ctx->fired[loop->n_events].data = event; loop->n_events++; diff --git a/lib/monkey/mk_server/mk_http.c b/lib/monkey/mk_server/mk_http.c index 1e2d219ded5..184a695a811 100644 --- a/lib/monkey/mk_server/mk_http.c +++ b/lib/monkey/mk_server/mk_http.c @@ -1150,6 +1150,7 @@ int mk_http_request_end(struct mk_http_session *cs, struct mk_server *server) mk_http_parser_init(&cs->parser); status = mk_http_parser(sr, &cs->parser, cs->body, cs->body_length, server); + if (status == MK_HTTP_PARSER_OK) { ret = mk_http_request_prepare(cs, sr, server); if (ret == MK_EXIT_ABORT) { @@ -1564,8 +1565,10 @@ int mk_http_sched_read(struct mk_sched_conn *conn, else { sr = mk_list_entry_first(&cs->request_list, struct mk_http_request, _head); } + status = mk_http_parser(sr, &cs->parser, cs->body, cs->body_length, server); + if (status == MK_HTTP_PARSER_OK) { MK_TRACE("[FD %i] HTTP_PARSER_OK", socket); if (mk_http_status_completed(cs, conn) == -1) { diff --git a/lib/monkey/mk_server/mk_http_parser.c b/lib/monkey/mk_server/mk_http_parser.c index 4e7aa31616d..f973571e9ed 100644 --- a/lib/monkey/mk_server/mk_http_parser.c +++ b/lib/monkey/mk_server/mk_http_parser.c @@ -74,8 +74,9 @@ struct row_entry mk_headers_table[] = { { 19, "last-modified-since" }, { 5, "range" }, { 7, "referer" }, + { 17, "transfer-encoding" }, { 7, "upgrade" }, - { 10, "user-agent" } + { 10, "user-agent" }, }; static inline void reverse_char_lookup(char *buf, char c, int len, struct mk_http_parser *p) @@ -226,6 +227,7 @@ static inline int header_lookup(struct mk_http_parser *p, char *buffer) p->header_count++; mk_list_add(&header->_head, &p->header_list); + if (i == MK_HEADER_HOST) { /* Handle a possible port number in the Host header */ int sep = str_searchr(header->val.data, ':', header->val.len); @@ -314,6 +316,52 @@ static inline int header_lookup(struct mk_http_parser *p, char *buffer) } } } + else if (i == MK_HEADER_TRANSFER_ENCODING) { + /* Check Transfer-Encoding: chunked */ + pos = mk_string_search_n(header->val.data, + "chunked", + MK_STR_INSENSITIVE, + header->val.len); + if (pos >= 0) { + p->header_transfer_encoding |= MK_HTTP_PARSER_TRANSFER_ENCODING_CHUNKED; + } + + /* Check Transfer-Encoding: gzip */ + pos = mk_string_search_n(header->val.data, + "gzip", + MK_STR_INSENSITIVE, + header->val.len); + if (pos >= 0) { + p->header_transfer_encoding |= MK_HTTP_PARSER_TRANSFER_ENCODING_GZIP; + } + + /* Check Transfer-Encoding: compress */ + pos = mk_string_search_n(header->val.data, + "compress", + MK_STR_INSENSITIVE, + header->val.len); + if (pos >= 0) { + p->header_transfer_encoding |= MK_HTTP_PARSER_TRANSFER_ENCODING_COMPRESS; + } + + /* Check Transfer-Encoding: deflate */ + pos = mk_string_search_n(header->val.data, + "deflate", + MK_STR_INSENSITIVE, + header->val.len); + if (pos >= 0) { + p->header_transfer_encoding |= MK_HTTP_PARSER_TRANSFER_ENCODING_DEFLATE; + } + + /* Check Transfer-Encoding: identity */ + pos = mk_string_search_n(header->val.data, + "identity", + MK_STR_INSENSITIVE, + header->val.len); + if (pos >= 0) { + p->header_transfer_encoding |= MK_HTTP_PARSER_TRANSFER_ENCODING_IDENTITY; + } + } else if (i == MK_HEADER_UPGRADE) { if (header_cmp(MK_UPGRADE_H2C, header->val.data, header->val.len) == 0) { @@ -354,6 +402,290 @@ static inline int header_lookup(struct mk_http_parser *p, char *buffer) return -MK_CLIENT_REQUEST_ENTITY_TOO_LARGE; } + +/* check if the HTTP content is chunked so it contain hexa string length headers */ +int mk_http_parser_is_content_chunked(struct mk_http_parser *p) +{ + return p->header_transfer_encoding & MK_HTTP_PARSER_TRANSFER_ENCODING_CHUNKED; +} + +size_t mk_http_parser_content_length(struct mk_http_parser *p) +{ + /* + * returns the content length of the payload. If the content-length header was + * set, it will return the value of the header. If the content-length header was + * not set and instead the transfer-encoding header was set to chunked, it will + * return the length of the payload withouto counting the chunked headers. + */ + + if (!mk_http_parser_is_content_chunked(p)) { + return p->header_content_length; + } + else { + return p->chunk_total_size_received; + } + + return 0; +} + + +int cb_debug_chunk_complete(char *in, size_t in_len, char *out, size_t out_len, size_t *out_len_processed) +{ + (void) out; + (void) out_len; + char *buf; + + /* copy the chunked content into the buffer */ + buf = mk_mem_alloc(in_len + 1); + if (!buf) { + return -1; + } + + memcpy(buf, in, in_len); + buf[in_len] = '\0'; + + printf("==CHUNK DETECTED CONTENT (length=%zu)==\n'%s'\n---\n", in_len, buf); + mk_mem_free(buf); + + *out_len_processed = in_len; + + return 0; +} + +/* + * Check if the request body is complete, incomplete or if it has an error while processing + * the chunks for a chunked transfer encoded payload + */ +static int http_parser_transfer_encoding_chunked(struct mk_http_parser *p, + char *buf_request, size_t buf_request_len, + int (*cb_chunk_complete)(char *in, size_t in_len, char *out, size_t out_len, size_t *out_len_processed), + char *out_buf, size_t out_buf_size, size_t *out_buf_len) +{ + int64_t len; + int64_t chunk_len; + int64_t pos; + char tmp[32]; + char *ptr; + char *content_start; + size_t available_bytes; + + p->level = REQ_LEVEL_BODY; + +parse_more: + + /* read the payload and check if the request has finished based on the logic of transfer encoding chunked */ + if (!p->chunk_processed_start) { + /* + * if p->chunk_processed_start is not set, it means we are parsing from the beginning. Note that + * p->chunk_expected_start is set, it means the content was already processed before, so we just + * adjust the pointer, otherwise we use the parser iterator index (p->i) for it. + */ + if (p->chunk_expected_start) { + p->chunk_processed_start = p->chunk_expected_start; + } + else { + p->chunk_processed_start = buf_request + p->i; + + /* Mark the very first chunk */ + p->chunk_expected_start = p->chunk_processed_start; + } + + len = buf_request_len - p->i; + if (len == 0) { + return MK_HTTP_PARSER_PENDING; + } + + if (p->chunk_processed_start[0] != '\n') { + return MK_HTTP_PARSER_ERROR; + } + + /* we are at the beginning of a chunk, we need to find the end */ + p->chunk_processed_start++; + len--; + + } + else { + len = buf_request_len - (p->chunk_processed_end - buf_request); + } + + /* find the end of the 'chunk header' (e.g: ffae\r\n\r\n) */ + pos = mk_string_search_n(p->chunk_processed_start, "\r\n", MK_STR_SENSITIVE, len); + if (pos < 0) { + return MK_HTTP_PARSER_PENDING; + } + + /* length of the hex string */ + len = (p->chunk_processed_start + pos) - p->chunk_processed_start; + if (((unsigned long) len > sizeof(tmp) - 1) || len == 0) { + return MK_HTTP_PARSER_ERROR; + } + + /* copy the hex string to a temporary buffer */ + memcpy(tmp, p->chunk_processed_start, len); + tmp[len] = '\0'; + + /* convert the hex string to a number */ + errno = 0; + chunk_len = strtol(tmp, &ptr, 16); + if ((errno == ERANGE && (chunk_len == LONG_MAX || chunk_len == LONG_MIN)) || + (errno != 0)) { + return MK_HTTP_PARSER_ERROR; + } + + if (chunk_len < 0) { + return MK_HTTP_PARSER_ERROR; + } + else if (chunk_len == 0) { + /* we have reached the end of the request, validate the last \r\n\r\n exists */ + len = buf_request_len - (p->chunk_processed_start - buf_request); + + if (len < 5) { + return MK_HTTP_PARSER_PENDING; + } + + /* all or nothing */ + if (strncmp(p->chunk_processed_start, "0\r\n\r\n", 5) != 0) { + return MK_HTTP_PARSER_ERROR; + } + + return MK_HTTP_PARSER_OK; + } + else { + /* set the new markers: required size and start position after the hex string length */ + p->chunk_expected_size = chunk_len; + + /* the content starts after the hex_str_length\r\n */ + content_start = p->chunk_processed_start + pos + 2; + + /* calculate the amount of available bytes 'after' content_start */ + available_bytes = buf_request_len - (content_start - buf_request); + + /* do we have all the remaining data needed in our buffer ? */ + if (available_bytes >= p->chunk_expected_size + 2 /* \r\n */) { + /* we have all the data needed */ + p->chunk_processed_end = content_start + p->chunk_expected_size; + + /* check for delimiter \r\n */ + if (p->chunk_processed_end[0] != '\r' || p->chunk_processed_end[1] != '\n') { + return MK_HTTP_PARSER_ERROR; + } + + /* + * If the callback function has been set, invoke it: this callback might be useful for + * debugging and/or provide a way to copy the chunked content into a buffer + */ + if (cb_chunk_complete) { + cb_chunk_complete(content_start, chunk_len, out_buf, out_buf_size, out_buf_len); + } + + /* set the new start for the new chunk */ + p->chunk_processed_start = p->chunk_processed_end + 2; + p->chunk_total_size_received += chunk_len; + goto parse_more; + } + else { + /* we need more data */ + return MK_HTTP_PARSER_PENDING; + } + + } + /* is our chunk complete ? */ + return MK_HTTP_PARSER_PENDING; + +} + +/* Read the chunked content and invoke callback if it has been set */ +int mk_http_parser_read_chunked_content(struct mk_http_parser *p, + char *buf_request, size_t buf_request_len, + int (*cb_chunk_complete)(char *in, size_t in_len, char *out, size_t out_size, size_t *out_len), + char *out_buf, size_t out_buf_size, size_t *out_buf_len) +{ + p->chunk_processed_start = NULL; + p->chunk_processed_end = NULL; + + return http_parser_transfer_encoding_chunked(p, + buf_request, buf_request_len, + cb_chunk_complete, + out_buf, out_buf_size, out_buf_len); +} + +/* + * Callback function used by mk_http_parser_chunked_decode to provide a new buffer with the content + * of the payload decoded + */ +static int cb_copy_chunk(char *in, size_t in_len, char *out, size_t out_size, size_t *out_len_processed) +{ + (void) out_size; + + /* check we don't overflow the buffer */ + if (*out_len_processed + in_len > out_size) { + return -1; + } + + /* copy the chunk */ + memcpy(out + *out_len_processed, in, in_len); + *out_len_processed += in_len; + + return 0; +} + +/* + * This function assumes that the output buffer size has enough space to copy the desired + * chunked content. We do some sanity checks but if the buffer is smaller the data will + * be truncated. + */ +int mk_http_parser_chunked_decode_buf(struct mk_http_parser *p, + char *buf_request, size_t buf_request_len, + char *out_buf, size_t out_buf_size, size_t *out_buf_len) +{ + int ret; + size_t written_bytes = 0; + + ret = mk_http_parser_read_chunked_content(p, + buf_request, buf_request_len, + cb_copy_chunk, + out_buf, out_buf_size, &written_bytes); + if (ret == MK_HTTP_PARSER_OK) { + *out_buf_len = written_bytes; + return 0; + } + + return -1; +} + +int mk_http_parser_chunked_decode(struct mk_http_parser *p, + char *buf_request, size_t buf_request_len, + char **out_buf, size_t *out_buf_size) +{ + int ret; + char *tmp_buf; + size_t tmp_buf_size = 0; + size_t tmp_written_bytes = 0; + + tmp_buf_size = mk_http_parser_content_length(p); + if (tmp_buf_size == 0) { + return -1; + } + + tmp_buf = mk_mem_alloc(tmp_buf_size); + if (!tmp_buf) { + return -1; + } + + ret = mk_http_parser_chunked_decode_buf(p, + buf_request, buf_request_len, + tmp_buf, tmp_buf_size, &tmp_written_bytes); + if (ret == -1) { + mk_mem_free(tmp_buf); + return -1; + } + + *out_buf = tmp_buf; + *out_buf_size = tmp_written_bytes; + + return 0; +} + /* * This function is invoked everytime the parser evaluate the request is * OK. Here we perform some extra validations mostly based on some logic @@ -361,8 +693,11 @@ static inline int header_lookup(struct mk_http_parser *p, char *buffer) */ static inline int mk_http_parser_ok(struct mk_http_request *req, struct mk_http_parser *p, + char *buf_request, size_t buf_request_len, struct mk_server *server) { + int ret; + /* Validate HTTP Version */ if (req->protocol == MK_HTTP_PROTOCOL_UNKNOWN) { mk_http_error(MK_SERVER_HTTP_VERSION_UNSUP, req->session, req, server); @@ -371,10 +706,20 @@ static inline int mk_http_parser_ok(struct mk_http_request *req, /* POST checks */ if (req->method == MK_METHOD_POST || req->method == MK_METHOD_PUT) { - /* validate Content-Length exists */ - if (p->headers[MK_HEADER_CONTENT_LENGTH].type == 0) { - mk_http_error(MK_CLIENT_LENGTH_REQUIRED, req->session, req, server); - return MK_HTTP_PARSER_ERROR; + /* validate Content-Length exists for non-chunked requests */ + if (mk_http_parser_is_content_chunked(p)) { + p->level = REQ_LEVEL_BODY; + + ret = http_parser_transfer_encoding_chunked(p, + buf_request, buf_request_len, + NULL, NULL, 0, NULL); + return ret; + } + else { + if (p->headers[MK_HEADER_CONTENT_LENGTH].type == 0) { + mk_http_error(MK_CLIENT_LENGTH_REQUIRED, req->session, req, server); + return MK_HTTP_PARSER_ERROR; + } } } @@ -543,7 +888,7 @@ int mk_http_parser(struct mk_http_request *req, struct mk_http_parser *p, break; case MK_ST_BLOCK_END: if (buffer[p->i] == '\n') { - return mk_http_parser_ok(req, p, server); + return mk_http_parser_ok(req, p, buffer, buf_len, server); } else { return MK_HTTP_PARSER_ERROR; @@ -614,6 +959,9 @@ int mk_http_parser(struct mk_http_request *req, struct mk_http_parser *p, p->header_min = MK_HEADER_UPGRADE; p->header_max = MK_HEADER_USER_AGENT; break; + case 't': + header_scope_eq(p, MK_HEADER_TRANSFER_ENCODING); + break; default: p->header_key = -1; p->header_sep = -1; @@ -710,7 +1058,7 @@ int mk_http_parser(struct mk_http_request *req, struct mk_http_parser *p, start_next(); } else { - return mk_http_parser_ok(req, p, server); + return mk_http_parser_ok(req, p, buffer, buf_len, server); } } else { @@ -736,7 +1084,7 @@ int mk_http_parser(struct mk_http_request *req, struct mk_http_parser *p, req->data.len = p->body_received; req->data.data = (buffer + p->start); } - return mk_http_parser_ok(req, p, server); + return mk_http_parser_ok(req, p, buffer, buf_len, server); } } diff --git a/lib/monkey/plugins/logger/logger.c b/lib/monkey/plugins/logger/logger.c index 4dfcfb00192..271b39e1e9b 100644 --- a/lib/monkey/plugins/logger/logger.c +++ b/lib/monkey/plugins/logger/logger.c @@ -197,7 +197,7 @@ static void mk_logger_start_worker(void *args) timeout = clk + mk_logger_timeout; - flog = open(target, O_WRONLY | O_CREAT | O_CLOEXEC, 0644); + flog = open(target, O_WRONLY | O_CREAT | O_CLOEXEC, 0600); if (mk_unlikely(flog == -1)) { mk_warn("Could not open logfile '%s' (%s)", target, strerror(errno)); @@ -327,7 +327,7 @@ int mk_logger_plugin_init(struct plugin_api **api, char *confdir) /* Check masterlog */ if (mk_logger_master_path) { - fd = open(mk_logger_master_path, O_WRONLY | O_CREAT | O_CLOEXEC, 0644); + fd = open(mk_logger_master_path, O_WRONLY | O_CREAT | O_CLOEXEC, 0600); if (fd == -1) { mk_err("Could not open/create master logfile %s", mk_logger_master_path); exit(EXIT_FAILURE);