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() */
12 #include "gcc-compat.h"
18 #include "select.cmdline.h"
21 #define GLOBAL_LIST_ATOMS \
24 ATOM(dirname, STRING) \
26 #define ATOM(x, y) { .name = #x, .type = AT_ ## y},
27 struct atom global_list_atoms[] = {
32 #define ATOM(x, y) gla_ ## x,
33 enum global_list_atoms {GLOBAL_LIST_ATOMS};
37 #define GLOBAL_SUMMARY_ATOMS \
42 #define ATOM(x, y) { .name = #x, .type = AT_ ## y},
43 struct atom global_summary_atoms[] = {
48 #define ATOM(x, y) gsa_ ## x,
49 enum global_summary_atoms {GLOBAL_SUMMARY_ATOMS};
53 #define USER_LIST_ATOMS \
54 ATOM(pw_name, STRING) \
58 ATOM(dirname, STRING) \
60 #define ATOM(x, y) { .name = #x, .type = AT_ ## y},
61 struct atom user_list_atoms[] = {
66 #define ATOM(x, y) ula_ ## x,
67 enum user_list_atoms {USER_LIST_ATOMS};
71 #define USER_SUMMARY_ATOMS \
72 ATOM(pw_name, STRING) \
78 #define ATOM(x, y) { .name = #x, .type = AT_ ## y},
79 struct atom user_summary_atoms[] = {
84 #define ATOM(x, y) usa_ ## x,
85 enum user_summary_atoms {USER_SUMMARY_ATOMS};
88 struct global_list_info {
92 struct format_info *fi;
95 struct global_summary_info {
96 /** Global dir count. */
98 /** Global files count. */
100 /** Global bytes count. */
106 struct user_list_info {
108 struct user_info *ui;
109 struct format_info *fi;
114 struct user_summary_info {
115 struct user_info *ui;
120 struct user_summary_line_info {
121 struct format_info *fi;
125 static FILE *output_file;
127 __printf_1_2 static int output(const char const *fmt, ...)
133 ret = vfprintf(output_file, fmt, argp);
135 return ret < 0? -E_OUTPUT : 1;
138 static int get_dir_name_by_number(uint64_t *dirnum, char **name)
140 char *result = NULL, *tmp;
142 uint64_t val = *dirnum;
143 struct osl_object obj;
149 obj.size = sizeof(val);
150 ret = osl(osl_get_row(dir_table, DT_NUM, &obj, &row));
153 ret = osl(osl_get_object(dir_table, row, DT_PARENT_NUM, &obj));
156 val = *(uint64_t *)obj.data;
157 ret = osl(osl_get_object(dir_table, row, DT_NAME, &obj));
160 pfx = (select_conf.print_base_dir_given || val)? (char *)obj.data : ".";
161 tmp = make_message("%s/%s", pfx, result? result : "");
177 static int get_dir_name_of_row(struct osl_row *dir_table_row, char **name)
179 struct osl_object obj;
183 ret = osl(osl_get_object(dir_table, dir_table_row, DT_NUM, &obj));
186 return get_dir_name_by_number((uint64_t *)obj.data, name);
189 static int get_dir_name_of_user_row(struct osl_row *user_table_row,
190 struct user_info *ui, char **dirname)
192 struct osl_object obj;
193 int ret = osl(osl_get_object(ui->table, user_table_row,
198 return get_dir_name_by_number((uint64_t *)obj.data, dirname);
201 static int get_num_files_of_row(struct osl_row *row, uint64_t *num_files)
203 struct osl_object obj;
204 int ret = osl(osl_get_object(dir_table, row, DT_FILES, &obj));
207 *num_files = *(uint64_t *)obj.data;
211 static int get_num_user_files(struct osl_row *row, struct user_info *ui,
214 struct osl_object obj;
215 int ret = osl(osl_get_object(ui->table, row, UT_FILES, &obj));
219 *num_files = *(uint64_t *)obj.data;
223 static int get_num_bytes_of_row(struct osl_row *row, uint64_t *num_bytes)
225 struct osl_object obj;
226 int ret = osl(osl_get_object(dir_table, row, DT_BYTES, &obj));
229 *num_bytes = *(uint64_t *)obj.data;
233 static int get_num_user_bytes(struct osl_row *row, struct user_info *ui,
236 struct osl_object obj;
237 int ret = osl(osl_get_object(ui->table, row, UT_BYTES, &obj));
241 *num_bytes = *(uint64_t *)obj.data;
245 static int check_loop_return(int ret, int loop_ret, int loop_osl_errno)
249 assert(ret == -E_OSL);
250 if (osl_errno != E_OSL_LOOP)
251 /* error not caused by loop function returning negative. */
253 assert(loop_ret < 0);
254 if (loop_ret == -E_LOOP_COMPLETE) /* no error */
256 if (loop_ret == -E_OSL) { /* osl error in loop function */
257 assert(loop_osl_errno);
258 osl_errno = loop_osl_errno;
263 static int adu_loop_reverse(struct osl_table *t, unsigned col_num, void *private_data,
264 osl_rbtree_loop_func *func, int *loop_ret, int *loop_osl_errno)
266 int ret = osl(osl_rbtree_loop_reverse(t, col_num, private_data, func));
267 return check_loop_return(ret, *loop_ret, *loop_osl_errno);
270 static int global_summary_loop_function(struct osl_row *row, void *data)
272 struct global_summary_info *gsi = data;
276 ret = get_num_files_of_row(row, &num);
279 gsi->num_files += num;
281 ret = get_num_bytes_of_row(row, &num);
284 gsi->num_bytes += num;
289 gsi->osl_errno = (ret == -E_OSL)? osl_errno : 0;
293 static int print_global_summary(struct format_info *fi)
297 struct global_summary_info gsi = {.num_dirs = 0};
299 union atom_value values[] = {
300 [gsa_dirs] = {.num_value = 0ULL},
301 [gsa_files] = {.num_value = 0ULL},
302 [gsa_size] = {.num_value = 0ULL}
305 ret = adu_loop_reverse(dir_table, DT_BYTES, &gsi,
306 global_summary_loop_function, &gsi.ret, &gsi.osl_errno);
309 values[gsa_dirs].num_value = (long long unsigned)gsi.num_dirs;
310 values[gsa_files].num_value = (long long unsigned)gsi.num_files;
311 values[gsa_size].num_value = (long long unsigned)gsi.num_bytes;
312 if (!select_conf.no_headers_given) {
313 ret = output("Global summary\n");
317 buf = format_items(fi, values);
318 ret = output("%s", buf);
323 static int user_summary_loop_function(struct osl_row *row, void *data)
325 struct user_summary_info *usi = data;
329 ret = get_num_user_files(row, usi->ui, &num);
332 usi->ui->files += num;
333 ret = get_num_user_bytes(row, usi->ui, &num);
336 usi->ui->bytes += num;
341 usi->osl_errno = (ret == -E_OSL)? osl_errno : 0;
345 static int compute_user_summary(struct user_info *ui, __a_unused void *data)
347 struct user_summary_info usi = {.ui = ui};
349 return adu_loop_reverse(ui->table, UT_BYTES, &usi, user_summary_loop_function,
350 &usi.ret, &usi.osl_errno);
353 static int print_user_summary_line(struct user_info *ui, void *data)
355 struct user_summary_line_info *usli = data;
356 union atom_value values[] = {
357 [usa_pw_name] = {.string_value = ui->pw_name?
359 [usa_uid] = {.num_value = (long long unsigned)ui->uid},
360 [usa_dirs] = {.num_value = (long long unsigned)ui->dirs},
361 [usa_files] = {.num_value = (long long unsigned)ui->files},
362 [usa_size] = {.num_value = (long long unsigned)ui->bytes}
365 int ret = -E_LOOP_COMPLETE;
370 buf = format_items(usli->fi, values);
371 ret = output("%s", buf);
377 static int name_comp(struct user_info *a, struct user_info *b)
379 char *x = a->pw_name;
380 char *y = b->pw_name;
389 static int uid_comp(struct user_info *a, struct user_info *b)
391 return -NUM_COMPARE(a->uid, b->uid);
394 static int dir_count_comp(struct user_info *a, struct user_info *b)
396 return NUM_COMPARE(a->dirs, b->dirs);
399 static int file_count_comp(struct user_info *a, struct user_info *b)
401 return NUM_COMPARE(a->files, b->files);
404 static int size_comp(struct user_info *a, struct user_info *b)
406 return NUM_COMPARE(a->bytes, b->bytes);
409 static int print_user_summary(struct format_info *fi)
412 int (*comp)(struct user_info *a, struct user_info *b);
413 struct user_summary_line_info usli = {
415 .count = select_conf.limit_arg
418 if (!select_conf.no_headers_given) {
419 ret = output("User summary\n");
423 ret = for_each_admissible_user(compute_user_summary, NULL);
426 switch (select_conf.user_summary_sort_arg) {
427 case user_summary_sort_arg_name:
430 case user_summary_sort_arg_uid:
433 case user_summary_sort_arg_dir_count:
434 comp = dir_count_comp;
436 case user_summary_sort_arg_file_count:
437 comp = file_count_comp;
439 case user_summary_sort_arg_size:
443 sort_hash_table(comp);
444 ret = for_each_admissible_user(print_user_summary_line, &usli);
445 if (ret == -E_LOOP_COMPLETE)
450 static int user_list_loop_function(struct osl_row *row, void *data)
452 struct user_list_info *uli = data;
453 union atom_value values[] = {
454 [ula_pw_name] = {.string_value = uli->ui->pw_name?
455 uli->ui->pw_name : "?"},
456 [ula_uid] = {.num_value = (long long unsigned)uli->ui->uid},
457 [ula_files] = {.num_value = 0ULL},
458 [ula_size] = {.num_value = 0ULL},
459 [ula_dirname] = {.string_value = NULL}
466 ret = -E_LOOP_COMPLETE;
470 ret = get_num_user_files(row, uli->ui, &num);
473 values[ula_files].num_value = num;
475 ret = get_num_user_bytes(row, uli->ui, &num);
478 values[ula_size].num_value = num;
480 ret = get_dir_name_of_user_row(row, uli->ui, &dirname);
483 values[ula_dirname].string_value = dirname;
485 buf = format_items(uli->fi, values);
487 ret = output("%s", buf);
495 uli->osl_errno = (ret == -E_OSL)? osl_errno : 0;
499 static int print_user_list(struct user_info *ui, void *data)
501 struct format_info *fi = data;
503 enum user_table_columns sort_column = UT_BYTES;
504 struct user_list_info uli = {
507 .count = select_conf.limit_arg
510 if (select_conf.list_sort_arg == list_sort_arg_file_count)
511 sort_column = UT_FILES;
513 if (!select_conf.no_headers_given) {
514 ret = output("%s (uid %u)\n",
515 ui->pw_name? ui->pw_name : "?", (unsigned)ui->uid);
519 return adu_loop_reverse(ui->table, sort_column, &uli, user_list_loop_function,
520 &uli.ret, &uli.osl_errno);
523 static int global_list_loop_function(struct osl_row *row, void *data)
525 struct global_list_info *gli = data;
526 union atom_value values[] = {
527 [gla_size] = {.num_value = 0ULL},
528 [gla_files] = {.num_value = 0ULL},
529 [gla_dirname] = {.string_value = NULL}
531 uint64_t num_files, num_bytes;
536 ret = -E_LOOP_COMPLETE;
540 ret = get_num_files_of_row(row, &num_files);
543 values[gla_files].num_value = (long long unsigned)num_files;
545 ret = get_num_bytes_of_row(row, &num_bytes);
548 values[gla_size].num_value = (long long unsigned)num_bytes;
550 ret = get_dir_name_of_row(row, &dirname);
553 values[gla_dirname].string_value = dirname;
555 buf = format_items(gli->fi, values);
557 ret = output("%s", buf);
566 gli->osl_errno = (ret == -E_OSL)? osl_errno : 0;
570 static int print_global_list(struct format_info *fi)
573 enum dir_table_columns sort_column = DT_BYTES;
574 struct global_list_info gli = {
576 .count = select_conf.limit_arg
579 if (!select_conf.no_headers_given) {
580 ret = output("Global list\n");
584 if (select_conf.list_sort_arg == list_sort_arg_file_count)
585 sort_column = DT_FILES;
586 return adu_loop_reverse(dir_table, sort_column, &gli,
587 global_list_loop_function, &gli.ret, &gli.osl_errno);
590 static int print_statistics(struct format_info *fi)
592 switch (select_conf.select_mode_arg) {
593 case select_mode_arg_global_list:
594 return print_global_list(fi);
595 case select_mode_arg_global_summary:
596 return print_global_summary(fi);
597 case select_mode_arg_user_list:
598 return for_each_admissible_user(print_user_list, fi);
599 case select_mode_arg_user_summary:
600 return print_user_summary(fi);
602 ERROR_LOG("bad select mode\n");
603 return -ERRNO_TO_ERROR(EINVAL);
606 static int open_pipe(char *path)
613 return ERRNO_TO_ERROR(errno);
616 return ERRNO_TO_ERROR(errno);
617 if (ret) { /* parent */
618 DEBUG_LOG("created process %d\n", ret);
620 output_file = fdopen(p[1], "w");
622 return ERRNO_TO_ERROR(errno);
626 if (p[0] != STDIN_FILENO)
627 dup2(p[0], STDIN_FILENO);
628 DEBUG_LOG("executing %s\n", path);
629 argc = split_args(path, &argv, " \t");
630 execvp(argv[0], argv);
631 ERROR_LOG("error executing %s: %s\n", path,
632 adu_strerror(ERRNO_TO_ERROR(errno)));
636 static int open_output_stream(void)
639 int ret, flags = O_WRONLY | O_CREAT;
641 if (!select_conf.output_given)
643 p = select_conf.output_arg;
645 case '\0': /* empty string */
648 if (!p[1]) /* "-" means stdout */
650 /* string starting with a dash */
654 if (!p[1]) /* ">" is invalid */
661 /* string starting with ">>" */
662 if (!p[2]) /* ">>" is invalid */
668 if (!p[1]) /* "|" is invalid */
672 default: /* args starts with no magic character */
677 output_file = stdout;
681 return -E_BAD_OUTPUT_ARG;
684 * glibc's 'x' mode to fopen is not portable, so use open() and
687 ret = open(p, flags, 0644);
689 return -ERRNO_TO_ERROR(errno);
690 output_file = fdopen(ret, "w");
692 return -ERRNO_TO_ERROR(errno);
696 int run_select_query(struct uid_range *admissible_uids, struct format_info *fi)
698 int ret = open_output_stream();
702 ret = open_dir_table(0);
706 ret = open_admissible_user_tables(admissible_uids);
710 ret = print_statistics(fi);
712 if (output_file && output_file != stdout) {
719 #define GLOBAL_LIST_DFLT_FMT "%(size:r:8) %(files:r:8) %(dirname)\n"
720 #define GLOBAL_SUMMARY_DFLT_FMT "#directories: %(dirs), #files: %(files), size: %(size)\n\n"
721 #define USER_LIST_DFLT_FMT "%(size:r:5) %(files:r:5) %(dirname)\n"
722 #define USER_SUMMARY_DFLT_FMT "%(pw_name:l:16) %(uid:r:5) %(dirs:r:5) %(files:r:5) %(size:r:5)\n"
724 static int setup_format_string(char *fmt, struct format_info **fi)
729 INFO_LOG("using default format string\n");
730 switch (select_conf.select_mode_arg) {
731 case select_mode_arg_global_list:
733 fmt = GLOBAL_LIST_DFLT_FMT;
734 atoms = global_list_atoms;
736 case select_mode_arg_global_summary:
738 fmt = GLOBAL_SUMMARY_DFLT_FMT;
739 atoms = global_summary_atoms;
741 case select_mode_arg_user_list:
743 fmt = USER_LIST_DFLT_FMT;
744 atoms = user_list_atoms;
746 case select_mode_arg_user_summary:
748 fmt = USER_SUMMARY_DFLT_FMT;
749 atoms = user_summary_atoms;
752 ERROR_LOG("bad select mode\n");
753 return -ERRNO_TO_ERROR(EINVAL);
755 INFO_LOG("format string: %s\n", fmt);
756 return parse_format_string(fmt, atoms, fi);
759 /* return: < 0: error, >0: OK, == 0: help given */
760 int parse_select_options(char *string, struct select_cmdline_parser_params *params,
761 struct uid_range **admissible_uids, struct format_info **fi)
763 int ret, num_uid_ranges;
771 ret = create_argv(string, &argv);
775 ret = select_cmdline_parser_ext(argc, argv, &select_conf, params);
779 if (select_conf.help_given || select_conf.detailed_help_given)
781 fmt = select_conf.format_arg;
783 ret = parse_uid_arg(select_conf.uid_arg, admissible_uids);
786 num_uid_ranges = ret;
787 ret = append_users(select_conf.user_arg, select_conf.user_given,
788 admissible_uids, num_uid_ranges);
791 return setup_format_string(fmt, fi);
793 line = select_conf.detailed_help_given?
794 select_args_info_detailed_help : select_args_info_help;
796 output_file = stdout;
797 for (; *line; line++) {
798 ret = output("%s\n", *line);
807 struct uid_range *admissible_uids = NULL;
809 struct format_info *fi;
810 struct select_cmdline_parser_params params = {
814 .check_ambiguity = 1,
818 ret = parse_select_options(conf.select_options_arg, ¶ms,
819 &admissible_uids, &fi);
821 ret = read_uid_file();
824 ret = run_select_query(admissible_uids, fi);
825 free_format_info(fi);
828 select_cmdline_parser_free(&select_conf);