#include <stdlib.h>
#include <fcntl.h>
-#include <getopt.h>
#include <sys/epoll.h>
#include <log_file.h>
-#include <sort_vector.h>
-#include <dlog-internal.h>
+#include <util_parser.h>
#include "logutil_doc.h"
// buffers to use by default, when nothing specified
static const int default_buffers = (1 << LOG_ID_MAIN) | (1 << LOG_ID_SYSTEM) | (1 << LOG_ID_APPS);
-typedef enum action_e {
- ACTION_PRINT = 0,
- ACTION_GET_CAPACITY,
- ACTION_CLEAR,
-} action_e;
-
static void show_version(const char *name)
{
printf("%s version: %s-%s\n", name, __DLOG_VERSION, __DLOG_RELEASE);
}
-struct parse_result {
- enum {
- PARSE_OK,
- PARSE_OOM,
- PARSE_BAD_COLOR,
- PARSE_BAD_SORT_BY,
- PARSE_BAD_BUFFER,
- PARSE_BAD_FORMAT,
- PARSE_OPTION_REPEATED,
- PARSE_OPTION_NUMERICAL_ARGUMENT,
- PARSE_VERSION_REQUESTED,
- PARSE_HELP_REQUESTED,
- PARSE_NO_PARSE,
- } status;
- union {
- struct {
- bool colors_auto;
- bool colors_force;
- log_print_format format;
- size_t rotate_size_kbytes;
- size_t max_rotated;
- const char *file_path;
- size_t write_buffer_size;
- int enabled_buffers;
- action_e action;
- list_head filterspecs;
- list_head pid_filters;
- list_head tid_filters;
- int sorting_settings;
- dlogutil_mode_e mode;
- unsigned dump_size;
- dlogutil_sorting_order_e sort_by;
- };
- const char *which_option;
- const char *bad_contents;
- };
-};
-
-void parse_result_cleanup(struct parse_result *pr)
-{
- if (pr && pr->status == PARSE_OK) {
- list_clear(&pr->filterspecs);
- list_clear_free_contents(&pr->pid_filters);
- list_clear_free_contents(&pr->tid_filters);
- }
-}
-
-static struct parse_result parse_options(int argc, char **argv)
-{
- bool colors_auto = true;
- bool colors_force = false;
- log_print_format format = FORMAT_BRIEF;
- size_t rotate_size_kbytes = DEFAULT_ROTATE_SIZE_KB;
- size_t max_rotated = DEFAULT_ROTATE_NUM_FILES;
- const char *file_path = NULL;
- size_t write_buffer_size = 0;
- int enabled_buffers = 0;
- action_e action = ACTION_PRINT;
- __attribute__((cleanup(list_clear))) list_head filterspecs = NULL;
- __attribute__((cleanup(list_clear_free_contents))) list_head pid_filters = NULL;
- __attribute__((cleanup(list_clear_free_contents))) list_head tid_filters = NULL;
- int sorting_settings = DEFAULT_SORT_BUFFER_SIZE;
- dlogutil_mode_e mode = DLOGUTIL_MODE_CONTINUOUS;
- unsigned dump_size = DLOGUTIL_MAX_DUMP_SIZE;
- dlogutil_sorting_order_e sort_by = DLOGUTIL_SORT_DEFAULT;
-
- bool buffer_made = false;
-
- 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'},
- {0}
- };
- int option = getopt_long(argc, argv, "cdmt:gsf:r:n:v:b:u:e:h", long_options, NULL);
-
- if (option < 0)
- break;
-
- switch (option) {
- case 0: { /* tid filter */
- int tid;
- if (sscanf(optarg, "%d", &tid) != 1)
- return (struct parse_result) { .status = PARSE_OPTION_NUMERICAL_ARGUMENT, .which_option = "--tid", };
- else {
- pid_t *insert = calloc(1, sizeof(pid_t));
- if (insert == NULL)
- return (struct parse_result) { .status = PARSE_OOM, };
- *insert = tid;
- if (!list_add(&tid_filters, insert)) {
- free(insert);
- return (struct parse_result) { .status = PARSE_OOM, };
- }
- }
- break;
- }
- case 1: { /* pid filter */
- int pid;
- if (sscanf(optarg, "%d", &pid) != 1)
- return (struct parse_result) { .status = PARSE_OPTION_NUMERICAL_ARGUMENT, .which_option = "--pid", };
- else {
- pid_t *insert = calloc(1, sizeof(pid_t));
- if (insert == NULL)
- return (struct parse_result) { .status = PARSE_OOM, };
- *insert = pid;
- if (!list_add(&tid_filters, insert)) {
- free(insert);
- return (struct parse_result) { .status = PARSE_OOM, };
- }
- }
- break;
- }
- case 2: { /* version */
- return (struct parse_result) { .status = PARSE_VERSION_REQUESTED, };
- }
- case 3: /* colored headers */
- colors_auto = false;
- if (!strcmp(optarg, "always"))
- colors_force = true;
- else if (!strcmp(optarg, "auto"))
- colors_auto = true;
- else if (!strcmp(optarg, "never"))
- colors_force = false;
- else
- return (struct parse_result) { .status = PARSE_BAD_COLOR, .bad_contents = optarg, };
- break;
- case 4: /* timestamp */
- if (!strcmp(optarg, "default"))
- sort_by = DLOGUTIL_SORT_DEFAULT;
- else {
- sort_by = get_order_from_string(optarg);
- if (sort_by == DLOGUTIL_SORT_DEFAULT)
- return (struct parse_result) { .status = PARSE_BAD_SORT_BY, .bad_contents = optarg, };
- }
- break;
- case 'd':
- mode = DLOGUTIL_MODE_DUMP;
- break;
- case 'm':
- mode = DLOGUTIL_MODE_MONITOR;
- break;
- case 't':
- mode = DLOGUTIL_MODE_DUMP;
- if (sscanf(optarg, "%u", &dump_size) != 1)
- return (struct parse_result) { .status = PARSE_OPTION_NUMERICAL_ARGUMENT, .which_option = "-t", };
- break;
- case 'c':
- action = ACTION_CLEAR;
- break;
- case 'g':
- action = ACTION_GET_CAPACITY;
- break;
- case 'b': {
- log_id_t id = log_id_by_name(optarg);
- if (id == LOG_ID_INVALID)
- return (struct parse_result) { .status = PARSE_BAD_BUFFER, .bad_contents = optarg, };
- bit_set(&enabled_buffers, id);
- break;
- }
- case 'u': {
- unsigned size;
- if (sscanf(optarg, "%u", &size) != 1)
- return (struct parse_result) { .status = PARSE_OPTION_NUMERICAL_ARGUMENT, .which_option = "-u", };
- if (size == 0)
- sorting_settings = 0;
- else
- sorting_settings = size;
- break;
- }
- case 'f':
- file_path = optarg;
- break;
- case 'v': {
- format = log_format_from_string(optarg);
- if (format == FORMAT_OFF)
- return (struct parse_result) { .status = PARSE_BAD_FORMAT, .bad_contents = optarg, };
- break;
- }
- case 's':
- if (!list_add(&filterspecs, "*:S"))
- return (struct parse_result) { .status = PARSE_OOM, };
- break;
- case 'r':
- if (sscanf(optarg, "%zu", &rotate_size_kbytes) != 1)
- return (struct parse_result) { .status = PARSE_OPTION_NUMERICAL_ARGUMENT, .which_option = "-r", };
- break;
- case 'n':
- if (sscanf(optarg, "%zu", &max_rotated) != 1)
- return (struct parse_result) { .status = PARSE_OPTION_NUMERICAL_ARGUMENT, .which_option = "-n", };
- break;
- case 'e': {
- if (buffer_made)
- return (struct parse_result) { .status = PARSE_OPTION_REPEATED, .which_option = "-e", };
- if (sscanf(optarg, "%zu", &write_buffer_size) != 1)
- return (struct parse_result) { .status = PARSE_OPTION_NUMERICAL_ARGUMENT, .which_option = "-e", };
- buffer_made = true;
- break;
- }
- case 'h':
- return (struct parse_result) { .status = PARSE_HELP_REQUESTED, };
- default: // invalid option or missing mandatory parameter
- return (struct parse_result) { .status = PARSE_NO_PARSE, };
- }
- }
-
- while (optind < argc) {
- /* TODO: We don't want to call log_filter_set_filterspec/dlogutil_config_filter_filterspec here,
- * as we want the option parser to be general. Unfortunately, this means that the user has to parse
- * the filterspecs and not us. Any idea on how to make this nicer? */
- if (!list_add(&filterspecs, argv[optind++]))
- return (struct parse_result) { .status = PARSE_OOM, };
- }
-
- if (enabled_buffers == 0)
- enabled_buffers = default_buffers;
-
- if (!buffer_made && mode == DLOGUTIL_MODE_DUMP)
- write_buffer_size = DEFAULT_WRITE_BUFFER_SIZE;
-
- struct parse_result ret = (struct parse_result) {
- .status = PARSE_OK,
- .colors_auto = colors_auto,
- .colors_force = colors_force,
- .format = format,
- .rotate_size_kbytes = rotate_size_kbytes,
- .max_rotated = max_rotated,
- .file_path = file_path,
- .write_buffer_size = write_buffer_size,
- .enabled_buffers = enabled_buffers,
- .action = action,
- .filterspecs = filterspecs,
- .pid_filters = pid_filters,
- .tid_filters = tid_filters,
- .sorting_settings = sorting_settings,
- .mode = mode,
- .dump_size = dump_size,
- .sort_by = sort_by,
- };
- filterspecs = NULL;
- pid_filters = NULL;
- tid_filters = NULL;
- return ret;
-}
-
static void config_cleanup(dlogutil_config_s *const *config) {
assert(config);
dlogutil_config_destroy(*config);
ERR("Error: out of memory while applying TID filter\n");
}
+ int enabled_buffers = pr.enabled_buffers != 0 ? pr.enabled_buffers : default_buffers;
+
int r;
switch (pr.action) {
case ACTION_PRINT: {
- r = do_print(pr.mode, pr.dump_size, pr.enabled_buffers, pr.sort_by, config, &l_file);
+ r = do_print(pr.mode, pr.dump_size, enabled_buffers, pr.sort_by, config, &l_file);
break;
}
case ACTION_GET_CAPACITY: {
- r = for_each_buffer(pr.enabled_buffers, print_buffer_capacity);
+ r = for_each_buffer(enabled_buffers, print_buffer_capacity);
break;
}
case ACTION_CLEAR:
- r = for_each_buffer(pr.enabled_buffers, clear_buffer);
+ r = for_each_buffer(enabled_buffers, clear_buffer);
break;
}
--- /dev/null
+#include <getopt.h>
+#include <util_parser.h>
+#include <sort_vector.h>
+
+#define DEFAULT_WRITE_BUFFER_SIZE 1048576
+
+void parse_result_cleanup(struct parse_result *pr)
+{
+ if (pr && pr->status == PARSE_OK) {
+ list_clear(&pr->filterspecs);
+ list_clear_free_contents(&pr->pid_filters);
+ list_clear_free_contents(&pr->tid_filters);
+ }
+}
+
+struct parse_result parse_options(int argc, char **argv)
+{
+ bool colors_auto = true;
+ bool colors_force = false;
+ log_print_format format = FORMAT_BRIEF;
+ size_t rotate_size_kbytes = DEFAULT_ROTATE_SIZE_KB;
+ size_t max_rotated = DEFAULT_ROTATE_NUM_FILES;
+ const char *file_path = NULL;
+ size_t write_buffer_size = 0;
+ int enabled_buffers = 0;
+ action_e action = ACTION_PRINT;
+ __attribute__((cleanup(list_clear))) list_head filterspecs = NULL;
+ __attribute__((cleanup(list_clear_free_contents))) list_head pid_filters = NULL;
+ __attribute__((cleanup(list_clear_free_contents))) list_head tid_filters = NULL;
+ int sorting_settings = DEFAULT_SORT_BUFFER_SIZE;
+ dlogutil_mode_e mode = DLOGUTIL_MODE_CONTINUOUS;
+ unsigned dump_size = DLOGUTIL_MAX_DUMP_SIZE;
+ dlogutil_sorting_order_e sort_by = DLOGUTIL_SORT_DEFAULT;
+
+ bool buffer_made = false;
+
+ 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'},
+ {0}
+ };
+ int option = getopt_long(argc, argv, "cdmt:gsf:r:n:v:b:u:e:h", long_options, NULL);
+
+ if (option < 0)
+ break;
+
+ switch (option) {
+ case 0: { /* tid filter */
+ int tid;
+ if (sscanf(optarg, "%d", &tid) != 1)
+ return (struct parse_result) { .status = PARSE_OPTION_NUMERICAL_ARGUMENT, .which_option = "--tid", };
+ else {
+ pid_t *insert = calloc(1, sizeof(pid_t));
+ if (insert == NULL)
+ return (struct parse_result) { .status = PARSE_OOM, };
+ *insert = tid;
+ if (!list_add(&tid_filters, insert)) {
+ free(insert);
+ return (struct parse_result) { .status = PARSE_OOM, };
+ }
+ }
+ break;
+ }
+ case 1: { /* pid filter */
+ int pid;
+ if (sscanf(optarg, "%d", &pid) != 1)
+ return (struct parse_result) { .status = PARSE_OPTION_NUMERICAL_ARGUMENT, .which_option = "--pid", };
+ else {
+ pid_t *insert = calloc(1, sizeof(pid_t));
+ if (insert == NULL)
+ return (struct parse_result) { .status = PARSE_OOM, };
+ *insert = pid;
+ if (!list_add(&tid_filters, insert)) {
+ free(insert);
+ return (struct parse_result) { .status = PARSE_OOM, };
+ }
+ }
+ break;
+ }
+ case 2: { /* version */
+ return (struct parse_result) { .status = PARSE_VERSION_REQUESTED, };
+ }
+ case 3: /* colored headers */
+ colors_auto = false;
+ if (!strcmp(optarg, "always"))
+ colors_force = true;
+ else if (!strcmp(optarg, "auto"))
+ colors_auto = true;
+ else if (!strcmp(optarg, "never"))
+ colors_force = false;
+ else
+ return (struct parse_result) { .status = PARSE_BAD_COLOR, .bad_contents = optarg, };
+ break;
+ case 4: /* timestamp */
+ if (!strcmp(optarg, "default"))
+ sort_by = DLOGUTIL_SORT_DEFAULT;
+ else {
+ sort_by = get_order_from_string(optarg);
+ if (sort_by == DLOGUTIL_SORT_DEFAULT)
+ return (struct parse_result) { .status = PARSE_BAD_SORT_BY, .bad_contents = optarg, };
+ }
+ break;
+ case 'd':
+ mode = DLOGUTIL_MODE_DUMP;
+ break;
+ case 'm':
+ mode = DLOGUTIL_MODE_MONITOR;
+ break;
+ case 't':
+ mode = DLOGUTIL_MODE_DUMP;
+ if (sscanf(optarg, "%u", &dump_size) != 1)
+ return (struct parse_result) { .status = PARSE_OPTION_NUMERICAL_ARGUMENT, .which_option = "-t", };
+ break;
+ case 'c':
+ action = ACTION_CLEAR;
+ break;
+ case 'g':
+ action = ACTION_GET_CAPACITY;
+ break;
+ case 'b': {
+ log_id_t id = log_id_by_name(optarg);
+ if (id == LOG_ID_INVALID)
+ return (struct parse_result) { .status = PARSE_BAD_BUFFER, .bad_contents = optarg, };
+ bit_set(&enabled_buffers, id);
+ break;
+ }
+ case 'u': {
+ unsigned size;
+ if (sscanf(optarg, "%u", &size) != 1)
+ return (struct parse_result) { .status = PARSE_OPTION_NUMERICAL_ARGUMENT, .which_option = "-u", };
+ if (size == 0)
+ sorting_settings = 0;
+ else
+ sorting_settings = size;
+ break;
+ }
+ case 'f':
+ file_path = optarg;
+ break;
+ case 'v': {
+ format = log_format_from_string(optarg);
+ if (format == FORMAT_OFF)
+ return (struct parse_result) { .status = PARSE_BAD_FORMAT, .bad_contents = optarg, };
+ break;
+ }
+ case 's':
+ if (!list_add(&filterspecs, "*:S"))
+ return (struct parse_result) { .status = PARSE_OOM, };
+ break;
+ case 'r':
+ if (sscanf(optarg, "%zu", &rotate_size_kbytes) != 1)
+ return (struct parse_result) { .status = PARSE_OPTION_NUMERICAL_ARGUMENT, .which_option = "-r", };
+ break;
+ case 'n':
+ if (sscanf(optarg, "%zu", &max_rotated) != 1)
+ return (struct parse_result) { .status = PARSE_OPTION_NUMERICAL_ARGUMENT, .which_option = "-n", };
+ break;
+ case 'e': {
+ if (buffer_made)
+ return (struct parse_result) { .status = PARSE_OPTION_REPEATED, .which_option = "-e", };
+ if (sscanf(optarg, "%zu", &write_buffer_size) != 1)
+ return (struct parse_result) { .status = PARSE_OPTION_NUMERICAL_ARGUMENT, .which_option = "-e", };
+ buffer_made = true;
+ break;
+ }
+ case 'h':
+ return (struct parse_result) { .status = PARSE_HELP_REQUESTED, };
+ default: // invalid option or missing mandatory parameter
+ return (struct parse_result) { .status = PARSE_NO_PARSE, };
+ }
+ }
+
+ while (optind < argc) {
+ /* TODO: We don't want to call log_filter_set_filterspec/dlogutil_config_filter_filterspec here,
+ * as we want the option parser to be general. Unfortunately, this means that the user has to parse
+ * the filterspecs and not us. Any idea on how to make this nicer? */
+ if (!list_add(&filterspecs, argv[optind++]))
+ return (struct parse_result) { .status = PARSE_OOM, };
+ }
+
+ if (!buffer_made && mode == DLOGUTIL_MODE_DUMP)
+ write_buffer_size = DEFAULT_WRITE_BUFFER_SIZE;
+
+ struct parse_result ret = (struct parse_result) {
+ .status = PARSE_OK,
+ .colors_auto = colors_auto,
+ .colors_force = colors_force,
+ .format = format,
+ .rotate_size_kbytes = rotate_size_kbytes,
+ .max_rotated = max_rotated,
+ .file_path = file_path,
+ .write_buffer_size = write_buffer_size,
+ .enabled_buffers = enabled_buffers,
+ .action = action,
+ .filterspecs = filterspecs,
+ .pid_filters = pid_filters,
+ .tid_filters = tid_filters,
+ .sorting_settings = sorting_settings,
+ .mode = mode,
+ .dump_size = dump_size,
+ .sort_by = sort_by,
+ };
+ filterspecs = NULL;
+ pid_filters = NULL;
+ tid_filters = NULL;
+ return ret;
+}