X-Git-Url: http://git.tuebingen.mpg.de/?p=adu.git;a=blobdiff_plain;f=adu.c;h=bc0689320ffe1823da87e04f7682d5648f51a3f8;hp=b3ec80e2377c49e8df37ee2288530212a0e200fa;hb=f638c88fad8a1350cf56d5f60ddef297ece92805;hpb=9e41a10fbee567866d78903b11646d341cfd9882 diff --git a/adu.c b/adu.c index b3ec80e..bc06893 100644 --- a/adu.c +++ b/adu.c @@ -1,16 +1,21 @@ -/** \file adu.c The main functions used by all modes of operation. */ +/* + * Copyright (C) 2008 Andre Noll + * + * Licensed under the GPL v2. For licencing details see COPYING. + */ + +/** \file adu.c \brief The main functions used by all modes of operation. */ #include "adu.h" #include /* readdir() */ #include #include "format.h" +#include "user.h" +#include "select.cmdline.h" #include "select.h" - -#include "gcc-compat.h" #include "cmdline.h" #include "fd.h" #include "string.h" #include "error.h" -#include "portable_io.h" DEFINE_ERRLIST; int osl_errno; @@ -24,81 +29,12 @@ struct gengetopt_args_info conf; /** Options passed to --select-options. */ struct select_args_info select_conf; -/** The number of different uids found so far. */ -uint32_t num_uids = 0; - -/** This is always a power of two. It is set in create_hash_table(). */ -static uint32_t uid_hash_table_size; - -/** - * Contains info for each user that owns at least one regular file. - * - * Even users that are not taken into account because of the --uid - * option occupy a slot in this hash table. This allows to find out - * quicky whether a uid is admissible. And yes, this has to be fast. - */ -static struct user_info *uid_hash_table; - -static inline int ui_used(struct user_info *ui) -{ - return ui->flags & UI_FL_SLOT_USED; -} - -static inline int ui_admissible(struct user_info *ui) -{ - return ui->flags & UI_FL_ADMISSIBLE; -} /** * The table containing the directory names and statistics. */ struct osl_table *dir_table = NULL; -/** - * Compare the size of two directories - * - * \param obj1 Pointer to the first object. - * \param obj2 Pointer to the second object. - * - * This function first compares the size values as usual integers. If they compare as - * equal, the address of \a obj1 and \a obj2 are compared. So this compare function - * returns zero if and only if \a obj1 and \a obj2 point to the same memory area. - */ -static int size_compare(const struct osl_object *obj1, const struct osl_object *obj2) -{ - uint64_t d1 = *(uint64_t *)obj1->data; - uint64_t d2 = *(uint64_t *)obj2->data; - int ret = NUM_COMPARE(d2, d1); - - if (ret) - return ret; - //INFO_LOG("addresses: %p, %p\n", obj1->data, obj2->data); - return NUM_COMPARE(obj2->data, obj1->data); -} - -/** - * Compare two osl objects pointing to unsigned integers of 64 bit size. - * - * \param obj1 Pointer to the first integer. - * \param obj2 Pointer to the second integer. - * - * \return The values required for an osl compare function. - * - * \sa osl_compare_func, osl_hash_compare(). - */ -static int uint64_compare(const struct osl_object *obj1, - const struct osl_object *obj2) -{ - uint64_t d1 = read_u64((const char *)obj1->data); - uint64_t d2 = read_u64((const char *)obj2->data); - - if (d1 < d2) - return 1; - if (d1 > d2) - return -1; - return 0; -} - static struct osl_column_description dir_table_cols[] = { [DT_NAME] = { .storage_type = OSL_MAPPED_STORAGE, @@ -142,39 +78,6 @@ static struct osl_table_description dir_table_desc = { .column_descriptions = dir_table_cols, }; -/* - * The columns of the per-user tables. - * - * Adu tracks disk usage on a per-user basis. For each user, a user table is - * being created. The rows of the user table have three columns: The directory - * number that may be resolved to the path using the directory table, the - * number of bytes and the number of files in that directory owned by the given - * user. - */ -static struct osl_column_description user_table_cols[] = { - [UT_DIR_NUM] = { - .storage_type = OSL_MAPPED_STORAGE, - .storage_flags = OSL_RBTREE | OSL_FIXED_SIZE | OSL_UNIQUE, - .name = "dir_num", - .compare_function = uint64_compare, - .data_size = sizeof(uint64_t) - }, - [UT_BYTES] = { - .storage_type = OSL_MAPPED_STORAGE, - .storage_flags = OSL_RBTREE | OSL_FIXED_SIZE, - .compare_function = size_compare, - .name = "num_bytes", - .data_size = sizeof(uint64_t) - }, - [UT_FILES] = { - .storage_type = OSL_MAPPED_STORAGE, - .storage_flags = OSL_RBTREE | OSL_FIXED_SIZE, - .compare_function = size_compare, - .name = "num_files", - .data_size = sizeof(uint64_t) - }, -}; - /** * The log function. * @@ -203,88 +106,13 @@ __printf_2_3 void __log(int ll, const char* fmt,...) va_end(argp); } -static int open_user_table(struct user_info *ui, int create) -{ - int ret; - struct passwd *pw; - - ui->desc = adu_malloc(sizeof(*ui->desc)); - ui->desc->num_columns = NUM_UT_COLUMNS; - ui->desc->flags = 0; - ui->desc->column_descriptions = user_table_cols; - ui->desc->dir = adu_strdup(conf.database_dir_arg); - ui->desc->name = make_message("%u", (unsigned)ui->uid); - pw = getpwuid(ui->uid); - if (pw && pw->pw_name) - ui->pw_name = adu_strdup(pw->pw_name); - - INFO_LOG(".............................uid #%u: %u\n", - (unsigned)num_uids, (unsigned)ui->uid); - if (create) { - ret = osl(osl_create_table(ui->desc)); - if (ret < 0) - goto err; - num_uids++; - } - ret = osl(osl_open_table(ui->desc, &ui->table)); - if (ret < 0) - goto err; - return 1; -err: - free((char *)ui->desc->name); - free((char *)ui->desc->dir); - free(ui->pw_name); - free(ui->desc); - ui->desc->name = NULL; - ui->desc->dir = NULL; - ui->desc = NULL; - ui->table = NULL; - ui->flags = 0; - return ret; -} - -int for_each_admissible_user(int (*func)(struct user_info *, void *), - void *data) -{ - struct user_info *ui = uid_hash_table; - - if (!ui) - return -ERRNO_TO_ERROR(EFAULT); - - for (; ui < uid_hash_table + uid_hash_table_size; ui++) { - int ret; - - if (!ui_used(ui) || !ui_admissible(ui)) - continue; - ret = func(ui, data); - if (ret < 0) - return ret; - } - return 1; -} - -#define PRIME1 0xb11924e1 -#define PRIME2 0x01000193 - -void create_hash_table(unsigned bits) -{ - uid_hash_table_size = 1 << bits; - uid_hash_table = adu_calloc(uid_hash_table_size * - sizeof(struct user_info)); -} - -static void free_hash_table(void) -{ - free(uid_hash_table); - uid_hash_table = NULL; -} - static void close_dir_table(void) { int ret; if (!dir_table) return; + NOTICE_LOG("closing dir table\n"); ret = osl(osl_close_table(dir_table, OSL_MARK_CLEAN)); if (ret < 0) ERROR_LOG("failed to close dir table: %s\n", adu_strerror(-ret)); @@ -292,37 +120,10 @@ static void close_dir_table(void) dir_table = NULL; } -static int close_user_table(struct user_info *ui, __a_unused void *data) -{ - int ret; - - ret = osl(osl_close_table(ui->table, OSL_MARK_CLEAN)); - if (ret < 0) - ERROR_LOG("failed to close user table %u: %s\n", - (unsigned) ui->uid, adu_strerror(-ret)); - free((char *)ui->desc->name); - ui->desc->name = NULL; - free((char *)ui->desc->dir); - ui->desc->dir = NULL; - free(ui->pw_name); - ui->pw_name = NULL; - free(ui->desc); - ui->desc = NULL; - ui->table = NULL; - ui->flags = 0; - return 1; -} - -static void close_user_tables(void) -{ - for_each_admissible_user(close_user_table, NULL); -} - -void close_all_tables(void) +static void close_all_tables(void) { close_dir_table(); close_user_tables(); - free_hash_table(); } static void signal_handler(int s) @@ -350,101 +151,22 @@ static int init_signals(void) return 1; } -/* - * We use a hash table of size s=2^uid_hash_bits to map the uids into the - * interval [0..s]. Hash collisions are treated by open addressing, i.e. - * unused slots in the table are used to store different uids that hash to the - * same slot. - * - * If a hash collision occurs, different slots are successively probed in order - * to find an unused slot for the new uid. Probing is implemented via a second - * hash function that maps the uid to h=(uid * PRIME2) | 1, which is always an - * odd number. - * - * An odd number is sufficient to make sure each entry of the hash table gets - * probed for probe_num between 0 and s-1 because s is a power of two, hence - * the second hash value has never a common divisor with the hash table size. - * IOW: h is invertible in the ring [0..s]. - */ -static uint32_t double_hash(uint32_t uid, uint32_t probe_num) -{ - return (uid * PRIME1 + ((uid * PRIME2) | 1) * probe_num) - % uid_hash_table_size; -} - -static int uid_is_admissible(uint32_t uid, struct uid_range *urs) +int open_dir_table(int create) { - struct uid_range *ur; - int ret = 1; - if (!urs) /* empty array means all uids are allowed */ + if (dir_table) return 1; - FOR_EACH_UID_RANGE(ur, urs) - if (ur->low <= uid && ur->high >= uid) - goto out; - ret = 0; -out: - DEBUG_LOG("uid %u is %sadmissible\n", (unsigned)uid, - ret? "" : "not "); - return ret; -} - -int search_uid(uint32_t uid, struct uid_range *urs, - enum search_uid_flags flags, struct user_info **ui_ptr) -{ - uint32_t p; - - for (p = 0; p < uid_hash_table_size; p++) { - struct user_info *ui = uid_hash_table + double_hash(uid, p); - - if (!ui_used(ui)) { - int ret; - if (!flags) - return -E_BAD_UID; - ui->uid = uid; - ui->flags |= UI_FL_SLOT_USED; - if (!uid_is_admissible(uid, urs)) - return 0; - ui->flags |= UI_FL_ADMISSIBLE; - ret = open_user_table(ui, flags & CREATE_USER_TABLE); - if (ret < 0) - return ret; - - if (ui_ptr) - *ui_ptr = ui; - return 1; - } - if (ui->uid != uid) - continue; - if (ui_ptr) - *ui_ptr = ui; - return 0; - } - return flags? -E_HASH_TABLE_OVERFLOW : -E_BAD_UID; -} - -char *get_uid_list_name(void) -{ - return make_message("%s/uid_list", conf.database_dir_arg); -} - -void sort_hash_table(int (*comp)(const void *, const void *)) -{ - qsort(uid_hash_table, uid_hash_table_size, sizeof(struct user_info), - comp); -} - -int open_dir_table(int create) -{ dir_table_desc.dir = adu_strdup(conf.database_dir_arg); if (create) { + NOTICE_LOG("creating dir table\n"); int ret = osl(osl_create_table(&dir_table_desc)); if (ret < 0) { free((char *)dir_table_desc.dir); return ret; } } + INFO_LOG("opening dir table\n"); return osl(osl_open_table(&dir_table_desc, &dir_table)); } @@ -472,7 +194,6 @@ static int check_args(void) static int print_complete_help_and_die(void) { const char **line; - select_cmdline_parser_init(&select_conf); printf("%s-%s\n", CMDLINE_PARSER_PACKAGE, CMDLINE_PARSER_VERSION); printf("%s\n\n", gengetopt_args_info_purpose); @@ -495,6 +216,8 @@ static int print_complete_help_and_die(void) printf("Interactive commands:\n"); print_interactive_help(); + cmdline_parser_free(&conf); + select_cmdline_parser_free(&select_conf); exit(EXIT_FAILURE); } @@ -502,16 +225,19 @@ int main(int argc, char **argv) { int ret; struct cmdline_parser_params params = { - .override = 0, + .override = 1, .initialize = 1, .check_required = 0, .check_ambiguity = 0, .print_errors = 0 }; + select_cmdline_parser_init(&select_conf); + cmdline_parser_init(&conf); /* ignore errors and print complete help if --help was given */ cmdline_parser_ext(argc, argv, &conf, ¶ms); if (conf.help_given || conf.detailed_help_given) print_complete_help_and_die(); + cmdline_parser_free(&conf); params.check_required = 1; params.check_ambiguity = 1; params.print_errors = 1; @@ -535,9 +261,12 @@ int main(int argc, char **argv) if (ret < 0) goto out; out: + close_all_tables(); if (ret < 0) { ERROR_LOG("%s\n", adu_strerror(-ret)); return -EXIT_FAILURE; } + cmdline_parser_free(&conf); + select_cmdline_parser_free(&select_conf); return EXIT_SUCCESS; }