2 * Copyright (c) 2016-2020, Samsung Electronics Co., Ltd. All rights reserved.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
23 #include <sys/epoll.h>
26 #include <sort_vector.h>
27 #include <dlog-internal.h>
29 #include "logutil_doc.h"
32 * @addtogroup DLOG_IMPLEMENTATION
34 * @defgroup DLOG_UTIL Util
35 * @brief The log retrieval utility
36 * @details The utility responsible for reading and processing logs for the user.
40 // buffers to use by default, when nothing specified
41 static const int default_buffers = (1 << LOG_ID_MAIN) | (1 << LOG_ID_SYSTEM) | (1 << LOG_ID_APPS);
43 typedef enum action_e {
49 static void show_version(const char *name)
51 printf("%s version: %s-%s\n", name, __DLOG_VERSION, __DLOG_RELEASE);
54 static int parse_options(int argc, char **argv, struct log_file *l_file, int *enabled_buffers, action_e *action,
55 dlogutil_config_s *config, dlogutil_mode_e *mode, unsigned int *dump_size, dlogutil_sorting_order_e *sort_by)
59 assert(enabled_buffers);
65 *mode = DLOGUTIL_MODE_CONTINUOUS;
66 *dump_size = DLOGUTIL_MAX_DUMP_SIZE;
69 static const struct option long_options[] = {
70 {"tid" , required_argument, NULL, 0},
71 {"pid" , required_argument, NULL, 1},
72 {"version", no_argument, NULL, 2},
73 {"color" , required_argument, NULL, 3},
74 {"sort-by", required_argument, NULL, 4},
75 {"help" , no_argument, NULL, 'h'},
78 int err_arg_nondigit = 0;
79 int option = getopt_long(argc, argv, "cdmt:gsf:r:n:v:b:u:h", long_options, NULL);
85 case 0: { /* tid filter */
87 if (sscanf(optarg, "%d", &tid) != 1)
89 else if (dlogutil_config_filter_tid(config, tid))
93 case 1: { /* pid filter */
95 if (sscanf(optarg, "%d", &pid) != 1)
97 else if (dlogutil_config_filter_pid(config, pid))
101 case 2: { /* version */
102 show_version(argv[0]);
105 case 3: /* colored headers */
106 l_file->colors_auto = false;
107 if (!strcmp(optarg, "always"))
108 l_file->format.color = true;
109 else if (!strcmp(optarg, "auto"))
110 l_file->colors_auto = true;
111 else if (!strcmp(optarg, "never"))
112 l_file->format.color = false;
114 ERR("Error: invalid color setting\n");
118 case 4: /* timestamp */
119 if (!strcmp(optarg, "default"))
120 *sort_by = DLOGUTIL_SORT_DEFAULT;
122 *sort_by = get_order_from_string(optarg);
123 if (*sort_by == DLOGUTIL_SORT_DEFAULT) {
124 ERR("Error: invalid sort by\n");
130 *mode = DLOGUTIL_MODE_DUMP;
133 *mode = DLOGUTIL_MODE_MONITOR;
136 *mode = DLOGUTIL_MODE_DUMP;
137 if (sscanf(optarg, "%u", dump_size) != 1)
138 err_arg_nondigit = 1;
141 *action = ACTION_CLEAR;
144 *action = ACTION_GET_CAPACITY;
147 log_id_t id = log_id_by_name(optarg);
148 if (id == LOG_ID_INVALID) {
149 ERR("Error: there is no buffer \"%s\"\n", optarg);
152 bit_set(enabled_buffers, id);
157 if (sscanf(optarg, "%u", &size) != 1)
158 err_arg_nondigit = 1;
160 dlogutil_config_sorting_disable(config);
162 dlogutil_config_sorting_enable_with_size(config, size);
166 if (logfile_set_path(l_file, optarg) < 0)
170 l_file->format.format = log_format_from_string(optarg);
171 if (l_file->format.format == FORMAT_OFF) {
172 ERR("Error: invalid format\n");
178 if (dlogutil_config_filter_filterspec(config, "*:S"))
182 if (sscanf(optarg, "%zu", &l_file->rotate_size_kbytes) != 1)
183 err_arg_nondigit = 1;
186 if (sscanf(optarg, "%zu", &l_file->max_rotated) != 1)
187 err_arg_nondigit = 1;
190 show_help(argv[0], true);
192 default: // invalid option or missing mandatory parameter
193 show_help(argv[0], false);
197 if (err_arg_nondigit) {
198 ERR("Error: -%c requires a numerical parameter\n", option);
203 while (optind < argc) {
204 int r = dlogutil_config_filter_filterspec(config, argv[optind++]);
206 case TIZEN_ERROR_INVALID_PARAMETER:
207 /* We assert filter is not NULL above, and the system should
208 * guarantee that argv[i] are also not NULL. Therefore this
209 * error always means invalid string contents passed by the
210 * user, for example `dlogutil :D`. */
211 show_help(argv[0], false);
213 case TIZEN_ERROR_OUT_OF_MEMORY:
215 case TIZEN_ERROR_NONE:
222 if (*enabled_buffers == 0)
223 *enabled_buffers = default_buffers;
228 ERR("Error: out of memory\n");
232 static void config_cleanup(dlogutil_config_s *const *config) {
234 dlogutil_config_destroy(*config);
237 static int print_buffer_capacity(dlogutil_state_s *state, log_id_t buffer)
240 int r = dlogutil_buffer_get_name(buffer, &name);
245 r = dlogutil_buffer_get_alias(state, buffer, &aliased);
248 if (aliased == LOG_ID_INVALID) {
249 printf("%s: disabled\n", name);
250 return TIZEN_ERROR_NONE;
251 } else if (aliased != buffer) {
252 const char *aliased_name;
253 int r = dlogutil_buffer_get_name(aliased, &aliased_name);
257 printf("%s: alias of %s\n", name, aliased_name);
258 return TIZEN_ERROR_NONE;
261 unsigned int usage, capacity;
262 r = dlogutil_buffer_get_capacity(state, buffer, &capacity);
266 r = dlogutil_buffer_get_usage(state, buffer, &usage);
270 printf("%s: %u KiB, of which %u used (%u%%)\n", name, BtoKiB(capacity), BtoKiB(usage), 100 * usage / capacity);
274 static int clear_buffer(dlogutil_state_s *state, log_id_t buffer)
277 int r = dlogutil_buffer_get_alias(state, buffer, &aliased);
280 if (aliased != buffer)
283 return dlogutil_buffer_clear(state, buffer);
286 static int for_each_buffer(int enabled_buffers, int (*func)(dlogutil_state_s *state, log_id_t buffer))
288 __attribute__((cleanup(config_cleanup))) dlogutil_config_s *c = dlogutil_config_create();
291 for (int i = 0; i < LOG_ID_MAX; ++i) {
292 const int single_buf_mask = (1 << i);
293 if (single_buf_mask & enabled_buffers) {
294 r = dlogutil_config_buffer_add(c, i);
302 r = dlogutil_config_connect(c, &s);
306 for (int i = 0; i < LOG_ID_MAX; ++i) {
307 const int single_buf_mask = (1 << i);
308 if (single_buf_mask & enabled_buffers) {
319 * @return An error code
321 * @retval 1 Silent failure
322 * @retval <0 Failure as denoted by value: -errno
324 static int do_print(dlogutil_mode_e mode, unsigned int dump_size, int enabled_buffers, dlogutil_sorting_order_e sort_by,
325 dlogutil_config_s *config, struct log_file *l_file)
327 /* Optimisation for short-lived (i.e. dumping) instances.
329 * We can assume that such instances will not see a timezone
330 * change happen during their lifetime, and even if they do,
331 * it is probably best if they ignore it (a dump would ideally
332 * behave as if it was atomic). This means we can optimize the
333 * behaviour where printing the timestamp requires us to check
334 * whether the timezone changed, which involves calling stat()
335 * on /etc/localtime, which is not cheap on its own and made
336 * even worse when it has to go through multiple symlinks.
338 * glibc has a feature where setting the TZ environmental var
339 * will cache the timezone, achieving precisely what we want. */
340 if (mode != DLOGUTIL_MODE_CONTINUOUS && !getenv("TZ"))
341 putenv((char *) "TZ=:/etc/localtime");
343 int r = l_file->path ? logfile_open(l_file) : 0;
346 ERR("Error while creating the output file: %m\n");
350 if (sort_by == DLOGUTIL_SORT_DEFAULT)
351 sort_by = get_format_sorting(l_file->format.format);
352 dlogutil_config_order_set(config, sort_by);
354 /* We implicitly assume three things here:
355 * 1. if a timestamp is available for one of the buffers,
356 * it is available for all of them (which is true),
357 * 2. enabled_buffers != 0 (set by parse_options),
358 * 3. we will sort by the format timestamp if it is available
359 * and the default if it is not (which isn't 100% true,
360 * but is a good approximation). */
363 // one_buffer will contain exactly one of the enabled buffers
364 int enabled_buffers_copy = enabled_buffers;
365 while ((enabled_buffers_copy & 1) != 1) {
366 enabled_buffers_copy >>= 1;
371 bool available = false;
372 if (sort_by != DLOGUTIL_SORT_DEFAULT) {
373 r = dlogutil_buffer_check_ts_type_available((dlogutil_buffer_e) one_buffer, sort_by, &available);
376 ERR("Error while checking timestamp availability: %m");
381 r = dlogutil_buffer_get_default_ts_type((dlogutil_buffer_e) one_buffer, &sort_by);
384 ERR("Error while fetching default timestamp type: %m");
389 /* TODO: this should be done right when parsing options, but
390 * for now clear() and getsize() don't support that yet */
391 for (int i = 0; i < LOG_ID_MAX; ++i)
392 if (enabled_buffers & (1 << i))
393 dlogutil_config_buffer_add(config, (log_id_t) i);
395 dlogutil_state_s *state;
396 if (mode == DLOGUTIL_MODE_MONITOR)
397 r = dlogutil_config_mode_set_monitor(config);
398 else if (mode == DLOGUTIL_MODE_CONTINUOUS)
399 r = dlogutil_config_mode_set_continuous(config);
401 r = dlogutil_config_mode_set_dump(config, dump_size);
404 r = dlogutil_config_connect(config, &state);
408 ERR("Error while preparing libdlogutil: %m");
413 __attribute__((cleanup(free_ptr))) dlogutil_entry_s *entry;
415 r = dlogutil_get_log(state, -1, &entry);
416 if (r == TIZEN_ERROR_NO_DATA)
420 ERR("Error while retrieving a log: %m");
424 r = logfile_write_with_rotation(entry, l_file, sort_by);
427 ERR("Error while printing a log: %m");
430 return 0; // Quiet failure for some reason
435 int main(int argc, char **argv)
437 int enabled_buffers = 0; // bitset
438 action_e action = ACTION_PRINT;
439 __attribute__ ((cleanup(logfile_free))) struct log_file l_file;
440 logfile_init(&l_file);
441 logfile_set_fd(&l_file, fileno(stdout), 0);
443 __attribute__((cleanup(config_cleanup))) dlogutil_config_s *config = dlogutil_config_create();
446 ERR("Error while initialising: %m\n");
449 dlogutil_mode_e mode;
450 unsigned int dump_size;
451 dlogutil_sorting_order_e sort_by = DLOGUTIL_SORT_DEFAULT;
453 int r = parse_options(argc, argv, &l_file, &enabled_buffers, &action, config, &mode, &dump_size, &sort_by);
455 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
459 r = do_print(mode, dump_size, enabled_buffers, sort_by, config, &l_file);
462 case ACTION_GET_CAPACITY: {
463 r = for_each_buffer(enabled_buffers, print_buffer_capacity);
467 r = for_each_buffer(enabled_buffers, clear_buffer);
472 static const char *action_names[] = {
473 [ACTION_PRINT] = "printing logs",
474 [ACTION_GET_CAPACITY] = "getting capacity",
475 [ACTION_CLEAR] = "clearing buffer",
478 ERR("Error while %s: %m\n", action_names[action]);
482 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;