*.[oa]
foo*
bar*
-cmdline.[ch]
+*cmdline.[ch]
GPATH
GRTAGS
GSYMS
-objects := adu.o string.o cmdline.o fd.o select.o create.o
+objects := adu.o string.o cmdline.o fd.o select.o create.o interactive.o select.cmdline.o
all: adu
+version := 0.0.3
DEBUG_CPPFLAGS += -Wno-sign-compare -g -Wunused -Wundef -W
DEBUG_CPPFLAGS += -Wredundant-decls
-#CPPFLAGS += -Os
+CPPFLAGS += -Os
CPPFLAGS += -Wall
-#CPPFLAGS += -Wuninitialized
+CPPFLAGS += -Wuninitialized
CPPFLAGS += -Wchar-subscripts
CPPFLAGS += -Wformat-security
CPPFLAGS += -Werror-implicit-function-declaration
CPPFLAGS += -Wbad-function-cast
CPPFLAGS += -D_LARGEFILE64_SOURCE
CPPFLAGS += $(shell getconf LFS64_CFLAGS)
+CPPFLAGS += -DVERSION='"$(version)"'
LDFLAGS += -D_LARGEFILE64_SOURCE
LDFLAGS += $(shell getconf LFS64_LDFLAGS)
%.o: %.c Makefile
$(CC) -c $(CPPFLAGS) $(DEBUG_CPPFLAGS) $<
+select.cmdline.c select.cmdline.h: select.ggo
+ gengetopt --string-parser \
+ --set-package=select \
+ --no-handle-help \
+ --no-handle-error \
+ --no-handle-version \
+ --arg-struct-name=select_args_info \
+ --file-name=$(subst .ggo,,$<).cmdline \
+ --func-name $(subst .ggo,,$<)_cmdline_parser < $<
+
+select.cmdline.o: select.cmdline.c select.cmdline.h
+ $(CC) -c $(CPPFLAGS) $<
+
clean:
rm -f *.o adu
#include "string.h"
#include "error.h"
#include "portable_io.h"
+#include "select.cmdline.h"
DEFINE_ERRLIST;
int osl_errno;
/** Command line and config file options. */
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;
*/
struct osl_table *dir_table = NULL;
-/**
- * The array of all uid ranges that were given at the command line.
- */
-struct uid_range *admissible_uids;
-
-
/**
* Compare the size of two directories
*
},
};
-static int check_uid_arg(const char *arg, uint32_t *uid)
-{
- const uint32_t max = ~0U;
- /*
- * we need an 64-bit int for string -> uid conversion because strtoll()
- * returns a signed value.
- */
- int64_t val;
- int ret = atoi64(arg, &val);
-
- if (ret < 0)
- return ret;
- if (val < 0 || val > max)
- return -ERRNO_TO_ERROR(EINVAL);
- *uid = val;
- return 1;
-}
-
-static int parse_uid_range(const char *orig_arg, struct uid_range *ur)
-{
- int ret;
- char *arg = adu_strdup(orig_arg), *p = strchr(arg, '-');
-
- if (!p || p == arg) { /* -42 or 42 */
- ret = check_uid_arg(p? p + 1 : arg, &ur->high);
- if (ret < 0)
- goto out;
- ur->low = p? 0 : ur->high;
- ret = 1;
- goto out;
- }
- /* 42- or 42-4711 */
- *p = '\0';
- p++;
- ret = check_uid_arg(arg, &ur->low);
- if (ret < 0)
- goto out;
- ur->high = ~0U;
- if (*p) { /* 42-4711 */
- ret = check_uid_arg(p, &ur->high);
- if (ret < 0)
- goto out;
- }
- if (ur->low > ur->high)
- ret = -ERRNO_TO_ERROR(EINVAL);
-out:
- if (ret < 0)
- ERROR_LOG("bad uid option: %s\n", orig_arg);
- else
- INFO_LOG("admissible uid range: %u - %u\n", ur->low,
- ur->high);
- free(arg);
- return ret;
-}
-
/**
* The log function.
*
% uid_hash_table_size;
}
-static int uid_is_admissible(uint32_t uid)
+static int uid_is_admissible(uint32_t uid, struct uid_range *urs)
{
- int i;
-
- for (i = 0; i < conf.uid_given; i++) {
- struct uid_range *ur = admissible_uids + i;
+ struct uid_range *ur;
+ int ret = 1;
+ if (!urs) /* empty array means all uids are allowed */
+ return 1;
+ FOR_EACH_UID_RANGE(ur, urs)
if (ur->low <= uid && ur->high >= uid)
- break;
- }
- i = !conf.uid_given || i < conf.uid_given;
+ goto out;
+ ret = 0;
+out:
DEBUG_LOG("uid %u is %sadmissible\n", (unsigned)uid,
- i? "" : "not ");
- return i;
+ ret? "" : "not ");
+ return ret;
}
-int search_uid(uint32_t uid, enum search_uid_flags flags,
- struct user_info **ui_ptr)
+int search_uid(uint32_t uid, struct uid_range *urs,
+ enum search_uid_flags flags, struct user_info **ui_ptr)
{
uint32_t p;
return -E_BAD_UID;
ui->uid = uid;
ui->flags |= UI_FL_SLOT_USED;
- if (!uid_is_admissible(uid))
+ if (!uid_is_admissible(uid, urs))
return 0;
ui->flags |= UI_FL_ADMISSIBLE;
ret = open_user_table(ui, flags & CREATE_USER_TABLE);
static int check_args(void)
{
- int i, ret;
-
-
if (conf.create_given && !conf.base_dir_given)
return -E_SYNTAX;
conf.base_dir_arg[len] = '\0';
}
}
- if (!conf.uid_given)
- return 0;
- admissible_uids = adu_malloc(conf.uid_given * sizeof(*admissible_uids));
- for (i = 0; i < conf.uid_given; i++) {
- ret = parse_uid_range(conf.uid_arg[i], admissible_uids + i);
- if (ret < 0)
- goto err;
- }
return 1;
-err:
- free(admissible_uids);
- admissible_uids = NULL;
- return ret;
}
int main(int argc, char **argv)
ret = -E_SYNTAX;
if (conf.select_given)
ret = com_select();
- else
+ else if (conf.create_given)
ret = com_create();
+ else
+ ret = com_interactive();
if (ret < 0)
goto out;
out:
- free(admissible_uids);
if (ret < 0) {
ERROR_LOG("%s\n", adu_strerror(-ret));
return -EXIT_FAILURE;
# Licensed under the GPL v2. For licencing details see COPYING.
package "adu"
-version "0.0.2"
purpose "advanced disk usage
adu creates a database containing disk usage statistics of a given
goes to stdout. Lower values mean more verbose logging.
"
-option "uid" u
-#~~~~~~~~~~~~~
-"user id(s) to take into account"
-string typestr="uid_spec"
-optional
-multiple
-details="
- An uid specifier may be a single number, or a range of uids.
- Example:
-
- --uid 42 # only consider uid 42
- --uid 42- # only consider uids greater or equal than 42
- --uid 23-42 # only consider uids between 23 and 42, inclusively.
-
- This option may be given multiple times. An uid is taken into
- account if it satisfies at least one --uid option.
-"
-
-
option "paths" p
#~~~~~~~~~~~~~~~
"files to take into account"
that directory.
"
+groupoption "interactive" I
+#~~~~~~~~~~~~~~~~~~~~~~~~~~
+"activate interactive mode"
+group="mode"
+details="
+ In this mode, adu reads commands from stdin.
+"
+
groupoption "select" S
#~~~~~~~~~~~~~~~~~~~~~
"query a database previously created with --create"
users. Decreasing the value causes adu to use slightly less memory.
"
-
-##############################
-section "Options for --select"
-##############################
-
-option "limit" L
-#~~~~~~~~~~~~~~~
-"Limit output"
-int typestr="num"
-default="-1"
-optional
-dependon="select"
-details="
- Only print num lines of output. If negative (the default),
- print all lines.
-"
-
-option "size-unit" -
-#~~~~~~~~~~~~~~~~~~~
-"select output format for sizes"
-enum typestr="format"
-values="h","b","k","m","g","t"
-default="h"
-optional
-dependon="select"
-details="
- Print sizes in the given unit: human-readable, bytes,
- kilobytes (2^10), megabytes (2^20), gigabytes (2^30), terabytes
- (2^40). The default is \"h\", i.e. human-readable.
-"
-
-option "count-unit" -
-#~~~~~~~~~~~~~~~~~~~~
-"select output format for counted values"
-enum typestr="format"
-values="h","n","k","m","g","t"
-default="h"
-optional
-dependon="select"
-details="
- Print the number of files/directories in the given unit:
- human-readable, number, number/10^3, number/10^6, number/10^12,
- number/10^15. The default is to print numbers in human-readable
- format.
-"
-
-option "print-base-dir" -
-#~~~~~~~~~~~~~~~~~~~~~~~~
-"whether to include the base-dir in the output"
-flag off
-details="
- If this flag is given, all directories printed are prefixed
- with the base directory. The default is to print paths relative
- to the base dir.
-"
-
-option "no-headers" -
-#~~~~~~~~~~~~~~~~~~~~
-"supress descriptions for listings/tables"
-flag off
-dependon="select"
-details="
- This is mostly useful to feed the output of adu to scripts.
-"
-
-option "global-list" -
-#~~~~~~~~~~~~~~~~~~~~~
-"how to print global directory listings"
-enum typestr="which"
-values="size","file_count","both","none"
-default="both"
-optional
-dependon="select"
-details="
- By default adu prints two global directory listings: The
- first prints the directory names ordered by the sum of the
- sizes of the contained files while the second listing prints
- them sorted by the number of files. This option can be used
- to print only one or neither of these two listings.
-"
-
-option "no-global-summary" -
-#~~~~~~~~~~~~~~~~~~~~~~~~~~~
-"do not print the summary line"
-flag off
-dependon="select"
-
-option "user-list" -
-#~~~~~~~~~~~~~~~~~~~
-"how to print per-user directory listings"
-enum typestr="which"
-values="size","file_count","both","none"
-default="both"
-optional
-dependon="select"
-details="
- Similar to the global directory listings mentioned above,
- adu can print two directory listings per user. This option
- controls which of the these should be printed.
-"
-
-option "no-user-summary" -
-#~~~~~~~~~~~~~~~~~~~~~~~~~~~
-"do not print the user summary table"
-flag off
-dependon="select"
-
-
-option "user-summary-sort" -
-#~~~~~~~~~~~~~~~~~~~~~~~~~~~
-"how to sort the user-summary"
-enum typestr="col_spec"
-values="name","uid","dir_count","file_count","size"
-default="size"
+option "select-options" s
+#~~~~~~~~~~~~~~~~~~~~~~~~~
+"Options for select mode"
+string typestr="<options>"
optional
dependon="select"
details="
- It is enough to specify the first letter of the column specifier,
- e.g. \"--user-summary-sort f\" sorts by file count.
+ Try --select-options \"-h\"
"
CREATE_USER_TABLE = 2,
};
+#define FOR_EACH_UID_RANGE(ur, urs) for (ur = urs; ur->low <= ur->high; ur++)
+
extern uint32_t num_uids;
extern struct osl_table *dir_table;
extern struct gengetopt_args_info conf;
+extern struct select_args_info select_conf;
/* adu.c */
__printf_2_3 void __log(int, const char*, ...);
void close_all_tables(void);
char *get_uid_list_name(void);
void create_hash_table(unsigned bits);
-int search_uid(uint32_t uid, enum search_uid_flags flags,
- struct user_info **ui_ptr);
+int search_uid(uint32_t uid, struct uid_range *urs,
+ enum search_uid_flags flags, struct user_info **ui_ptr);
int for_each_admissible_user(int (*func)(struct user_info *, void *),
void *data);
void sort_hash_table(int (*comp)(const void *, const void *));
/* select.c */
+int run_select_query(struct uid_range *admissible_uids);
int com_select(void);
/* create.h */
int com_create(void);
+int com_interactive(void);
static int write_uid_list(void)
{
char *buf, *p, *filename;
- uint32_t count = 0;
- struct user_info *ui;
size_t size = num_uids * sizeof(uint32_t);
int ret;
dir_size += size;
dir_files++;
uid = s.st_uid;
- ret = search_uid(uid, CREATE_USER_TABLE | OPEN_USER_TABLE, &ui);
+ ret = search_uid(uid, NULL, CREATE_USER_TABLE | OPEN_USER_TABLE, &ui);
if (ret < 0)
goto out;
ui->bytes += size;
return ret;
}
-int com_create()
+int com_create(void)
{
uint64_t zero = 0ULL;
int ret;
_ERROR(MMAP, "mmap error") \
_ERROR(OSL, "osl error") \
_ERROR(SIGNAL_SIG_ERR, "signal() returned SIG_ERR") \
+ _ERROR(OUTPUT, "error writing output file") \
/**
--- /dev/null
+#include "adu.h"
+#include "string.h"
+#include "error.h"
+#include "cmdline.h"
+#include "select.cmdline.h"
+
+struct interactive_command {
+ const char *name;
+ int (*handler)(char *);
+ const char *desc;
+};
+
+static struct uid_range *admissible_uids;
+
+#define INTERACTIVE_COMMANDS \
+ INTERACTIVE_COMMAND(dump, "dump the current configuration") \
+ INTERACTIVE_COMMAND(set, "change the current configuration") \
+ INTERACTIVE_COMMAND(reset, "reset configuration to defaults") \
+ INTERACTIVE_COMMAND(help, "show list of commands and one-line descriptions") \
+ INTERACTIVE_COMMAND(run, "start the query according to the current options")
+
+
+#define INTERACTIVE_COMMAND(name, desc) \
+ static int icom_ ## name (char *line);
+
+INTERACTIVE_COMMANDS
+
+#undef INTERACTIVE_COMMAND
+
+#define INTERACTIVE_COMMAND(_name, _desc) \
+ { \
+ .name = #_name, \
+ .handler = icom_ ## _name, \
+ .desc = _desc \
+ },
+
+struct interactive_command icmds[] = {
+ INTERACTIVE_COMMANDS
+ {.name = NULL}
+};
+
+#define FOR_EACH_COMMAND(c) for (c = icmds; c->name; c++)
+
+static int read_input_line(char *line, size_t size)
+{
+ return fgets(line, size, stdin)? 1 : -1;
+}
+
+static int icom_run(__a_unused char *line)
+{
+ return run_select_query(admissible_uids);
+}
+
+static int icom_help(__a_unused char *line)
+{
+ struct interactive_command *c;
+
+ FOR_EACH_COMMAND(c)
+ fprintf(stdout, "%s\t%s\n", c->name, c->desc);
+ return 1;
+}
+
+static int icom_reset(__a_unused char *line)
+{
+ select_cmdline_parser_init(&select_conf);
+ return 1;
+}
+
+static int icom_set(char *line)
+{
+ struct select_cmdline_parser_params params = {
+ .override = 1,
+ .initialize = 0,
+ .check_required = 1,
+ .check_ambiguity = 0,
+ .print_errors = 1
+ };
+ if (select_cmdline_parser_string_ext(line, &select_conf, "select",
+ ¶ms))
+ return -E_SYNTAX;
+ return parse_uid_arg(select_conf.uid_arg, &admissible_uids);
+}
+
+static int icom_dump(__a_unused char *line)
+{
+ ERROR_LOG("dump: %s\n", select_conf.format_arg);
+ select_cmdline_parser_dump(stdout, &select_conf);
+ return 1;
+}
+
+static int exec_interactive_command(char *line)
+{
+ const char const *delim = "\t\n ";
+ int i;
+ char *cmd = adu_strdup(line + strspn(line, delim));
+ char *p = cmd + strcspn(cmd, delim);
+ int ret = -E_SYNTAX;
+
+ *p = '\0';
+ p++;
+ for (i = 0; icmds[i].name; i++) {
+ ERROR_LOG("name: %s, cmd: %s.\n", icmds[i].name, cmd);
+ if (strcmp(icmds[i].name, cmd))
+ continue;
+ ERROR_LOG("exec cmd: %s, args: %s\n", cmd, p);
+ ret = icmds[i].handler(p);
+ break;
+ }
+ free(cmd);
+ return ret;
+}
+
+int com_interactive(void)
+{
+ char line[255];
+ int ret = 1;
+
+ select_cmdline_parser_init(&select_conf);
+ while (read_input_line(line, sizeof(line)) >= 0) {
+ size_t len = strlen(line);
+ if (!len)
+ continue;
+ if (line[len - 1] == '\n')
+ line[len - 1] = '\0';
+ ret = exec_interactive_command(line);
+ if (ret < 0)
+ printf("%s\n", adu_strerror(-ret));
+ }
+ return ret;
+}
#include "string.h"
#include "error.h"
#include "portable_io.h"
+#include "select.cmdline.h"
/** Global dir count. */
static uint64_t num_dirs;
static const char size_unit_abbrevs[] = " BKMGT";
static const char count_unit_abbrevs[] = " kmgt";
-
static enum enum_size_unit format_size_value(enum enum_size_unit unit,
uint64_t value, int print_unit, char *result)
{
return u;
}
+static FILE *output_file;
+
+__printf_1_2 static int output(const char const *fmt, ...)
+{
+ va_list argp;
+ int ret;
+
+ va_start(argp, fmt);
+ ret = vfprintf(output_file, fmt, argp);
+ va_end(argp);
+ return ret < 0? -E_OUTPUT : 1;
+}
+
static int get_dir_name_by_number(uint64_t *dirnum, char **name)
{
char *result = NULL, *tmp;
ret = osl(osl_get_object(dir_table, row, DT_NAME, &obj));
if (ret < 0)
goto out;
- pfx = (conf.print_base_dir_given || val)? (char *)obj.data : ".";
+ pfx = (select_conf.print_base_dir_given || val)? (char *)obj.data : ".";
tmp = make_message("%s/%s", pfx, result? result : "");
free(result);
result = tmp;
goto err;
files = *(uint64_t *)obj.data;
if (usi->count && (usi->flags & USF_PRINT_FILES)) {
- format_count_value(conf.count_unit_arg, files,
- conf.count_unit_arg == count_unit_arg_h,
+ format_count_value(select_conf.count_unit_arg, files,
+ select_conf.count_unit_arg == count_unit_arg_h,
formated_value);
- printf("\t%s%s", formated_value,
+ ret = output("\t%s%s", formated_value,
(usi->flags & (USF_PRINT_BYTES | USF_PRINT_DIRNAME))?
- "\t" : "\n"
- );
+ "\t" : "\n");
+ if (ret < 0)
+ goto err;
}
if (summary)
usi->ui->files += files;
goto err;
bytes = *(uint64_t *)obj.data;
if (usi->count && (usi->flags & USF_PRINT_BYTES)) {
- format_size_value(conf.size_unit_arg, bytes,
- conf.size_unit_arg == size_unit_arg_h,
+ format_size_value(select_conf.size_unit_arg, bytes,
+ select_conf.size_unit_arg == size_unit_arg_h,
formated_value);
- printf("%s%s%s",
+ ret = output("%s%s%s",
(usi->flags & USF_PRINT_FILES)? "" : "\t",
formated_value,
usi->flags & USF_PRINT_DIRNAME? "\t" : "\n"
);
+ if (ret < 0)
+ goto err;
}
if (summary) {
usi->ui->bytes += bytes;
ret = get_dir_name_by_number((uint64_t *)obj.data, &dirname);
if (ret < 0)
goto err;
- printf("%s%s\n",
+ ret = output("%s%s\n",
(usi->flags & (USF_PRINT_BYTES | USF_PRINT_FILES))? "" : "\t",
dirname);
free(dirname);
+ if (ret < 0)
+ goto err;
}
if (usi->count > 0)
usi->count--;
goto err;
files = *(uint64_t *)obj.data;
if (gsi->count && (gsi->flags & GSF_PRINT_FILES)) {
- format_count_value(conf.count_unit_arg, files,
- conf.count_unit_arg == count_unit_arg_h,
+ format_count_value(select_conf.count_unit_arg, files,
+ select_conf.count_unit_arg == count_unit_arg_h,
formated_value);
- printf("\t%s%s", formated_value,
+ ret = output("\t%s%s", formated_value,
(gsi->flags & (GSF_PRINT_BYTES | GSF_PRINT_DIRNAME))?
"\t" : "\n");
+ if (ret < 0)
+ goto err;
}
if (summary)
num_files += files;
goto err;
bytes = *(uint64_t *)obj.data;
if (gsi->count && (gsi->flags & GSF_PRINT_BYTES)) {
- format_size_value(conf.size_unit_arg, bytes,
- conf.size_unit_arg == size_unit_arg_h,
+ format_size_value(select_conf.size_unit_arg, bytes,
+ select_conf.size_unit_arg == size_unit_arg_h,
formated_value);
- printf("%s%s%s",
+ ret = output("%s%s%s",
(gsi->flags & GSF_PRINT_FILES)? "" : "\t",
formated_value,
(gsi->flags & GSF_PRINT_DIRNAME)? "\t" : "\n"
);
+ if (ret < 0)
+ goto err;
}
if (summary) {
num_bytes += bytes;
ret = get_dir_name_of_row(row, &dirname);
if (ret < 0)
goto err;
- printf("%s%s\n",
+ ret = output("%s%s\n",
(gsi->flags & (GSF_PRINT_BYTES | GSF_PRINT_FILES))? "" : "\t",
dirname);
free(dirname);
+ if (ret < 0)
+ goto err;
}
if (gsi->count > 0)
gsi->count--;
return check_loop_return(ret, *loop_ret, *loop_osl_errno);
}
-static void print_global_summary(void)
+static int print_global_summary(void)
{
char d[FORMATED_VALUE_SIZE], f[FORMATED_VALUE_SIZE],
s[FORMATED_VALUE_SIZE];
enum enum_count_unit ud, uf;
enum enum_size_unit us;
+ int ret;
- if (conf.no_global_summary_given)
- return;
- ud = format_count_value(conf.count_unit_arg, num_dirs, 0, d);
- uf = format_count_value(conf.count_unit_arg, num_files, 0, f);
- us = format_size_value(conf.size_unit_arg, num_bytes, 0, s);
+ if (select_conf.no_global_summary_given)
+ return 1;
+ ud = format_count_value(select_conf.count_unit_arg, num_dirs, 0, d);
+ uf = format_count_value(select_conf.count_unit_arg, num_files, 0, f);
+ us = format_size_value(select_conf.size_unit_arg, num_bytes, 0, s);
- if (!conf.no_headers_given)
- printf("Global summary "
+ if (!select_conf.no_headers_given) {
+ ret = output("Global summary "
"(dirs(%c)/files(%c)/size(%c))\n",
count_unit_abbrevs[ud],
count_unit_abbrevs[uf],
size_unit_abbrevs[us]
);
- printf("\t%s\t%s\t%s\n\n", d, f, s);
+ if (ret < 0)
+ return ret;
+ }
+ return output("\t%s\t%s\t%s\n\n", d, f, s);
}
static int print_user_summary_line(struct user_info *ui, __a_unused void *data)
formated_file_count[FORMATED_VALUE_SIZE],
formated_bytes[FORMATED_VALUE_SIZE ];
- format_count_value(conf.count_unit_arg, ui->dirs,
- conf.count_unit_arg == count_unit_arg_h,
+ format_count_value(select_conf.count_unit_arg, ui->dirs,
+ select_conf.count_unit_arg == count_unit_arg_h,
formated_dir_count);
- format_count_value(conf.count_unit_arg, ui->files,
- conf.count_unit_arg == count_unit_arg_h,
+ format_count_value(select_conf.count_unit_arg, ui->files,
+ select_conf.count_unit_arg == count_unit_arg_h,
formated_file_count);
- format_size_value(conf.size_unit_arg, ui->bytes,
- conf.size_unit_arg == size_unit_arg_h,
+ format_size_value(select_conf.size_unit_arg, ui->bytes,
+ select_conf.size_unit_arg == size_unit_arg_h,
formated_bytes);
- printf("\t%s\t%u\t%s\t%s\t%s\n",
+ return output("\t%s\t%u\t%s\t%s\t%s\n",
ui->pw_name? ui->pw_name : "?",
(unsigned)ui->uid,
formated_dir_count,
formated_file_count,
formated_bytes
);
- return 1;
}
static int name_comp(const void *a, const void *b)
[user_summary_sort_arg_size] = size_comp,
};
-static void print_user_summary(void)
+static int print_user_summary(void)
{
- if (conf.no_user_summary_given)
- return;
- if (!conf.no_headers_given)
- printf("User summary "
+ if (select_conf.no_user_summary_given)
+ return 1;
+ if (!select_conf.no_headers_given) {
+ int ret = output("User summary "
"(pw_name/uid/dirs%s/files%s/size%s):\n",
count_unit_buf, count_unit_buf, size_unit_buf);
- sort_hash_table(summary_comparators[conf.user_summary_sort_arg]);
- for_each_admissible_user(print_user_summary_line, NULL);
+ if (ret < 0)
+ return ret;
+ }
+ sort_hash_table(summary_comparators[select_conf.user_summary_sort_arg]);
+ return for_each_admissible_user(print_user_summary_line, NULL);
}
static int print_user_list(struct user_info *ui, __a_unused void *data)
{
int ret;
struct user_stats_info usi;
- enum enum_user_list ula = conf.user_list_arg;
+ enum enum_user_list ula = select_conf.user_list_arg;
int print_size_list = (ula == user_list_arg_size
|| ula == user_list_arg_both);
if (print_size_list) {
- usi.count = conf.limit_arg;
+ usi.count = select_conf.limit_arg;
usi.ui = ui;
usi.flags = USF_PRINT_DIRNAME | USF_PRINT_BYTES | USF_COMPUTE_SUMMARY;
- if (!conf.no_headers_given)
- printf("%s (uid %u), by size%s:\n",
+ if (!select_conf.no_headers_given) {
+ ret = output("%s (uid %u), by size%s:\n",
ui->pw_name? ui->pw_name : "?", (unsigned)ui->uid,
size_unit_buf);
+ if (ret < 0)
+ return ret;
+ }
ret = adu_loop_reverse(ui->table, UT_BYTES, &usi, user_stats_loop_function,
&usi.ret, &usi.osl_errno);
if (ret < 0)
return ret;
- printf("\n");
+ ret = output("\n");
+ if (ret < 0)
+ return ret;
}
if (ula == user_list_arg_file_count || ula == user_list_arg_both) {
- if (!conf.no_headers_given)
- printf("%s (uid %u), by file count%s:\n",
+ if (!select_conf.no_headers_given) {
+ ret = output("%s (uid %u), by file count%s:\n",
ui->pw_name? ui->pw_name : "?", (unsigned)ui->uid,
count_unit_buf);
- usi.count = conf.limit_arg,
+ if (ret < 0)
+ return ret;
+ }
+ usi.count = select_conf.limit_arg,
usi.ui = ui;
usi.flags = USF_PRINT_DIRNAME | USF_PRINT_FILES;
ret = adu_loop_reverse(ui->table, UT_FILES, &usi, user_stats_loop_function,
&usi.ret, &usi.osl_errno);
if (ret < 0)
return ret;
- printf("\n");
+ ret = output("\n");
+ if (ret < 0)
+ return ret;
}
- if (ula == user_list_arg_none && !conf.no_user_summary_given) {
- usi.count = conf.limit_arg;
+ if (ula == user_list_arg_none && !select_conf.no_user_summary_given) {
+ usi.count = select_conf.limit_arg;
usi.ui = ui;
usi.flags = USF_COMPUTE_SUMMARY;
ret = adu_loop_reverse(ui->table, UT_FILES, &usi, user_stats_loop_function,
{
struct global_stats_info gsi;
int ret;
- enum enum_global_list gla = conf.global_list_arg;
+ enum enum_global_list gla = select_conf.global_list_arg;
int print_size_list = (gla == global_list_arg_size
|| gla == global_list_arg_both);
if (print_size_list) {
- gsi.count = conf.limit_arg;
+ gsi.count = select_conf.limit_arg;
gsi.flags = GSF_PRINT_DIRNAME | GSF_PRINT_BYTES | GSF_COMPUTE_SUMMARY;
- if (!conf.no_headers_given)
- printf("By size%s:\n", size_unit_buf);
+ if (!select_conf.no_headers_given) {
+ ret = output("By size%s:\n", size_unit_buf);
+ if (ret < 0)
+ return ret;
+ }
ret = adu_loop_reverse(dir_table, DT_BYTES, &gsi,
global_stats_loop_function, &gsi.ret, &gsi.osl_errno);
if (ret < 0)
return ret;
- printf("\n");
+ ret = output("\n");
+ if (ret < 0)
+ return ret;
}
if (gla == global_list_arg_file_count || gla == global_list_arg_both) {
- gsi.count = conf.limit_arg;
+ gsi.count = select_conf.limit_arg;
gsi.flags = GSF_PRINT_DIRNAME | GSF_PRINT_FILES;
if (!print_size_list)
gsi.flags |= GSF_COMPUTE_SUMMARY;
- if (!conf.no_headers_given)
- printf("By file count%s:\n", count_unit_buf);
+ if (!select_conf.no_headers_given) {
+ ret = output("By file count%s:\n", count_unit_buf);
+ if (ret < 0)
+ return ret;
+ }
ret = adu_loop_reverse(dir_table, DT_FILES, &gsi,
global_stats_loop_function, &gsi.ret, &gsi.osl_errno);
if (ret < 0)
return ret;
- printf("\n");
+ ret = output("\n");
+ if (ret < 0)
+ return ret;
}
- if (gla == global_list_arg_none && !conf.no_global_summary_given) {
+ if (gla == global_list_arg_none && !select_conf.no_global_summary_given) {
/* must compute summary */
- gsi.count = conf.limit_arg;
+ gsi.count = select_conf.limit_arg;
gsi.flags = GSF_COMPUTE_SUMMARY;
ret = adu_loop_reverse(dir_table, DT_FILES, &gsi,
global_stats_loop_function, &gsi.ret, &gsi.osl_errno);
ret = print_global_lists();
if (ret < 0)
return ret;
- print_global_summary();
- print_user_lists();
- print_user_summary();
+ ret = print_global_summary();
+ if (ret < 0)
+ return ret;
+ ret = print_user_lists();
+ if (ret < 0)
+ return ret;
+ ret = print_user_summary();
+ if (ret < 0)
+ return ret;
return 1;
}
-static int read_uid_file(void)
+static int read_uid_file(struct uid_range *admissible_uids)
{
size_t size;
uint32_t n;
create_hash_table(bits);
for (n = 0; n < num_uids; n++) {
uint32_t uid = read_u32(map + n * sizeof(uid));
- ret = search_uid(uid, OPEN_USER_TABLE, NULL);
+ ret = search_uid(uid, admissible_uids, OPEN_USER_TABLE, NULL);
if (ret < 0)
goto out;
}
return ret;
}
-int com_select(void)
+int run_select_query(struct uid_range *admissible_uids)
{
int ret;
- if (conf.count_unit_arg != count_unit_arg_h)
- count_unit_buf[1] = count_unit_abbrevs[conf.count_unit_arg];
+ if (select_conf.output_given && strcmp(select_conf.output_arg, "-")) {
+ output_file = fopen(select_conf.output_arg, "w");
+ if (!output_file)
+ return -ERRNO_TO_ERROR(errno);
+ } else
+ output_file = stdout;
+
+ if (select_conf.count_unit_arg != count_unit_arg_h)
+ count_unit_buf[1] = count_unit_abbrevs[select_conf.count_unit_arg];
else
count_unit_buf[0] = '\0';
- if (conf.size_unit_arg != size_unit_arg_h)
- size_unit_buf[1] = size_unit_abbrevs[conf.size_unit_arg];
+ if (select_conf.size_unit_arg != size_unit_arg_h)
+ size_unit_buf[1] = size_unit_abbrevs[select_conf.size_unit_arg];
else
size_unit_buf[0] = '\0';
ret = open_dir_table(0);
if (ret < 0)
- return ret;
+ goto out;
check_signals();
- ret = read_uid_file();
+ ret = read_uid_file(admissible_uids);
if (ret < 0)
- return ret;
+ goto out;
check_signals();
ret = print_statistics();
+out:
close_all_tables();
+ if (output_file != stdout)
+ fclose(output_file);
return ret;
}
+
+int com_select(void)
+{
+ int ret;
+ struct uid_range *admissible_uids = NULL;
+
+ if (conf.select_options_given) {
+ struct select_cmdline_parser_params params = {
+ .override = 1,
+ .initialize = 1,
+ .check_required = 1,
+ .check_ambiguity = 1,
+ .print_errors = 1
+ };
+
+ if (select_cmdline_parser_string_ext(conf.select_options_arg,
+ &select_conf, "select", ¶ms))
+ return -E_SYNTAX;
+ ret = parse_uid_arg(select_conf.uid_arg, &admissible_uids);
+ if (ret < 0)
+ return ret;
+ }
+ return run_select_query(admissible_uids);
+}
--- /dev/null
+
+option "uid" u
+#~~~~~~~~~~~~~
+"user id(s) to take into account"
+string typestr="uid_spec"
+optional
+details="
+ An uid specifier may be a single number, or a range of uids.
+ Example:
+
+ --uid 42 # only consider uid 42
+ --uid 42- # only consider uids greater or equal than 42
+ --uid 23-42 # only consider uids between 23 and 42, inclusively.
+ --uid 23-42,666-777,88 # consider uids 23-42, 666-777 and 88.
+"
+
+option "limit" L
+#~~~~~~~~~~~~~~~
+"Limit output"
+int typestr="num"
+default="-1"
+optional
+details="
+ Only print num lines of output. If negative (the default),
+ print all lines.
+"
+
+option "no-headers" -
+#~~~~~~~~~~~~~~~~~~~~
+"supress descriptions for listings/tables"
+flag off
+details="
+ This is mostly useful to feed the output of adu to scripts.
+"
+
+option "sort" s
+#~~~~~~~~~~~~~~~
+"how to sort the output"
+enum typestr="<key>"
+values="sizes","files","unsorted"
+default="sizes"
+optional
+details="
+ Sort by file size, file count or unsorted.
+"
+
+option "format" f
+#~~~~~~~~~~~~~~~~~
+"how to format the output"
+string typestr="<format>"
+optional
+details="
+ %(basedir) -- the path given to --base-dir during create
+ %(dir) -- the name of the directory
+ %(dir_size) -- the size of the sum of all regular files in this directory
+ %(num_files) -- the number of regular files in this directory
+ %% -- interpolates to %
+ %xx -- interpolates to the character with hex code xx
+"
+
+option "output" o
+#~~~~~~~~~~~~~~~~
+"file to write output to"
+string typestr="<path>"
+optional
+default="-"
+details="
+ If empty, or not given, use stdout.
+"
+
+option "size-unit" -
+#~~~~~~~~~~~~~~~~~~~
+"select output format for sizes"
+enum typestr="format"
+values="h","b","k","m","g","t"
+default="h"
+optional
+details="
+ Print sizes in the given unit: human-readable, bytes,
+ kilobytes (2^10), megabytes (2^20), gigabytes (2^30), terabytes
+ (2^40). The default is \"h\", i.e. human-readable.
+"
+
+option "count-unit" -
+#~~~~~~~~~~~~~~~~~~~~
+"select output format for counted values"
+enum typestr="format"
+values="h","n","k","m","g","t"
+default="h"
+optional
+details="
+ Print the number of files/directories in the given unit:
+ human-readable, number, number/10^3, number/10^6, number/10^12,
+ number/10^15. The default is to print numbers in human-readable
+ format.
+"
+
+
+option "user-summary-sort" -
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~
+"how to sort the user-summary"
+enum typestr="col_spec"
+values="name","uid","dir_count","file_count","size"
+default="size"
+optional
+details="
+ It is enough to specify the first letter of the column specifier,
+ e.g. \"--user-summary-sort f\" sorts by file count.
+"
+
+option "no-user-summary" -
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~
+"do not print the user summary table"
+flag off
+
+
+option "user-list" -
+#~~~~~~~~~~~~~~~~~~~
+"how to print per-user directory listings"
+enum typestr="which"
+values="size","file_count","both","none"
+default="both"
+optional
+details="
+ Similar to the global directory listings mentioned above,
+ adu can print two directory listings per user. This option
+ controls which of the these should be printed.
+"
+option "no-global-summary" -
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~
+"do not print the summary line"
+flag off
+
+option "global-list" -
+#~~~~~~~~~~~~~~~~~~~~~
+"how to print global directory listings"
+enum typestr="which"
+values="size","file_count","both","none"
+default="both"
+optional
+details="
+ By default adu prints two global directory listings: The
+ first prints the directory names ordered by the sum of the
+ sizes of the contained files while the second listing prints
+ them sorted by the number of files. This option can be used
+ to print only one or neither of these two listings.
+"
+option "print-base-dir" -
+#~~~~~~~~~~~~~~~~~~~~~~~~
+"whether to include the base-dir in the output"
+flag off
+details="
+ If this flag is given, all directories printed are prefixed
+ with the base directory. The default is to print paths relative
+ to the base dir.
+"
*result = tmp;
return 1;
}
+
+/**
+ * Split string and return pointers to its parts.
+ *
+ * \param args The string to be split.
+ * \param argv_ptr Pointer to the list of substrings.
+ * \param delim Delimiter.
+ *
+ * This function modifies \a args by replacing each occurance of \a delim by
+ * zero. A \p NULL-terminated array of pointers to char* is allocated dynamically
+ * and these pointers are initialized to point to the broken-up substrings
+ * within \a args. A pointer to this array is returned via \a argv_ptr.
+ *
+ * \return The number of substrings found in \a args.
+ */
+__must_check unsigned split_args(char *args, char *** const argv_ptr, const char *delim)
+{
+ char *p = args;
+ char **argv;
+ size_t n = 0, i, j;
+
+ p = args + strspn(args, delim);
+ for (;;) {
+ i = strcspn(p, delim);
+ if (!i)
+ break;
+ p += i;
+ n++;
+ p += strspn(p, delim);
+ }
+ *argv_ptr = adu_malloc((n + 1) * sizeof(char *));
+ argv = *argv_ptr;
+ i = 0;
+ p = args + strspn(args, delim);
+ while (p) {
+ argv[i] = p;
+ j = strcspn(p, delim);
+ if (!j)
+ break;
+ p += strcspn(p, delim);
+ if (*p) {
+ *p = '\0';
+ p++;
+ p += strspn(p, delim);
+ }
+ i++;
+ }
+ argv[n] = NULL;
+ return n;
+}
+
+static int check_uid_arg(const char *arg, uint32_t *uid)
+{
+ const uint32_t max = ~0U;
+ /*
+ * we need an 64-bit int for string -> uid conversion because strtoll()
+ * returns a signed value.
+ */
+ int64_t val;
+ int ret = atoi64(arg, &val);
+
+ if (ret < 0)
+ return ret;
+ if (val < 0 || val > max)
+ return -ERRNO_TO_ERROR(EINVAL);
+ *uid = val;
+ return 1;
+}
+
+int parse_uid_range(const char *orig_arg, struct uid_range *ur)
+{
+ int ret;
+ char *arg = adu_strdup(orig_arg), *p = strchr(arg, '-');
+
+ if (!p || p == arg) { /* -42 or 42 */
+ ret = check_uid_arg(p? p + 1 : arg, &ur->high);
+ if (ret < 0)
+ goto out;
+ ur->low = p? 0 : ur->high;
+ ret = 1;
+ goto out;
+ }
+ /* 42- or 42-4711 */
+ *p = '\0';
+ p++;
+ ret = check_uid_arg(arg, &ur->low);
+ if (ret < 0)
+ goto out;
+ ur->high = ~0U;
+ if (*p) { /* 42-4711 */
+ ret = check_uid_arg(p, &ur->high);
+ if (ret < 0)
+ goto out;
+ }
+ if (ur->low > ur->high)
+ ret = -ERRNO_TO_ERROR(EINVAL);
+out:
+ if (ret < 0)
+ ERROR_LOG("bad uid option: %s\n", orig_arg);
+ else
+ INFO_LOG("admissible uid range: %u - %u\n", ur->low,
+ ur->high);
+ free(arg);
+ return ret;
+}
+
+int parse_uid_arg(const char *orig_arg, struct uid_range **ur)
+{
+ char *arg, **argv;
+ unsigned n;
+ int i, ret = 1;
+
+ if (!orig_arg)
+ return 0;
+ arg = adu_strdup(orig_arg);
+ n = split_args(arg, &argv, ",");
+ if (!n)
+ return -E_SYNTAX;
+ *ur = adu_malloc((n + 1) * sizeof(struct uid_range));
+ for (i = 0; i < n; i++) {
+ ret = parse_uid_range(argv[i], *ur + i);
+ if (ret < 0)
+ break;
+ }
+ free(arg);
+ if (ret < 0) {
+ free(*ur);
+ *ur = NULL;
+ }
+ /* an empty range indicates the end of the list */
+ (*ur)[n].low = 1;
+ (*ur)[n].high = 0;
+ return n;
+}
__must_check __malloc char *adu_strdup(const char *s);
__must_check __malloc __printf_1_2 char *make_message(const char *fmt, ...);
__must_check int atoi64(const char *str, int64_t *result);
+int parse_uid_arg(const char *orig_arg, struct uid_range **ur);