2 * Copyright (C) 2008 Andre Noll <maan@systemlinux.org>
4 * Licensed under the GPL v2. For licencing details see COPYING.
7 /** \file select.c The select mode of adu. */
9 #include <dirent.h> /* readdir() */
11 #include "gcc-compat.h"
16 #include "portable_io.h"
18 /** The decimal representation of an uint64_t never exceeds that size. */
19 #define FORMATED_VALUE_SIZE 25
21 /* these get filled in by the select command. */
22 static char count_unit_buf[4] = "( )", size_unit_buf[4] = "( )";
24 enum global_stats_flags {
25 GSF_PRINT_DIRNAME = 1,
28 GSF_COMPUTE_SUMMARY = 8,
31 struct global_stats_info {
35 enum global_stats_flags flags;
38 enum user_stats_flags {
39 USF_PRINT_DIRNAME = 1,
42 USF_COMPUTE_SUMMARY = 8,
45 struct user_stats_info {
47 enum user_stats_flags flags;
53 static const uint64_t size_unit_divisors[] = {
54 [size_unit_arg_b] = 1ULL,
55 [size_unit_arg_k] = 1024ULL,
56 [size_unit_arg_m] = 1024ULL * 1024ULL,
57 [size_unit_arg_g] = 1024ULL * 1024ULL * 1024ULL,
58 [size_unit_arg_t] = 1024ULL * 1024ULL * 1024ULL * 1024ULL,
61 static const uint64_t count_unit_divisors[] = {
63 [count_unit_arg_n] = 1ULL,
64 [count_unit_arg_k] = 1000ULL,
65 [count_unit_arg_m] = 1000ULL * 1000ULL,
66 [count_unit_arg_g] = 1000ULL * 1000ULL * 1000ULL,
67 [count_unit_arg_t] = 1000ULL * 1000ULL * 1000ULL * 1000ULL,
70 static const char size_unit_abbrevs[] = " BKMGT";
71 static const char count_unit_abbrevs[] = " kmgt";
73 static enum enum_size_unit format_size_value(enum enum_size_unit unit,
74 uint64_t value, int print_unit, char *result)
76 enum enum_size_unit u = unit;
77 char unit_buf[2] = "\0\0";
79 if (unit == size_unit_arg_h) /* human readable */
80 for (u = size_unit_arg_b; u < size_unit_arg_t &&
81 value > size_unit_divisors[u + 1]; u++)
84 unit_buf[0] = size_unit_abbrevs[u];
85 sprintf(result, "%llu%s",
86 (long long unsigned)value / size_unit_divisors[u], unit_buf);
90 static enum enum_count_unit format_count_value(enum enum_count_unit unit,
91 uint64_t value, int print_unit, char *result)
93 enum enum_count_unit u = unit;
94 char unit_buf[2] = "\0\0";
96 if (unit == count_unit_arg_h) /* human readable */
97 for (u = count_unit_arg_n; u < count_unit_arg_t &&
98 value > count_unit_divisors[u + 1]; u++)
101 unit_buf[0] = count_unit_abbrevs[u];
102 sprintf(result, "%llu%s",
103 (long long unsigned)value / count_unit_divisors[u], unit_buf);
107 static int get_dir_name_by_number(uint64_t *dirnum, char **name)
109 char *result = NULL, *tmp;
111 uint64_t val = *dirnum;
112 struct osl_object obj = {.data = &val, .size = sizeof(val)};
116 ret = osl(osl_get_row(dir_table, DT_NUM, &obj, &row));
119 ret = osl(osl_get_object(dir_table, row, DT_NAME, &obj));
123 tmp = make_message("%s/%s", (char *)obj.data, result);
127 result = adu_strdup((char *)obj.data);
128 ret = osl(osl_get_object(dir_table, row, DT_PARENT_NUM, &obj));
131 val = *(uint64_t *)obj.data;
143 static int get_dir_name_of_row(struct osl_row *dir_table_row, char **name)
145 struct osl_object obj;
147 char *this_dir, *prefix = NULL;
150 ret = osl(osl_get_object(dir_table, dir_table_row, DT_NAME, &obj));
153 this_dir = adu_strdup((char *)obj.data);
154 ret = osl(osl_get_object(dir_table, dir_table_row, DT_PARENT_NUM, &obj));
157 if (!*(uint64_t *)obj.data) {
161 ret = get_dir_name_by_number((uint64_t *)obj.data, &prefix);
164 *name = make_message("%s/%s", prefix, this_dir);
171 static int user_stats_loop_function(struct osl_row *row, void *data)
173 struct user_stats_info *usi = data;
174 struct osl_object obj;
175 int ret, summary = usi->flags & GSF_COMPUTE_SUMMARY;
176 char formated_value[FORMATED_VALUE_SIZE];
179 if (!usi->count && !summary) {
180 ret = -E_LOOP_COMPLETE;
183 if (summary || (usi->count && (usi->flags & USF_PRINT_FILES))) {
185 ret = osl(osl_get_object(usi->ui->table, row, UT_FILES, &obj));
188 files = *(uint64_t *)obj.data;
189 if (usi->count && (usi->flags & USF_PRINT_FILES)) {
190 format_count_value(conf.count_unit_arg, files,
191 conf.count_unit_arg == count_unit_arg_h,
193 printf("\t%s%s", formated_value,
194 (usi->flags & (USF_PRINT_BYTES | USF_PRINT_DIRNAME))?
199 usi->ui->files += files;
201 if (summary || (usi->count && (usi->flags & USF_PRINT_BYTES))) {
203 ret = osl(osl_get_object(usi->ui->table, row, UT_BYTES, &obj));
206 bytes = *(uint64_t *)obj.data;
207 if (usi->count && (usi->flags & USF_PRINT_BYTES)) {
208 format_size_value(conf.size_unit_arg, bytes,
209 conf.size_unit_arg == size_unit_arg_h,
212 (usi->flags & USF_PRINT_FILES)? "" : "\t",
214 usi->flags & USF_PRINT_DIRNAME? "\t" : "\n"
218 usi->ui->bytes += bytes;
223 if (usi->count && (usi->flags & USF_PRINT_DIRNAME)) {
225 ret = osl(osl_get_object(usi->ui->table, row, UT_DIR_NUM, &obj));
228 ret = get_dir_name_by_number((uint64_t *)obj.data, &dirname);
232 (usi->flags & (USF_PRINT_BYTES | USF_PRINT_FILES))? "" : "\t",
241 usi->osl_errno = (ret == -E_OSL)? osl_errno : 0;
245 static int global_stats_loop_function(struct osl_row *row, void *data)
247 struct global_stats_info *gsi = data;
248 struct osl_object obj;
249 char *dirname, formated_value[FORMATED_VALUE_SIZE];
250 int ret, summary = gsi->flags & GSF_COMPUTE_SUMMARY;
253 if (!gsi->count && !summary) {
254 ret = -E_LOOP_COMPLETE;
257 if (summary || (gsi->count && (gsi->flags & GSF_PRINT_FILES))) {
259 ret = osl(osl_get_object(dir_table, row, DT_FILES, &obj));
262 files = *(uint64_t *)obj.data;
263 if (gsi->count && (gsi->flags & GSF_PRINT_FILES)) {
264 format_count_value(conf.count_unit_arg, files,
265 conf.count_unit_arg == count_unit_arg_h,
267 printf("\t%s%s", formated_value,
268 (gsi->flags & (GSF_PRINT_BYTES | GSF_PRINT_DIRNAME))?
274 if (summary || (gsi->count && (gsi->flags & GSF_PRINT_BYTES))) {
276 ret = osl(osl_get_object(dir_table, row, DT_BYTES, &obj));
279 bytes = *(uint64_t *)obj.data;
280 if (gsi->count && (gsi->flags & GSF_PRINT_BYTES)) {
281 format_size_value(conf.size_unit_arg, bytes,
282 conf.size_unit_arg == size_unit_arg_h,
285 (gsi->flags & GSF_PRINT_FILES)? "" : "\t",
287 (gsi->flags & GSF_PRINT_DIRNAME)? "\t" : "\n"
295 if (gsi->count && (gsi->flags & GSF_PRINT_DIRNAME)) {
296 ret = get_dir_name_of_row(row, &dirname);
300 (gsi->flags & (GSF_PRINT_BYTES | GSF_PRINT_FILES))? "" : "\t",
309 gsi->osl_errno = (ret == -E_OSL)? osl_errno : 0;
313 static int check_loop_return(int ret, int loop_ret, int loop_osl_errno)
317 assert(ret == -E_OSL);
318 if (osl_errno != E_OSL_LOOP)
319 /* error not caused by loop function returning negative. */
321 assert(loop_ret < 0);
322 if (loop_ret == -E_LOOP_COMPLETE) /* no error */
324 if (loop_ret == -E_OSL) { /* osl error in loop function */
325 assert(loop_osl_errno);
326 osl_errno = loop_osl_errno;
331 static int adu_loop_reverse(struct osl_table *t, unsigned col_num, void *private_data,
332 osl_rbtree_loop_func *func, int *loop_ret, int *loop_osl_errno)
334 int ret = osl(osl_rbtree_loop_reverse(t, col_num, private_data, func));
335 return check_loop_return(ret, *loop_ret, *loop_osl_errno);
338 static void print_global_summary(void)
340 char d[FORMATED_VALUE_SIZE], f[FORMATED_VALUE_SIZE],
341 s[FORMATED_VALUE_SIZE];
342 enum enum_count_unit ud, uf;
343 enum enum_size_unit us;
345 ud = format_count_value(conf.count_unit_arg, num_dirs, 0, d);
346 uf = format_count_value(conf.count_unit_arg, num_files, 0, f);
347 us = format_size_value(conf.size_unit_arg, num_bytes, 0, s);
349 printf("Global summary "
350 "(dirs(%c)/files(%c)/size(%c))\n"
352 count_unit_abbrevs[ud],
353 count_unit_abbrevs[uf],
354 size_unit_abbrevs[us],
360 static int print_user_summary_line(struct user_info *ui, __a_unused void *data)
362 char formated_dir_count[FORMATED_VALUE_SIZE],
363 formated_file_count[FORMATED_VALUE_SIZE],
364 formated_bytes[FORMATED_VALUE_SIZE ];
366 format_count_value(conf.count_unit_arg, ui->dirs,
367 conf.count_unit_arg == count_unit_arg_h,
369 format_count_value(conf.count_unit_arg, ui->files,
370 conf.count_unit_arg == count_unit_arg_h,
371 formated_file_count);
372 format_size_value(conf.size_unit_arg, ui->bytes,
373 conf.size_unit_arg == size_unit_arg_h,
375 printf("\t%s\t%u\t%s\t%s\t%s\n",
376 ui->pw_name? ui->pw_name : "?",
385 static void print_user_summary(void)
387 printf("User summary "
388 "(pw_name/uid/dirs%s/files%s/size%s):\n",
389 count_unit_buf, count_unit_buf, size_unit_buf);
390 for_each_admissible_user(print_user_summary_line, NULL);
393 static int print_user_stat(struct user_info *ui, __a_unused void *data)
396 struct user_stats_info usi = {
397 .count = conf.limit_arg,
401 usi.flags = USF_PRINT_DIRNAME | USF_PRINT_BYTES | USF_COMPUTE_SUMMARY;
402 printf("%s (uid %u), by size%s:\n",
403 ui->pw_name? ui->pw_name : "?", (unsigned)ui->uid,
405 ret = adu_loop_reverse(ui->table, UT_BYTES, &usi, user_stats_loop_function,
406 &usi.ret, &usi.osl_errno);
409 printf("\n%s (uid %u), by file count%s:\n",
410 ui->pw_name? ui->pw_name : "?", (unsigned)ui->uid,
412 usi.count = conf.limit_arg,
413 usi.flags = USF_PRINT_DIRNAME | USF_PRINT_FILES;
414 ret = adu_loop_reverse(ui->table, UT_FILES, &usi, user_stats_loop_function,
415 &usi.ret, &usi.osl_errno);
422 static int print_user_stats(void)
424 return for_each_admissible_user(print_user_stat, NULL);
427 static int print_statistics(void)
430 struct global_stats_info gsi = {
431 .count = conf.limit_arg,
432 .flags = GSF_PRINT_DIRNAME | GSF_PRINT_BYTES | GSF_COMPUTE_SUMMARY
435 printf("By size%s:\n",
437 ret = adu_loop_reverse(dir_table, DT_BYTES, &gsi,
438 global_stats_loop_function, &gsi.ret, &gsi.osl_errno);
443 gsi.count = conf.limit_arg;
444 gsi.flags = GSF_PRINT_DIRNAME | GSF_PRINT_FILES;
445 printf("By file count%s:\n",
447 ret = adu_loop_reverse(dir_table, DT_FILES, &gsi,
448 global_stats_loop_function, &gsi.ret, &gsi.osl_errno);
452 print_global_summary();
454 print_user_summary();
458 static int read_uid_file(void)
462 char *filename = get_uid_list_name(), *map;
463 int ret = mmap_full_file(filename, O_RDONLY, (void **)&map, &size, NULL);
467 INFO_LOG("failed to map %s\n", filename);
472 INFO_LOG("found %u uids in %s\n", (unsigned)num_uids, filename);
475 * Compute number of hash table bits. The hash table size must be a
476 * power of two and larger than the number of uids.
479 while (1 << bits < num_uids)
481 create_hash_table(bits);
482 for (n = 0; n < num_uids; n++) {
483 uint32_t uid = read_u32(map + n * sizeof(uid));
484 ret = search_uid(uid, OPEN_USER_TABLE, NULL);
489 adu_munmap(map, size);
497 if (conf.count_unit_arg != count_unit_arg_h)
498 count_unit_buf[1] = count_unit_abbrevs[conf.count_unit_arg];
500 count_unit_buf[0] = '\0';
501 if (conf.size_unit_arg != size_unit_arg_h)
502 size_unit_buf[1] = size_unit_abbrevs[conf.size_unit_arg];
504 size_unit_buf[0] = '\0';
506 ret = open_dir_table(0);
510 ret = read_uid_file();
514 ret = print_statistics();