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
+ $(CXX) $(CPPFLAGS) -MMD -MF .$@.d $(CFLAGS) -o $@ -c $<
+
+LIBDIR := ../lib
+
+kafs: $(KAFS_OBJS) $(LIBDIR)/libkafs_utils.a
+ $(CXX) -o $@ $(KAFS_OBJS) -L$(LIBDIR) -lkafs_utils -lkafs_client -luuid -lfmt
+
+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 <cstdarg>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <cctype>
+#include <vector>
+#include <string>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <alloca.h>
+extern "C" {
+#include <kafs/cellserv.h>
+}
+#include "arg_parse.H"
+
+namespace kafs {
+void bash_complete_command_name(const Suite *suite, char *word);
+}
+
+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 kafs::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 kafs::bash_complete_command_suite(const char *s)
+{
+ const Suite *suite;
+ bool need_space = false;
+ int i, l = strlen(s);
+
+ comp_log("Word '%s'\n", s);
+
+ for (i = 0; command_suites[i]; i++) {
+ suite = command_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 std::vector<std::string> complete_cell_name(const char *prefix)
+{
+ std::vector<std::string> cells;
+ int c, l = strlen(prefix);
+
+ cells.reserve(kafs_cellserv_db->nr_cells);
+
+ c = 0;
+ for (size_t 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 kafs::bash_complete_command_name(const 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 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 kafs::Argument *canon_flag;
+ const kafs::Argument *word_type;
+ bool additional_arg;
+};
+
+/*
+ * Perform command line completion.
+ */
+void kafs::bash_complete(unsigned int argc, char **argv, unsigned int target,
+ const Suite *suite)
+{
+ const Argument *last_sw, *arg, *word_type, *canon;
+ const Argument *next_required; //, *next_opts;
+ const Command *cmd;
+ struct arg_notes *notes;
+ const char *word, *special, *space = "";
+ char **p, *eliminated_args;
+ bool skip_flag, additional_arg;
+ unsigned int pos, i, j, wl;
+
+ //completion_log = stderr;
+ comp_log("Compl %d\n", target);
+ comp_log("Suite: %s\n", suite->name);
+
+ if (target == 0)
+ bash_complete_command_name(suite, argv[0]);
+ 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[0]);
+ if (!cmd) {
+ comp_log("Unknown command %s\n", argv[0]);
+ exit(1);
+ }
+
+ /* Discard the suite name and the command name */
+ argc -= 1;
+ argv += 1;
+ target -= 1;
+
+ /* 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 = (struct arg_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 = (char *)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 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 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 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) {
+ std::vector<std::string> cells = complete_cell_name(word);
+
+ for (size_t i = 0; i < cells.size(); i++) {
+ const std::string &c = cells[i];
+ printf("%s%s", space, c.c_str());
+ space = " ";
+ }
+ printf("\n");
+ } 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.
+ */
+
+#include <fmt/core.h>
+#include <cstdarg>
+#include <cstdlib>
+#include <cstring>
+#include <ctype.h>
+#include <sys/socket.h>
+#include <netdb.h>
+extern "C" {
+#include <kafs/cellserv.h>
+}
+#include "rxrpc.H"
+#include "afs_xg.H"
+#include "arg_parse.H"
+
+using rxrpc::ref;
+
+namespace kafs {
+static Extracted_arg &find_switch(const Command *cmd, char *sw,
+ std::vector<Extracted_arg> &results);
+static void append_argument(Extracted_arg &result, char *a);
+static void get_local_cell(Context *ctx);
+}
+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 kafs::Extracted_arg &kafs::find_switch(const kafs::Command *cmd, char *sw,
+ std::vector<Extracted_arg> &results)
+{
+ const kafs::Argument *arg;
+ unsigned int j;
+ size_t l = strlen(sw);
+
+ /* Look up the switch in the table of possible arguments and flags. */
+ for (j = 0; j < cmd->nr_args; j++) {
+ arg = &cmd->args[j];
+ Extracted_arg &result = results[j];
+ if (strcmp(sw, arg->name) == 0)
+ return result;
+ }
+
+ /* 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];
+ Extracted_arg &result = results[j];
+ if (l > strlen(arg->name))
+ continue;
+ if (memcmp(sw, arg->name, l) != 0)
+ continue;
+ if (l >= arg->mink)
+ return result;
+
+ throw std::invalid_argument(
+ fmt::format("Ambiguous switch name abbreviation '-{}'", sw));
+ }
+
+ throw std::invalid_argument(fmt::format("Unsupported switch '-{}'", sw));
+}
+
+/*
+ * Append an argument to a list.
+ */
+static void kafs::append_argument(Extracted_arg &result, char *a)
+{
+ const kafs::Argument *arg = result.arg;
+ size_t size = strlen(a);
+
+ /* Check that none of the arguments are too big. */
+ if (arg->size_limit && size > arg->size_limit)
+ throw std::invalid_argument(
+ fmt::format("Switch '-{}' has an overlong argument",
+ result.arg->name));
+
+ result.values.push_back(a);
+}
+
+/*
+ * 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.
+ *
+ */
+void kafs::extract_arguments(Context *ctx, char **args,
+ const Command *cmd,
+ std::vector<Extracted_arg> &results)
+{
+ const Nocombine *combo;
+ const Argument *arg;
+ bool need_switch = false;
+ unsigned int av = 0; /* Available arguments index; */
+ unsigned int i;
+
+ 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;
+
+ if (cmd->args[0].req)
+ throw std::invalid_argument("Missing required parameters");
+ return;
+ }
+
+ /* 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)
+ throw std::invalid_argument(
+ fmt::format("Need switch before argument {:d}", i));
+ if (av >= cmd->nr_args)
+ throw std::invalid_argument("Unexpected positional argument");
+
+ arg = &cmd->args[av];
+ if (arg->flag)
+ throw std::invalid_argument("Unexpected positional argument");
+
+ Extracted_arg &result = results[av];
+ av = av + 1;
+
+ append_argument(result, a);
+ 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;
+ append_argument(result, a);
+ }
+ need_switch = true;
+ }
+ }
+ result.seen = true;
+
+ } else {
+ /* Deal with tagged arguments */
+ char *sw = a + 1;
+
+ args++;
+
+ if (!sw[0])
+ throw std::invalid_argument("Missing switch name");
+
+ if (strcmp(sw, "help") == 0)
+ goto help;
+
+ Extracted_arg &result = find_switch(cmd, sw, results);
+ arg = result.arg;
+
+ /* Reject repeat flags */
+ if (result.seen)
+ throw std::invalid_argument(
+ fmt::format("Duplicate switch '-{}' not permitted", sw));
+ 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_argument(result, a);
+ }
+ }
+ }
+
+ /* Check for missing required arguments */
+ for (i = 0; i < cmd->nr_args; i++) {
+ const Argument *arg = &cmd->args[i];
+
+ if (!arg->req)
+ break;
+
+ if (!results[i].seen)
+ throw std::invalid_argument(
+ fmt::format("Missing '-{}' argument", arg->name));
+ }
+
+ /* 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)
+ throw std::invalid_argument(
+ fmt::format("Can't combine -{} with -{}",
+ results[combo->a].arg->name,
+ results[combo->b].arg->name));
+ }
+ }
+
+ /* Check argument syntax and value counts */
+ for (i = 0; i < cmd->nr_args; i++) {
+ const Argument *arg = &cmd->args[i];
+ Extracted_arg &result = results[i];
+
+ if (!results[i].seen)
+ continue;
+
+ /* Check that we have the number of arguments we're expecting */
+ if (arg->flag && result.values.size() > 0)
+ throw std::invalid_argument(
+ fmt::format("Switch '-{}' expects no arguments", arg->name));
+ if (arg->single && result.values.size() != 1)
+ throw std::invalid_argument(
+ fmt::format("Switch '-{}' expects one argument", arg->name));
+ if (arg->multi && result.values.size() < 1)
+ throw std::invalid_argument(
+ fmt::format("Switch '-{}' expects one or more arguments", arg->name));
+
+ /* Call the syntax checker */
+ if (arg->syntax)
+ arg->syntax(result);
+ }
+
+ return;
+
+help:
+ command_usage(cmd);
+ throw Gave_help();
+}
+
+/*
+ * Destroy a context.
+ */
+kafs::Context::~Context()
+{
+ if (cell_db)
+ kafs_free_cell(cell_db);
+}
+
+void kafs::extract_kafs_context(Context *ctx, 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;
+ }
+
+ if (strcmp(name, "localauth") == 0) {
+ if (arg.seen)
+ ctx->sec_level = rxrpc::security_local_auth;
+ return;
+ }
+
+ if (strcmp(name, "encrypt") == 0) {
+ if (arg.seen)
+ ctx->sec_level = rxrpc::security_encrypt;
+ return;
+ }
+
+ printf("Unimplemented: extract_kafs_auth %s\n", arg.arg->name);
+ abort();
+}
+
+static void kafs::get_local_cell(Context *ctx)
+{
+ size_t size;
+ char buf[256];
+ FILE *f;
+ int err = 0;
+
+ f = fopen("/proc/net/afs/rootcell", "r");
+ if (!f)
+ throw rxrpc::syserror("/proc/net/afs/rootcell");
+
+ size = fread(buf, sizeof(char), sizeof(buf) - 1, f);
+ if (ferror(f))
+ err = errno;
+ fclose(f);
+ if (err)
+ throw rxrpc::syserror(err, "/proc/net/afs/rootcell");
+
+ if (size > 0 && buf[size - 1] == '\n')
+ size--;
+
+ if (size == sizeof(buf) - 1)
+ throw std::invalid_argument("Local cell name unexpectedly long");
+ if (!size)
+ throw std::invalid_argument("Local cell name not set");
+
+ ctx->cell_name = std::string(buf, size);
+}
+
+/*
+ * Determine the cell we're working in.
+ */
+void kafs::extract_kafs_cell(Context *ctx, Extracted_arg &arg)
+{
+ if (!arg.seen)
+ kafs::get_local_cell(ctx);
+ else
+ ctx->cell_name = arg.values[0];
+
+ ctx->cell_db = kafs_lookup_cell(ctx->cell_name.c_str(), &lookup_context);
+ if (!ctx->cell_db)
+ throw rxrpc::syserror(fmt::format("{}: Unknown cell", ctx->cell_name.c_str()));
+}
+
+/*
+ * Extract the volume server name or address.
+ */
+void kafs::extract(Context *ctx, Extracted_arg &arg, Vlserver_spec &vls)
+{
+ struct kafs_server *s;
+
+ if (!arg.seen)
+ return;
+
+ vls.specified = true;
+
+ vls.name = arg.values[0];
+
+ vls.slist = (struct kafs_server_list *)calloc(1, sizeof(struct kafs_server_list));
+ if (!vls.slist)
+ throw rxrpc::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 = (struct kafs_server *)calloc(1, sizeof(struct kafs_server));
+ if (!vls.slist->servers)
+ throw rxrpc::nomem();
+
+ s = &vls.slist->servers[0];
+ s->name = (char *)vls.name.c_str();
+ s->borrowed_name = true;
+
+ if (kafs_dns_lookup_addresses(vls.slist, &lookup_context) < 0)
+ throw rxrpc::network_error("Unable to look up DNS");
+}
+
+/*
+ * Extract the volume server name or address.
+ */
+void kafs::extract(Context *ctx, Extracted_arg &arg, Volserver_spec &vs)
+{
+ if (arg.seen) {
+ vs.specified = true;
+ vs.name = arg.values[0];
+ }
+}
+
+/*
+ * Extract the volume server specifier name or address.
+ */
+void kafs::extract(Context *ctx, Extracted_arg &arg, Fileserver_spec &fs)
+{
+ if (arg.seen) {
+ fs.specified = true;
+ fs.name = arg.values[0];
+ }
+}
+
+/*
+ * Extract the name of a partition.
+ */
+void kafs::extract(Context *ctx, Extracted_arg &arg, Partition_spec &part)
+{
+ unsigned long n;
+ const char *name, *a;
+ char *end;
+
+ if (!arg.seen)
+ return;
+ part.specified = true;
+
+ name = arg.values[0].c_str();
+
+ if (!*name)
+ throw std::invalid_argument("Empty partition name string");
+
+ n = strtoul(name, &end, 0);
+ if (*end) {
+ /* The ID is not strictly numeric in form. */
+ 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])
+ throw std::invalid_argument(
+ fmt::format("Unparseable partition ID '{}'", name));
+ 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 {
+ throw std::invalid_argument(
+ fmt::format("Unparseable partition ID '{}'", name));
+ }
+ }
+
+ if (n < 0 || n > 255)
+ throw std::invalid_argument(
+ fmt::format("Partition ID '{}' out of range", name));
+
+ part.id = n;
+}
+
+/*
+ * Extract a volume name.
+ */
+void kafs::extract(Context *ctx, Extracted_arg &arg, Volume_spec &vs)
+{
+ char *end;
+
+ if (!arg.seen)
+ return;
+ vs.specified = true;
+
+ if (arg.values[0].size() == 0)
+ throw std::invalid_argument("Empty volume name");
+ if (arg.values[0].size() >= afs::VLDB_MAXNAMELEN)
+ throw std::invalid_argument("Volume name too long");
+
+ 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].c_str(), &end, 0);
+ if (*end)
+ throw std::invalid_argument("Volume ID is not a number");
+
+ vs.name = fmt::format("{:d}", vs.number);
+ vs.is_numeric = true;
+ } else {
+ vs.name = arg.values[0];
+ }
+}
+
+/*
+ * Extract a user name.
+ */
+void kafs::extract(Context *ctx, Extracted_arg &arg, User_spec &user)
+{
+ if (arg.seen) {
+ user.specified = true;
+ user.name = arg.values[0];
+ }
+}
+
+/*
+ * Extract a list of user names.
+ */
+void kafs::extract(Context *ctx, Extracted_arg &arg, std::vector<User_spec> &users)
+{
+ if (arg.seen) {
+ users.resize(arg.values.size());
+ for (size_t i = 0; i < arg.values.size(); i++) {
+ users[i].specified = true;
+ users[i].name = arg.values[i];
+ }
+ }
+}
+
+/*
+ * Extract a group name.
+ */
+void kafs::extract(Context *ctx, Extracted_arg &arg, Group_spec &group)
+{
+ if (arg.seen) {
+ group.specified = true;
+ group.name = arg.values[0];
+ }
+}
+
+/*
+ * Extract a list of group names.
+ */
+void kafs::extract(Context *ctx, Extracted_arg &arg, std::vector<Group_spec> &groups)
+{
+ if (arg.seen) {
+ groups.resize(arg.values.size());
+ for (size_t i = 0; i < arg.values.size(); i++) {
+ groups[i].specified = true;
+ groups[i].name = arg.values[i];
+ }
+ }
+}
+
+void kafs::extract(Context *ctx, Extracted_arg &arg, std::string &s)
+{
+ if (arg.seen)
+ s = arg.values[0];
+}
+
+void kafs::extract(Context *ctx, Extracted_arg &arg, std::vector<std::string> &sl)
+{
+ if (arg.seen)
+ sl.swap(arg.values);
+}
+
+void kafs::extract(Context *ctx, Extracted_arg &arg, bool &b)
+{
+ if (arg.seen)
+ b = true;
+}
+
+void kafs::extract(Context *ctx, Extracted_arg &arg, Uuid_spec &u)
+{
+ if (arg.seen) {
+ u.specified = true;
+ if (uuid_parse(arg.values[0].c_str(), u.uuid.uuid) < 0)
+ throw std::invalid_argument("Failed to parse UUID");
+ }
+}
--- /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 "kafs.H"
+
+namespace kafs {
+
+struct Argument;
+struct Command;
+struct Nocombine;
+
+struct Extracted_arg {
+ const Argument *arg;
+ std::vector<std::string> values;
+ bool seen;
+ bool last;
+
+ Extracted_arg() { arg = NULL; seen = false; last = false; }
+};
+
+extern void extract_kafs_context(Context *, Extracted_arg &);
+extern void extract_kafs_cell(Context *, Extracted_arg &);
+extern void extract(Context *, Extracted_arg &, std::string &);
+extern void extract(Context *, Extracted_arg &, bool &);
+extern void extract(Context *, Extracted_arg &, Volserver_spec &);
+extern void extract(Context *, Extracted_arg &, Vlserver_spec &);
+extern void extract(Context *, Extracted_arg &, Fileserver_spec &);
+extern void extract(Context *, Extracted_arg &, Partition_spec &);
+extern void extract(Context *, Extracted_arg &, Volume_spec &);
+extern void extract(Context *, Extracted_arg &, User_spec &);
+extern void extract(Context *, Extracted_arg &, Group_spec &);
+extern void extract(Context *, Extracted_arg &, Uuid_spec &);
+extern void extract(Context *, Extracted_arg &, std::vector<User_spec> &);
+extern void extract(Context *, Extracted_arg &, std::vector<Group_spec> &);
+extern void extract(Context *, Extracted_arg &, std::vector<std::string> &);
+
+struct Command {
+ const 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 Argument *args; /* Argument list terminated with {}. */
+ unsigned int nr_args; /* Number of arguments */
+ unsigned char mink; /* Minimum size of abbreviation of name */
+ void (*handler)(Context *ctx, char **argv);
+ const Nocombine *no_combine; /* Arguments that cannot be combined */
+ const char *help; /* Single-line help text */
+ const char *desc; /* Multi-line description */
+};
+
+struct Suite {
+ const char *name;
+ const Command **commands;
+};
+
+struct 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 */
+ void (*syntax)(Extracted_arg &result);
+};
+
+struct Nocombine {
+ unsigned int a;
+ unsigned int b;
+};
+
+class Gave_help : std::exception {
+};
+
+/*
+ * arg_parse.C
+ */
+extern bool debug_completion;
+
+extern void extract_arguments(Context *ctx, char **args,
+ const Command *command_table,
+ std::vector<Extracted_arg> &results);
+
+/*
+ * kafs.C
+ */
+extern const Suite *command_suites[];
+
+extern void command_usage(const Command *cmd);
+extern void help(const Suite *suite, std::string &topic=empty_string);
+extern const Command *look_up_command(const Suite *suite, char *command);
+
+/*
+ * arg_completion.C
+ */
+extern void comp_log(const char *fmt, ...);
+extern void bash_complete_command_suite(const char *s);
+extern void bash_complete(unsigned int argc, char **argv, unsigned int completion_argc,
+ const Suite *suite);
+
+} /* end namespace kafs */
+
+#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 <iostream>
+#include "bos.H"
+
+/***
+ * COMMAND: bos help - Get help on commands
+ * ARG: "[-topic <help string>+]"
+ * ARG: "[-admin]"
+ */
+void COMMAND_bos_help(
+ kafs::Context *ctx,
+ std::vector<std::string> &a_topic,
+ bool a_admin)
+{
+ if (a_topic.empty())
+ return kafs::help(&gen_bos_suite, kafs::empty_string);
+ for (std::vector<std::string>::iterator i = a_topic.begin(); i != a_topic.end(); i++)
+ kafs::help(&gen_bos_suite, *i);
+}
+
+/***
+ * COMMAND: bos apropos - Search for a command by its help text
+ * ARG: "-topic <help string>"
+ */
+void COMMAND_bos_apropos(
+ kafs::Context *ctx,
+ std::string &a_topic)
+{
+ const 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.c_str()) ||
+ strstr(cmd->help, a_topic.c_str()))
+ std::cout << cmd->name << ": " << cmd->help << "\n";
+ }
+}
--- /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"
+
+namespace kafs {
+
+/*
+ * display_error.C
+ */
+extern bool kafs_display_error(Context *);
+
+} /* end namespace kafs */
+
+#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 <cstdarg>
+#include <cstdio>
+#include <string>
+#include "kafs.H"
+#include "afs_xg.H"
+
+#if 0
+/*
+ * 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;
+}
+#endif
--- /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 "fs.H"
+
+/***
+ * COMMAND: fs help - Get help on commands
+ * ARG: "[-topic <help string>+]"
+ * ARG: "[-admin]"
+ */
+void COMMAND_fs_help(
+ kafs::Context *ctx,
+ std::vector<std::string> &a_topic,
+ bool a_admin)
+{
+ if (a_topic.empty())
+ return kafs::help(&gen_fs_suite);
+ for (std::vector<std::string>::iterator i = a_topic.begin(); i != a_topic.end(); i++)
+ kafs::help(&gen_fs_suite, *i);
+}
+
+/***
+ * COMMAND: fs apropos - Search for a command by its help text
+ * ARG: "-topic <help string>"
+ */
+void COMMAND_fs_apropos(
+ kafs::Context *ctx,
+ std::string &a_topic)
+{
+ const 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.c_str()) ||
+ strstr(cmd->help, a_topic.c_str()))
+ printf("%s: %s\n", cmd->name, cmd->help);
+ }
+}
--- /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_decl = None # Argument C type, including pointer markers
+ self.c_name = None # Argument name
+ self.c_var_type = None # Temp variable type
+ self.is_list = False # If list of objects
+
+ arg_re = re.compile(r'(const)?\s*([_a-zA-Z:0-9]+)(<[_a-zA-Z:0-9]+>)? ([*&]*)([_a-zA-Z0-9]*)')
+ m = arg_re.fullmatch(arg)
+ if not m:
+ raise RuntimeError("Func arg could not be parsed")
+ m_const = m.group(1)
+ m_type = m.group(2)
+ m_template = m.group(3)
+ m_ptr = m.group(4)
+ m_name = m.group(5)
+
+ if m_const == None:
+ m_const = ""
+ else:
+ m_const += " "
+
+ is_list = False
+ if m_template:
+ c_type = m_template.strip("<>")
+ c_decl = m_const + m_type + m_template
+ c_var = m_type + m_template
+ if m_type == "std::vector":
+ is_list = True
+ else:
+ c_type = m_type
+ c_decl = m_const + m_type
+ c_var = m_type
+
+ if m_ptr:
+ c_decl += m_ptr
+ self.c_decl = c_type
+ self.is_list = is_list
+
+ name = m_name
+ if name != "ctx":
+ if not name.startswith("a_"):
+ raise RuntimeError("Function argument should begin with 'a_'")
+ name = name[2:]
+
+ self.c_name = name
+ self.c_var_type = c_var
+
+ if self.c_name == "cell":
+ raise RuntimeError("'cell' function argument should be in ctx")
+
+ if (m_type == "std::string" or
+ m_type == "char" and m_ptr.startswith("*") or
+ m_type == "const char" and m_ptr.startswith("*")):
+ self.syntax = "string"
+ else:
+ (a, space, b) = m_type.partition(" ")
+ if space:
+ self.syntax = b
+ else:
+ self.syntax = a
+
+ #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("void 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 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 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 void gen_handler_", cmd.suite, "_", cmd.name,
+ "(kafs::Context *ctx, char **argv);\n")
+ output("static const kafs::Command gen_cmd_", cmd.suite, "_", cmd.name, " = {\n")
+ output("\t.suite = \"", cmd.suite, "\",\n")
+ output("\t.name = \"", cmd.name, "\",\n")
+ output("\t.args = gen_", cmd.suite, "_", cmd.name, "_arguments,\n")
+ output("\t.nr_args = ", len(cmd.params), ",\n")
+ output("\t.mink = ", cmd.mink, ",\n")
+ output("\t.handler = gen_handler_", cmd.suite, "_", cmd.name, ",\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 kafs::Command gen_cmd_", cmd.suite, "_", cmd.name, " = {\n")
+ output("\t.alias = &gen_cmd_", cmd.suite, "_", cmd.alias, ",\n")
+ output("\t.suite = \"", cmd.suite, "\",\n")
+ output("\t.name = \"", cmd.name, "\",\n")
+ output("\t.args = gen_", cmd.suite, "_", cmd.alias, "_arguments,\n")
+ output("\t.help = \"-- Alias of ", cmd.alias, "\",\n")
+ output("};\n")
+
+def emit_command_handler(cmd):
+ header("extern void ", cmd.func_name, "(\n")
+ header("\tkafs::Context *ctx")
+ for i in cmd.func_args:
+ header(",\n")
+ if (i.c_decl == "bool"):
+ header("\t", i.c_var_type, " a_", i.c_name)
+ else:
+ header("\t", i.c_var_type, " &a_", i.c_name)
+ header(");\n")
+
+ output("static void gen_handler_", cmd.suite, "_", cmd.name,
+ "(kafs::Context *ctx, char **args)\n")
+ output("{\n")
+
+ # Declare variables
+ output("\tstd::vector<kafs::Extracted_arg> results(", len(cmd.params), ");\n")
+ for i in cmd.func_args:
+ if i.c_var_type == "kafs::Context *":
+ pass
+ 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, ";\n")
+ output("\n")
+ output("\textract_arguments(ctx, args, &gen_cmd_", cmd.suite, "_", cmd.name, ", results);\n")
+
+ # Write extraction list
+ for p in cmd.params:
+ if p.auth_info:
+ output("\textract_kafs_context (ctx, results[", p.index, "]);\n")
+ if "cell" in cmd.param_by_name:
+ p = cmd.param_by_name["cell"]
+ fu = "extract_kafs_cell"
+ output("\t", fu.ljust(30), "(ctx, results[", p.index, "]);\n")
+ for p in cmd.params:
+ if not p.auth_info and p.name != "cell":
+ fa = cmd.func_args_by_name[p.name]
+ output("\textract(ctx, results[", p.index, "], a_", p.name, ");\n")
+
+ # Call processor function
+ output("\n")
+ output("\t", cmd.func_name, "(\n")
+ output("\t\tctx")
+ for i in cmd.func_args:
+ output(",\n")
+ if i.c_var_type == "kafs::Context *":
+ output("\t\t&a_", i.c_name)
+ else:
+ output("\t\ta_", i.c_name)
+ output(");\n")
+ output("}\n")
+
+def emit_command_table(suite):
+ names = suite.command_names + suite.alias_names
+ output("\n")
+ output("static const 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 kafs::Suite gen_", suite.name, "_suite;\n")
+
+ output("\n")
+ output("const 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 <cstdlib>\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 <iostream>
+#include <cstdarg>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <cctype>
+#include <fmt/core.h>
+extern "C" {
+#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"
+
+using rxrpc::ref;
+
+namespace kafs {
+void dispatch_command(const Suite *suite, char *command, char **args);
+}
+
+bool kafs::debug_caps;
+bool kafs::debug_trace;
+std::string kafs::empty_string("");
+
+static ref<kafs::Context> context;
+
+const kafs::Suite *kafs::command_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);
+}
+
+kafs::Context::Context()
+{
+ cell_db = NULL;
+ no_resolve = false;
+ sec_level = rxrpc::security_unset;
+}
+
+void kafs::open_endpoint(kafs::Context *ctx)
+{
+ char buf[1024];
+
+ rxrpc::find_transport();
+
+ if (!ctx->security) {
+ snprintf(buf, sizeof(buf), "afs@%s", ctx->cell_name.c_str());
+ ctx->security = rxrpc::new_security(buf, ctx->sec_level);
+ }
+
+ if (!ctx->endpoint)
+ ctx->endpoint = rxrpc::new_local_endpoint(NULL, ctx->security);
+}
+
+void kafs::command_usage(const Command *cmd)
+{
+ const 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");
+}
+
+void kafs::help(const Suite *suite, std::string &topic)
+{
+ const Command *cmd;
+ size_t cl;
+ bool ambiguous = false;
+ int i;
+
+ if (topic.empty()) {
+ 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;
+ }
+
+ cl = topic.size();
+ for (i = 0; suite->commands[i]; i++) {
+ cmd = suite->commands[i];
+ if (topic == cmd->name)
+ goto matched_command;
+ if (cl >= strlen(cmd->name))
+ continue;
+ if (topic.compare(0, cl, cmd->name) == 0) {
+ if (cl >= cmd->mink)
+ goto matched_command;
+ ambiguous = true;
+ }
+ }
+
+ if (ambiguous)
+ fprintf(stderr, "%s: Ambiguous topic '%s'; use 'apropos' to list\n",
+ suite->name, topic.c_str());
+ else
+ fprintf(stderr, "%s: Unknown topic '%s'\n",
+ suite->name, topic.c_str());
+ throw std::invalid_argument(fmt::format("{}: Unrecognised or ambiguous command name",
+ suite->name));
+
+matched_command:
+ return kafs::command_usage(cmd);
+}
+
+const kafs::Command *kafs::look_up_command(const Suite *suite, char *command)
+{
+ const 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;
+}
+
+void kafs::dispatch_command(const Suite *suite, char *command, char **args)
+{
+ const kafs::Command *cmd;
+
+ _enter("%s", command);
+
+ cmd = kafs::look_up_command(suite, command);
+ if (cmd) {
+ _debug("found %s", cmd->name);
+ cmd->handler(context, args);
+ }
+}
+
+struct kafs_lookup_context kafs::lookup_context = {
+ .report = {
+ .error = error_report,
+ .verbose = verbose,
+ .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;
+ else
+ fprintf(stderr, "Unrecognised debug type '%s'\n", p);
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ using kafs::comp_log;
+ const kafs::Suite *suite;
+ char *prog, *suite_name, **p;
+ int completion_argc = -1;
+ int i;
+
+ for (p = argv; *p; p++)
+ comp_log(" '%s'", *p);
+ comp_log("\n");
+
+ prog = strrchr(argv[0], '/');
+ if (prog)
+ prog++;
+ else
+ prog = argv[0];
+ argc -= 1;
+ argv += 1;
+
+ // We see: kafs --complete <cargc> <realargv[0]> <realargv[1]>...
+ // cargc is 0 at realargv[0]
+ // But --x=y get split into 3 around the '='
+ 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;
+ 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];
+ argc -= 2;
+ argv += 2;
+ }
+ } else {
+ // Bash split the argument into three
+ 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);
+ }
+
+ context = new kafs::Context;
+
+ 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 (strcmp(prog, "kafs") == 0 ||
+ strcmp(prog, "afs") == 0) {
+ if (completion_argc == 0)
+ kafs::bash_complete_command_suite(argv[0] ?: "");
+ if (argc == 0)
+ goto no_suite;
+
+ suite_name = argv[0];
+ argc--;
+ argv++;
+ completion_argc--;
+ } else {
+ suite_name = prog;
+ }
+
+ for (i = 0; kafs::command_suites[i]; i++) {
+ suite = kafs::command_suites[i];
+ if (strcmp(suite_name, suite->name) == 0)
+ goto use_suite;
+ }
+ fprintf(stderr, "%s: Unrecognised command suite\n", prog);
+ exit(2);
+
+no_suite:
+ fprintf(stderr, "kafs supports the following command suites:\n");
+ for (i = 0; kafs::command_suites[i]; i++) {
+ suite = kafs::command_suites[i];
+ printf(" %s\n", suite->name);
+ }
+
+ printf("Type 'kafs <suite> help' to get a command list\n");
+ exit(1);
+
+use_suite:
+ comp_log("suite: %s\n", suite->name);
+ if (completion_argc >= 0)
+ kafs::bash_complete(argc, argv, completion_argc, suite);
+
+ if (!*argv) {
+ fprintf(stderr, "%s: Type '%s help' or '%s help <topic>' for help\n",
+ suite->name, suite->name, suite->name);
+ exit(2);
+ }
+
+ try {
+ dispatch_command(suite, *argv, argv + 1);
+ exit(0);
+ } catch (const kafs::Gave_help &g) {
+ exit(0);
+ } catch (const rxrpc::rx_abort &ab) {
+ std::cerr << ab.what() << " (" << ab.get_abort_code() << ")\n";
+ exit(1);
+ } catch (const rxrpc::network_error &e) {
+ std::cerr << "Network failure: " << e.what() << "\n";
+ exit(1);
+ } catch (const rxrpc::syserror &e) {
+ std::cerr << "Local failure: " << e.what() << "\n";
+ exit(1);
+ } catch (const std::runtime_error &a) {
+ std::cerr << a.what() << "\n";
+ exit(1);
+ } catch (const std::invalid_argument &a) {
+ std::cerr << a.what() << "\n";
+ exit(2);
+ }
+}
--- /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 <cstdlib>
+#include <uuid/uuid.h>
+#include <cerrno>
+#include <rxrpc.H>
+extern "C" {
+#include <kafs/cellserv.h>
+}
+
+namespace kafs {
+
+namespace afs {
+constexpr unsigned int FS_PORT = 7000; /* AFS file server port */
+constexpr unsigned int FS_SERVICE = 1; /* AFS File Service ID */
+}
+
+typedef unsigned long long Volume_id;
+
+enum Service {
+ service_afs,
+ service_yfs,
+};
+
+class Context : public rxrpc::refcount {
+public:
+ rxrpc::ref<rxrpc::Endpoint> endpoint;
+ rxrpc::ref<rxrpc::Security> security;
+
+ std::string cell_name;
+ struct kafs_cell *cell_db;
+ bool no_resolve; /* Don't resolve addresses for display */
+
+ rxrpc::security_auth_level sec_level; /* Security level */
+
+ Context();
+ ~Context();
+};
+
+struct Spec {
+ bool specified;
+ Spec() { specified = false; }
+};
+
+struct User_spec : public Spec {
+ std::string name;
+};
+
+struct Group_spec : public Spec {
+ std::string name;
+};
+
+struct Partition_spec : public Spec {
+ unsigned int id;
+ Partition_spec() { id = 0; }
+};
+
+struct FVserver_spec : public Spec {
+ std::string name;
+ rxrpc::Uuid uuid;
+ unsigned int nr_addrs;
+ struct sockaddr_rxrpc *addrs;
+ FVserver_spec() { nr_addrs = 0; addrs = NULL; }
+ virtual bool is_volser_spec() const = 0;
+};
+
+struct Fileserver_spec : public FVserver_spec {
+ bool is_volser_spec() const;
+};
+
+struct Volserver_spec : public FVserver_spec {
+ bool is_volser_spec() const;
+};
+
+struct Vlserver_spec : public Spec {
+ std::string name;
+ struct kafs_server_list *slist;
+
+ Vlserver_spec() { slist = NULL; }
+ ~Vlserver_spec() { kafs_free_server_list(slist); }
+};
+
+struct Volume_spec : public Spec {
+ bool is_numeric;
+ std::string name;
+ Volume_id number;
+ Volume_spec() { is_numeric = false; number = 0; }
+};
+
+struct Uuid_spec : public Spec {
+ rxrpc::Uuid uuid;
+};
+
+/*
+ * kafs.C
+ */
+extern std::string empty_string;
+extern struct kafs_lookup_context lookup_context;
+extern void open_endpoint(Context *);
+
+/*
+ * Debugging.
+ */
+extern bool debug_trace;
+extern bool 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
+
+} /* end namespace kafs */
+
+#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 "pts.H"
+
+/***
+ * COMMAND: pts help - Get help on commands
+ * ARG: "[-topic <help string>+]"
+ * ARG: "[-admin]"
+ */
+void COMMAND_pts_help(
+ kafs::Context *ctx,
+ std::vector<std::string> &a_topic,
+ bool a_admin)
+{
+ if (a_topic.empty())
+ return kafs::help(&gen_pts_suite);
+ for (std::vector<std::string>::iterator i = a_topic.begin(); i != a_topic.end(); i++)
+ kafs::help(&gen_pts_suite, *i);
+}
+
+/***
+ * COMMAND: pts apropos - Search for a command by its help text
+ * ARG: "-topic <help string>"
+ */
+void COMMAND_pts_apropos(
+ kafs::Context *ctx,
+ std::string &a_topic)
+{
+ const 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.c_str()) ||
+ strstr(cmd->help, a_topic.c_str()))
+ printf("%s: %s\n", cmd->name, cmd->help);
+ }
+}
--- /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 "vos.H"
+
+/***
+ * COMMAND: vos help - Get help on commands
+ * ARG: "[-topic <help string>+]"
+ * ARG: "[-admin]"
+ */
+void COMMAND_vos_help(
+ kafs::Context *ctx,
+ std::vector<std::string> &a_topic,
+ bool a_admin)
+{
+ if (a_topic.empty())
+ return kafs::help(&gen_vos_suite);
+ for (std::vector<std::string>::iterator i = a_topic.begin(); i != a_topic.end(); i++)
+ kafs::help(&gen_vos_suite, *i);
+}
+
+/***
+ * COMMAND: vos apropos - Search for a command by its help text
+ * ARG: "-topic <help string>"
+ */
+void COMMAND_vos_apropos(
+ kafs::Context *ctx,
+ std::string &a_topic)
+{
+ const 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.c_str()) ||
+ strstr(cmd->help, a_topic.c_str()))
+ printf("%s: %s\n", cmd->name, cmd->help);
+ }
+}