Merge commit 'athcx/bloom'
authorAndre Noll <maan@systemlinux.org>
Fri, 24 Apr 2009 09:37:08 +0000 (11:37 +0200)
committerAndre Noll <maan@systemlinux.org>
Fri, 24 Apr 2009 09:37:08 +0000 (11:37 +0200)
Conflicts:

create.c

14 files changed:
Makefile
adu.c
adu.ggo
adu.h
create.c
error.h
fd.c
fd.h
interactive.c
select.c
select.ggo
string.c
user.c
user.h

index a040478..28fb2d4 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -19,6 +19,8 @@ CPPFLAGS += -I/usr/local/include
 
 LDFLAGS += -L/usr/local/lib
 
+PREFIX ?= /usr/local
+
 
 ifeq (,$(findstring BSD,$(uname_S)))
        CPPFLAGS += -D_LARGEFILE64_SOURCE
@@ -82,3 +84,7 @@ index.html: adu.1.html index.html.in INSTALL README
        sed -e '1,/@INSTALL@/d' -e '/@MAN_PAGE@/,$$d' index.html.in >> $@
        sed -e '1,/Return to Main Contents/d' -e '/Index/,$$d' adu.1.html >> $@
        sed -e '1,/@MAN_PAGE@/d' index.html.in >> $@
+
+install: adu adu.1
+       install -s adu $(PREFIX)/bin
+       install -m 0644 adu.1 $(PREFIX)/man/man1
diff --git a/adu.c b/adu.c
index b227589..68a48de 100644 (file)
--- a/adu.c
+++ b/adu.c
@@ -46,6 +46,8 @@ struct gengetopt_args_info conf;
 /** Options passed to --select-options. */
 struct select_args_info select_conf;
 
+/** Computed database dir */
+char *database_dir;
 
 /**
  * The table containing the directory names and statistics.
@@ -183,21 +185,27 @@ static int init_signals(void)
  */
 int open_dir_table(int create)
 {
+       int ret;
 
        if (dir_table)
                return 1;
-       dir_table_desc.dir = adu_strdup(conf.database_dir_arg);
 
+       dir_table_desc.dir = adu_strdup(database_dir);
        if (create) {
+               INFO_LOG("creating database directory structure\n");
+               ret = mkpath(dir_table_desc.dir, 0777);
+               if (ret < 0)
+                       goto out;
                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;
-               }
+               ret = osl(osl_create_table(&dir_table_desc));
+               if (ret < 0)
+                       goto out;
        }
        INFO_LOG("opening dir table\n");
        return osl(osl_open_table(&dir_table_desc, &dir_table));
+out:
+       free((char *)dir_table_desc.dir);
+       return ret;
 }
 
 static int check_args(void)
@@ -293,6 +301,11 @@ int main(int argc, char **argv)
        if (ret < 0)
                goto out;
        ret = -E_SYNTAX;
+       if (conf.database_dir_given)
+               database_dir = adu_strdup(conf.database_dir_arg);
+       else
+               database_dir = make_message("%s%s",
+                       conf.database_root_arg, conf.base_dir_arg);
        if (conf.select_given)
                ret = com_select();
        else if (conf.create_given)
@@ -307,6 +320,7 @@ out:
                ERROR_LOG("%s\n", adu_strerror(-ret));
                return -EXIT_FAILURE;
        }
+       free(database_dir);
        cmdline_parser_free(&conf);
        select_cmdline_parser_free(&select_conf);
        return EXIT_SUCCESS;
diff --git a/adu.ggo b/adu.ggo
index a180f1f..873cc22 100644 (file)
--- a/adu.ggo
+++ b/adu.ggo
@@ -14,17 +14,18 @@ usage patterns of subdirectories and/or files owned by a given user id.
 section "General options"
 #########################
 
-option "database-dir" d
-#~~~~~~~~~~~~~~~~~~~~~~
-"directory containing the osl tables"
-string typestr="path"
-required
+option "config-file" c
+#~~~~~~~~~~~~~~~~~~~~~
+"(default='~/.adurc')"
+string typestr="filename"
+optional
 details="
-       Full path to the directory containing the osl tables. This
-       directory must exist. It must be writable for the user running
-       adu in --create mode and readable in --select mode.
-
+       Options may be given at the command line or in the
+       configuration file. As usual, if an option is given both at
+       the command line and in the configuration file, the command
+       line option takes precedence.
 "
+
 option "loglevel" l
 #~~~~~~~~~~~~~~~~~~
 "Set loglevel (0-6)"
@@ -36,6 +37,46 @@ details="
        goes to stdout. Lower values mean more verbose logging.
 "
 
+defgroup "database"
+#==================
+groupdesc="
+       There are two ways to specify a database directory. You can either
+       specify a full path using the database-dir option or a root path
+       using the database-root option. In the latter case, a directory
+       structure matching that of the base-dir argument is created
+       below the given full path.
+
+       The advantage of using database-root is that the base-dir is
+       used to find the relevant database both in create and select mode
+       and you do not have to care for setting the database-dir explicitly.
+"
+
+groupoption "database-dir" d
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~
+"directory containing the osl tables"
+group="database"
+string typestr="path"
+details="
+       Full path to the directory containing the osl tables. This
+       directory is created if it does not exist. It must be writable for the
+       user running adu in --create mode and readable in --select mode.
+"
+
+groupoption "database-root" r
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+"directory containing directories containing the osl tables"
+group="database"
+string typestr="path"
+default="/var/lib/adu"
+dependon="base-dir"
+optional
+details="
+       Base path to the directory containing the osl tables. The real
+       database-dir is generated by appending base-dir. This
+       directory is created if it does not exist. When used in select
+       mode you have to specify the base-dir as well.
+"
+
 ###############
 section "Modes"
 ###############
@@ -46,7 +87,6 @@ groupdesc="
        adu may be started in one of three possible modes, each of
        which corresponds to a different command line option. Exactly
        one of these options must be given.
-
 "
 required
 
@@ -93,7 +133,6 @@ option "base-dir" b
 #~~~~~~~~~~~~~~~~~~
 "directory to traverse"
 string typestr="path"
-dependon="create"
 optional
 details="
        The base directory to be traversed recursively. A warning
diff --git a/adu.h b/adu.h
index 5e99709..42b9471 100644 (file)
--- a/adu.h
+++ b/adu.h
@@ -139,6 +139,9 @@ extern struct osl_table *dir_table;
 /** The adu command line options. */
 extern struct gengetopt_args_info conf;
 
+/** Computed database dir. */
+extern char *database_dir;
+
 /**
  * The select command line options.
  *
index 408ab63..0394412 100644 (file)
--- a/create.c
+++ b/create.c
@@ -169,7 +169,7 @@ static int scan_dir(char *dirname, uint64_t *parent_dir_num)
                                continue;
                        dir_size += s.st_size;
                        dir_files++;
-                       ret = create_user_table(conf.database_dir_arg, s.st_uid, &ui);
+                       ret = create_user_table(s.st_uid, &ui);
                        if (ret < 0)
                                goto out;
                        ret = update_user_row(ui->table, this_dir_num,
@@ -190,7 +190,7 @@ static int scan_dir(char *dirname, uint64_t *parent_dir_num)
                else
                        dir_size += s.st_size;
                dir_files++;
-               ret = create_user_table(conf.database_dir_arg, s.st_uid, &ui);
+               ret = create_user_table(s.st_uid, &ui);
                if (ret < 0)
                        goto out;
                ret = insert_user_bloom(&s, bloom_buf);
@@ -269,7 +269,7 @@ int com_create(void)
        ret = scan_dir(conf.base_dir_arg, &zero);
        if (ret < 0)
                goto out;
-       ret = write_uid_file(conf.database_dir_arg);
+       ret = write_uid_file();
        log_bloom_stats();
 out:
        bloom_free(global_bloom_filter);
diff --git a/error.h b/error.h
index 00bdede..5a5aefb 100644 (file)
--- a/error.h
+++ b/error.h
@@ -42,7 +42,8 @@
        _ERROR(BAD_UNIT, "invalid unit specifier") \
        _ERROR(BAD_ATOM, "invalid atom") \
        _ERROR(BAD_OUTPUT_ARG, "invalid name for output") \
-       _ERROR(REGEX, "regular expression error")
+       _ERROR(REGEX, "regular expression error") \
+       _ERROR(MKDIR, "could not create directory")
 
 
 /**
diff --git a/fd.c b/fd.c
index e1d243e..e661e71 100644 (file)
--- a/fd.c
+++ b/fd.c
@@ -9,9 +9,11 @@
 #include <sys/types.h>
 #include <dirent.h>
 #include <sys/mman.h>
+#include <string.h>
 
 #include "adu.h"
 #include "error.h"
+#include "string.h"
 
 /**
  * Wrapper for the write system call.
@@ -273,3 +275,54 @@ int adu_munmap(void *start, size_t length)
                strerror(err));
        return -ERRNO_TO_ERROR(err);
 }
+
+__must_check __malloc static char *adu_dirname(const char *name)
+{
+       char *p, *ret;
+
+       if (!name || !*name)
+               return NULL;
+       ret = adu_strdup(name);
+       p = strrchr(ret, '/');
+       if (!p)
+               *ret = '\0';
+       else
+               *p = '\0';
+       return ret;
+}
+
+/**
+ * Recursive mkdir
+ *
+ * \param p Full path that should be created.
+ *
+ * \param mode Use this mode when creating directories.
+ *
+ * \return 0 if successful, -E_MKDIR on errors.
+ */
+int mkpath(const char *p, mode_t mode)
+{
+       char *parent, *path;
+       int ret = -E_MKDIR;
+
+       DEBUG_LOG("%s\n", p);
+       if (strcmp(p, ".") == 0 || strcmp(p, "/") == 0 || strcmp(p, "") == 0) {
+               DEBUG_LOG("reached beginning of path\n");
+               return 0;
+       }
+       path = adu_strdup(p);
+       parent = adu_dirname(p);
+       if (!parent)
+               goto out;
+       ret = mkpath(parent, mode);
+       if (ret < 0)
+               goto out;
+       INFO_LOG("making dir %s\n", path);
+       ret = 0;
+       if ((mkdir(path, mode) == -1) && (errno != EEXIST))
+               ret = -E_MKDIR;
+out:
+       free(parent);
+       free(path);
+       return ret;
+}
diff --git a/fd.h b/fd.h
index 6ebd9c9..32a3ad3 100644 (file)
--- a/fd.h
+++ b/fd.h
@@ -12,3 +12,4 @@ int mmap_full_file(const char *filename, int open_mode, void **map,
        size_t *size, int *fd_ptr);
 int adu_munmap(void *start, size_t length);
 int adu_write_file(const char *filename, const void *buf, size_t size);
+int mkpath(const char *p, mode_t mode);
index 08f700f..1c75622 100644 (file)
@@ -214,13 +214,14 @@ int com_interactive(void)
        ret = parse_select_options(NULL, NULL, &admissible_uids, &fi);
        if (ret< 0)
                return ret;
-       ret = read_uid_file(conf.database_dir_arg);
+       ret = read_uid_file();
        if (ret < 0)
                return ret;
        while (read_input_line(line, sizeof(line)) >= 0) {
                ret = exec_interactive_command(line);
                if (ret < 0)
                        printf("%s\n", adu_strerror(-ret));
+               fflush(NULL);
        }
        return ret;
 }
index 4ebfbbf..9dda089 100644 (file)
--- a/select.c
+++ b/select.c
@@ -72,18 +72,18 @@ enum user_list_atoms {USER_LIST_ATOMS};
 #undef ATOM
 
 /* user list header */
-#define USER_LIST_HEADER_ATOMS \
+#define USER_LIST_HEADER_TRAILER_ATOMS \
        ATOM(pw_name, STRING) \
        ATOM(uid, ID)
 
 #define ATOM(x, y) { .name = #x, .type = AT_ ## y},
-struct atom user_list_header_atoms[] = {
-       USER_LIST_HEADER_ATOMS
+struct atom user_list_header_trailer_atoms[] = {
+       USER_LIST_HEADER_TRAILER_ATOMS
        {.name = NULL}
 };
 #undef ATOM
 #define ATOM(x, y) ulha_ ## x,
-enum user_list_header_atoms {USER_LIST_HEADER_ATOMS};
+enum user_list_header_trailer_atoms {USER_LIST_HEADER_TRAILER_ATOMS};
 #undef ATOM
 
 /* user summary */
@@ -141,6 +141,7 @@ struct user_list_info {
 struct user_list_format_info {
        struct format_info *fi;
        struct format_info *header_fi;
+       struct format_info *trailer_fi;
 };
 
 struct user_summary_info {
@@ -430,7 +431,9 @@ static int print_global_summary(struct format_info *fi)
        buf = format_items(fi, values);
        ret = output("%s", buf);
        free(buf);
-       return ret;
+       if (ret < 0)
+               return ret;
+       return output("%s", select_conf.trailer_arg);
 }
 
 static int user_summary_loop_function(struct osl_row *row, void *data)
@@ -590,8 +593,11 @@ static int print_user_summary(struct format_info *fi)
        for (i = 0; i < usld.num_admissible_users; i++) {
                if (select_conf.limit_arg >= 0 && i > select_conf.limit_arg)
                        break;
-               print_user_summary_line(usld.usis + i, usld.fi);
+               ret = print_user_summary_line(usld.usis + i, usld.fi);
+               if (ret < 0)
+                       goto out;
        }
+       ret = output("%s", select_conf.trailer_arg);
 out:
        free(usld.usis);
        return ret;
@@ -662,12 +668,12 @@ static int print_user_list(struct user_info *ui, void *data)
                .fi = ulfi->fi,
                .count = select_conf.limit_arg
        };
-       union atom_value header_values[] = {
+       union atom_value header_trailer_values[] = {
                [ulha_uid] = {.num_value = (long long unsigned)ui->uid},
                [ulha_pw_name] = {.string_value = ui->pw_name?
                        ui->pw_name : "?"}
        };
-       char *buf = format_items(ulfi->header_fi, header_values);
+       char *buf = format_items(ulfi->header_fi, header_trailer_values);
 
        ret = output("%s", buf);
        free(buf);
@@ -684,6 +690,11 @@ static int print_user_list(struct user_info *ui, void *data)
        ret = adu_loop_reverse(ui->table, sort_column, &uli,
                user_list_loop_function, &uli.ret, &uli.osl_errno);
        free_regex(uli.preg);
+       if (ret < 0)
+               return ret;
+       buf = format_items(ulfi->trailer_fi, header_trailer_values);
+       ret = output("%s", buf);
+       free(buf);
        return ret;
 }
 
@@ -692,12 +703,18 @@ static int print_user_lists(struct format_info *fi)
        struct user_list_format_info ulfi = {.fi = fi};
        char *header_fmt = select_conf.header_given?
                select_conf.header_arg : "uid %(uid)(%(pw_name)):\n";
+       char *trailer_fmt = select_conf.trailer_arg;
        int ret = parse_format_string(header_fmt,
-               user_list_header_atoms, &ulfi.header_fi);
+               user_list_header_trailer_atoms, &ulfi.header_fi);
+       if (ret < 0)
+               return ret;
+       ret = parse_format_string(trailer_fmt,
+               user_list_header_trailer_atoms, &ulfi.trailer_fi);
        if (ret < 0)
                return ret;
        ret = for_each_admissible_user(print_user_list, &ulfi);
        free_format_info(ulfi.header_fi);
+       free_format_info(ulfi.trailer_fi);
        return ret;
 }
 
@@ -778,7 +795,9 @@ static int print_global_list(struct format_info *fi)
        ret = adu_loop_reverse(dir_table, sort_column, &gli,
                global_list_loop_function, &gli.ret, &gli.osl_errno);
        free_regex(gli.preg);
-       return ret;
+       if (ret < 0)
+               return ret;
+       return output("%s", select_conf.trailer_arg);
 }
 
 static int print_statistics(struct format_info *fi)
@@ -912,8 +931,7 @@ int run_select_query(struct uid_range *admissible_uids, struct format_info *fi)
        if (ret < 0)
                goto out;
        check_signals();
-       ret = open_admissible_user_tables(conf.database_dir_arg,
-               admissible_uids);
+       ret = open_admissible_user_tables(admissible_uids);
        if (ret < 0)
                goto out;
        check_signals();
@@ -929,7 +947,7 @@ out:
 /** Default format string for global_list mode. */
 #define GLOBAL_LIST_DFLT_FMT "%(size:r:8) %(files:r:8) %(dirname)\n"
 /** Default format string for global_summary mode. */
-#define GLOBAL_SUMMARY_DFLT_FMT "#directories: %(dirs), #files: %(files), size: %(size)\n\n"
+#define GLOBAL_SUMMARY_DFLT_FMT "#directories: %(dirs), #files: %(files), size: %(size)\n"
 /** Default format string for user_list mode. */
 #define USER_LIST_DFLT_FMT "%(size:r:5) %(files:r:5) %(dirname)\n"
 /** Default format string for user_summary mode. */
@@ -1063,7 +1081,7 @@ int com_select(void)
        ret = parse_select_options(conf.select_options_arg, &params,
                &admissible_uids, &fi);
        if (ret > 0) {
-               ret = read_uid_file(conf.database_dir_arg);
+               ret = read_uid_file();
                if (ret < 0)
                        goto out;
                ret = run_select_query(admissible_uids, fi);
index c4e1ec0..e2c91f8 100644 (file)
@@ -87,6 +87,20 @@ details="
        output to scripts.
 "
 
+option "trailer" T
+#~~~~~~~~~~~~~~~~~
+"use a customized trailer for listings/summaries"
+string typestr="string"
+optional
+default=""
+details="
+       This option can be used to print any string at the end of
+       the query output.
+
+       In user_list mode the trailer is a format string with the
+       same semantics like the header string.
+"
+
 option "select-mode" m
 #~~~~~~~~~~~~~~~~~~~~~
 "How to print the results of the query"
index 77c90cb..946faab 100644 (file)
--- a/string.c
+++ b/string.c
@@ -138,7 +138,7 @@ __must_check __printf_1_2 __malloc char *make_message(const char *fmt, ...)
  * Append \p b to \p a.
  *
  * \return If \a a is \p NULL, return a pointer to a copy of \a b, i.e.
- * para_strcat(NULL, b) is equivalent to para_strdup(b). If \a b is \p NULL,
+ * adu_strcat(NULL, b) is equivalent to adu_strdup(b). If \a b is \p NULL,
  * return \a a without making a copy of \a a.  Otherwise, construct the
  * concatenation \a c, free \a a (but not \a b) and return \a c.
  *
diff --git a/user.c b/user.c
index dfa1c10..0ae4a6c 100644 (file)
--- a/user.c
+++ b/user.c
@@ -257,7 +257,7 @@ static inline int ui_admissible(struct user_info *ui)
        return ui->flags & UI_FL_ADMISSIBLE;
 }
 
-static int open_user_table(const char *dirname, struct user_info *ui, int create)
+static int open_user_table(struct user_info *ui, int create)
 {
        int ret;
        struct passwd *pw;
@@ -266,7 +266,7 @@ static int open_user_table(const char *dirname, struct user_info *ui, int create
        ui->desc->num_columns = NUM_UT_COLUMNS;
        ui->desc->flags = 0;
        ui->desc->column_descriptions = user_table_cols;
-       ui->desc->dir = adu_strdup(dirname);
+       ui->desc->dir = adu_strdup(database_dir);
        ui->desc->name = make_message("%u", (unsigned)ui->uid);
        pw = getpwuid(ui->uid);
        if (pw && pw->pw_name)
@@ -435,8 +435,7 @@ static struct user_info *lookup_uid(uint32_t uid)
  *
  * \return Standard.
  */
-int create_user_table(const char *dirname, uint32_t uid,
-               struct user_info **ui_ptr)
+int create_user_table(uint32_t uid, struct user_info **ui_ptr)
 {
        struct user_info *ui = lookup_uid(uid);
 
@@ -447,12 +446,12 @@ int create_user_table(const char *dirname, uint32_t uid,
                return 1;
        ui->uid = uid;
        ui->flags |= UI_FL_SLOT_USED;
-       return open_user_table(dirname, ui, 1);
+       return open_user_table(ui, 1);
 }
 
-static char *get_uid_list_name(const char *dirname)
+static char *get_uid_list_name(void)
 {
-       return make_message("%s/uid_list", dirname);
+       return make_message("%s/uid_list", database_dir);
 }
 /**
  * Open the osl tables for all admissible uids.
@@ -469,8 +468,7 @@ static char *get_uid_list_name(const char *dirname)
  *
  * \return Stamdard.
  */
-int open_admissible_user_tables(const char *dirname,
-               struct uid_range *admissible_uids)
+int open_admissible_user_tables(struct uid_range *admissible_uids)
 {
        struct user_info *ui;
 
@@ -489,7 +487,7 @@ int open_admissible_user_tables(const char *dirname,
                ui->flags |= UI_FL_ADMISSIBLE;
                if (ui->table)
                        continue;
-               ret = open_user_table(dirname, ui, 0);
+               ret = open_user_table(ui, 0);
                if (ret < 0)
                        return ret;
        }
@@ -508,11 +506,11 @@ int open_admissible_user_tables(const char *dirname,
  *
  * \return Standard.
  */
-int read_uid_file(const char *dirname)
+int read_uid_file(void)
 {
        size_t size;
        uint32_t n;
-       char *filename = get_uid_list_name(dirname), *map;
+       char *filename = get_uid_list_name(), *map;
        int ret = mmap_full_file(filename, O_RDONLY, (void **)&map, &size, NULL);
        unsigned bits;
 
@@ -562,7 +560,7 @@ out:
  *
  * \return Standard.
  */
-int write_uid_file(const char *dirname)
+int write_uid_file(void)
 {
        char *buf, *p, *filename;
        size_t size = num_uids * sizeof(uint32_t);
@@ -578,7 +576,7 @@ int write_uid_file(const char *dirname)
                write_u32(p, ui->uid);
                p += sizeof(uint32_t);
        }
-       filename = get_uid_list_name(dirname);
+       filename = get_uid_list_name();
        ret = adu_write_file(filename, buf, size);
        free(filename);
        free(buf);
diff --git a/user.h b/user.h
index c64983e..a6fbbf6 100644 (file)
--- a/user.h
+++ b/user.h
@@ -35,10 +35,9 @@ struct user_info {
 /** An opaque struct that contains info about which users are admissible. */
 struct uid_range;
 
-int create_user_table(const char *dirname, uint32_t uid,
-               struct user_info **ui_ptr);
-int read_uid_file(const char *dirname);
-int write_uid_file(const char *dirname);
+int create_user_table(uint32_t uid, struct user_info **ui_ptr);
+int read_uid_file(void);
+int write_uid_file(void);
 
 void create_hash_table(unsigned bits);
 int for_each_admissible_user(int (*func)(struct user_info *, void *),
@@ -47,5 +46,4 @@ int parse_uid_arg(const char *orig_arg, struct uid_range **ur);
 int append_users(char **users, int num_users,
                struct uid_range **admissible_uids, int num_uid_ranges);
 void close_user_tables(void);
-int open_admissible_user_tables(const char *dirname,
-               struct uid_range *admissible_uids);
+int open_admissible_user_tables(struct uid_range *admissible_uids);