X-Git-Url: http://git.tuebingen.mpg.de/?p=adu.git;a=blobdiff_plain;f=adu.c;h=b8c7b68f905b2448aa069eb2d6820e029ca42baf;hp=02bfa302779ccdf5bc299e8a6a81f5b3c2fb8f60;hb=66f28f8d1a9562cdba6f1611a06f23f13ee15469;hpb=cecbe8525740b4a899cb1a6c91064e195ae1ada2 diff --git a/adu.c b/adu.c index 02bfa30..b8c7b68 100644 --- a/adu.c +++ b/adu.c @@ -1,16 +1,41 @@ +/* + * 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. */ + +/** + * \mainpage adu API reference + * + * - Modes of operation: \ref select.c, \ref create.c, \ref interactive.c + * - User handling: \ref user.c + * - Error handling: \ref error.h + * - Library-type functions: \ref fd.c, \ref format.c, \ref string.c, \ref portable_io.h + * - Generated files: \ref cmdline.h, \ref select.cmdline.h + */ + #include "adu.h" #include /* readdir() */ #include - -#include "gcc-compat.h" +#include "format.h" +#include "user.h" +#include "select.cmdline.h" +#include "select.h" #include "cmdline.h" #include "fd.h" #include "string.h" #include "error.h" -#include "portable_io.h" -#include "select.cmdline.h" +/** Define the array of error descriptions. */ DEFINE_ERRLIST; + +/** + * The error code of the last osl library function. + * + * \sa osl(). + */ int osl_errno; /** In case a signal is received, its number is stored here. */ @@ -22,81 +47,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, @@ -140,35 +96,11 @@ static struct osl_table_description dir_table_desc = { .column_descriptions = dir_table_cols, }; -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. * * \param ll Loglevel. - * \param fml Usual format string. + * \param fmt Usual format string. * * All XXX_LOG() macros use this function. */ @@ -192,88 +124,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)); @@ -281,37 +138,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) @@ -319,6 +149,12 @@ static void signal_handler(int s) signum = s; } +/** + * Check whether to terminate adu. + * + * Check whether a signal was caught that should terminate the + * adu process. If yes, close all osl tables and exit gracefully. + */ void check_signals(void) { if (likely(!signum)) @@ -339,101 +175,29 @@ 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. +/** + * Open the directory table. * - * 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. + * \param create If non-zero, create the table first. * - * 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]. + * \return Standard. */ -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)); } @@ -458,18 +222,71 @@ static int check_args(void) return 1; } +static int print_complete_help_and_die(void) +{ + const char **line; + + printf("%s-%s\n", CMDLINE_PARSER_PACKAGE, CMDLINE_PARSER_VERSION); + printf("%s\n\n", gengetopt_args_info_purpose); + printf("%s\n\n", gengetopt_args_info_usage); + + if (conf.help_given) + line = gengetopt_args_info_help; + else + line = gengetopt_args_info_detailed_help; + for (; *line; line++) + printf("%s\n", *line); + + if (conf.help_given) + line = select_args_info_help; + else + line = select_args_info_detailed_help; + printf("Select options:\n"); + for (; *line; line++) + printf("%s\n", *line); + + printf("Interactive commands:\n"); + print_interactive_help(); + cmdline_parser_free(&conf); + select_cmdline_parser_free(&select_conf); + exit(EXIT_FAILURE); +} + +/** + * The main function of adu. + * + * \param argc Usual argument count. + * \param argv Usual argument vector. + * + * Check command line options, init the signal handlers and + * call the main function of the selected mode. + * + * \return \p EXIT_SUCCESS on success, \p EXIT_FAILURE otherwise. + */ int main(int argc, char **argv) { int ret; struct cmdline_parser_params params = { - .override = 0, + .override = 1, .initialize = 1, - .check_required = 1, - .check_ambiguity = 1, - .print_errors = 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; + ret = cmdline_parser_ext(argc, argv, &conf, ¶ms); + if (ret) + exit(EXIT_FAILURE); - cmdline_parser_ext(argc, argv, &conf, ¶ms); /* aborts on errors */ ret = check_args(); if (ret < 0) goto out; @@ -486,9 +303,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; }