printf("%s version: %s-%s\n", name, __DLOG_VERSION, __DLOG_RELEASE);
}
-static int enomem_err()
+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)
{
- ERR("Error: out of memory\n");
- return -ENOMEM;
+ 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 int parse_options(int argc, char **argv, struct log_file *l_file, int *enabled_buffers, action_e *action,
- dlogutil_config_s *config, dlogutil_mode_e *mode, unsigned int *dump_size, dlogutil_sorting_order_e *sort_by)
+static struct parse_result parse_options(int argc, char **argv)
{
- assert(argv);
- assert(l_file);
- assert(enabled_buffers);
- assert(action);
- assert(config);
- assert(mode);
- assert(sort_by);
-
- *mode = DLOGUTIL_MODE_CONTINUOUS;
- *dump_size = DLOGUTIL_MAX_DUMP_SIZE;
+ 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;
{"help" , no_argument, NULL, 'h'},
{0}
};
- int err_arg_nondigit = 0;
int option = getopt_long(argc, argv, "cdmt:gsf:r:n:v:b:u:e:h", long_options, NULL);
if (option < 0)
case 0: { /* tid filter */
int tid;
if (sscanf(optarg, "%d", &tid) != 1)
- err_arg_nondigit = 1;
- else if (dlogutil_config_filter_tid(config, tid))
- return enomem_err();
+ 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)
- err_arg_nondigit = 1;
- else if (dlogutil_config_filter_pid(config, pid))
- return enomem_err();
+ 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 */
- show_version(argv[0]);
- return 1;
+ return (struct parse_result) { .status = PARSE_VERSION_REQUESTED, };
}
case 3: /* colored headers */
- l_file->colors_auto = false;
+ colors_auto = false;
if (!strcmp(optarg, "always"))
- l_file->format.color = true;
+ colors_force = true;
else if (!strcmp(optarg, "auto"))
- l_file->colors_auto = true;
+ colors_auto = true;
else if (!strcmp(optarg, "never"))
- l_file->format.color = false;
- else {
- ERR("Error: invalid color setting\n");
- return -EINVAL;
- }
+ 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;
+ sort_by = DLOGUTIL_SORT_DEFAULT;
else {
- *sort_by = get_order_from_string(optarg);
- if (*sort_by == DLOGUTIL_SORT_DEFAULT) {
- ERR("Error: invalid sort by\n");
- return -EINVAL;
- }
+ 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;
+ mode = DLOGUTIL_MODE_DUMP;
break;
case 'm':
- *mode = DLOGUTIL_MODE_MONITOR;
+ mode = DLOGUTIL_MODE_MONITOR;
break;
case 't':
- *mode = DLOGUTIL_MODE_DUMP;
- if (sscanf(optarg, "%u", dump_size) != 1)
- err_arg_nondigit = 1;
+ 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;
+ action = ACTION_CLEAR;
break;
case 'g':
- *action = ACTION_GET_CAPACITY;
+ action = ACTION_GET_CAPACITY;
break;
case 'b': {
log_id_t id = log_id_by_name(optarg);
- if (id == LOG_ID_INVALID) {
- ERR("Error: there is no buffer \"%s\"\n", optarg);
- return -ENOENT;
- }
- bit_set(enabled_buffers, id);
+ 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)
- err_arg_nondigit = 1;
+ return (struct parse_result) { .status = PARSE_OPTION_NUMERICAL_ARGUMENT, .which_option = "-u", };
if (size == 0)
- dlogutil_config_sorting_disable(config);
+ sorting_settings = 0;
else
- dlogutil_config_sorting_enable_with_size(config, size);
+ sorting_settings = size;
break;
}
case 'f':
- if (logfile_set_path(l_file, optarg) < 0)
- return enomem_err();
+ file_path = optarg;
break;
case 'v': {
- l_file->format.format = log_format_from_string(optarg);
- if (l_file->format.format == FORMAT_OFF) {
- ERR("Error: invalid format\n");
- return -EINVAL;
- }
+ 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 (dlogutil_config_filter_filterspec(config, "*:S"))
- return enomem_err();
+ if (!list_add(&filterspecs, "*:S"))
+ return (struct parse_result) { .status = PARSE_OOM, };
break;
case 'r':
- if (sscanf(optarg, "%zu", &l_file->rotate_size_kbytes) != 1)
- err_arg_nondigit = 1;
+ 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", &l_file->max_rotated) != 1)
- err_arg_nondigit = 1;
+ if (sscanf(optarg, "%zu", &max_rotated) != 1)
+ return (struct parse_result) { .status = PARSE_OPTION_NUMERICAL_ARGUMENT, .which_option = "-n", };
break;
case 'e': {
- size_t buf_size;
- if (buffer_made) {
- ERR("Multiple -e\n");
- return -EINVAL;
- }
- if (sscanf(optarg, "%zu", &buf_size) != 1)
- err_arg_nondigit = 1;
- if (buf_size > 0)
- if (!logfile_init_buffer(l_file, buf_size))
- ERR("Warning: failed to create the write buffer\n");
- // Not an error, since we can continue
+ 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':
- show_help(argv[0], true);
- return 1;
+ return (struct parse_result) { .status = PARSE_HELP_REQUESTED, };
default: // invalid option or missing mandatory parameter
- show_help(argv[0], false);
- return -EINVAL;
- }
-
- if (err_arg_nondigit) {
- ERR("Error: -%c requires a numerical parameter\n", option);
- return -EINVAL;
+ return (struct parse_result) { .status = PARSE_NO_PARSE, };
}
}
while (optind < argc) {
- int r = dlogutil_config_filter_filterspec(config, argv[optind++]);
- switch (r) {
- case TIZEN_ERROR_INVALID_PARAMETER:
- /* We assert filter is not NULL above, and the system should
- * guarantee that argv[i] are also not NULL. Therefore this
- * error always means invalid string contents passed by the
- * user, for example `dlogutil :D`. */
- show_help(argv[0], false);
- return -EINVAL;
- case TIZEN_ERROR_OUT_OF_MEMORY:
- return enomem_err();
- case TIZEN_ERROR_NONE:
- continue;
- default:
- assert(false);
- }
+ /* 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)
- if (!logfile_init_buffer(l_file, DEFAULT_WRITE_BUFFER_SIZE))
- ERR("Warning: failed to create write buffer\n");
- // Again, not an error, since we can continue
-
- return 0;
+ 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) {
#ifndef UNIT_TEST
int main(int argc, char **argv)
{
- int enabled_buffers = 0; // bitset
- action_e action = ACTION_PRINT;
__attribute__ ((cleanup(logfile_free))) struct log_file l_file;
logfile_init(&l_file);
logfile_set_fd(&l_file, fileno(stdout), 0);
+ __attribute__((cleanup(parse_result_cleanup))) struct parse_result pr = parse_options(argc, argv);
+ switch (pr.status) {
+ case PARSE_OK:
+ break;
+ case PARSE_OOM:
+ ERR("Error: out of memory while cmdline parsing\n");
+ return EXIT_FAILURE;
+ case PARSE_BAD_COLOR:
+ ERR("Error: invalid color setting '%s'\n", pr.bad_contents);
+ return EXIT_FAILURE;
+ case PARSE_BAD_SORT_BY:
+ ERR("Error: invalid sort by '%s'\n", pr.bad_contents);
+ return EXIT_FAILURE;
+ case PARSE_BAD_BUFFER:
+ ERR("Error: there is no buffer '%s'\n", pr.bad_contents);
+ return EXIT_FAILURE;
+ case PARSE_BAD_FORMAT:
+ ERR("Error: invalid format '%s'\n", pr.bad_contents);
+ return EXIT_FAILURE;
+ case PARSE_OPTION_REPEATED:
+ ERR("Error: multiple %s\n", pr.which_option);
+ return EXIT_FAILURE;
+ case PARSE_OPTION_NUMERICAL_ARGUMENT:
+ ERR("Error: %s requires a numerical parameter\n", pr.which_option);
+ return EXIT_FAILURE;
+ case PARSE_VERSION_REQUESTED:
+ show_version(argv[0]);
+ return EXIT_SUCCESS;
+ case PARSE_HELP_REQUESTED:
+ show_help(argv[0], false);
+ return EXIT_SUCCESS;
+ case PARSE_NO_PARSE:
+ show_help(argv[0], true);
+ return EXIT_FAILURE;
+ }
+
+ l_file.colors_auto = pr.colors_auto;
+ l_file.format.color = pr.colors_force;
+ l_file.format.format = pr.format;
+ l_file.rotate_size_kbytes = pr.rotate_size_kbytes;
+ l_file.max_rotated = pr.max_rotated;
+
+ if (pr.file_path) {
+ if (logfile_set_path(&l_file, pr.file_path) < 0) {
+ ERR("Error: out of memory while logfile initialization\n");
+ return EXIT_FAILURE;
+ }
+ }
+ if (pr.write_buffer_size > 0) {
+ if (!logfile_init_buffer(&l_file, pr.write_buffer_size))
+ ERR("Warning: failed to create the write buffer\n");
+ }
+
__attribute__((cleanup(config_cleanup))) dlogutil_config_s *config = dlogutil_config_create();
if (!config) {
errno = ENOMEM;
ERR("Error while initialising: %m\n");
return EXIT_FAILURE;
}
- dlogutil_mode_e mode;
- unsigned int dump_size;
- dlogutil_sorting_order_e sort_by = DLOGUTIL_SORT_DEFAULT;
-
- int r = parse_options(argc, argv, &l_file, &enabled_buffers, &action, config, &mode, &dump_size, &sort_by);
- if (r)
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ if (pr.sorting_settings == 0)
+ dlogutil_config_sorting_disable(config);
+ else
+ dlogutil_config_sorting_enable_with_size(config, pr.sorting_settings);
+ for (list_head it = pr.filterspecs; it != NULL; list_next(&it)) {
+ int r = dlogutil_config_filter_filterspec(config, list_at(it));
+ switch (r) {
+ case TIZEN_ERROR_INVALID_PARAMETER:
+ /* The parser makes sure the value isn't NULL. Therefore this
+ * error always means invalid string contents passed by the
+ * user, for example `dlogutil :D`. */
+ show_help(argv[0], true);
+ return EXIT_FAILURE;
+ case TIZEN_ERROR_OUT_OF_MEMORY:
+ ERR("Error: out of memory while applying filterspec filter\n");
+ return EXIT_FAILURE;
+ case TIZEN_ERROR_NONE:
+ continue;
+ default:
+ assert(false);
+ }
+ }
+ for (list_head it = pr.pid_filters; it != NULL; list_next(&it)) {
+ if (dlogutil_config_filter_pid(config, *(pid_t *)list_at(it)))
+ ERR("Error: out of memory while applying PID filter\n");
+ }
+ for (list_head it = pr.tid_filters; it != NULL; list_next(&it)) {
+ if (dlogutil_config_filter_tid(config, *(pid_t *)list_at(it)))
+ ERR("Error: out of memory while applying TID filter\n");
+ }
- switch (action) {
+ int r;
+ switch (pr.action) {
case ACTION_PRINT: {
- r = do_print(mode, dump_size, enabled_buffers, sort_by, config, &l_file);
+ r = do_print(pr.mode, pr.dump_size, pr.enabled_buffers, pr.sort_by, config, &l_file);
break;
}
case ACTION_GET_CAPACITY: {
- r = for_each_buffer(enabled_buffers, print_buffer_capacity);
+ r = for_each_buffer(pr.enabled_buffers, print_buffer_capacity);
break;
}
case ACTION_CLEAR:
- r = for_each_buffer(enabled_buffers, clear_buffer);
+ r = for_each_buffer(pr.enabled_buffers, clear_buffer);
break;
}
[ACTION_CLEAR] = "clearing buffer",
};
errno = -r;
- ERR("Error while %s: %m\n", action_names[action]);
+ ERR("Error while %s: %m\n", action_names[pr.action]);
return EXIT_FAILURE;
}