LDFLAGS += -L/usr/local/lib
+PREFIX ?= /usr/local
+
ifeq (,$(findstring BSD,$(uname_S)))
CPPFLAGS += -D_LARGEFILE64_SOURCE
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
/** 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.
*/
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)
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)
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;
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)"
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"
###############
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
#~~~~~~~~~~~~~~~~~~
"directory to traverse"
string typestr="path"
-dependon="create"
optional
details="
The base directory to be traversed recursively. A warning
/** The adu command line options. */
extern struct gengetopt_args_info conf;
+/** Computed database dir. */
+extern char *database_dir;
+
/**
* The select command line options.
*
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,
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);
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);
_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")
/**
#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.
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;
+}
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);
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;
}
#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 */
struct user_list_format_info {
struct format_info *fi;
struct format_info *header_fi;
+ struct format_info *trailer_fi;
};
struct user_summary_info {
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)
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;
.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);
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;
}
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;
}
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)
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();
/** 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. */
ret = parse_select_options(conf.select_options_arg, ¶ms,
&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);
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"
* 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.
*
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;
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)
*
* \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);
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.
*
* \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;
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;
}
*
* \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;
*
* \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);
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);
/** 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 *),
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);