all:
$(MAKE) -C lib
+ $(MAKE) -C kafs
clean:
$(RM) *~
$(MAKE) -C lib clean
+ $(MAKE) -C kafs clean
$(MAKE) -C rxgen clean
+
+clean-deps:
+ $(RM) lib/.*.o.d
+ $(RM) kafs/.*.o.d
--- /dev/null
+bos.[ch]
+fs.[ch]
+pts.[ch]
+vos.[ch]
+kafs
--- /dev/null
+RXGEN := ../rxgen/rxgen.py
+
+CPPFLAGS := -I ../lib -I .
+CFLAGS := -g -Wall -Wformat -fpie
+
+CORE_SRCS := \
+ kafs.c \
+ arg_completion.c \
+ arg_parse.c \
+ display_error.c
+
+BOS_SRCS := \
+ bos.c \
+ bos_help.c
+
+FS_SRCS := \
+ fs.c \
+ fs_help.c
+
+PTS_SRCS := \
+ pts.c \
+ pts_help.c
+
+VOS_SRCS := \
+ vos.c \
+ vos_help.c
+
+CORE_OBJS := $(patsubst %.c,%.o,$(CORE_SRCS))
+BOS_OBJS := $(patsubst %.c,%.o,$(BOS_SRCS))
+FS_OBJS := $(patsubst %.c,%.o,$(FS_SRCS))
+PTS_OBJS := $(patsubst %.c,%.o,$(PTS_SRCS))
+VOS_OBJS := $(patsubst %.c,%.o,$(VOS_SRCS))
+KAFS_OBJS := $(CORE_OBJS) $(BOS_OBJS) $(FS_OBJS) $(PTS_OBJS) $(VOS_OBJS)
+
+all: kafs
+
+bos.c bos.h: gen_command.py $(filter-out bos.c, $(BOS_SRCS))
+ ./gen_command.py bos $(filter-out bos.c, $(BOS_SRCS))
+
+fs.c fs.h: gen_command.py $(filter-out fs.c, $(FS_SRCS))
+ ./gen_command.py fs $(filter-out fs.c, $(FS_SRCS))
+
+pts.c pts.h: gen_command.py $(filter-out pts.c, $(PTS_SRCS))
+ ./gen_command.py pts $(filter-out pts.c, $(PTS_SRCS))
+
+vos.c vos.h: gen_command.py $(filter-out vos.c, $(VOS_SRCS))
+ ./gen_command.py vos $(filter-out vos.c, $(VOS_SRCS))
+
+GENFILES := \
+ bos.c bos.h \
+ fs.c fs.h \
+ pts.c pts.h \
+ vos.c vos.h
+
+$(KAFS_OBJS) $(GENFILES): Makefile
+
+DEPS := $(wildcard .*.o.d)
+ifneq ($(DEPS),)
+include $(DEPS)
+else
+$(KAFS_OBJS): bos.h fs.h pts.h vos.h
+endif
+
+%.o: %.c
+ $(CC) $(CPPFLAGS) -MMD -MF .$@.d $(CFLAGS) -o $@ -c $<
+
+LIBDIR := ../lib
+
+kafs: $(KAFS_OBJS) $(LIBDIR)/libkafs_utils.a
+ $(CC) -o $@ $(KAFS_OBJS) -L$(LIBDIR) -lkafs_utils -lkafs_client -luuid
+
+clean:
+ $(RM) *~ *.o $(DEPS)
+ $(RM) bos.[ch] fs.[ch] pts.[ch] vos.[ch]
+ $(RM) kafs
--- /dev/null
+/* Bash completion helper
+ *
+ * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <alloca.h>
+#include <kafs/cellserv.h>
+#include "arg_parse.h"
+
+bool kafs_debug_completion;
+
+static FILE *completion_log;
+
+static bool str_is_flag(const char *p)
+{
+ if (!*p)
+ return false;
+ if (p[0] != '-')
+ return false;
+ if (!*p)
+ return false;
+ for (; *p; p++)
+ if (!isdigit(*p))
+ return true;
+ return false;
+}
+
+void comp_log(const char *fmt, ...)
+{
+ va_list va;
+
+ if (kafs_debug_completion) {
+ if (!completion_log) {
+ completion_log = fopen("/tmp/kafs_completer.log", "a");
+ if (!completion_log) {
+ perror("/tmp/kafs_completer.log");
+ exit(2);
+ }
+ setlinebuf(completion_log);
+ }
+
+ va_start(va, fmt);
+ vfprintf(completion_log, fmt, va);
+ va_end(va);
+ }
+}
+
+/*
+ * Complete the name of the command suite.
+ */
+void bash_complete_command_suite(char *s)
+{
+ const struct kafs_suite *suite;
+ bool need_space = false;
+ int i, l = strlen(s);
+
+ comp_log("Word '%s'\n", s);
+
+ for (i = 0; kafs_suites[i]; i++) {
+ suite = kafs_suites[i];
+ if (strncmp(s, suite->name, l) == 0) {
+ if (need_space)
+ putchar(' ');
+ fputs(suite->name, stdout);
+ need_space = true;
+ }
+ }
+
+ exit(0);
+}
+
+/*
+ * Generate a list of matching cell names.
+ */
+static char **complete_cell_name(const char *prefix)
+{
+ char **cells;
+ int i, c, l = strlen(prefix);
+
+ cells = calloc(kafs_cellserv_db->nr_cells + 1, sizeof(char *));
+ if (!cells) {
+ perror(NULL);
+ exit(1);
+ }
+
+ c = 0;
+ for (i = 0; i < kafs_cellserv_db->nr_cells; i++) {
+ struct kafs_cell *cell = kafs_cellserv_db->cells[i];
+
+ if (strncmp(prefix, cell->name, l) == 0)
+ cells[c++] = cell->name;
+ }
+
+ return cells;
+}
+
+/*
+ * Expand a command name.
+ */
+void bash_complete_command_name(const struct kafs_suite *suite, char *word)
+{
+ bool need_space = false;
+ int i, l = strlen(word);
+
+ comp_log("Word '%s'\n", word);
+
+ for (i = 0; suite->commands[i]; i++) {
+ const struct kafs_command *cmd = suite->commands[i];
+
+ if (strncmp(word, cmd->name, l) == 0) {
+ if (need_space)
+ putchar(' ');
+ fputs(cmd->name, stdout);
+ need_space = true;
+ }
+ }
+
+ exit(0);
+}
+
+struct arg_notes {
+ const struct kafs_argument *canon_flag;
+ const struct kafs_argument *word_type;
+ bool additional_arg;
+};
+
+/*
+ * Perform command line completion.
+ */
+void bash_complete(int argc, char **argv, int target,
+ const struct kafs_suite *suite)
+{
+ const struct kafs_argument *last_sw, *arg, *word_type, *canon;
+ const struct kafs_argument *next_required; //, *next_opts;
+ const struct kafs_command *cmd;
+ struct arg_notes *notes;
+ const char *word, *special, *space = "";
+ char **p, *eliminated_args;
+ bool skip_flag, additional_arg;
+ int pos, i, j, wl;
+
+ //completion_log = stderr;
+ comp_log("Compl %d\n", target);
+ comp_log("Suite: %s\n", suite->name);
+
+ if (target == 1)
+ bash_complete_command_name(suite, argv[1]);
+ if (target >= argc)
+ exit(0);
+
+ for (p = argv; *p; p++) {
+ if (p - argv == target)
+ comp_log(" >%s<", *p);
+ else
+ comp_log(" %s", *p);
+ }
+ comp_log("\n");
+
+ cmd = look_up_command(suite, argv[1]);
+ if (!cmd) {
+ comp_log("Unknown command %s\n", argv[1]);
+ exit(1);
+ }
+
+ /* Discard the suite name and the command name */
+ argc -= 2;
+ argv += 2;
+ target -= 2;
+
+ /* Ignore any arguments after the one being expanded */
+ argv[argc] = NULL;
+
+ comp_log("--- TARGET %s\n", argv[target]);
+
+ // Determine a list of canonicalised flag names and initialise an array
+ // to keep track of the non-flag argument types.
+ //
+ // An argument beginning with a dash that directly follows on from a
+ // flag that takes an argument is assumed to be an argument, not a
+ // flag.
+ notes = alloca(sizeof(struct arg_notes) * argc);
+ memset(notes, 0, sizeof(struct arg_notes) * argc);
+
+ // Keep track of any arguments that have been eliminated
+ eliminated_args = alloca(cmd->nr_args + 1);
+ memset(eliminated_args, '-', cmd->nr_args);
+ eliminated_args[cmd->nr_args] = 0;
+ comp_log("ELIMINATED_ARGS 0: %s\n", eliminated_args);
+
+ skip_flag = false;
+ for (i = 0; i < argc; i++) {
+ word = argv[i];
+ notes[i].canon_flag = NULL;
+ notes[i].word_type = NULL;
+ notes[i].additional_arg = false;
+
+ if (str_is_flag(word) && !skip_flag) {
+ const struct kafs_argument *match = NULL;
+ const char *sw = word + 1;
+ int swl = strlen(word) - 1;
+
+ for (j = 0; j < cmd->nr_args; j++) {
+ arg = &cmd->args[j];
+
+ if (strcmp(sw, arg->name) == 0) {
+ match = arg;
+ break;
+ }
+ if (strncmp(sw, arg->name, swl) == 0) {
+ if (match) {
+ comp_log("AMBIGUOUS %s\n", word);
+ match = NULL;
+ break;
+ }
+ match = arg;
+ }
+ }
+
+ if (match) {
+ comp_log("CANON %s\n", sw);
+ notes[i].canon_flag = match;
+ eliminated_args[match - cmd->args] = 'y';
+ if (!match->flag)
+ skip_flag = true;
+ }
+ } else {
+ skip_flag = false;
+ }
+ }
+
+ comp_log("ELIMINATED_ARGS 1: %s\n", eliminated_args);
+
+ // Try to eliminate required arguments by position where the flag is
+ // implicit.
+ pos = 0;
+ for (j = 0; j < cmd->nr_args; j++) {
+ const struct kafs_argument *arg = &cmd->args[j];
+
+ if (pos >= argc)
+ break;
+ word = argv[pos];
+ if (str_is_flag(word))
+ break;
+ if (!arg->req)
+ break;
+
+ // We have a required argument. If the first word of this
+ // argument is currently being expanded and is blank, then we
+ // stick the flag in first.
+ if (!word[0] && pos == target) {
+ printf("-%s\n", arg->name);
+ exit(0);
+ }
+
+ notes[pos].word_type = arg;
+ eliminated_args[j] = true;
+ pos++;
+ if (arg->single)
+ continue;
+ if (j + 1 < cmd->nr_args && cmd->args[j + 1].req)
+ continue;
+ while (pos < argc) {
+ word = argv[pos];
+ if (str_is_flag(word))
+ break;
+ notes[pos].word_type = arg;
+ notes[pos].additional_arg = true;
+ pos++;
+ }
+ break;
+ }
+
+ comp_log("ELIMINATED_ARGS 2: %s\n", eliminated_args);
+
+ // Work out the types of any optional arguments
+ last_sw = NULL;
+ additional_arg = false;
+ for (i = 0; i < argc; i++) {
+ arg = notes[i].canon_flag;
+ if (arg) {
+ if (!arg->flag)
+ last_sw = arg;
+ else
+ last_sw = NULL;
+ additional_arg = false;
+ } else if (last_sw) {
+ notes[i].word_type = last_sw;
+ notes[i].additional_arg = additional_arg;
+ if (last_sw->single)
+ last_sw = NULL;
+ else
+ additional_arg = true;
+ }
+ }
+
+ comp_log("TYPES [");
+ for (i = 0; i < argc; i++) {
+ if (notes[i].canon_flag)
+ comp_log(" -%s", notes[i].canon_flag->name);
+ //else if (notes[i].word_type == "-")
+ // comp_log(" -?");
+ else if (notes[i].word_type)
+ comp_log(" <%s>", notes[i].word_type->val);
+ else
+ comp_log(" ??");
+ }
+ comp_log(" ]\n");
+
+ // Try to determine how to deal with the word being expanded
+ word = argv[target];
+ wl = strlen(word);
+ word_type = notes[target].word_type;
+ canon = notes[target].canon_flag;
+ additional_arg = notes[target].additional_arg;
+ comp_log("WORD \"%s\"\n", word);
+ comp_log("WORD_TYPE %s\n", word_type ? word_type->name : "*NULL*");
+ comp_log("CANON %s\n", canon ? canon->name : "*NULL*");
+
+ // Expand unambiguous flags fully
+ if (canon) {
+ comp_log("*** GEN CANON %s\n", canon->name);
+ printf("-%s\n", canon->name);
+ exit(0);
+ }
+
+ next_required = NULL;
+ for (j = 0; j < cmd->nr_args; j++) {
+ const struct kafs_argument *arg = &cmd->args[j];
+ if (arg->req) {
+ if (eliminated_args[j] == '-') {
+ next_required = arg;
+ break;
+ }
+ }
+ }
+
+ // Insert a required flag now if there is one and if there is no
+ // mandatory argument to the previous flag
+ if (next_required) {
+ if (!word_type ||
+ (strcmp(word, "-") == 0 && additional_arg)) {
+ comp_log("*** GEN REQFLAG %s\n", next_required->name);
+ printf("-%s\n", next_required->name);
+ exit(0);
+ }
+ }
+
+ // Work out what type this argument will be if it's not a flag and
+ // instruct the parent to run compgen
+ special = "";
+ if (word_type) {
+ if (strcmp(word_type->name, "cell") == 0) {
+ special = "@CELL@";
+#if 0
+ } else if (word_type[1] == get_bosserver ||
+ word_type[1] == get_fileserver ||
+ word_type[1] == get_volserver ||
+ word_type[1] == get_vlservers ||
+ word_type[1] == get_machine_name ||
+ word_type[1] == get_machine_names) {
+ special = "@HOSTNAME@";
+ } else if (word_type[1] == get_volume_name ||
+ word_type[1] == get_volume_names) {
+ special = "@VOLNAME@";
+ } else if (word_type[1] == get_partition_id) {
+ special = "@PARTID@";
+ } else if (word_type[1] == get_path_name ||
+ word_type[1] == get_path_names ||
+ word_type[1] == get_file_name ||
+ word_type[1] == get_file_names) {
+ special = "@FILE@";
+#endif
+ } else {
+ special = "@OTHER@";
+ }
+ }
+ comp_log("SPECIAL %s\n", special);
+
+ // Expand an argument that can't be a flag
+ if (word_type && !additional_arg) {
+ comp_log("*** GEN NONFLAG %s\n", word_type->name);
+ goto expand_special;
+ }
+
+ // Work out a list of what options could go next;
+ if (!word[0] || (word[0] == '-' && !word[1])) {
+ for (j = 0; j < cmd->nr_args; j++) {
+ if (eliminated_args[j] == '-') {
+ arg = &cmd->args[j];
+ printf("%s-%s", space, arg->name);
+ space = " ";
+ }
+ }
+ } else {
+ for (j = 0; j < cmd->nr_args; j++) {
+ if (eliminated_args[j] == '-') {
+ arg = &cmd->args[j];
+ if (strncmp(word + 1, arg->name, wl - 1) == 0) {
+ printf("%s-%s", space, arg->name);
+ space = " ";
+ }
+ }
+ }
+ }
+
+
+#if 0
+ next_opts = NULL;
+ if (!word[0] || str_is_flag(word)) {
+ next_opts = next_required;
+ if (!next_opts) {
+ next_opts = "";
+ for (j = 0; j < cmd->nr_args; j++) {
+ if (eliminated_args[j] == 'y')
+ continue;
+ arg = &cmd->args[j];
+ if (strncmp(word + 1, arg->name, wl - 1) == 0) {
+ if (next_opts == "")
+ next_opts = "-";
+ else
+ next_opts += " -";
+ next_opts += i;
+ }
+ }
+ }
+ }
+ comp_log("NEXT OPTS %s\n", next_opts);
+
+ if (next_opts != "") {
+ // If the next word has to be a flag of some sort, display a
+ // list thereof
+ if (!word_type || (additional_arg && word[0] == '-')) {
+ comp_log("*** GEN OPTIONS %s\n", next_opts);
+ printf("%s\n", next_opts);
+ exit(0);
+ }
+ }
+#endif
+
+ // The next word can be a flag or an additional argument
+ if (additional_arg) {
+ comp_log("*** GEN ADDARG %s\n", special);
+ goto expand_special;
+ }
+
+ // Nothing left;
+ comp_log("*** GEN NOTHING\n");
+ printf("\n");
+ exit(0);
+
+expand_special:
+ if (strcmp(special, "@CELL@") == 0) {
+ char **cells = complete_cell_name(word), **c;
+
+ for (c = cells; *c; c++) {
+ printf("%s%s", space, *c);
+ space = " ";
+ }
+ printf("\n");
+ free(cells);
+ } else {
+ printf("%s\n", special);
+ }
+ exit(0);
+}
--- /dev/null
+/* Argument parser.
+ *
+ * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#define _GNU_SOURCE
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <kafs/cellserv.h>
+#include "rxrpc.h"
+#include "afs_xg.h"
+#include "arg_parse.h"
+
+static bool str_is_flag(const char *p)
+{
+ if (!*p)
+ return false;
+ if (p[0] != '-')
+ return false;
+ if (!*p)
+ return false;
+ for (; *p; p++)
+ if (!isdigit(*p))
+ return true;
+ return false;
+}
+
+static bool append_param(struct extracted_arg *result, char *a)
+{
+ unsigned int n = result->nr_values;
+ char **p = result->values;
+
+ p = realloc(p, (n + 2) * sizeof(const char *));
+ if (!p)
+ return false;
+ p[n] = a;
+ p[n + 1] = NULL;
+ result->values = p;
+ result->nr_values = n + 1;
+ return true;
+}
+
+void free_extracted_args(struct extracted_arg *arg)
+{
+ struct extracted_arg *p = arg;
+
+ for (;;) {
+ if (p->values) {
+ //for (i = 0; i < p->nr_values; i++)
+ // free(p->values[i]);
+ free(p->values);
+ }
+
+ if (p->last)
+ break;
+ p++;
+ }
+}
+
+/*
+ * Parse an argument list according to a defined set of switches.
+ *
+ * The following is an excerpt from the AFS Reference Manual, Introduction to
+ * AFS commands:
+ *
+ * CONDITIONS FOR OMITTING SWITCHES
+ *
+ * It is always acceptable to type the switch part of an argument, but in
+ * many cases it is not necessary. Specifically, switches can be omitted if
+ * the following conditions are met.
+ *
+ * (*) All of the command's required arguments appear in the order prescribed
+ * by the syntax statement.
+ *
+ * (*) No switch is provided for any argument.
+ *
+ * (*) There is only one value for each argument (but note the important
+ * exception discussed in the following paragraph).
+ *
+ * Omitting switches is possible only because there is a prescribed order for
+ * each command's arguments. When the issuer does not include switches, the
+ * command interpreter relies instead on the order of arguments; it assumes
+ * that the first element after the operation code is the command's first
+ * argument, the next element is the command's second argument, and so
+ * on. The important exception is when a command's final required argument
+ * accepts multiple values. In this case, the command interpreter assumes
+ * that the issuer has correctly provided one value for each argument up
+ * through the final one, so any additional values at the end belong to the
+ * final argument.
+ *
+ * The following list describes the rules for omitting switches from the
+ * opposite perspective: an argument's switch must be provided when any of
+ * the following conditions apply.
+ *
+ * (*) The command's arguments do not appear in the prescribed order.
+ *
+ * (*) An optional argument is omitted but a subsequent optional argument is
+ * provided.
+ *
+ * (*) A switch is provided for a preceding argument.
+ *
+ * (*) More than one value is supplied for a preceding argument (which must
+ * take multiple values, of course); without a switch on the current
+ * argument, the command interpreter assumes that the current argument is
+ * another value for the preceding argument.
+ *
+ */
+struct extracted_arg *extract_arguments(struct kafs_context *ctx, char **args,
+ const struct kafs_command *cmd)
+{
+ const struct kafs_nocombine *combo;
+ const struct kafs_argument *arg;
+ struct extracted_arg *results, *result;
+ bool need_switch = false;
+ unsigned int av = 0; /* Available arguments index; */
+ int i, j;
+
+ results = calloc(cmd->nr_args, sizeof(struct extracted_arg));
+ if (!results) {
+ kafs_nomem(ctx);
+ return NULL;
+ }
+
+ for (i = 0; i < cmd->nr_args; i++)
+ results[i].arg = &cmd->args[i];
+ results[cmd->nr_args - 1].last = true;
+
+ if (!args[0]) {
+ if (!cmd->nr_args)
+ return results;
+
+ if (cmd->args[0].req) {
+ kafs_inval(ctx, "Missing required parameters");
+ goto error;
+ }
+
+ return results;
+ }
+
+ /* Process all the optional arguments or switch-based required arguments */
+ while (*args) {
+ char *a = *args;
+
+ if (!str_is_flag(a)) {
+ /* Deal with positional arguments */
+ if (need_switch) {
+ kafs_inval(ctx, "Need switch before argument %d", i);
+ goto error;
+ }
+ if (av >= cmd->nr_args) {
+ kafs_inval(ctx, "Unexpected positional argument");
+ goto error;
+ }
+
+ arg = &cmd->args[av];
+ result = &results[av];
+ if (arg->flag) {
+ kafs_inval(ctx, "Unexpected positional argument");
+ goto error;
+ }
+ av = av + 1;
+
+ if (!append_param(result, a))
+ goto error;
+ args++;
+
+ if (arg->multi) {
+ /* Multiple-value arguments that are not
+ * preceded by their switch are forced to be
+ * single-value if there's yet another required
+ * argument following.
+ */
+ if (av < cmd->nr_args && cmd->args[av].req) {
+ ;
+ } else {
+ /* All remaining arguments up to the
+ * next switch belong to this.
+ */
+ for (; *args; args++) {
+ a = *args;
+ if (str_is_flag(a))
+ break;
+ if (!append_param(result, a))
+ goto error;
+ }
+ need_switch = true;
+ }
+ }
+ result->seen = true;
+
+ } else {
+ /* Deal with tagged arguments */
+ char *sw = a + 1;
+ size_t l = strlen(sw);
+
+ args++;
+
+ if (!sw[0]) {
+ kafs_inval(ctx, "Missing switch name");
+ goto error;
+ }
+
+ if (strcmp(sw, "help") == 0)
+ goto help;
+
+ /* Look up the switch in the table of possible
+ * arguments and flags.
+ */
+ for (j = 0; j < cmd->nr_args; j++) {
+ arg = &cmd->args[j];
+ result = &results[j];
+ if (strcmp(sw, arg->name) == 0)
+ goto found;
+ }
+
+ /* The switch name may be abbreviated to the shortest
+ * possible non-conflicting stem.
+ */
+ for (j = 0; j < cmd->nr_args; j++) {
+ arg = &cmd->args[j];
+ result = &results[j];
+ if (l > strlen(arg->name))
+ continue;
+ if (memcmp(sw, arg->name, l) != 0)
+ continue;
+ if (l >= arg->mink)
+ goto found;
+
+ kafs_inval(ctx, "Ambiguous switch name abbreviation '-%s'", sw);
+ goto error;
+ }
+
+ kafs_inval(ctx, "Unsupported switch '-%s'", sw);
+ goto error;
+
+ found:
+ /* Reject repeat flags */
+ if (result->seen) {
+ kafs_inval(ctx, "Duplicate switch '-%s' not permitted", sw);
+ goto error;
+ }
+ result->seen = true;
+
+ /* Arrange the parameters associated with the switch
+ * into a list.
+ */
+ for (; *args; args++) {
+ a = *args;
+ if (str_is_flag(a))
+ break;
+ append_param(result, a);
+ }
+
+ /* Check that we have the number of arguments we're expecting */
+ if (arg->flag && result->nr_values > 0) {
+ kafs_inval(ctx, "Switch '-%s' expects no arguments", sw);
+ goto error;
+ }
+ if (arg->single && result->nr_values != 1) {
+ kafs_inval(ctx, "Switch '-%s' expects one argument", sw);
+ goto error;
+ }
+ if (arg->multi && result->nr_values < 1) {
+ kafs_inval(ctx, "Switch '-%s' expects one or more arguments", sw);
+ goto error;
+ }
+ }
+
+ /* Check that none of the arguments are too big. */
+ if (arg->size_limit && result->nr_values > 0) {
+ for (i = 0; i < result->nr_values; i++) {
+ const char *v = result->values[i];
+ if (strlen(v) > arg->size_limit) {
+ kafs_inval(ctx, "Switch '-%s' has an overlong argument",
+ result->arg->name);
+ goto error;
+ }
+ }
+ }
+
+ /* Call the syntax checker */
+ if (arg->syntax) {
+ if (!arg->syntax(result))
+ goto error;
+ }
+ }
+
+ result = NULL;
+
+ /* Check for missing required arguments */
+ for (i = 0; i < cmd->nr_args; i++) {
+ arg = &cmd->args[i];
+ if (!arg->req)
+ break;
+
+ if (!results[i].seen) {
+ kafs_inval(ctx, "Missing '-%s' argument", arg->name);
+ goto error;
+ }
+ }
+
+ /* Check for invalid argument combinations */
+ if (cmd->no_combine) {
+ for (combo = cmd->no_combine; combo->a; combo++) {
+ if (results[combo->a].seen && results[combo->b].seen) {
+ kafs_inval(ctx, "Can't combine -%s with -%s",
+ results[combo->a].arg->name,
+ results[combo->b].arg->name);
+ goto error;
+ }
+ }
+ }
+
+ return results;
+
+error:
+ free_extracted_args(results);
+ return NULL;
+
+help:
+ kafs_command_usage(cmd);
+ free_extracted_args(results);
+ return NULL;
+}
+
+
+void free_kafs_context(struct kafs_context *ctx)
+{
+ /* Do nothing */
+
+#if 0
+ if (ctx->cell_db)
+ kafs_free_cell(cell->db);
+ free(ctx->cell_name);
+ free(ctx);
+#endif
+}
+
+void free_kafs_partition(struct kafs_partition *part)
+{
+ free(part);
+}
+
+
+bool extract_kafs_context(struct kafs_context *ctx, struct extracted_arg *arg)
+{
+ const char *name = arg->arg->name;
+
+ if (strcmp(name, "noauth") == 0) {
+ if (arg->seen)
+ ctx->sec_level = rxrpc_security_no_auth;
+ return true;
+ }
+
+ if (strcmp(name, "localauth") == 0) {
+ if (arg->seen)
+ ctx->sec_level = rxrpc_security_local_auth;
+ return true;
+ }
+
+ if (strcmp(name, "encrypt") == 0) {
+ if (arg->seen)
+ ctx->sec_level = rxrpc_security_encrypt;
+ return true;
+ }
+
+ printf("Unimplemented: extract_kafs_auth %s\n", arg->arg->name);
+ abort();
+}
+
+bool kafs_get_local_cell(struct kafs_context *ctx)
+{
+ size_t size;
+ char buf[256], *cell_name;
+ FILE *f;
+
+ f = fopen("/proc/net/afs/rootcell", "r");
+ if (!f)
+ goto error;
+
+ size = fread(buf, sizeof(char), sizeof(buf) - 1, f);
+ if (ferror(f))
+ goto error_f;
+ fclose(f);
+
+ if (size > 0 && buf[size - 1] == '\n')
+ size--;
+
+ if (size == sizeof(buf) - 1)
+ return kafs_error(ctx, "Local cell name unexpectedly long");
+ if (!size)
+ return kafs_error(ctx, "Local cell name not set");
+
+ cell_name = malloc(size + 1);
+ if (!cell_name)
+ return kafs_nomem(ctx);
+ cell_name[size] = 0;
+ ctx->cell_name = memcpy(cell_name, buf, size);
+ return true;
+
+error_f:
+ fclose(f);
+error:
+ return kafs_syserror(ctx, "/proc/net/afs/rootcell");
+}
+
+/*
+ * Determine the cell we're working in.
+ */
+bool extract_kafs_cell(struct kafs_context *ctx, struct extracted_arg *arg)
+{
+ if (arg->nr_values > 1)
+ return kafs_inval(ctx, "-cell with %d values\n", arg->nr_values);
+
+ if (!arg->seen) {
+ if (!kafs_get_local_cell(ctx))
+ return false;
+ } else {
+ ctx->cell_name = strdup(arg->values[0]);
+ if (!ctx->cell_name)
+ return kafs_nomem(ctx);
+ }
+
+ ctx->cell_db = kafs_lookup_cell(ctx->cell_name, &kafs_lookup_context);
+ if (!ctx->cell_db)
+ return kafs_syserror(ctx, "%s: Unknown cell", ctx->cell_name);
+
+ return true;
+}
+
+/*
+ * Extract the volume server name or address.
+ */
+bool extract_kafs_vlserver(struct kafs_context *ctx, struct extracted_arg *arg,
+ struct kafs_vlserver **_vls)
+{
+ struct kafs_vlserver *vls;
+ struct kafs_server *s;
+
+ if (!arg->seen) {
+ *_vls = NULL;
+ return true;
+ }
+
+ vls = calloc(1, sizeof(struct kafs_vlserver));
+ if (!vls) {
+ perror(NULL);
+ return false;
+ }
+
+ vls->name = strdup(arg->values[0]);
+ if (!vls->name)
+ goto nomem;
+
+ vls->slist = calloc(1, sizeof(struct kafs_server_list));
+ if (!vls->slist)
+ goto nomem;
+ vls->slist->source = kafs_record_from_config;
+ vls->slist->status = kafs_lookup_good;
+ vls->slist->nr_servers = 1;
+ vls->slist->max_servers = 1;
+
+ vls->slist->servers = calloc(1, sizeof(struct kafs_server_list));
+ if (!vls->slist->servers)
+ goto nomem;
+
+ s = &vls->slist->servers[0];
+ s->name = vls->name;
+ s->borrowed_name = true;
+
+ if (kafs_dns_lookup_addresses(vls->slist, &kafs_lookup_context) < 0)
+ goto error;
+
+ *_vls = vls;
+ return true;
+
+nomem:
+ perror(NULL);
+error:
+ free_kafs_vlserver(vls);
+ return false;
+}
+
+void free_kafs_vlserver(struct kafs_vlserver *vls)
+{
+ if (vls) {
+ kafs_free_server_list(vls->slist);
+ free(vls->name);
+ free(vls);
+ }
+}
+
+/*
+ * Extract the volume server name or address.
+ */
+bool extract_kafs_volserver_spec(struct kafs_context *ctx, struct extracted_arg *arg,
+ struct kafs_volserver_spec **_vs)
+{
+ struct kafs_volserver_spec *vs;
+
+ if (!arg->seen) {
+ *_vs = NULL;
+ return true;
+ }
+
+ vs = calloc(1, sizeof(struct kafs_volserver_spec));
+ if (!vs) {
+ perror(NULL);
+ return false;
+ }
+
+ vs->name = strdup(arg->values[0]);
+ if (!vs->name) {
+ perror(NULL);
+ goto error;
+ }
+
+ *_vs = vs;
+ return true;
+
+error:
+ free_kafs_volserver_spec(vs);
+ return false;
+}
+
+void free_kafs_volserver_spec(struct kafs_volserver_spec *vs)
+{
+ if (vs) {
+ free(vs->addrs);
+ free(vs->name);
+ free(vs);
+ }
+}
+
+/*
+ * Extract the volume server specifier name or address.
+ */
+bool extract_kafs_fileserver_spec(struct kafs_context *ctx, struct extracted_arg *arg,
+ struct kafs_fileserver_spec **_fs)
+{
+ struct kafs_fileserver_spec *fs;
+
+ if (!arg->seen) {
+ *_fs = NULL;
+ return true;
+ }
+
+ fs = calloc(1, sizeof(struct kafs_fileserver_spec));
+ if (!fs) {
+ perror(NULL);
+ return false;
+ }
+
+ fs->name = strdup(arg->values[0]);
+ if (!fs->name) {
+ perror(NULL);
+ goto error;
+ }
+
+ *_fs = fs;
+ return true;
+
+error:
+ free_kafs_fileserver_spec(fs);
+ return false;
+}
+
+void free_kafs_fileserver_spec(struct kafs_fileserver_spec *fs)
+{
+ if (fs) {
+ free(fs->addrs);
+ free(fs->name);
+ free(fs);
+ }
+}
+
+/*
+ * Extract the name of a partition.
+ */
+bool extract_kafs_partition(struct kafs_context *ctx, struct extracted_arg *arg,
+ struct kafs_partition **_part)
+{
+ struct kafs_partition *part;
+ unsigned long n;
+ char *name, *end, *a;
+
+ if (!arg->seen) {
+ *_part = NULL;
+ return true;
+ }
+
+ name = arg->values[0];
+
+ if (!*name) {
+ fprintf(stderr, "Empty partition name string\n");
+ return false;
+ }
+
+ n = strtoul(name, &end, 0);
+ if (!*end) {
+ ;
+ } else {
+ if (strncmp(name, "/vicep", 6) == 0)
+ a = name + 6;
+ else if (strncmp(name, "vicep", 5) == 0)
+ a = name + 5;
+ else
+ a = name;
+
+ if (!a[0]) {
+ goto unparseable;
+ } else if (!a[1] && isalpha(a[0])) {
+ n = tolower(a[0]) - 'a';
+ } else if (!a[2] && isalpha(a[0]) && isalpha(a[1])) {
+ n = (tolower(a[0]) - 'a') * 26;
+ n += tolower(a[1]) - 'a';
+ n += 26;
+ } else {
+ goto unparseable;
+ }
+ }
+
+ if (n < 0 || n > 255) {
+ fprintf(stderr, "Partition ID '%s' out of range\n", name);
+ return false;
+ }
+
+ part = calloc(1, sizeof(struct kafs_partition));
+ if (!part) {
+ perror(NULL);
+ return false;
+ }
+
+ part->id = n;
+ *_part = part;
+ return true;
+
+unparseable:
+ fprintf(stderr, "Unparseable partition ID '%s'\n", name);
+ return false;
+}
+
+/*
+ * Extract a volume name.
+ */
+bool extract_kafs_volume_spec(struct kafs_context *ctx, struct extracted_arg *arg,
+ struct kafs_volume_spec **_vs)
+{
+ struct kafs_volume_spec *vs;
+ char *end;
+
+ if (!arg->seen) {
+ *_vs = NULL;
+ return true;
+ }
+
+ if (strlen(arg->values[0]) >= VLDB_MAXNAMELEN) {
+ fprintf(stderr, "Volume name too long\n");
+ return false;
+ }
+
+ vs = calloc(1, sizeof(struct kafs_volume_spec));
+ if (!vs) {
+ perror(NULL);
+ return false;
+ }
+
+ if (isdigit(arg->values[0][0])) {
+ /* If it appears to be a number, attempt to parse it and turn
+ * it into decimal.
+ */
+ vs->number = strtoull(arg->values[0], &end, 0);
+ if (!*end) {
+ fprintf(stderr, "Volume ID is not a number\n");
+ goto error;
+ }
+
+ if (asprintf(&vs->name, "%llu", vs->number) == -1){
+ kafs_nomem(ctx);
+ goto error;
+ }
+ vs->is_numeric = true;
+ } else {
+ vs->name = strdup(arg->values[0]);
+ if (!vs->name) {
+ perror(NULL);
+ goto error;
+ }
+ }
+
+ *_vs = vs;
+ return true;
+
+error:
+ free_kafs_volume_spec(vs);
+ return false;
+}
+
+void free_kafs_volume_spec(struct kafs_volume_spec *vs)
+{
+ if (vs) {
+ free(vs->name);
+ free(vs);
+ }
+}
+
+/*
+ * Extract a user name.
+ */
+bool extract_kafs_user(struct kafs_context *ctx, struct extracted_arg *arg,
+ struct kafs_user **_user)
+{
+ struct kafs_user *user;
+
+ if (!arg->seen) {
+ *_user = NULL;
+ return true;
+ }
+
+ user = calloc(1, sizeof(struct kafs_user));
+ if (!user) {
+ perror(NULL);
+ return false;
+ }
+
+ user->name = strdup(arg->values[0]);
+ if (!user->name) {
+ perror(NULL);
+ goto error;
+ }
+
+ *_user = user;
+ return true;
+
+error:
+ free_kafs_user(user);
+ return false;
+}
+
+void free_kafs_user(struct kafs_user *user)
+{
+ if (user) {
+ free(user->name);
+ free(user);
+ }
+}
+
+/*
+ * Extract a list of user names.
+ */
+bool extract_list_kafs_user(struct kafs_context *ctx, struct extracted_arg *arg,
+ struct kafs_user ***_users)
+{
+ struct kafs_user **users;
+ int i;
+
+ if (!arg->seen) {
+ *_users = NULL;
+ return true;
+ }
+
+ users = calloc(arg->nr_values + 1, sizeof(struct kafs_user *));
+ if (!users) {
+ perror(NULL);
+ return false;
+ }
+
+ for (i = 0; i < arg->nr_values; i++) {
+ users[i] = calloc(1, sizeof(struct kafs_user));
+ if (!users[i]) {
+ perror(NULL);
+ goto error;
+ }
+
+ users[i]->name = strdup(arg->values[0]);
+ if (!users[i]->name) {
+ perror(NULL);
+ goto error;
+ }
+ }
+
+ *_users = users;
+ return true;
+
+error:
+ free_list_kafs_user(users);
+ return false;
+}
+
+void free_list_kafs_user(struct kafs_user **users)
+{
+ struct kafs_user **p;
+
+ if (users) {
+ for (p = users; *p; p++)
+ free_kafs_user(*p);
+ free(users);
+ }
+}
+
+/*
+ * Extract a group name.
+ */
+bool extract_kafs_group(struct kafs_context *ctx, struct extracted_arg *arg,
+ struct kafs_group **_group)
+{
+ struct kafs_group *group;
+
+ if (!arg->seen) {
+ *_group = NULL;
+ return true;
+ }
+
+ group = calloc(1, sizeof(struct kafs_group));
+ if (!group) {
+ perror(NULL);
+ return false;
+ }
+
+ group->name = strdup(arg->values[0]);
+ if (!group->name) {
+ perror(NULL);
+ goto error;
+ }
+
+ *_group = group;
+ return true;
+
+error:
+ free_kafs_group(group);
+ return false;
+}
+
+void free_kafs_group(struct kafs_group *group)
+{
+ if (group) {
+ free(group->name);
+ free(group);
+ }
+}
+
+/*
+ * Extract a list of group names.
+ */
+bool extract_list_kafs_group(struct kafs_context *ctx, struct extracted_arg *arg,
+ struct kafs_group ***_groups)
+{
+ struct kafs_group **groups;
+ int i;
+
+ if (!arg->seen) {
+ *_groups = NULL;
+ return true;
+ }
+
+ groups = calloc(arg->nr_values + 1, sizeof(struct kafs_group *));
+ if (!groups) {
+ perror(NULL);
+ return false;
+ }
+
+ for (i = 0; i < arg->nr_values; i++) {
+ groups[i] = calloc(1, sizeof(struct kafs_group));
+ if (!groups[i]) {
+ perror(NULL);
+ goto error;
+ }
+
+ groups[i]->name = strdup(arg->values[0]);
+ if (!groups[i]->name) {
+ perror(NULL);
+ goto error;
+ }
+ }
+
+ *_groups = groups;
+ return true;
+
+error:
+ free_list_kafs_group(groups);
+ return false;
+}
+
+void free_list_kafs_group(struct kafs_group **groups)
+{
+ struct kafs_group **p;
+
+ if (groups) {
+ for (p = groups; *p; p++)
+ free_kafs_group(*p);
+ free(groups);
+ }
+}
+
+bool extract_string(struct kafs_context *ctx, struct extracted_arg *arg,
+ char **_s)
+{
+ if (!arg->seen) {
+ *_s = NULL;
+ return true;
+ }
+
+ *_s = arg->values[0];
+ return true;
+}
+
+bool extract_list_string(struct kafs_context *ctx, struct extracted_arg *arg,
+ char ***_sl)
+{
+ *_sl = arg->values;
+ return true;
+}
+
+bool extract_bool(struct kafs_context *ctx, struct extracted_arg *arg,
+ bool *_b)
+{
+ if (arg->seen)
+ *_b = true;
+ return true;
+}
+
+bool extract_uuid_t(struct kafs_context *ctx, struct extracted_arg *arg,
+ uuid_t **_u)
+{
+ uuid_t *u;
+
+ if (arg->seen) {
+ u = malloc(sizeof(*u));
+ if (!u)
+ return kafs_nomem(ctx);
+ if (uuid_parse(arg->values[0], *u) < 0) {
+ free(u);
+ return kafs_error(ctx, "Failed to parse UUID");
+ }
+ *_u = u;
+ }
+ return true;
+}
--- /dev/null
+/* Argument parser definitions.
+ *
+ * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#ifndef ARG_PARSER_H
+#define ARG_PARSER_H
+
+#include <stdbool.h>
+#include "kafs.h"
+
+struct extracted_arg {
+ const struct kafs_argument *arg;
+ char **values;
+ unsigned int nr_values;
+ bool seen;
+ bool last;
+};
+
+extern bool extract_kafs_context(struct kafs_context *, struct extracted_arg *);
+extern bool extract_kafs_cell(struct kafs_context *, struct extracted_arg *);
+extern bool extract_string(struct kafs_context *, struct extracted_arg *, char **);
+extern bool extract_bool(struct kafs_context *, struct extracted_arg *, bool *);
+extern bool extract_uuid_t(struct kafs_context *, struct extracted_arg *, uuid_t **);
+extern bool extract_kafs_volserver_spec(struct kafs_context *, struct extracted_arg *,
+ struct kafs_volserver_spec **);
+extern bool extract_kafs_vlserver(struct kafs_context *, struct extracted_arg *,
+ struct kafs_vlserver **);
+extern bool extract_kafs_fileserver_spec(struct kafs_context *, struct extracted_arg *,
+ struct kafs_fileserver_spec **);
+extern bool extract_kafs_partition(struct kafs_context *, struct extracted_arg *,
+ struct kafs_partition **);
+extern bool extract_kafs_volume_spec(struct kafs_context *, struct extracted_arg *,
+ struct kafs_volume_spec **);
+extern bool extract_kafs_user(struct kafs_context *, struct extracted_arg *,
+ struct kafs_user **);
+extern bool extract_kafs_group(struct kafs_context *, struct extracted_arg *,
+ struct kafs_group **);
+extern bool extract_list_kafs_user(struct kafs_context *, struct extracted_arg *,
+ struct kafs_user ***);
+extern bool extract_list_kafs_group(struct kafs_context *, struct extracted_arg *,
+ struct kafs_group ***);
+
+extern bool extract_list_string(struct kafs_context *, struct extracted_arg *, char ***);
+
+struct kafs_command {
+ const struct kafs_command *alias; /* Command this is an alias of */
+ const char *suite; /* Name of the command suite of which part */
+ const char *name; /* Name of the command */
+ const char *abbrev; /* Abbreviation of the command name (or NULL) */
+ const char *help; /* Single-line help text */
+ const char *desc; /* Multi-line description */
+ const struct kafs_argument *args; /* Argument list terminated with {}. */
+ const struct kafs_nocombine *no_combine; /* Arguments that cannot be combined */
+ int nr_args; /* Number of arguments */
+ unsigned char mink; /* Minimum size of abbreviation of name */
+ bool (*handler)(struct kafs_context *ctx, char **argv);
+};
+
+struct kafs_suite {
+ const char *name;
+ const struct kafs_command **commands;
+};
+
+struct kafs_argument {
+ const char *name; /* Argument name */
+ unsigned char mink; /* Minimum size of abbreviation of name */
+ const char *val; /* Value description */
+ unsigned int size_limit; /* Value size limit */
+ bool req; /* T if argument mandatory */
+ bool flag; /* T if takes no argument */
+ bool single; /* T if takes a single argument */
+ bool multi; /* T if takes multiple arguments */
+ bool newn; /* T if new name (ie. no tab expansion) */
+ bool auth; /* T if auth data */
+ bool (*syntax)(struct extracted_arg *result);
+};
+
+struct kafs_nocombine {
+ unsigned int a;
+ unsigned int b;
+};
+
+/*
+ * arg_parse.c
+ */
+extern bool kafs_debug_completion;
+
+extern void free_extracted_args(struct extracted_arg *arg);
+extern struct extracted_arg *extract_arguments(struct kafs_context *ctx, char **args,
+ const struct kafs_command *cmd);
+
+/*
+ * kafs.c
+ */
+extern const struct kafs_suite *kafs_suites[];
+
+extern bool kafs_command_usage(const struct kafs_command *cmd);
+extern bool kafs_help(const struct kafs_suite *suite, char *topic);
+extern const struct kafs_command *look_up_command(const struct kafs_suite *suite, char *command);
+
+/*
+ * completion.c
+ */
+extern void comp_log(const char *fmt, ...);
+extern void bash_complete_command_suite(char *s);
+extern void bash_complete(int argc, char **argv, int completion_argc,
+ const struct kafs_suite *suite);
+
+#endif /* ARG_PARSER_H */
--- /dev/null
+# -*- sh -*-
+# bash completion script for kafs-utils
+#
+# To use these routines:
+#
+# 1. Copy this file to somewhere (e.g. ~/.kafs-completion.bash).
+#
+# 2. Add the following line to your .bashrc:
+# . ~/.kafs-completion.bash
+
+_kafs () {
+ COMPREPLY=($(/data/afs/kafs-utils/kafs/kafs --complete $COMP_CWORD "${COMP_WORDS[@]}"))
+
+ case "${COMPREPLY[0]}" in
+ @HOSTNAME@)
+ COMPREPLY=($(compgen -A hostname -- "${COMP_WORDS[$COMP_CWORD]}"))
+ ;;
+ @VOLNAME@|@PARTID@|@FILE@|@OTHER@)
+ COMPREPLY=($(compgen -A file -- "${COMP_WORDS[$COMP_CWORD]}"))
+ ;;
+ esac
+}
+
+complete -o bashdefault -o default -F _kafs kafs bos pts vos
--- /dev/null
+/* The bos help command
+ *
+ * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "bos.h"
+
+/***
+ * COMMAND: bos help - Get help on commands
+ * ARG: "[-topic <help string>+]"
+ * ARG: "[-admin]"
+ */
+bool COMMAND_bos_help(
+ struct kafs_context *ctx,
+ char **a_topic,
+ bool a_admin)
+{
+ if (!a_topic)
+ return kafs_help(&gen_bos_suite, NULL);
+ for (; *a_topic; a_topic++)
+ if (!kafs_help(&gen_bos_suite, *a_topic))
+ return false;
+ return true;
+}
+
+/***
+ * COMMAND: bos apropos - Search for a command by its help text
+ * ARG: "-topic <help string>"
+ */
+bool COMMAND_bos_apropos(
+ struct kafs_context *ctx,
+ char *a_topic)
+{
+ const struct kafs_command *cmd;
+ int i;
+
+ for (i = 0; gen_bos_suite.commands[i]; i++) {
+ cmd = gen_bos_suite.commands[i];
+
+ if (strstr(cmd->name, a_topic) ||
+ strstr(cmd->help, a_topic))
+ printf("%s: %s\n", cmd->name, cmd->help);
+ }
+
+ return true;
+}
--- /dev/null
+/* Display routine definitions
+ *
+ * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#ifndef DISPLAY_H
+#define DISPLAY_H
+
+#include "kafs.h"
+
+/*
+ * display_error.c
+ */
+extern bool kafs_display_error(struct kafs_context *);
+
+#endif /* DISPLAY_H */
--- /dev/null
+/* Error handling and displaying
+ *
+ * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include "kafs.h"
+#include "afs_xg.h"
+
+bool kafs_inval(struct kafs_context *ctx, const char *fmt, ...)
+{
+ va_list va;
+
+ va_start(va, fmt);
+ vsnprintf(ctx->err_buf, sizeof(ctx->err_buf), fmt, va);
+ va_end(va);
+
+ ctx->result.source = rxrpc_error_from_parameters;
+ ctx->result.error = 0;
+ ctx->result.abort_code = 0;
+ return false;
+}
+
+bool kafs_error(struct kafs_context *ctx, const char *fmt, ...)
+{
+ va_list va;
+
+ va_start(va, fmt);
+ vsnprintf(ctx->err_buf, sizeof(ctx->err_buf), fmt, va);
+ va_end(va);
+
+ ctx->result.source = rxrpc_error_from_parameters;
+ ctx->result.error = 0;
+ ctx->result.abort_code = 0;
+ return false;
+}
+
+/*
+ * Default error display.
+ */
+bool kafs_display_error(struct kafs_context *ctx)
+{
+ char buf[128], *p;
+
+ switch (ctx->result.source) {
+ case rxrpc_error_none:
+ fprintf(stderr, "No error!\n");
+ break;
+ case rxrpc_error_remote_abort:
+ fprintf(stderr, "Remote abort: %d\n", ctx->result.abort_code);
+ break;
+ case rxrpc_error_peer_busy:
+ fprintf(stderr, "Peer says busy\n");
+ break;
+ case rxrpc_error_from_system:
+ case rxrpc_error_from_network:
+ buf[0] = 0;
+#if (_POSIX_C_SOURCE >= 200112L) && !_GNU_SOURCE
+ strerror_r(ctx->result.error, buf, sizeof(buf));
+ p = buf;
+#else
+ p = strerror_r(ctx->result.error, buf, sizeof(buf));
+#endif
+ fprintf(stderr, "%s error: %s\n",
+ (ctx->result.source == rxrpc_error_from_system) ? "System" : "Network",
+ p);
+ break;
+ case rxrpc_error_from_parameters:
+ fprintf(stderr, "%s\n", ctx->err_buf);
+ break;
+ default:
+ fprintf(stderr, "Undefined error source\n");
+ break;
+ }
+
+ return false;
+}
--- /dev/null
+/* The fs help command
+ *
+ * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "fs.h"
+
+/***
+ * COMMAND: fs help - Get help on commands
+ * ARG: "[-topic <help string>+]"
+ * ARG: "[-admin]"
+ */
+bool COMMAND_fs_help(
+ struct kafs_context *ctx,
+ char **a_topic,
+ bool a_admin)
+{
+ if (!a_topic)
+ return kafs_help(&gen_fs_suite, NULL);
+ for (; *a_topic; a_topic++)
+ if (!kafs_help(&gen_fs_suite, *a_topic))
+ return false;
+ return true;
+}
+
+/***
+ * COMMAND: fs apropos - Search for a command by its help text
+ * ARG: "-topic <help string>"
+ */
+bool COMMAND_fs_apropos(
+ struct kafs_context *ctx,
+ char *a_topic)
+{
+ const struct kafs_command *cmd;
+ int i;
+
+ for (i = 0; gen_fs_suite.commands[i]; i++) {
+ cmd = gen_fs_suite.commands[i];
+
+ if (strstr(cmd->name, a_topic) ||
+ strstr(cmd->help, a_topic))
+ printf("%s: %s\n", cmd->name, cmd->help);
+ }
+
+ return true;
+}
--- /dev/null
+#!/usr/bin/python3
+# -*- coding: utf-8 -*-
+#
+# Tool for processing vos, pts, etc. command definitions into parser stubs,
+# argument tables and tab expansion drivers.
+#
+
+__copyright__ = """
+Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
+Written by David Howells (dhowells@redhat.com)
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public Licence version 2 as
+published by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public Licence for more details.
+
+You should have received a copy of the GNU General Public Licence
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+"""
+
+import sys
+import re
+
+suite_name = None
+output_file = None
+header_file = None
+
+def output(*va):
+ for i in va:
+ output_file.write(str(i))
+
+def header(*va):
+ for i in va:
+ header_file.write(str(i))
+
+def verbose(*va):
+ print(*va, file=sys.stderr)
+
+def error(*va):
+ print(*va, file=sys.stderr)
+
+#
+# Command argument definition
+# Looks like:
+# ARG: "-name" -- Required flag with no value
+# ARG: "-name <value>" -- Required flag with value
+# ARG: "-name <value>+" -- Required flag with multiple values
+# ARG: "[-name <value>]" -- Optional flag with value
+# ARG: "-name <value>" - New -- Required flag with new value (can't tab-expand)
+# ARG: "-name <value>" - Auth -- Required flag with auth contribution
+#
+class kafs_argument:
+ def __init__(self, source, line):
+ self.source = source
+ self.name = None
+ self.multi = False
+ self.optional = False
+ self.value = None
+ self.auth_info = False
+ self.name_new = False
+ self.index = None
+
+ arg_re = re.compile(r' (["][^"]*["])( [-] (.*))?')
+ m = arg_re.fullmatch(line)
+ if not m:
+ raise RuntimeError("ARG didn't match")
+ flag = m.group(1).strip('"')
+ quals = m.group(3)
+
+ if flag[0] == "[":
+ if flag[-1] != "]":
+ raise RuntimeError("Missing ']' in ARG")
+ flag = flag[1:-1]
+ if flag[-1] == "]":
+ raise RuntimeError("Extra ']' in ARG")
+ self.optional = True
+ if flag[-1] == "+":
+ flag = flag[0:-1]
+ if flag[-1] == "+":
+ raise RuntimeError("Multiple '+' in ARG")
+ self.multi = True
+ if flag[0] != "-":
+ raise RuntimeError("Missing '-' at beginning of flag name")
+ flag = flag[1:]
+
+ (flag, sep, value) = flag.partition(" ")
+ if not flag:
+ raise RuntimeError("Missing flag name")
+ self.name = flag
+ if value:
+ if value[0] != "<":
+ raise RuntimeError("Missing '<' in ARG value")
+ if value[-1] != ">":
+ raise RuntimeError("Missing '>' in ARG value")
+ value = value[1:-1]
+ if not value:
+ raise RuntimeError("Missing description in ARG value")
+ self.value = value
+ else:
+ if self.multi:
+ raise RuntimeError("Multiple values, but no value type")
+
+ if quals:
+ for q in quals.split():
+ if q == "Auth":
+ self.auth_info = True
+ elif q == "New":
+ self.name_new = True
+ else:
+ raise RuntimeError("Unknown qualifier '" + q + "'")
+
+#
+# Function argument definition
+#
+class kafs_func_argument:
+ def __init__(self, arg):
+ self.syntax = None # Extraction function name
+ self.c_type = None # Argument C type, including pointer markers
+ self.c_name = None # Argument name
+ self.c_var_type = None # Temp variable type
+
+ arg_re = re.compile(r'([_a-zA-Z0-9 ]+) ([*]*)([_a-zA-Z0-9]*)')
+ m = arg_re.fullmatch(arg)
+ if not m:
+ raise RuntimeError("Func arg could not be parsed")
+ if m.group(2):
+ self.c_type = m.group(1) + " " + m.group(2)
+ else:
+ self.c_type = m.group(1)
+
+ name = m.group(3)
+ if name != "ctx":
+ if name.startswith("a_"):
+ name = name[2:]
+ else:
+ raise RuntimeError("Function argument should begin with 'a_'")
+
+ self.c_name = name
+ self.c_var_type = self.c_type
+
+ if self.c_name == "cell":
+ raise RuntimeError("'cell' function argument should be in ctx")
+
+ if (m.group(1) == "char" and m.group(2).startswith("*") or
+ m.group(1) == "const char" and m.group(2).startswith("*")):
+ self.syntax = "string"
+ else:
+ (a, space, b) = m.group(1).partition(" ")
+ if space:
+ self.syntax = b
+ else:
+ self.syntax = a
+
+ if self.c_type.startswith("const "):
+ self.c_var_type = self.c_type[6:]
+
+ #verbose("ARG", self.syntax, self.c_name)
+
+#
+# Command definition
+#
+class kafs_command:
+ def __init__(self, line):
+ self.suite = None
+ self.name = None
+ self.help_str = None
+ self.desc = list()
+ self.params = list()
+ self.param_by_name = dict()
+ self.func_name = None
+ self.func_args = list()
+ self.func_args_by_name = dict()
+ self.no_combine = list()
+ self.seen_ctx_arg = False
+
+ (line, sep, help_str) = line.partition("-")
+ line = line.strip()
+ self.help_str = help_str.lstrip()
+ if not self.help_str:
+ raise RuntimeError("Missing help text on COMMAND")
+
+ (self.suite, sep, self.name) = line.partition(" ")
+ if not self.suite:
+ raise RuntimeError("Missing suite name")
+ if not self.name:
+ raise RuntimeError("Missing command name")
+
+ def add_arg(self, arg):
+ arg.index = len(self.params)
+ self.params.append(arg)
+ self.param_by_name[arg.name] = arg
+
+ def add_nocombine(self, arg):
+ (a, sep, b) = arg.strip().partition(",")
+ a = a.rstrip()
+ b = b.lstrip()
+ if not a or not b:
+ raise RuntimeError("Improperly formatted NOCOMBINE statement")
+ self.no_combine.append((a, b))
+
+ def add_to_description(self, line):
+ if line[0] == "*":
+ line = line[1:]
+ line = line.strip()
+ if line or self.desc:
+ self.desc.append(line)
+
+ def set_func(self, func):
+ self.func_name = func.strip()
+
+ def add_func_arg(self, arg):
+ if len(self.func_args) == 0 and not self.seen_ctx_arg:
+ if arg.c_name != "ctx":
+ raise RuntimeError("First command func argument must be 'ctx'")
+ self.seen_ctx_arg = True
+ return
+ self.func_args.append(arg)
+ self.func_args_by_name[arg.c_name] = arg
+
+ def finalise(self):
+ if "localauth" in self.param_by_name:
+ if "cell" in self.param_by_name:
+ self.no_combine.append(("cell", "localauth"))
+ if "noauth" in self.param_by_name:
+ self.no_combine.append(("noauth", "localauth"))
+
+#
+# Alias for a command
+#
+class kafs_command_alias:
+ def __init__(self, line):
+ self.suite = None
+ self.name = None
+ self.alias = None
+
+ (line, sep, alias) = line.partition("-")
+ self.alias = alias.lstrip()
+ if not self.alias:
+ raise RuntimeError("Missing alias-to on ALIAS")
+
+ line = line.strip()
+ (self.suite, sep, self.name) = line.partition(" ")
+ if not self.suite:
+ raise RuntimeError("Missing suite name")
+ if not self.name:
+ raise RuntimeError("Missing command name")
+
+ def finalise(self):
+ pass
+
+#
+# Command suite definition
+#
+class kafs_suite:
+ def __init__(self, name):
+ self.name = name
+ self.commands = dict() # Commands in the suite
+ self.command_names = None
+ self.aliases = dict() # Command aliases in the suite
+ self.alias_names = None
+
+ def add_command(self, cmd):
+ self.commands[cmd.name] = cmd
+
+ def add_alias(self, cmd):
+ self.aliases[cmd.name] = cmd
+
+ def finalise(self):
+ self.command_names = list(self.commands.keys())
+ self.command_names.sort()
+ for cmd in self.commands.values():
+ cmd.mink = find_longest_abbrev(cmd.name, self.command_names)
+ cmd.finalise()
+
+ self.alias_names = list(self.aliases.keys())
+ self.alias_names.sort()
+ for cmd in self.aliases.values():
+ cmd.mink = find_longest_abbrev(cmd.name, self.alias_names)
+ cmd.finalise()
+
+###############################################################################
+#
+# Parse a file, letting the caller handle error reporting
+#
+###############################################################################
+class line_parser:
+ def __init__(self):
+ self.commands = list()
+ self.source = None
+ self.lineno = 0
+ self.lines = list()
+ self.phase = 0
+ self.cmd = None
+ self.suites = dict()
+ self.suite_names = None
+
+ def new_file(self, infile):
+ self.source = infile
+ self.lineno = 0
+
+ def parse_line(self, line):
+ self.lineno += 1
+
+ if line == "/***":
+ self.phase = 1
+ return
+
+ if self.phase == 0:
+ return
+
+ line = " ".join(line.split())
+ #verbose("[" + self.phase + "] " + line)
+
+ if self.phase == 1:
+ if line.startswith("* COMMAND:"):
+ cmd = kafs_command(line[10:])
+ self.commands.append(cmd)
+ self.cmd = cmd
+ if cmd.suite not in self.suites:
+ self.suites[cmd.suite] = kafs_suite(cmd.suite)
+ self.suites[cmd.suite].add_command(cmd)
+ self.phase = 2
+ return
+
+ if line.startswith("* ALIAS:"):
+ cmd = kafs_command_alias(line[8:])
+ self.commands.append(cmd)
+ if cmd.suite not in self.suites:
+ self.suites[cmd.suite] = kafs_suite(cmd.suite)
+ self.suites[cmd.suite].add_alias(cmd)
+ self.phase = 0
+ return
+
+ raise RuntimeError("Missing command and help text")
+
+ if self.phase == 2:
+ if line == "/***":
+ raise RuntimeError("Re-opening command")
+ if not line.startswith("*"):
+ raise RuntimeError("Expected comment continuation")
+ if line == "*/":
+ self.phase = 3
+ return
+
+ if line.startswith("* ARG:"):
+ self.cmd.add_arg(kafs_argument(self.source + ":" + str(self.lineno),
+ line[6:]))
+ return
+
+ if line.startswith("* NOCOMBINE:"):
+ self.cmd.add_nocombine(line[12:])
+ return
+
+ self.cmd.add_to_description(line)
+ return
+
+ if self.phase == 3:
+ if line.startswith("bool COMMAND_"):
+ ob = line.find("(")
+ if ob == -1:
+ raise RuntimeError("Missing opening bracket on function")
+ self.cmd.set_func(line[4:ob])
+ arg = line[ob + 1:]
+ if arg == "":
+ self.phase = 4
+ return
+
+ cb = arg.find(")")
+ if cb != -1:
+ arg = arg[:cb - 1]
+
+ if arg == "void" and cb != -1:
+ self.phase = 0
+ return
+ self.cmd.add_func_arg(kafs_func_argument(arg))
+ if cb != -1:
+ self.phase = 0
+ else:
+ self.phase = 4
+ return
+
+ raise RuntimeError("Missing COMMAND_ function def")
+
+ if self.phase == 4:
+ line = line.lstrip()
+ if line == ")":
+ self.cmd = None
+ self.phase = 0
+ return
+ if line == "":
+ raise RuntimeError("Blank line in argument list")
+
+ cb = line.find(")")
+ if cb != -1:
+ line = line[:cb]
+
+ for arg in line.split(","):
+ arg = arg.strip()
+ if arg != "":
+ self.cmd.add_func_arg(kafs_func_argument(arg))
+
+ if cb != -1:
+ self.phase = 0
+ return
+
+###############################################################################
+#
+# Bits
+#
+###############################################################################
+def find_common_prefix_size(a, b):
+ "Find the size of the common prefix between two strings"
+ al = len(a)
+ l = min(al, len(b))
+ uniq = al - 1
+ for i in range(0, l):
+ if a[i] != b[i]:
+ uniq = len(a) - i - 1
+ #verbose("MIN", a, b, i, uniq)
+ break
+ return uniq
+
+def find_longest_abbrev(a, args):
+ "Find the longest abbreviation for an argument"
+ m = len(a) - 1
+ for i in args:
+ p = find_common_prefix_size(a, i)
+ if (p < m):
+ m = p
+ return len(a) - m
+
+###############################################################################
+#
+# Emit an argument parser stub to call the command function.
+#
+###############################################################################
+def emit_argument_table(cmd):
+ output("static const struct kafs_argument gen_", cmd.suite, "_", cmd.name, "_arguments[] = {\n")
+ for p in cmd.params:
+ name_q = "\"" + p.name + "\","
+ output("\t[", p.index, "] = { ", name_q.ljust(14), "");
+ output("", find_longest_abbrev(p.name, cmd.param_by_name.keys()))
+ if p.value:
+ output(", \"", p.value, "\"");
+ if not p.optional:
+ output(", .req = 1")
+ if p.multi:
+ output(", .multi = 1")
+ elif p.value:
+ output(", .single = 1")
+ else:
+ output(", .flag = 1")
+ if p.name_new:
+ output(", .newn = 1")
+ if p.auth_info:
+ output(", .auth = 1")
+ output(" },\n")
+ output("\t{}\n")
+ output("};\n")
+
+def emit_no_combining_table(cmd):
+ output("\n")
+ output("static const struct kafs_nocombine gen_", cmd.suite, "_", cmd.name, "_no_combine[] = {\n")
+ for i in cmd.no_combine:
+ a = cmd.param_by_name[i[0]]
+ b = cmd.param_by_name[i[1]]
+ output("\t{ ", a.index, ", ", b.index, " }, // ", i[0], ", ", i[1], "\n")
+ output("\t{}\n")
+ output("};\n")
+
+def emit_command_def(cmd):
+ emit_argument_table(cmd)
+ if cmd.no_combine:
+ emit_no_combining_table(cmd)
+ output("\n")
+ output("static bool gen_handler_", cmd.suite, "_", cmd.name, "(struct kafs_context *ctx, char **argv);\n")
+ output("static const struct kafs_command gen_cmd_", cmd.suite, "_", cmd.name, " = {\n")
+ output("\t.handler = gen_handler_", cmd.suite, "_", cmd.name, ",\n")
+ output("\t.suite = \"", cmd.suite, "\",\n")
+ output("\t.name = \"", cmd.name, "\",\n")
+ output("\t.mink = ", cmd.mink, ",\n")
+ output("\t.args = gen_", cmd.suite, "_", cmd.name, "_arguments,\n")
+ output("\t.nr_args = ", len(cmd.params), ",\n")
+ if cmd.no_combine:
+ output("\t.no_combine = gen_", cmd.suite, "_", cmd.name, "_no_combine,\n")
+ output("\t.help = \"", cmd.help_str, "\",\n")
+ if cmd.desc:
+ output("\t.desc =")
+ for d in cmd.desc:
+ output("\n")
+ output("\t\t\"", d, "\"")
+ output(",\n")
+
+ output("};\n")
+
+def emit_alias_def(cmd):
+ output("static const struct kafs_command gen_cmd_", cmd.suite, "_", cmd.name, " = {\n")
+ output("\t.suite = \"", cmd.suite, "\",\n")
+ output("\t.name = \"", cmd.name, "\",\n")
+ output("\t.alias = &gen_cmd_", cmd.suite, "_", cmd.alias, ",\n")
+ output("\t.help = \"-- Alias of ", cmd.alias, "\",\n")
+ output("\t.args = gen_", cmd.suite, "_", cmd.alias, "_arguments,\n")
+ output("};\n")
+
+def emit_command_handler(cmd):
+ header("extern bool ", cmd.func_name, "(\n")
+ header("\tstruct kafs_context *ctx")
+ for i in cmd.func_args:
+ header(",\n")
+ header("\t", i.c_type, " a_", i.c_name)
+ header(");\n")
+
+ output("static bool gen_handler_", cmd.suite, "_", cmd.name, "(struct kafs_context *ctx, char **args)\n")
+ output("{\n")
+
+ # Declare variables
+ output("\tstruct extracted_arg *results = NULL;\n")
+ for i in cmd.func_args:
+ if i.c_var_type == "struct kafs_context *":
+ pass
+ elif i.c_var_type.endswith("*"):
+ output("\t", i.c_var_type, "a_", i.c_name, " = NULL;\n")
+ elif i.c_var_type == "bool":
+ output("\t", i.c_var_type, " a_", i.c_name, " = false;\n")
+ else:
+ output("\t", i.c_var_type, " a_", i.c_name, " = 0;\n")
+ output("\tbool ret;\n")
+ output("\n")
+ output("\tresults = extract_arguments(ctx, args, &gen_cmd_", cmd.suite, "_", cmd.name, ");\n")
+ output("\tif (!results)\n")
+ output("\t\treturn false;\n")
+
+ # Write extraction list
+ output("\n")
+ output("\tif (")
+ need_cont = False
+ for p in cmd.params:
+ if p.auth_info:
+ if need_cont:
+ output(" ||\n\t ")
+ output("!extract_kafs_context (ctx, &results[", p.index, "])")
+ need_cont = True
+ if "cell" in cmd.param_by_name:
+ p = cmd.param_by_name["cell"]
+ if need_cont:
+ output(" ||\n\t ")
+ fu = "extract_kafs_cell"
+ output("!", fu.ljust(30), "(ctx, &results[", p.index, "])")
+ need_cont = True
+ for p in cmd.params:
+ if not p.auth_info and p.name != "cell":
+ fa = cmd.func_args_by_name[p.name]
+ if need_cont:
+ output(" ||\n\t ")
+ fu = "extract_"
+ if p.multi:
+ fu += "list_"
+ fu += fa.syntax
+ output("!", fu.ljust(30), "(ctx, &results[", p.index, "], &a_", p.name, ")")
+ need_cont = True
+ output(")\n")
+ output("\t\tgoto error;\n")
+
+ # Call processor function
+ output("\n")
+ output("\tret = ", cmd.func_name, "(\n")
+ output("\t\tctx")
+ for i in cmd.func_args:
+ output(",\n")
+ if i.c_var_type == "struct kafs_context *":
+ output("\t\t&a_", i.c_name)
+ else:
+ output("\t\ta_", i.c_name)
+ output(");\n")
+
+ # Do cleanup
+ output("\n")
+ output("error:\n")
+ for i in cmd.func_args:
+ if i.c_var_type == "struct kafs_context *":
+ pass
+ elif i.syntax == "string":
+ pass # output("\tfree(a_", i.c_name, ");\n")
+ elif i.syntax == "bool":
+ pass
+ elif i.c_var_type.endswith("**"):
+ output("\tfree_list_", i.syntax, "(a_", i.c_name, ");\n")
+ else:
+ output("\tfree_", i.syntax, "(a_", i.c_name, ");\n")
+ output("\tfree_kafs_context(ctx);\n")
+ output("\tfree_extracted_args(results);\n")
+ output("\treturn ret;\n")
+ output("}\n")
+
+def emit_command_table(suite):
+ names = suite.command_names + suite.alias_names
+ output("\n")
+ output("static const struct kafs_command *gen_", suite.name, "_commands[] = {\n")
+ for command in names:
+ try:
+ cmd = suite.commands[command]
+ output("\t&gen_cmd_", cmd.suite, "_", cmd.name, ",\n")
+ except KeyError:
+ cmd = suite.aliases[command]
+ output("\t&gen_cmd_", cmd.suite, "_", cmd.name, ",\n")
+ output("\tNULL\n")
+ output("};\n")
+
+def emit_suite_def(suite):
+ header("\n")
+ header("extern const struct kafs_suite gen_", suite.name, "_suite;\n")
+
+ output("\n")
+ output("const struct kafs_suite gen_", suite.name, "_suite = {\n")
+ output("\t.name = \"", suite.name, "\",\n")
+ output("\t.commands = gen_", suite.name, "_commands,\n")
+ output("};\n")
+
+
+
+###############################################################################
+#
+# Do the work
+#
+###############################################################################
+def run(sname, files):
+ suite_name = sname
+
+ parser = line_parser()
+ for infile in files:
+ parser.new_file(infile)
+ verbose("Parsing", infile)
+ f = open(infile, encoding="utf-8")
+ for line in f.readlines():
+ try:
+ parser.parse_line(line.rstrip())
+ except RuntimeError as e:
+ error(infile + ":" + str(parser.lineno) + ":", e)
+ f.close()
+
+ suites = parser.suites
+ if not suites:
+ error("No suites defined")
+ exit(1)
+
+ for suite in suites.values():
+ suite.finalise()
+ suite_names = list(suites.keys())
+ suite_names.sort()
+ parser.suite_names = suite_names
+
+ global output_file
+ output_file = open(suite_name + ".c", "w", encoding="utf-8")
+ if not output_file:
+ error(suite_name, ".c: Unable to open file")
+
+ global header_file
+ header_file = open(suite_name + ".h", "w", encoding="utf-8")
+ if not header_file:
+ error(suite_name, ".h: Unable to open file")
+
+ header("// AUTOGENERATED - DO NOT EDIT\n")
+ header("#include <stdlib.h>\n");
+ header("#include \"arg_parse.h\"\n");
+ header("\n")
+
+ output("// AUTOGENERATED - DO NOT EDIT\n")
+ output("#include \"", suite_name, ".h\"\n");
+
+ # Emit command table
+ for s in suite_names:
+ suite = suites[s]
+
+ output("\n")
+ output("/* ---- SUITE ", suite.name, " ---- */\n")
+
+ for command in suite.command_names:
+ output("\n")
+ cmd = suite.commands[command]
+ emit_command_def(cmd)
+ output("\n")
+ emit_command_handler(cmd)
+
+ for command in suite.alias_names:
+ output("\n")
+ cmd = suite.aliases[command]
+ emit_alias_def(cmd)
+
+ emit_command_table(suite)
+ emit_suite_def(suite)
+
+ # Emit an argument parser stub
+
+
+
+
+if len(sys.argv) < 3:
+ verbose("Usage: {:s} <suite_name> <filename>+".format(sys.argv[0]))
+ sys.exit(1)
+
+run(sys.argv[1], sys.argv[2:])
--- /dev/null
+/* AFS utility suite main entry point.
+ *
+ * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <kafs/cellserv.h>
+#include <rxrpc.h>
+#include "kafs.h"
+#include "arg_parse.h"
+#include "bos.h"
+#include "fs.h"
+#include "pts.h"
+#include "vos.h"
+#include "display.h"
+
+bool kafs_debug_caps;
+bool kafs_debug_trace;
+
+static struct kafs_context context;
+
+const struct kafs_suite *kafs_suites[] = {
+ &gen_bos_suite,
+ &gen_fs_suite,
+ &gen_pts_suite,
+ &gen_vos_suite,
+ NULL
+};
+
+static void error_report(const char *fmt, ...)
+{
+ va_list va;
+
+ va_start(va, fmt);
+ vfprintf(stderr, fmt, va);
+ fputc('\n', stderr);
+ va_end(va);
+}
+
+static void verbose(const char *fmt, ...)
+{
+ va_list va;
+
+ va_start(va, fmt);
+ //printf("[V] ");
+ //vprintf(fmt, va);
+ //putchar('\n');
+ va_end(va);
+}
+
+bool kafs_open_endpoint(struct kafs_context *ctx)
+{
+ struct rxrpc_local_endpoint *endpoint;
+ struct rxrpc_security *security;
+ char buf[1024];
+
+ if (!ctx->endpoint) {
+ endpoint = rxrpc_new_local_endpoint(NULL, 0);
+ if (!endpoint) {
+ perror("Local endpoint");
+ exit(1);
+ }
+
+ ctx->endpoint = endpoint;
+ }
+
+ if (!ctx->security) {
+ snprintf(buf, sizeof(buf), "afs@%s", ctx->cell_name);
+ security = rxrpc_new_security(ctx->endpoint, buf, ctx->sec_level, &ctx->result);
+ if (!security)
+ return false;
+
+ ctx->security = security;
+ }
+
+ return true;
+}
+
+bool kafs_command_usage(const struct kafs_command *cmd)
+{
+ const struct kafs_argument *arg;
+ size_t seg;
+ char buf[256], *p;
+ int llen;
+
+ llen = printf("Usage: %s %s", cmd->suite, cmd->name);
+ for (arg = cmd->args; arg->name; arg++) {
+ p = buf;
+ *p++ = ' ';
+ if (!arg->req)
+ *p++ = '[';
+ *p++ = '-';
+ strcpy(p, arg->name);
+ p += strlen(p);
+ if (!arg->flag)
+ p += sprintf(p, " <%s>", arg->val);
+ if (arg->multi)
+ *p++ = '+';
+ if (!arg->req)
+ *p++ = ']';
+ *p = 0;
+
+ seg = p - buf;
+ if (llen + seg > 79) {
+ putchar('\n');
+ llen = printf(" ");
+ }
+ llen += printf("%s", buf);
+ }
+
+ seg = sprintf(buf, " [-help]");
+ if (llen + seg > 79) {
+ putchar('\n');
+ llen = printf(" ");
+ }
+ llen += printf("%s", buf);
+ printf("\n");
+ return true;
+}
+
+bool kafs_help(const struct kafs_suite *suite, char *topic)
+{
+ const struct kafs_command *cmd;
+ size_t cl;
+ bool ambiguous = false;
+ int i;
+
+ if (!topic) {
+ printf("%s: Commands are:\n", suite->name);
+ for (i = 0; suite->commands[i]; i++) {
+ cmd = suite->commands[i];
+ printf("%-15s %s\n", cmd->name, cmd->help);
+ }
+ return true;
+ }
+
+ cl = strlen(topic);
+ for (i = 0; suite->commands[i]; i++) {
+ cmd = suite->commands[i];
+ if (strcmp(topic, cmd->name) == 0)
+ goto matched_command;
+ if (cl >= strlen(cmd->name))
+ continue;
+ if (cl >= cmd->mink && strncmp(topic, cmd->name, cl) == 0)
+ goto matched_command;
+ if (strncmp(topic, cmd->name, cl) == 0)
+ ambiguous = true;
+ }
+
+ if (ambiguous)
+ fprintf(stderr, "%s: Ambiguous topic '%s'; use 'apropos' to list\n",
+ suite->name, topic);
+ else
+ fprintf(stderr, "%s: Unknown topic '%s'\n",
+ suite->name, topic);
+ fprintf(stderr, "%s: Unrecognised or ambiguous command name\n", suite->name);
+ return false;
+
+matched_command:
+ return kafs_command_usage(cmd);
+}
+
+const struct kafs_command *look_up_command(const struct kafs_suite *suite, char *command)
+{
+ const struct kafs_command *cmd;
+ size_t cl;
+ bool ambiguous = false;
+ int i;
+
+ cl = strlen(command);
+
+ for (i = 0; suite->commands[i]; i++) {
+ cmd = suite->commands[i];
+ if (strcmp(command, cmd->name) == 0)
+ goto match;
+ if (cl >= strlen(cmd->name))
+ continue;
+ if (cl >= cmd->mink && strncmp(command, cmd->name, cl) == 0)
+ goto match;
+ if (strncmp(command, cmd->name, cl) == 0)
+ ambiguous = true;
+ }
+
+ if (ambiguous)
+ fprintf(stderr, "%s: Ambiguous command '%s'; type '%s help' for list\n",
+ suite->name, command, suite->name);
+ else
+ fprintf(stderr, "%s: Unrecognised command '%s'; type '%s help' for list\n",
+ suite->name, command, suite->name);
+ return NULL;
+
+match:
+ return cmd->alias ? cmd->alias : cmd;
+}
+
+bool dispatch_command(const struct kafs_suite *suite, char *command, char **args)
+{
+ const struct kafs_command *cmd;
+
+ _enter("%s", command);
+
+ cmd = look_up_command(suite, command);
+ if (cmd) {
+ _debug("found %s", cmd->name);
+ return cmd->handler(&context, args);
+ }
+ return false;
+}
+
+struct kafs_lookup_context kafs_lookup_context = {
+ .report.error = error_report,
+ .report.verbose = verbose,
+ .report.verbose2 = verbose,
+ .want_ipv4_addrs = true,
+ .want_ipv6_addrs = true,
+};
+
+static void parse_debug(char *args)
+{
+ char *p, *save = NULL;
+
+ while ((p = strtok_r(args, ",", &save))) {
+ args = NULL;
+ if (strcmp(p, "buf") == 0) rxrpc_debug_buffers = true;
+ else if (strcmp(p, "compl") == 0) kafs_debug_completion = true;
+ else if (strcmp(p, "core") == 0) rxrpc_debug_core = true;
+ else if (strcmp(p, "caps") == 0) kafs_debug_caps = true;
+ else if (strcmp(p, "data") == 0) rxrpc_debug_data = true;
+ else if (strcmp(p, "ops") == 0) rxrpc_debug_ops = true;
+ else if (strcmp(p, "trans") == 0) rxrpc_debug_transport = true;
+ else if (strcmp(p, "xdr") == 0) rxrpc_debug_xdr = true;
+ else if (strcmp(p, "trace") == 0) kafs_debug_trace = true;
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ const struct kafs_suite *suite;
+ char *prog, *s, **p;
+ int completion_argc = -1;
+ int i;
+
+ prog = strrchr(argv[0], '/');
+ if (prog)
+ prog++;
+ else
+ prog = argv[0];
+ argc -= 1;
+ argv += 1;
+
+ if (argc > 0 && strcmp(argv[0], "--complete") == 0) {
+ if (argc < 2) {
+ fprintf(stderr, "Missing completion arg number\n");
+ exit(2);
+ }
+
+ completion_argc = atoi(argv[1]);
+ if (completion_argc < 0) {
+ fprintf(stderr, "Completion arg is negative\n");
+ exit(2);
+ }
+
+ prog = strrchr(argv[2], '/');
+ if (prog)
+ prog++;
+ else
+ prog = argv[2];
+
+ completion_argc--;
+ argc -= 3;
+ argv += 3;
+
+ comp_log("\n");
+ comp_log("Compl %d\n", completion_argc);
+ for (p = argv; *p; p++) {
+ if (p - argv == completion_argc)
+ comp_log(" >%s<", *p);
+ else
+ comp_log(" '%s'", *p);
+ }
+ comp_log("\n");
+ }
+
+ if (argc > 0 && strncmp(argv[0], "--debug", 7) == 0) {
+ char *deb = NULL;
+ if (completion_argc < 0) {
+ if (argv[0][7] == '=') {
+ deb = argv[0] + 8;
+ completion_argc--;
+ argc -= 1;
+ argv += 1;
+ } else if (argv[0][7] != '\0') {
+ fprintf(stderr, "Unknown option '%s'\n", argv[0]);
+ exit(2);
+ } else if (argc == 1) {
+ fprintf(stderr, "Missing --debug parameter\n");
+ exit(2);
+ } else {
+ deb = argv[1];
+ completion_argc -= 2;
+ argc -= 2;
+ argv += 2;
+ }
+ } else {
+ if (argc < 3) {
+ fprintf(stderr, "Missing --debug parameter\n");
+ exit(2);
+ }
+
+ if (strcmp(argv[1], "=") != 0) {
+ fprintf(stderr, "Unknown separator '%s'\n", argv[1]);
+ exit(2);
+ }
+
+ deb = argv[2];
+ completion_argc -= 3;
+ argc -= 3;
+ argv += 3;
+ }
+ parse_debug(deb);
+ }
+
+ if (kafs_init_lookup_context(&kafs_lookup_context) < 0)
+ exit(1);
+
+ if (kafs_read_config(NULL, &kafs_lookup_context.report) < 0)
+ exit(kafs_lookup_context.report.bad_config ? 3 : 1);
+
+ if (completion_argc == 0)
+ bash_complete_command_suite(argv[0]);
+
+ if (strcmp(prog, "kafs") != 0 &&
+ strcmp(prog, "afs") != 0) {
+ for (i = 0; kafs_suites[i]; i++) {
+ suite = kafs_suites[i];
+ if (strcmp(prog, suite->name) == 0)
+ goto use_suite;
+ }
+ fprintf(stderr, "%s: Unrecognised command suite\n", prog);
+ exit(2);
+ }
+
+ if (argc == 0) {
+ fprintf(stderr, "kafs supports the following command suites:\n");
+ for (i = 0; kafs_suites[i]; i++) {
+ suite = kafs_suites[i];
+ printf(" %s\n", suite->name);
+ }
+
+ printf("Type 'kafs <suite> help' to get a command list\n");
+ exit(1);
+ }
+
+ s = argv[0];
+ for (i = 0; kafs_suites[i]; i++) {
+ suite = kafs_suites[i];
+ if (strcmp(s, suite->name) == 0)
+ goto use_suite;
+ }
+
+ fprintf(stderr, "Cannot determine command suite to use\n");
+ exit(2);
+
+use_suite:
+ comp_log("suite: %s\n", suite->name);
+ if (completion_argc >= 0)
+ bash_complete(argc, argv, completion_argc, suite);
+ argc--;
+ argv++;
+
+ if (!*argv) {
+ fprintf(stderr, "%s: Type '%s help' or '%s help <topic>' for help\n",
+ suite->name, suite->name, suite->name);
+ exit(2);
+ }
+
+ if (!dispatch_command(suite, *argv, argv + 1))
+ kafs_display_error(&context);
+ exit(0);
+}
--- /dev/null
+/* KAFS program definitions
+ *
+ * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#ifndef KAFS_H
+#define KAFS_H
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <uuid/uuid.h>
+#include <errno.h>
+#include <kafs/cellserv.h>
+#include <rxrpc.h>
+
+#define FS_PORT 7000 /* AFS file server port */
+#define FS_SERVICE 1 /* AFS File Service ID */
+
+typedef unsigned long long kafs_volume_id_t;
+
+enum kafs_service_t {
+ kafs_service_afs,
+ kafs_service_yfs,
+};
+
+struct kafs_context {
+ struct rxrpc_local_endpoint *endpoint;
+ struct rxrpc_security *security;
+ struct rxrpc_result result;
+
+ char *cell_name;
+ struct kafs_cell *cell_db;
+ struct kafs_server *vl_server; /* Preferred VL server */
+ unsigned int vl_addr; /* Preferred address on vl_server */
+ enum kafs_service_t vl_service; /* Service provided on that server */
+ unsigned int vl_caps[1]; /* VL server capabilities */
+ struct rxrpc_call_params vl_params; /* Parameters for accessing pref VL server */
+
+ enum rxrpc_security_auth_level sec_level; /* Security level */
+ char err_buf[1024];
+};
+
+struct kafs_user {
+ char *name;
+};
+
+struct kafs_group {
+ char *name;
+};
+
+struct kafs_partition {
+ unsigned int id;
+};
+
+struct kafs_fileserver_spec {
+ uuid_t uuid;
+ char *name;
+ unsigned int nr_addrs;
+ struct sockaddr_rxrpc *addrs;
+};
+
+struct kafs_vlserver {
+ char *name;
+ struct kafs_server_list *slist;
+};
+
+struct kafs_volume_spec {
+ char *name;
+ unsigned long long number;
+ bool is_numeric:1;
+};
+
+struct kafs_volserver_spec {
+ char *name;
+ unsigned int nr_addrs;
+ struct sockaddr_rxrpc *addrs;
+};
+
+/*
+ * arg_parse.c
+ */
+extern void free_kafs_context(struct kafs_context *);
+extern void free_kafs_group(struct kafs_group *);
+extern void free_kafs_partition(struct kafs_partition *);
+extern void free_kafs_user(struct kafs_user *);
+extern void free_kafs_vlserver(struct kafs_vlserver *);
+extern void free_kafs_volserver_spec(struct kafs_volserver_spec *);
+extern void free_kafs_fileserver_spec(struct kafs_fileserver_spec *);
+extern void free_kafs_volume_spec(struct kafs_volume_spec *);
+extern void free_list_kafs_group(struct kafs_group **);
+extern void free_list_kafs_user(struct kafs_user **);
+
+static inline void free_uuid_t(uuid_t *uuid) { free(uuid); }
+
+/*
+ * display_error.c
+ */
+extern __attribute__((format(printf,2,3)))
+bool kafs_inval(struct kafs_context *ctx, const char *fmt, ...);
+extern __attribute__((format(printf,2,3)))
+bool kafs_error(struct kafs_context *ctx, const char *fmt, ...);
+
+/*
+ * kafs.c
+ */
+extern struct kafs_lookup_context kafs_lookup_context;
+extern bool kafs_open_endpoint(struct kafs_context *);
+
+/*
+ * Inline functions.
+ */
+
+static inline __attribute__((format(printf,2,3)))
+bool kafs_syserror(struct kafs_context *ctx, const char *fmt, ...)
+{
+ ctx->result.source = rxrpc_error_from_system;
+ ctx->result.error = errno;
+ ctx->result.abort_code = 0;
+ return false;
+}
+
+static inline bool kafs_nomem(struct kafs_context *ctx)
+{
+ ctx->result.source = rxrpc_error_from_system;
+ ctx->result.error = ENOMEM;
+ ctx->result.abort_code = 0;
+ return false;
+}
+
+extern bool kafs_debug_trace;
+extern bool kafs_debug_caps;
+
+#define kenter(FMT,...) printf("==> %s("FMT")\n",__func__ ,##__VA_ARGS__)
+#define kleave(FMT,...) printf("<== %s()"FMT"\n",__func__ ,##__VA_ARGS__)
+#define kdebug(FMT,...) printf(" "FMT"\n" ,##__VA_ARGS__)
+
+#if defined(__KDEBUG)
+#define _enter(FMT,...) kenter(FMT,##__VA_ARGS__)
+#define _leave(FMT,...) kleave(FMT,##__VA_ARGS__)
+#define _debug(FMT,...) kdebug(FMT,##__VA_ARGS__)
+
+#elif 1
+#define _enter(FMT,...) \
+do { \
+ if (kafs_debug_trace) \
+ kenter(FMT,##__VA_ARGS__); \
+} while (0)
+
+#define _leave(FMT,...) \
+do { \
+ if (kafs_debug_trace) \
+ kleave(FMT,##__VA_ARGS__); \
+} while (0)
+
+#define _debug(FMT,...) \
+do { \
+ if (kafs_debug_trace) \
+ kdebug(FMT,##__VA_ARGS__); \
+} while (0)
+
+#else
+#define _enter(FMT,...) if (0) printf("==> %s("FMT")",__func__ ,##__VA_ARGS__)
+#define _leave(FMT,...) if (0) printf("<== %s()"FMT"",__func__ ,##__VA_ARGS__)
+#define _debug(FMT,...) if (0) printf(" "FMT ,##__VA_ARGS__)
+#endif
+
+#endif /* KAFS_H */
--- /dev/null
+/* The pts help command
+ *
+ * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "pts.h"
+
+/***
+ * COMMAND: pts help - Get help on commands
+ * ARG: "[-topic <help string>+]"
+ * ARG: "[-admin]"
+ */
+bool COMMAND_pts_help(
+ struct kafs_context *ctx,
+ char **a_topic,
+ bool a_admin)
+{
+ if (!a_topic)
+ return kafs_help(&gen_pts_suite, NULL);
+ for (; *a_topic; a_topic++)
+ if (!kafs_help(&gen_pts_suite, *a_topic))
+ return false;
+ return true;
+}
+
+/***
+ * COMMAND: pts apropos - Search for a command by its help text
+ * ARG: "-topic <help string>"
+ */
+bool COMMAND_pts_apropos(
+ struct kafs_context *ctx,
+ char *a_topic)
+{
+ const struct kafs_command *cmd;
+ int i;
+
+ for (i = 0; gen_pts_suite.commands[i]; i++) {
+ cmd = gen_pts_suite.commands[i];
+
+ if (strstr(cmd->name, a_topic) ||
+ strstr(cmd->help, a_topic))
+ printf("%s: %s\n", cmd->name, cmd->help);
+ }
+
+ return true;
+}
--- /dev/null
+/* The vos help command
+ *
+ * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "kafs.h"
+#include "vos.h"
+
+/***
+ * COMMAND: vos help - Get help on commands
+ * ARG: "[-topic <help string>+]"
+ * ARG: "[-admin]"
+ */
+bool COMMAND_vos_help(
+ struct kafs_context *ctx,
+ char **a_topic,
+ bool a_admin)
+{
+ if (!a_topic)
+ return kafs_help(&gen_vos_suite, NULL);
+ for (; *a_topic; a_topic++)
+ if (!kafs_help(&gen_vos_suite, *a_topic))
+ return false;
+ return true;
+}
+
+/***
+ * COMMAND: vos apropos - Search for a command by its help text
+ * ARG: "-topic <help string>"
+ */
+bool COMMAND_vos_apropos(
+ struct kafs_context *ctx,
+ const char *a_topic)
+{
+ const struct kafs_command *cmd;
+ int i;
+
+ for (i = 0; gen_vos_suite.commands[i]; i++) {
+ cmd = gen_vos_suite.commands[i];
+
+ if (strstr(cmd->name, a_topic) ||
+ strstr(cmd->help, a_topic))
+ printf("%s: %s\n", cmd->name, cmd->help);
+ }
+
+ return true;
+}