In-memory compressed log backup 70/271770/9
authorMichal Bloch <m.bloch@samsung.com>
Tue, 26 Apr 2022 14:56:22 +0000 (16:56 +0200)
committerMichal Bloch <m.bloch@samsung.com>
Mon, 9 May 2022 12:59:18 +0000 (14:59 +0200)
Change-Id: I87a94908152fab5808a1a6cd4791a171ad225aea

26 files changed:
Makefile.am
include/dlogutil-internal.h
include/dlogutil.h
include/logpipe.h
include/util_parser.h
src/libdlogutil/fd_info.h
src/libdlogutil/fdi_logger.c
src/libdlogutil/fdi_pipe.c
src/libdlogutil/lib.c
src/libdlogutil/logretrieve.c
src/libdlogutil/logretrieve.h
src/logger/dlogutil_line.c
src/logger/dlogutil_line.h
src/logger/log_buffer.c
src/logger/logger.c
src/logger/logger_internal.h
src/logger/reader_memory.c [new file with mode: 0644]
src/logger/reader_memory.h [new file with mode: 0644]
src/logger/subreader_memory.c [new file with mode: 0644]
src/logger/subreader_memory.h [new file with mode: 0644]
src/logutil/logutil.c
src/shared/util_parser.c
src/tests/fdi_logger_neg.c
src/tests/fdi_logger_pos.c
src/tests/fdi_pipe_neg.c
src/tests/fdi_pipe_pos.c

index d85a108..3712a9f 100644 (file)
@@ -126,19 +126,23 @@ dlog_logger_LDFLAGS = \
 
 dlog_logger_SOURCES = \
        external/sd-daemon/sd-daemon.c \
+       external/fastlz/fastlz.c \
        src/logger/logger.c \
        src/logger/logger_privileges.c \
        src/logger/dlogutil_line.c \
        src/logger/fd_entity.c \
        src/logger/log_buffer.c \
        src/logger/log_storage.c \
+       src/logger/log_compressed_storage.c \
        src/logger/qos.c \
        src/logger/qos_distributions.c \
        src/logger/reader_common.c \
        src/logger/reader_logger.c \
        src/logger/reader_pipe.c \
+       src/logger/reader_memory.c \
        src/logger/subreader_dlogutil.c \
        src/logger/subreader_file.c \
+       src/logger/subreader_memory.c \
        src/logger/subreader_metrics.c \
        src/logger/socket.c \
        src/logger/writer.c \
index b4e84d6..894d1fa 100644 (file)
@@ -34,6 +34,7 @@ typedef enum {
        DLOGUTIL_MODE_CONTINUOUS,
        DLOGUTIL_MODE_DUMP,
        DLOGUTIL_MODE_MONITOR,
+       DLOGUTIL_MODE_COMPRESSED_MEMORY_DUMP,
 } dlogutil_mode_e;
 
 typedef enum {
@@ -53,6 +54,7 @@ struct dlogutil_config {
        unsigned int buffers;
        unsigned int dump_size;
        dlogutil_mode_e mode;
+       char *compress;
 };
 
 #ifdef __cplusplus
index f5d472f..f5a7d58 100644 (file)
@@ -305,6 +305,7 @@ void dlogutil_state_destroy(dlogutil_state_s *state);
  * @retval TIZEN_ERROR_INVALID_PARAMETER Config was NULL
  * @see dlogutil_config_mode_set_monitor()
  * @see dlogutil_config_mode_set_dump()
+ * @see dlogutil_config_mode_set_compressed_memory_dump()
  */
 int dlogutil_config_mode_set_continuous(dlogutil_config_s *config);
 
@@ -318,6 +319,7 @@ int dlogutil_config_mode_set_continuous(dlogutil_config_s *config);
  * @retval TIZEN_ERROR_INVALID_PARAMETER Config was NULL
  * @see dlogutil_config_mode_set_continuous()
  * @see dlogutil_config_mode_set_dump()
+ * @see dlogutil_config_mode_set_compressed_memory_dump()
  */
 int dlogutil_config_mode_set_monitor(dlogutil_config_s *config);
 
@@ -334,9 +336,26 @@ int dlogutil_config_mode_set_monitor(dlogutil_config_s *config);
  * @retval TIZEN_ERROR_INVALID_PARAMETER Config was NULL
  * @see dlogutil_config_mode_set_continuous()
  * @see dlogutil_config_mode_set_monitor()
+ * @see dlogutil_config_mode_set_compressed_memory_dump()
  */
 int dlogutil_config_mode_set_dump(dlogutil_config_s *config, unsigned int entry_count);
 
+/**
+ * @brief Set log retrieval mode to dumping compressed historical logs
+ * @since_tizen 7.0
+ * @remarks This is similar to `cat /var/log/dlog/xyz`. After dumping all the logs, dlogutil_get_log() will signal this by returning TIZEN_ERROR_NO_DATA.
+ * @param[in,out] config The configuration struct
+ * @param[in] compression_buffer The name of the compression storage entry
+ * @return An error code
+ * @retval TIZEN_ERROR_NONE Success
+ * @retval TIZEN_ERROR_OUT_OF_MEMORY Not enough memory. Parameters left unchanged
+ * @retval TIZEN_ERROR_INVALID_PARAMETER Config or compression_buffer was NULL
+ * @see dlogutil_config_mode_set_continuous()
+ * @see dlogutil_config_mode_set_monitor()
+ * @see dlogutil_config_mode_set_dump()
+ */
+int dlogutil_config_mode_set_compressed_memory_dump(dlogutil_config_s *config, const char *compression_buffer);
+
 
 /**
  * @brief Finalizes the config into a state struct by connecting to buffers
index ad99b88..7ca8da4 100644 (file)
@@ -46,6 +46,7 @@ enum {
        DLOG_REQ_STDOUT                       = 6 << 1,
        DLOG_REQ_GLOBAL_ENABLE_DISABLE_STDOUT = 7 << 1,
        DLOG_REQ_GET_STDOUT                   = 8 << 1,
+       DLOG_REQ_HANDLE_COMPRESSED_LOGUTIL    = 9 << 1,
 };
 
 enum {
index 991e819..ee894ff 100644 (file)
@@ -47,6 +47,7 @@ struct parse_result {
                        dlogutil_mode_e mode; /**< Log printing mode */
                        unsigned dump_size; /**< If dump mode is enabled, number of logs to be dumped, or #DLOGUTIL_MAX_DUMP_SIZE if infinite */
                        dlogutil_sorting_order_e sort_by; /**< The timestamp type to be sorted on */
+                       char *compression; /**< The compression storage name */
                };
                const char *which_option; /**< An statically allocated string meaning an option, including the initial dash(es) */
                const char *bad_contents; /**< An string from input argument which has been not a valid argument */
index c391ff0..7753f4a 100644 (file)
@@ -60,7 +60,7 @@ struct fd_ops {
 
        /// Transition into a state that allows reading and printing out logs
        /// Return values > 0 mean everything went fine but we don't want to print
-       int (*prepare_print)(struct fd_info *fdi, int dump, bool monitor, struct log_filter *filter_object);
+       int (*prepare_print)(struct fd_info *fdi, int dump, bool monitor, struct log_filter *filter_object, char *compress);
 
        /// Clear the buffer
        int (*clear)(struct fd_info *fdi);
index 57dd2ad..092b719 100644 (file)
@@ -173,12 +173,13 @@ static void logger_destroy(struct fd_info *fdi)
        fdi->priv_data = NULL;
 }
 
-static int logger_prepare_print(struct fd_info *fdi, int dump, bool monitor, struct log_filter *filter_object)
+static int logger_prepare_print(struct fd_info *fdi, int dump, bool monitor, struct log_filter *filter_object, char *compress)
 {
        assert(fdi);
        struct logger_priv_data *const lpd = (struct logger_priv_data *)fdi->priv_data;
        assert(lpd);
        assert(filter_object);
+       assert(!compress);
 
        lpd->monitor = monitor;
 
index c410953..1bf13fb 100644 (file)
@@ -138,20 +138,22 @@ size_t get_filter_size(struct log_filter *filters)
 }
 
 /**
- * @brief Send a logger request
- * @details Send a logging request to the daemon
+ * @brief Send a standard logger request
+ * @details Send a standard (not compressed memory) logging request to the daemon
  * @param[in] filters Log filters to use
  * @param[in] dump Whether dumping or not
  * @param[in] sock_fd Socket file descriptor
  * @return 0 on success, -errno on failure
  */
-static int send_logger_request(struct log_filter *filters, int dump, bool monitor, int sock_fd)
+static int send_logger_request(struct log_filter *filters, int dump, bool monitor, int sock_fd, char *compress)
 {
        assert(filters);
        assert(sock_fd >= 0);
        assert(!dump || !monitor);
 
+       static const char compress_opt_str[] = " --compress ";
        size_t request_string_len = get_filter_size(filters) +
+               (compress ? (strlen(compress) + sizeof compress_opt_str - 1) : 0) +
                (dump ? 3 : 0) + // " -d"
                (monitor ? 3 : 0); // " -m"
 
@@ -176,6 +178,14 @@ static int send_logger_request(struct log_filter *filters, int dump, bool monito
                len += sizeof monitor_str - 1;
        }
 
+       if (compress) {
+               strncat(request_string, compress_opt_str, request_string_len - len);
+               len += sizeof compress_opt_str - 1;
+
+               strncat(request_string, compress, request_string_len - len);
+               len += strlen(compress);
+       }
+
        int needed = 0;
 
        for (list_head iter = log_filter_get_list(filters); iter; list_next(&iter)) {
@@ -219,7 +229,7 @@ static int send_logger_request(struct log_filter *filters, int dump, bool monito
        len += needed;
        request_string[len - 1] = '\0';
 
-       return send_dlog_request(sock_fd, DLOG_REQ_HANDLE_LOGUTIL, request_string, len);
+       return send_dlog_request(sock_fd, compress ? DLOG_REQ_HANDLE_COMPRESSED_LOGUTIL : DLOG_REQ_HANDLE_LOGUTIL, request_string, len);
 }
 
 /**
@@ -273,7 +283,17 @@ static void pipe_destroy(struct fd_info *fdi)
        fdi->priv_data = NULL;
 }
 
-static int pipe_prepare_print(struct fd_info *fdi, int dump, bool monitor, struct log_filter *filter_object)
+/* NB: compressed logs are handled almost exactly the same as regular logs,
+ * with only the setup being slightly different (see below). This is because
+ * the decompression is done entirely in the daemon (so the logs we receive
+ * are already decompressed and look the same as usual).
+ *
+ * The potential downside of this design would usually be that the daemon
+ * is critical so it would be better to relegate the load (so as not to
+ * delay incoming logs) but the memory logs are only supposed to be dumped
+ * manually and quite rarely so performance isn't a worry, while this
+ * lets us keep compression and decompression in the same executable. */
+static int pipe_prepare_print(struct fd_info *fdi, int dump, bool monitor, struct log_filter *filter_object, char *compress)
 {
        assert(filter_object);
        assert(fdi);
@@ -281,7 +301,7 @@ static int pipe_prepare_print(struct fd_info *fdi, int dump, bool monitor, struc
        assert(ppd);
        assert(ppd->sock_fd >= 0);
 
-       int r = send_logger_request(filter_object, dump, monitor, ppd->sock_fd);
+       int r = send_logger_request(filter_object, dump, monitor, ppd->sock_fd, compress);
        if (r < 0)
                return r;
 
index ae92cf9..96645f5 100644 (file)
@@ -175,7 +175,7 @@ EXPORT_API int dlogutil_config_connect(dlogutil_config_s *config, dlogutil_state
        log_id_t aliased[LOG_ID_MAX];
        for (int i = 0; i < LOG_ID_MAX; ++i)
                aliased[i] = LOG_ID_INVALID;
-       int fdi_cnt = create_initial_fdis(&fdi_ptrs, config->buffers, is_pipe, &conf, aliased);
+       int fdi_cnt = create_initial_fdis(&fdi_ptrs, config->buffers, is_pipe, config->mode == DLOGUTIL_MODE_COMPRESSED_MEMORY_DUMP, &conf, aliased);
        if (fdi_cnt < 0)
                return TIZEN_ERROR_IO_ERROR;
        if (fdi_cnt == 0)
@@ -225,6 +225,22 @@ EXPORT_API int dlogutil_config_mode_set_dump(dlogutil_config_s *config, unsigned
        return TIZEN_ERROR_NONE;
 }
 
+EXPORT_API int dlogutil_config_mode_set_compressed_memory_dump(dlogutil_config_s *config, const char *compress_buffer)
+{
+       CHECK_PARAM(config);
+       CHECK_PARAM(compress_buffer);
+
+       char *copy = strdup(compress_buffer);
+       if (!copy)
+               return TIZEN_ERROR_OUT_OF_MEMORY;
+
+       free(config->compress);
+       config->compress = copy;
+       config->mode = DLOGUTIL_MODE_COMPRESSED_MEMORY_DUMP;
+
+       return TIZEN_ERROR_NONE;
+}
+
 EXPORT_API int dlogutil_get_log(dlogutil_state_s *state, int timeout, dlogutil_entry_s **entry_out)
 {
        CHECK_PARAM(state);
index ffb11be..842488b 100644 (file)
@@ -38,7 +38,7 @@ bool all_buffers_known_disabled(const struct log_config *conf, int buffers)
                && !log_config_get_boolean(conf, "handle_kmsg", true);
 }
 
-int create_initial_fdis(struct fd_info ***fdis, int enabled_buffers, bool is_pipe, const struct log_config *conf, log_id_t aliased[LOG_ID_MAX])
+int create_initial_fdis(struct fd_info ***fdis, int enabled_buffers, bool is_pipe, bool is_compressed_memory, const struct log_config *conf, log_id_t aliased[LOG_ID_MAX])
 {
        assert(fdis);
        assert(conf);
@@ -76,14 +76,21 @@ int create_initial_fdis(struct fd_info ***fdis, int enabled_buffers, bool is_pip
                        continue;
 
                struct fd_info *fdi;
-               switch (i) {
-               case LOG_ID_KMSG:
-               case LOG_ID_SYSLOG:
+               if (is_compressed_memory) {
+                       /* Ignore `is_pipe`. This feature is done
+                        * via pipes always, even on the Android
+                        * Logger backend. */
                        fdi = fdi_create(&ops_pipe, i);
-                       break;
-               default:
-                       fdi = fdi_create(is_pipe ? &ops_pipe : &ops_logger, i);
-                       break;
+               } else {
+                       switch (i) {
+                       case LOG_ID_KMSG:
+                       case LOG_ID_SYSLOG:
+                               fdi = fdi_create(&ops_pipe, i);
+                               break;
+                       default:
+                               fdi = fdi_create(is_pipe ? &ops_pipe : &ops_logger, i);
+                               break;
+                       }
                }
                if (!fdi)
                        return -ENOMEM;
@@ -171,12 +178,12 @@ int put_logs_into_vector(struct fd_info **data_fds, int fd_count, struct sort_ve
        return TIZEN_ERROR_NONE;
 }
 
-static int state_prepare_single_print(dlogutil_state_s *state, int nfds)
+static int state_prepare_single_print(dlogutil_state_s *state, int nfds, char *compress)
 {
        assert(state);
        assert(state->mode != DLOGUTIL_MODE_NONPRINTING);
 
-       int r = state->data_fds[nfds]->ops->prepare_print(state->data_fds[nfds], state->dump_size, state->mode == DLOGUTIL_MODE_MONITOR, state->filter_object);
+       int r = state->data_fds[nfds]->ops->prepare_print(state->data_fds[nfds], state->dump_size, state->mode == DLOGUTIL_MODE_MONITOR, state->filter_object, compress);
        if (r < 0)
                return TIZEN_ERROR_IO_ERROR;
        if (r > 0) {
@@ -201,7 +208,7 @@ static int state_prepare_single_print(dlogutil_state_s *state, int nfds)
        return TIZEN_ERROR_NONE;
 }
 
-static int state_prepare_prints(dlogutil_state_s *state)
+static int state_prepare_prints(dlogutil_state_s *state, char *compress)
 {
        assert(state);
        assert(state->mode != DLOGUTIL_MODE_NONPRINTING);
@@ -211,7 +218,7 @@ static int state_prepare_prints(dlogutil_state_s *state)
                return TIZEN_ERROR_OUT_OF_MEMORY;
 
        for (int nfds = 0; nfds < state->fd_count; ++nfds) {
-               int r = state_prepare_single_print(state, nfds);
+               int r = state_prepare_single_print(state, nfds, compress);
                if (r != TIZEN_ERROR_NONE)
                        return r;
        }
@@ -268,7 +275,7 @@ int dlogutil_state_init(dlogutil_state_s *state, struct fd_info ***data_fds_ptr,
                return TIZEN_ERROR_OUT_OF_MEMORY;
 
        if (state->mode != DLOGUTIL_MODE_NONPRINTING) {
-               int r = state_prepare_prints(state);
+               int r = state_prepare_prints(state, config->compress);
                if (r != TIZEN_ERROR_NONE)
                        return r;
        }
index 4e167a8..b28d51c 100644 (file)
@@ -46,6 +46,6 @@ typedef struct dlogutil_state {
        log_id_t aliased[LOG_ID_MAX];
 } dlogutil_state_s;
 
-int create_initial_fdis(struct fd_info ***fdis, int enabled_buffers, bool is_pipe, const struct log_config *conf, log_id_t aliased[LOG_ID_MAX]);
+int create_initial_fdis(struct fd_info ***fdis, int enabled_buffers, bool is_pipe, bool is_compressed_memory, const struct log_config *conf, log_id_t aliased[LOG_ID_MAX]);
 int dlogutil_state_init(dlogutil_state_s *state, struct fd_info ***data_fds_ptr, int fd_count, dlogutil_config_s *config, bool sorting_needed, dlogutil_sorting_order_e real_sort_by, const struct log_config *conf, log_id_t aliased[LOG_ID_MAX]);
 int do_print_once(dlogutil_state_s *state, int timeout, dlogutil_entry_s **out);
index bdc3134..a582c15 100644 (file)
@@ -12,6 +12,7 @@ bool initialize_dlogutil_line_params(struct dlogutil_line_params *params, struct
        params->is_dumping = false;
        params->buf_id = LOG_ID_INVALID;
        params->file_path = NULL;
+       params->compression = NULL;
 
        params->filter = log_filter_new();
        if (!params->filter)
@@ -24,6 +25,7 @@ void free_dlogutil_line_params(struct dlogutil_line_params *params)
 {
        logfile_free(&params->file);
        log_filter_free(params->filter);
+       free(params->compression);
        free(params->file_path);
 }
 
@@ -76,6 +78,7 @@ static int get_dlogutil_params_from_argc_argv(int argc, char **argv, struct dlog
                        return -ENOMEM;
        }
 
+       params->compression = pr.compression ? strdup(pr.compression) : NULL;
        params->file.rotate_size_kbytes = pr.rotate_size_kbytes;
        params->file.max_rotated = pr.max_rotated;
        params->file.format.format = pr.format;
index 462e578..76e84da 100644 (file)
@@ -1,6 +1,7 @@
 #pragma once
 
 #include <log_file.h>
+#include <util_parser.h>
 
 /**
  * @brief Settings for output buffering functionality
@@ -16,6 +17,7 @@ struct buf_params {
 struct dlogutil_line_params {
        bool monitor; /**< Whether the monitor mode is enabled */
        bool is_dumping; /**< Whether the dump mode is enabled */
+       char *compression; /**< Compressed memory backup parameter */
        struct log_file file; /**< The file information structure */
        log_id_t buf_id; /**< The buffer being dumped, or 0 if none selected */
        char *file_path; /**< The file to be written to, or NULL if none selected */
index e0fd735..443567c 100644 (file)
@@ -1,6 +1,7 @@
 #include "log_buffer.h"
 #include "logger_internal.h"
 #include "subreader_dlogutil.h"
+#include "reader_memory.h"
 #include <ptrs_list.h>
 
 /**
@@ -192,7 +193,7 @@ static int service_writer_stdout(struct logger *server, struct writer *wr, struc
 
 /**
  * @brief Service util request
- * @details Handle a request from util
+ * @details Handle a standard (not compressed memory) request from util
  * @param[in] server The logger server
  * @param[in] wr The writer who sent the request
  * @param[in] msg The message containing the request
@@ -231,6 +232,15 @@ static int service_writer_handle_req_util(struct logger *server, struct writer *
                goto cleanup;
        }
 
+       if (params.compression) {
+               /* Memory compression is only available from the text
+                * config. libdlogutil clients have a separate request
+                * type to obtain data. Eventually it would be good to
+                * merge them together, but that requires some thought
+                * put into it. */
+               goto cleanup;
+       }
+
        if (params.file_path) {
                /* Do not trust writer-based readers (only config-based).
                 * The control socket's privilege checks are fairly lenient
@@ -283,6 +293,94 @@ cleanup:
        return retval;
 }
 
+static int service_writer_handle_req_compressed_memory_util(struct logger *server, struct writer *wr, struct dlog_control_msg *msg)
+{
+       assert(server);
+       assert(wr);
+       assert(msg);
+
+       assert(msg->request == DLOG_REQ_HANDLE_COMPRESSED_LOGUTIL);
+
+       if (msg->length <= sizeof(struct dlog_control_msg) ||
+           msg->length > sizeof(struct dlog_control_msg) + MAX_LOGGER_REQUEST_LEN)
+               return -EINVAL;
+
+       if (msg->data[msg->length - sizeof(struct dlog_control_msg)] != 0)
+               return -EINVAL;
+
+       __attribute__((cleanup(reader_free_ptr))) struct reader_memory *reader = NULL;
+
+       int retval;
+       __attribute__((cleanup(free_dlogutil_line_params))) struct dlogutil_line_params params;
+       if (!initialize_dlogutil_line_params(&params, (struct buf_params) { })) {
+               /* TODO: cleanup discards this value, so there isn't much
+                * point setting it. Ideally it would be attached to the
+                * reply but that's a protocol change so not worth it atm */
+               // retval = -ENOMEM;
+               goto cleanup;
+       }
+
+       retval = get_dlogutil_line_params(msg->data, &params);
+       if (retval < 0) {
+               // retval = -ENOMEM; // see above
+               goto cleanup;
+       }
+
+       if (params.file_path) {
+               /* Do not trust writer-based readers (only config-based).
+                * The control socket's privilege checks are fairly lenient
+                * so this prevents people from asking us to overwrite
+                * some potentially important files at logger privilege.
+                *
+                * At some point it would be good to be able to skip the
+                * middleman and become able to write to a file directly
+                * though. The daemon should become able to receive an
+                * opened file descriptor from a writer. */
+               // retval = -EPERM; // see above
+               goto cleanup;
+       }
+
+       if (!params.compression)
+               goto cleanup;
+
+       retval = reader_memory_init_with_writer(&reader, wr, server, params.compression, params.monitor, params.is_dumping);
+       if (retval != 0)
+               goto cleanup;
+
+       int pipe_fd[2] = { -1, -1 };
+       retval = create_fifo_fds(server, pipe_fd, 0, reader->is_dumping);
+       if (retval < 0)
+               goto cleanup;
+
+       retval = reader_add_subreader_dlogutil(&reader->common, params.filter, pipe_fd[1]);
+       if (retval != 0)
+               goto cleanup;
+
+       set_write_fd_entity(&reader->common.fd_entity_sink, pipe_fd[1]);
+       retval = send_pipe(wr->fd_entity.fd, pipe_fd[0]);
+       if (pipe_fd[0] > 0)
+               close(pipe_fd[0]);
+       if (retval)
+               goto cleanup;
+
+       retval = add_reader_memory(server, reader);
+       if (retval < 0)
+               goto cleanup;
+
+       reader = NULL;
+       return 0;
+
+cleanup:
+       /* NB: reply success means that stuff is still stable enough that the client
+        * can probably get a second chance, so return a success from the whole func
+        * so as not to drop the client */
+       retval = send_dlog_reply(wr->fd_entity.fd, DLOG_REQ_HANDLE_COMPRESSED_LOGUTIL, DLOG_REQ_RESULT_ERR, NULL, 0);
+       if (retval < 0)
+               printf("ERROR: both create_reader_from_dlogutil_line() and send_dlog_reply() failed\n");
+
+       return retval;
+}
+
 static int service_writer_handle_req_get_usage(struct logger *server, struct writer *wr, struct dlog_control_msg *msg)
 {
        assert(server);
@@ -384,6 +482,7 @@ static int service_writer_handle_req_ctrl(struct logger *server, struct writer *
        switch (msg->request) {
                case DLOG_REQ_CLEAR:                         ret = service_writer_handle_req_clear                       (server, wr, msg); break;
                case DLOG_REQ_HANDLE_LOGUTIL:                ret = service_writer_handle_req_util                        (server, wr, msg); break;
+               case DLOG_REQ_HANDLE_COMPRESSED_LOGUTIL:     ret = service_writer_handle_req_compressed_memory_util      (server, wr, msg); break;
                case DLOG_REQ_GET_CAPACITY:                  ret = service_writer_handle_req_get_capacity                (server, wr, msg); break;
                case DLOG_REQ_GET_USAGE:                     ret = service_writer_handle_req_get_usage                   (server, wr, msg); break;
                case DLOG_REQ_GLOBAL_ENABLE_DISABLE_STDOUT:  ret = service_writer_handle_req_global_enable_disable_stdout(server, wr, msg); break;
index 6f2f4ce..233272a 100644 (file)
@@ -22,6 +22,7 @@
 #include "logger_privileges.h"
 #include "subreader_file.h"
 #include "subreader_metrics.h"
+#include "subreader_memory.h"
 
 #include <metrics.h>
 #include <getopt.h>
@@ -222,6 +223,25 @@ int add_reader_pipe(struct logger *server, struct reader_pipe *reader)
        return ret;
 }
 
+int add_reader_memory(struct logger *server, struct reader_memory *reader)
+{
+       assert(reader);
+       assert(server);
+
+       int ret = add_reader_common(server, &reader->common);
+       if (ret < 0)
+               return ret;
+
+       // `readers_logger` actually accepts any readers
+       if (!list_add(&server->readers_logger, reader)) {
+               if (reader->common.fd_entity_sink.fd >= 0)
+                       remove_fd_entity(&server->epoll_common, &reader->common.fd_entity_sink);
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
 static int add_reader_logger(struct logger *server, struct reader_logger *reader)
 {
        assert(reader);
@@ -276,8 +296,40 @@ int create_fifo_fds(struct logger *server, int pipe_fd[2], int flags, bool dump)
        return ret;
 }
 
+static int create_memory_subreader_for_common(struct dlogutil_line_params *params, struct reader_common *reader, struct logger *server)
+{
+       struct log_compressed_storage *storage = log_compressed_storage_create(params->file.rotate_size_kbytes, params->compression);
+       if (!storage)
+               return -ENOMEM;
+
+       list_add(&server->compressed_memories, storage);
+
+       return reader_add_subreader_memory(reader, params->filter, storage);
+}
+
+static int create_memory_subreader_from_dlogutil_line(struct dlogutil_line_params *params, struct logger *server)
+{
+       assert(params);
+       assert(params->compression);
+
+       if (params->file_path) // We're writing to memory, not to file
+               return -EINVAL;
+
+       if (params->buf_id == LOG_ID_INVALID)
+               return -EINVAL;
+
+       struct reader_logger *const reader = g_backend.logger_readers[params->buf_id];
+       if (!reader)
+               return -ENOENT;
+
+       return create_memory_subreader_for_common(params, &reader->common, server);
+}
+
 static int create_logger_subreader_from_dlogutil_line(struct dlogutil_line_params *params)
 {
+       assert(params);
+       assert(!params->compression);
+
        if (params->file_path) {
                int retval = logfile_set_path(&params->file, params->file_path);
                if (retval < 0)
@@ -322,16 +374,27 @@ static int create_reader_pipe_from_dlogutil_line(struct dlogutil_line_params *pa
        if (params->buf_id == LOG_ID_INVALID)
                return -EINVAL;
 
-       if (params->file.path == NULL)
+       if ((params->file.path == NULL && !params->compression)
+       ||  (params->file.path != NULL &&  params->compression))
                return -EINVAL;
 
        retval = reader_pipe_init(&reader, params->buf_id, server, params->monitor, params->is_dumping);
        if (retval != 0)
                return retval;
 
-       retval = reader_add_subreader_file(&reader->common, params->filter, &params->file, server->buffers[params->buf_id]->sort_by);
-       if (retval != 0)
-               return retval;
+       if (params->file.path == NULL) {
+               assert(params->compression);
+               retval = create_memory_subreader_for_common(params, &reader->common, server);
+               if (retval != 0)
+                       return retval;
+       } else {
+               /* FIXME: in theory these could be added independently (1 reader 2 subs),
+                * but so far that has not been needed and it sounds too fragile to do in haste. */
+               assert(!params->compression);
+               retval = reader_add_subreader_file(&reader->common, params->filter, &params->file, server->buffers[params->buf_id]->sort_by);
+               if (retval != 0)
+                       return retval;
+       }
 
        *rd = reader;
        reader = NULL;
@@ -1141,7 +1204,10 @@ void parse_logfile_config(void *value, void *userdata)
 
        int r;
        if (g_backend.use_logger_by_default && is_core_buffer(params.buf_id)) {
-               r = create_logger_subreader_from_dlogutil_line(&params);
+               r = params.compression
+                       ? create_memory_subreader_from_dlogutil_line(&params, server)
+                       : create_logger_subreader_from_dlogutil_line(&params)
+               ;
        } else {
                struct reader_pipe *reader = NULL;
                r = create_reader_pipe_from_dlogutil_line(&params, server, &reader);
index b65b149..c264ce1 100644 (file)
@@ -31,6 +31,7 @@
 #include "fd_entity.h"
 #include "reader_common.h"
 #include "reader_logger.h"
+#include "reader_memory.h"
 #include "reader_pipe.h"
 #include "socket.h"
 #include "dlogutil_line.h"
@@ -136,6 +137,7 @@ struct logger {
        struct buf_params     buf_params;
        int                   exiting;
        struct qos_module     qos;
+       list_head             compressed_memories;
 };
 
 struct logger_config_data {
@@ -164,6 +166,7 @@ int service_writer_syslog(struct logger *server, struct writer *wr, struct epoll
 void logger_add_writer(struct logger *l, struct writer *wr);
 int create_fifo_fds(struct logger *server, int pipe_fd[2], int flags, bool dump);
 int add_reader_pipe(struct logger *server, struct reader_pipe *reader);
+int add_reader_memory(struct logger *server, struct reader_memory *reader);
 void flush_logfile_timely(struct log_file *file, struct timespec ts, int flush_time);
 int get_now(struct now_t *now);
 
diff --git a/src/logger/reader_memory.c b/src/logger/reader_memory.c
new file mode 100644 (file)
index 0000000..d543c78
--- /dev/null
@@ -0,0 +1,198 @@
+#include "reader_memory.h"
+#include "logger_internal.h"
+#include "log_compressed_storage.h"
+#include "compression_common.h"
+#include "fastlz.h"
+
+static void reader_memory_free(struct reader_common *_reader)
+{
+       struct reader_memory *reader = (struct reader_memory *) _reader;
+
+       if (reader->log_compressed_storage_reader_ptr)
+               log_compressed_storage_release_reader(reader->log_compressed_storage_reader_ptr);
+       free(reader->uncompressed_chunk);
+}
+
+static int print_out_logs(struct reader_common *_reader, struct now_t _time);
+
+static void unpin_and_free(struct reader_memory *reader, struct logger *server)
+{
+       list_remove(&server->readers_logger, reader);
+       remove_reader_fd_entities(server, &reader->common);
+       reader_free(&reader->common);
+}
+
+static void dispatch_event_reader_memory(struct logger *server, struct epoll_event *event, void *userdata)
+{
+       struct reader_memory *const rm = (struct reader_memory *) userdata;
+       assert(rm);
+
+       if (event->events & (EPOLLHUP | EPOLLERR)) {
+               unpin_and_free(rm, server);
+               return;
+       }
+
+       struct now_t now;
+       int r = get_now(&now);
+       if (r < 0) {
+               unpin_and_free(rm, server);
+               return ;
+       }
+
+       r = rm->common.service_reader(&rm->common, now);
+       if (r != 0) {
+               unpin_and_free(rm, server);
+               return;
+       }
+}
+
+static struct reader_memory *reader_memory_alloc(bool monitor, bool is_dumping)
+{
+       struct reader_memory *ret = calloc(1, sizeof(*ret));
+       if (!ret)
+               return NULL;
+
+       init_fd_entity(&ret->common.fd_entity_sink  , NULL, NULL);
+       init_fd_entity(&ret->common.fd_entity_source, NULL, NULL);
+       ret->common.free_reader = reader_memory_free;
+
+       ret->uncompressed_chunk = malloc(COMPRESSION_CHUNK_SIZE);
+       if (!ret->uncompressed_chunk) {
+               free(ret);
+               return NULL;
+       }
+       ret->uncompressed_chunk_size = 0;
+       ret->uncompressed_chunk_index = 0;
+
+       ret->common.service_reader = print_out_logs;
+       ret->monitor = monitor;
+       ret->is_dumping = is_dumping;
+       ret->log_compressed_storage_reader_ptr = NULL;
+
+       return ret;
+}
+
+bool find_str(void *element, void *userdata)
+{
+       return !strcmp(log_compressed_storage_get_name((log_compressed_storage *) element), (char *) userdata);
+}
+
+void ignore_dropped_log(const struct compression_entry *ce, void *user_data)
+{
+       /* A compressed chunk is typically too large to try and salvage it.
+        * Drop it, it's quite unlikely that this actually happens given
+        * that a client should process logs quite fast compared to how
+        * slow they are produced. */
+
+       fprintf(stderr, "reader_memory: dropped a compressed chunk of length %zu / %zu\n"
+               , ce->size_out
+               , ce->size_in
+       );
+}
+
+int reader_memory_init_with_writer(struct reader_memory **reader, struct writer *writer, struct logger *server,
+       char *compress, bool monitor, bool is_dumping)
+{
+       assert(reader);
+       assert(writer);
+       assert(writer->buf_ptr);
+       assert(server);
+       assert(compress);
+
+       log_compressed_storage *storage = list_find_if(server->compressed_memories, compress, find_str);
+       if (!storage)
+               return -EINVAL;
+
+       __attribute__((cleanup(reader_free_ptr))) struct reader_memory *ret = reader_memory_alloc(monitor, is_dumping);
+       if (!ret)
+               return -ENOMEM;
+
+       ret->log_compressed_storage_reader_ptr = log_compressed_storage_new_reader(storage,
+               is_dumping, monitor, ignore_dropped_log, ret);
+       if (!ret->log_compressed_storage_reader_ptr)
+               return -ENOMEM;
+
+       init_fd_entity(&ret->common.fd_entity_sink, dispatch_event_reader_memory, ret);
+       init_fd_entity(&ret->common.fd_entity_source, dispatch_event_reader_memory, ret);
+
+       *reader = ret;
+       ret = NULL;
+       return 0;
+}
+
+int spend_queued_chunk(struct reader_memory *reader)
+{
+       while (reader->uncompressed_chunk_index < reader->uncompressed_chunk_size) {
+               dlogutil_entry_s *const due = (dlogutil_entry_s *)(reader->uncompressed_chunk + reader->uncompressed_chunk_index);
+               if (!due->len)
+                       return EINVAL;
+
+               reader->uncompressed_chunk_index += due->len;
+
+               int r = list_foreach_ret(reader->common.subs, due, subreader_apply_log);
+               if (r != 0)
+                       return r;
+       }
+
+       return 0;
+}
+
+void reader_memory_decompress_chunk(struct reader_memory *reader, const struct compression_entry *ce)
+{
+       if (reader->uncompressed_chunk_index < reader->uncompressed_chunk_size)
+               return;
+
+       /* Could also allocate the chunk here instead of a "static" large one,
+        * which saves some memory overhead in the optimistic case, but
+        * adds a lot of juggling around instead. */
+
+       reader->uncompressed_chunk_index = 0;
+       reader->uncompressed_chunk_size = ce->size_in;
+
+       if (!ce->size_out){
+               // Data wasn't compressed
+               memcpy(reader->uncompressed_chunk, ce->compressed_data, ce->size_in);
+       } else {
+               int decompressed_size = fastlz_decompress(ce->compressed_data, ce->size_out, reader->uncompressed_chunk, ce->size_in);
+               assert((size_t) decompressed_size == ce->size_in);
+       }
+}
+
+/**
+ * @brief Print out logs
+ * @details Make sure the reader is up to date on printed logs
+ * @param[in] reader The reader to read the data
+ * @param[in] _time Unused timestamps
+ * @return 0 if data remains for the next iteration, 1 if the buffer is to be removed, else -1
+ */
+static int print_out_logs(struct reader_common *_reader, struct now_t _time)
+{
+       struct reader_memory *const reader = (struct reader_memory *) _reader;
+       assert(reader);
+
+       int r = reader_flush(_reader, (struct timespec){0, 0}, 0);
+       if (r != 0)
+               return r > 0;
+
+       r = spend_queued_chunk(reader);
+       if (r != 0)
+               return r > 0;
+
+       while (log_compressed_storage_reader_is_new_entry_available(reader->log_compressed_storage_reader_ptr)) {
+               const struct compression_entry *ce = log_compressed_storage_reader_get_new_entry(reader->log_compressed_storage_reader_ptr);
+               assert(ce); // `is_new_entry_available` guarantees this
+
+               reader_memory_decompress_chunk(reader, ce);
+
+               r = spend_queued_chunk(reader);
+               if (r != 0)
+                       return r > 0;
+       }
+
+       /* Treat all readers as dumping. This is because monitoring makes
+        * little sense here (monitor the source instead!) and because
+        * these readers have quite a lot of overhead (so it's best if
+        * they don't linger around too long, even if by accident). */
+       return 1;
+}
+
diff --git a/src/logger/reader_memory.h b/src/logger/reader_memory.h
new file mode 100644 (file)
index 0000000..55d3319
--- /dev/null
@@ -0,0 +1,23 @@
+#pragma once
+
+#include "reader_common.h"
+
+#include <stdbool.h>
+#include <stddef.h>
+
+struct log_compressed_storage_reader;
+struct writer;
+struct logger;
+
+struct reader_memory {
+       struct reader_common common;
+       struct log_compressed_storage_reader *log_compressed_storage_reader_ptr;
+       bool is_dumping;
+       bool monitor;
+       char *uncompressed_chunk;
+       size_t uncompressed_chunk_size;
+       size_t uncompressed_chunk_index;
+};
+
+int reader_memory_init_with_writer(struct reader_memory **reader, struct writer *writer, struct logger *server,
+       char *compress, bool monitor, bool is_dumping);
diff --git a/src/logger/subreader_memory.c b/src/logger/subreader_memory.c
new file mode 100644 (file)
index 0000000..43f5dec
--- /dev/null
@@ -0,0 +1,108 @@
+#include "queued_entry.h"
+#include "subreader_memory.h"
+#include "compression_common.h"
+
+/* Don't flush, even on an explicit flush request, if
+ * the buffer is not filled at least this much.
+ *
+ * Should be lower than the full chunk size by at least
+ * a full log entry including metadata (about 4200).
+ *
+ * Keep in mind flushes occur periodically, every 99 logs
+ * and/or 600ms by default, so while it would be good to
+ * respect flush requests they happen a bit too frequently
+ * to do that at the moment (since compressing single logs
+ * gives fairly underwhelming results). */
+#define NO_FLUSH_SIZE (COMPRESSION_CHUNK_SIZE - 10000)
+
+static void subreader_memory_free(void *userdata)
+{
+       struct subreader_memory *const srm = (struct subreader_memory *) userdata;
+       assert(srm);
+
+       free(srm->to_be_compressed);
+}
+
+static int subreader_memory_flush(void *userdata, struct timespec ts, int flush_time)
+{
+       struct subreader_memory *const srm = (struct subreader_memory *) userdata;
+       assert(srm);
+
+       /* Compression minimum buffer requirement.
+        * Keep this even if excessive flushing is
+        * fixed and the check below is removed. */
+       if (srm->to_be_compressed_size < 16)
+               return 0;
+
+       if (srm->to_be_compressed_size < NO_FLUSH_SIZE)
+               return 0;
+
+       /* Disregard failures (other than a mention), since
+        * we can't do anything about insufficient memory. */
+       if (!log_compressed_storage_add_new_entry(srm->storage, srm->to_be_compressed, srm->to_be_compressed_size)) {
+               fprintf(stderr, "subreader_memory: failed to compress a chunk of size %zu\n"
+                       , srm->to_be_compressed_size
+               );
+       }
+       srm->to_be_compressed_size = 0;
+
+       return 0;
+}
+
+static int subreader_memory_apply_log(const struct subreader_common *sub, const struct dlogutil_entry *due)
+{
+       assert(sub);
+       assert(due);
+
+       struct subreader_memory *const srm = (struct subreader_memory *) sub->sub_userdata;
+       assert(srm);
+
+       if (due->len + srm->to_be_compressed_size > COMPRESSION_CHUNK_SIZE) {
+               subreader_memory_flush(srm, (struct timespec){0, 0}, 0);
+               if (due->len + srm->to_be_compressed_size > COMPRESSION_CHUNK_SIZE)
+                       return -1;
+       }
+
+       memcpy(srm->to_be_compressed + srm->to_be_compressed_size, due, due->len);
+       srm->to_be_compressed_size += due->len;
+
+       return 0;
+}
+
+int reader_add_subreader_memory(struct reader_common *reader, struct log_filter *filter, struct log_compressed_storage *storage)
+{
+       assert(reader);
+       assert(filter);
+       assert(storage);
+
+       struct subreader_common *const sub = malloc(sizeof *sub);
+       struct subreader_memory *const srm = malloc(sizeof *srm);
+       if (!sub || !srm)
+               goto free_structs;
+
+       srm->storage = storage;
+       srm->to_be_compressed_size = 0;
+       srm->to_be_compressed = malloc(COMPRESSION_CHUNK_SIZE); // FIXME: Maybe should just be `char to_be_compressed[COMPRESSION_CHUNK_SIZE]` instead? Looks too huge though
+       if (!srm->to_be_compressed)
+               goto free_srm_innards;
+
+       sub->sub_userdata = srm;
+       sub->sub_destroy = subreader_memory_free;
+       sub->sub_apply_log = subreader_memory_apply_log;
+       sub->sub_flush = subreader_memory_flush;
+       sub->filter = log_filter_move(filter);
+
+       list_add(&reader->subs, sub);
+
+       return 0;
+
+free_srm_innards:
+       free(srm->to_be_compressed);
+
+free_structs:
+       free(srm);
+       free(sub);
+
+       return -ENOMEM;
+}
+
diff --git a/src/logger/subreader_memory.h b/src/logger/subreader_memory.h
new file mode 100644 (file)
index 0000000..0a1f168
--- /dev/null
@@ -0,0 +1,13 @@
+#pragma once
+
+#include "reader_common.h"
+#include "log_compressed_storage.h"
+
+struct subreader_memory {
+       struct log_compressed_storage *storage;
+       char *to_be_compressed;
+       size_t to_be_compressed_size;
+};
+
+int reader_add_subreader_memory(struct reader_common *reader, struct log_filter *filter, struct log_compressed_storage *storage);
+
index 70c3d97..41f2f05 100644 (file)
@@ -137,7 +137,7 @@ static int for_each_buffer(int enabled_buffers, int (*func)(dlogutil_state_s *st
  * @retval <0 Failure as denoted by value: -errno
  */
 static int do_print(dlogutil_mode_e mode, unsigned int dump_size, int enabled_buffers, dlogutil_sorting_order_e sort_by,
-       dlogutil_config_s *config, struct log_file *l_file)
+       dlogutil_config_s *config, struct log_file *l_file, const char *compress)
 {
        /* Optimisation for short-lived (i.e. dumping) instances.
         *
@@ -206,12 +206,13 @@ static int do_print(dlogutil_mode_e mode, unsigned int dump_size, int enabled_bu
                        dlogutil_config_buffer_add(config, (log_id_t) i);
 
        dlogutil_state_s *state;
-       if (mode == DLOGUTIL_MODE_MONITOR)
-               r = dlogutil_config_mode_set_monitor(config);
-       else if (mode == DLOGUTIL_MODE_CONTINUOUS)
-               r = dlogutil_config_mode_set_continuous(config);
-       else
-               r = dlogutil_config_mode_set_dump(config, dump_size);
+       switch (mode) {
+       case DLOGUTIL_MODE_MONITOR:                r = dlogutil_config_mode_set_monitor               (config           ); break;
+       case DLOGUTIL_MODE_CONTINUOUS:             r = dlogutil_config_mode_set_continuous            (config           ); break;
+       case DLOGUTIL_MODE_DUMP:                   r = dlogutil_config_mode_set_dump                  (config, dump_size); break;
+       case DLOGUTIL_MODE_COMPRESSED_MEMORY_DUMP: r = dlogutil_config_mode_set_compressed_memory_dump(config, compress ); break;
+       default: assert(false);
+       }
 
        if (!r)
                r = dlogutil_config_connect(config, &state);
@@ -369,7 +370,7 @@ int main(int argc, char **argv)
        int r;
        switch (pr.action) {
        case ACTION_PRINT: {
-               r = do_print(pr.mode, pr.dump_size, enabled_buffers, pr.sort_by, config, &l_file);
+               r = do_print(pr.mode, pr.dump_size, enabled_buffers, pr.sort_by, config, &l_file, pr.compression);
                break;
        }
        case ACTION_GET_CAPACITY: {
index 5994d0f..b34c8f5 100644 (file)
@@ -21,6 +21,7 @@ struct parse_result parse_options(int argc, char **argv)
        size_t rotate_size_kbytes = DEFAULT_ROTATE_SIZE_KB;
        size_t max_rotated = DEFAULT_ROTATE_NUM_FILES;
        const char *file_path = NULL;
+       char *compression = NULL;
        size_t write_buffer_size = 0;
        int enabled_buffers = 0;
        action_e action = ACTION_PRINT;
@@ -40,12 +41,13 @@ struct parse_result parse_options(int argc, char **argv)
 
        while (1) {
                static const struct option long_options[] = {
-                       {"tid"    , required_argument, NULL,   0},
-                       {"pid"    , required_argument, NULL,   1},
-                       {"version",       no_argument, NULL,   2},
-                       {"color"  , required_argument, NULL,   3},
-                       {"sort-by", required_argument, NULL,   4},
-                       {"help"   ,       no_argument, NULL, 'h'},
+                       {"tid"     , required_argument, NULL,   0},
+                       {"pid"     , required_argument, NULL,   1},
+                       {"version" ,       no_argument, NULL,   2},
+                       {"color"   , required_argument, NULL,   3},
+                       {"sort-by" , required_argument, NULL,   4},
+                       {"compress", required_argument, NULL,   5},
+                       {"help"    ,       no_argument, NULL, 'h'},
                        {0}
                };
                int option = getopt_long(argc, argv, "cdmt:gsf:r:n:v:b:u:e:h", long_options, NULL);
@@ -109,6 +111,10 @@ struct parse_result parse_options(int argc, char **argv)
                                        return (struct parse_result) { .status = PARSE_BAD_SORT_BY, .bad_contents = optarg, };
                        }
                        break;
+               case 5: /* memory compression */
+                       mode = DLOGUTIL_MODE_COMPRESSED_MEMORY_DUMP;
+                       compression = optarg;
+                       break;
                case 'd':
                        mode = DLOGUTIL_MODE_DUMP;
                        break;
@@ -208,6 +214,7 @@ struct parse_result parse_options(int argc, char **argv)
                .mode = mode,
                .dump_size = dump_size,
                .sort_by = sort_by,
+               .compression = compression,
        };
        filterspecs = NULL;
        pid_filters = NULL;
index 36a684a..fb5fddb 100644 (file)
@@ -70,10 +70,10 @@ int main(void)
 
        expected_ioctl = LOGGER_GET_LOG_LEN;
        ioctl_ret = -67;
-       assert(-67 == ops_logger.prepare_print(&fdi, 123, false, (struct log_filter *) 0xCA5CADE));
+       assert(-67 == ops_logger.prepare_print(&fdi, 123, false, (struct log_filter *) 0xCA5CADE, NULL));
 
        expected_ioctl = -1;
-       assert(!ops_logger.prepare_print(&fdi, 0, false, (struct log_filter *) 0xCA5CADE));
+       assert(!ops_logger.prepare_print(&fdi, 0, false, (struct log_filter *) 0xCA5CADE, NULL));
 
        fail_read = true;
        assert(-334 == ops_logger.read(&fdi));
index 815c899..6f44fa8 100644 (file)
@@ -79,7 +79,7 @@ int main(void)
 
        expected_ioctl = LOGGER_GET_LOG_LEN;
        ioctl_ret = 0;
-       assert(1 == ops_logger.prepare_print(&fdi, 123, false, (struct log_filter *) 0xCA5CADE));
+       assert(1 == ops_logger.prepare_print(&fdi, 123, false, (struct log_filter *) 0xCA5CADE, NULL));
 
        monitor_more_reads(3, &fdi);
        monitor_more_reads(6, &fdi);
index a1c6d9f..6cca43b 100644 (file)
@@ -50,26 +50,31 @@ int main(void)
        correct_send_data = "dlogutil filter0:V *:S";
        correct_send_datalen = strlen(correct_send_data) + 1;
        send_return = -1;
-       assert(ops_pipe.prepare_print(&info, false, false, filter) == -1);
+       assert(ops_pipe.prepare_print(&info, false, false, filter, NULL) == -1);
 
        correct_send_data = "dlogutil -d filter0:V *:S";
        correct_send_datalen = strlen(correct_send_data) + 1;
-       assert(ops_pipe.prepare_print(&info, true, false, filter) == -1);
+       assert(ops_pipe.prepare_print(&info, true, false, filter, NULL) == -1);
 
        log_filter_set_filterspec(filter, "filter1");
        correct_send_data = "dlogutil filter1:V filter0:V *:S";
        correct_send_datalen = strlen(correct_send_data) + 1;
-       assert(ops_pipe.prepare_print(&info, false, false, filter) == -1);
+       assert(ops_pipe.prepare_print(&info, false, false, filter, NULL) == -1);
 
        log_filter_set_tid(filter, 123);
        log_filter_set_pid(filter, 456);
        correct_send_data = "dlogutil --pid 456 --tid 123 filter1:V filter0:V *:S";
        correct_send_datalen = strlen(correct_send_data) + 1;
-       assert(ops_pipe.prepare_print(&info, false, false, filter) == -1);
+       assert(ops_pipe.prepare_print(&info, false, false, filter, NULL) == -1);
+
+       correct_request = DLOG_REQ_HANDLE_COMPRESSED_LOGUTIL;
+       correct_send_data = "dlogutil --compress membackup --pid 456 --tid 123 filter1:V filter0:V *:S";
+       correct_send_datalen = strlen(correct_send_data) + 1;
+       assert(ops_pipe.prepare_print(&info, false, false, filter, (char *) "membackup") == -1);
 
        send_return = 0;
        recv_pipe_fail = true;
-       assert(ops_pipe.prepare_print(&info, false, false, filter) == -EIO);
+       assert(ops_pipe.prepare_print(&info, false, false, filter, (char *) "membackup") == -EIO);
        recv_pipe_fail = false;
 
        // Too long filter list test
@@ -81,7 +86,7 @@ int main(void)
        /* This is a long filter. In fact, it's too long to be accepted
         * by ops_pipe.prepare_print, which should return -E2BIG. */
        log_filter_set_filterspec(filter, too_long_filter);
-       assert(ops_pipe.prepare_print(&info, false, false, filter) == -E2BIG);
+       assert(ops_pipe.prepare_print(&info, false, false, filter, (char *) "membackup") == -E2BIG);
 
        log_filter_free(filter);
        correct_sockfd = 0;
index 44e1d56..a90d580 100644 (file)
@@ -74,7 +74,7 @@ void big_filter_test()
        // NB: both strings end in a space; the input filter should be parsed correctly despite this
        strncat(very_big_string_for_comparison, "*:S", sizeof(very_big_string_for_comparison) - 1);
 
-       correct_sockfd = 3;
+       correct_sockfd = 4;
        correct_request = DLOG_REQ_HANDLE_LOGUTIL;
 
        send_return = 0;
@@ -84,7 +84,7 @@ void big_filter_test()
        correct_send_datalen = strlen(very_big_string_for_comparison) + 1;
        /* long data test */
        log_filter_set_filterspec(filter, just_big_enough_filter);
-       assert(ops_pipe.prepare_print(&info, false, false, filter) == 0);
+       assert(ops_pipe.prepare_print(&info, false, false, filter, NULL) == 0);
 
        correct_request = DLOG_REQ_CLEAR;
        correct_send_data = NULL;
@@ -144,11 +144,17 @@ int main(void)
        recv_pipe_fail = false;
        correct_send_data = "dlogutil --pid 456 --tid 123 filter1:V filter0:V *:S";
        correct_send_datalen = strlen(correct_send_data) + 1;
-       assert(ops_pipe.prepare_print(&info, false, false, filter) == 0);
+       assert(ops_pipe.prepare_print(&info, false, false, filter, NULL) == 0);
        assert(info.fd == 2);
 
+       correct_request = DLOG_REQ_HANDLE_COMPRESSED_LOGUTIL;
+       correct_send_data = "dlogutil --compress membackup --pid 456 --tid 123 filter1:V filter0:V *:S";
+       correct_send_datalen = strlen(correct_send_data) + 1;
+       assert(ops_pipe.prepare_print(&info, false, false, filter, (char *) "membackup") == 0);
+       assert(info.fd == 3);
+
        log_filter_free(filter);
-       correct_sockfd = 2;
+       correct_sockfd = 3;
        read_errno = 0;
 
        read_datalen = sizeof(dlogutil_entry_s) + 4;
@@ -210,7 +216,6 @@ ok:
        assert(ops_pipe.has_log(&info));
        assert(!ops_pipe.extract_entry(&info));
        __real_free(read_data);
-
        correct_sockfd = 1;
        correct_request = DLOG_REQ_CLEAR;
        correct_send_data = NULL;