static size_t n_classes;
static struct shash all_commands = SHASH_INITIALIZER(&all_commands);
-static const struct ovsdb_idl_table_class *get_table(const char *table_name);
-static void set_column(const struct ovsdb_idl_table_class *,
- const struct ovsdb_idl_row *, const char *,
- struct ovsdb_symbol_table *);
+static char *get_table(const char *, const struct ovsdb_idl_table_class **);
+static char *set_column(const struct ovsdb_idl_table_class *,
+ const struct ovsdb_idl_row *, const char *,
+ struct ovsdb_symbol_table *);
\f
static struct option *
free(output);
}
-static void
-die_if_error(char *error)
-{
- if (error) {
- ctl_fatal("%s", error);
- }
-}
-
static int
to_lower_and_underscores(unsigned c)
{
return *s == '\0' ? score : 0;
}
-static struct ovsdb_symbol *
-create_symbol(struct ovsdb_symbol_table *symtab, const char *id, bool *newp)
+/* Returns NULL and sets 'symbolp' and 'newp' if symbol was created
+ * successfully. Otherwise returns a malloc()'ed error message on failure. */
+static char * OVS_WARN_UNUSED_RESULT
+create_symbol(struct ovsdb_symbol_table *symtab, const char *id,
+ struct ovsdb_symbol **symbolp, bool *newp)
{
struct ovsdb_symbol *symbol;
+ ovs_assert(symbolp);
+
if (id[0] != '@') {
- ctl_fatal("row id \"%s\" does not begin with \"@\"", id);
+ return xasprintf("row id \"%s\" does not begin with \"@\"", id);
}
if (newp) {
symbol = ovsdb_symbol_table_insert(symtab, id);
if (symbol->created) {
- ctl_fatal("row id \"%s\" may only be specified on one --id option",
- id);
+ return xasprintf("row id \"%s\" may only be specified on one --id "
+ "option", id);
}
symbol->created = true;
- return symbol;
+ *symbolp = symbol;
+ return NULL;
}
static bool
static const struct ovsdb_idl_row *
get_row_by_id(struct ctl_context *ctx,
const struct ovsdb_idl_table_class *table,
- const struct ctl_row_id *id, const char *record_id)
+ const struct ctl_row_id *id, const char *record_id,
+ bool *multiple_matches)
{
+ ovs_assert(multiple_matches);
+ *multiple_matches = false;
if (!id->name_column) {
return NULL;
/* If the name equals 'record_id', take it. */
if (record_id_equals(name, name_type, record_id)) {
if (referrer) {
- ctl_fatal("multiple rows in %s match \"%s\"",
- id_table->name, record_id);
+ *multiple_matches = true;
+ return NULL;
}
referrer = row;
}
return final;
}
-const struct ovsdb_idl_row *
+char * OVS_WARN_UNUSED_RESULT
ctl_get_row(struct ctl_context *ctx,
const struct ovsdb_idl_table_class *table, const char *record_id,
- bool must_exist)
+ bool must_exist, const struct ovsdb_idl_row **rowp)
{
const struct ovsdb_idl_row *row = NULL;
struct uuid uuid;
+ ovs_assert(rowp);
+
if (uuid_from_string(&uuid, record_id)) {
row = ovsdb_idl_get_row_for_uuid(ctx->idl, table, &uuid);
}
const struct ctl_table_class *ctl_class
= &ctl_classes[table - idl_classes];
for (int i = 0; i < ARRAY_SIZE(ctl_class->row_ids); i++) {
- row = get_row_by_id(ctx, table, &ctl_class->row_ids[i],
- record_id);
+ const struct ctl_row_id *id = &ctl_class->row_ids[i];
+ bool multiple_matches;
+
+ row = get_row_by_id(ctx, table, id, record_id, &multiple_matches);
+ if (multiple_matches) {
+ const struct ovsdb_idl_class *class =
+ ovsdb_idl_get_class(ctx->idl);
+ const struct ovsdb_idl_table_class *table_class =
+ ovsdb_idl_table_class_from_column(class, id->name_column);
+ return xasprintf("multiple rows in %s match \"%s\"",
+ table_class->name, record_id);
+ }
if (row) {
break;
}
if (!row) {
row = r;
} else {
- ctl_fatal("%s contains 2 or more rows whose UUIDs begin "
- "with %s: at least "UUID_FMT" and "UUID_FMT,
- table->name, record_id,
- UUID_ARGS(&row->uuid),
- UUID_ARGS(&r->uuid));
+ return xasprintf("%s contains 2 or more rows whose UUIDs "
+ "begin with %s: at least "UUID_FMT" "
+ "and "UUID_FMT, table->name, record_id,
+ UUID_ARGS(&row->uuid),
+ UUID_ARGS(&r->uuid));
}
}
}
}
if (must_exist && !row) {
- ctl_fatal("no row \"%s\" in table %s", record_id, table->name);
+ return xasprintf("no row \"%s\" in table %s", record_id, table->name);
}
- return row;
+
+ *rowp = row;
+ return NULL;
}
static char *
get_column(const struct ovsdb_idl_table_class *table, const char *column_name,
const struct ovsdb_idl_column **columnp)
{
+ struct sset best_matches = SSET_INITIALIZER(&best_matches);
const struct ovsdb_idl_column *best_match = NULL;
unsigned int best_score = 0;
- size_t i;
- for (i = 0; i < table->n_columns; i++) {
+ for (size_t i = 0; i < table->n_columns; i++) {
const struct ovsdb_idl_column *column = &table->columns[i];
unsigned int score = score_partial_match(column->name, column_name);
- if (score > best_score) {
+ if (score && score >= best_score) {
+ if (score > best_score) {
+ sset_clear(&best_matches);
+ }
+ sset_add(&best_matches, column->name);
best_match = column;
best_score = score;
- } else if (score == best_score) {
- best_match = NULL;
}
}
- *columnp = best_match;
- if (best_match) {
- return NULL;
- } else if (best_score) {
- return xasprintf("%s contains more than one column whose name "
- "matches \"%s\"", table->name, column_name);
+ char *error = NULL;
+ *columnp = NULL;
+ if (!best_match) {
+ error = xasprintf("%s does not contain a column whose name matches "
+ "\"%s\"", table->name, column_name);
+ } else if (sset_count(&best_matches) == 1) {
+ *columnp = best_match;
} else {
- return xasprintf("%s does not contain a column whose name matches "
- "\"%s\"", table->name, column_name);
+ char *matches = sset_join(&best_matches, ", ", "");
+ error = xasprintf("%s contains more than one column "
+ "whose name matches \"%s\": %s",
+ table->name, column_name, matches);
+ free(matches);
}
+ sset_destroy(&best_matches);
+ return error;
}
-static void
+static char * OVS_WARN_UNUSED_RESULT
pre_get_column(struct ctl_context *ctx,
const struct ovsdb_idl_table_class *table,
const char *column_name,
const struct ovsdb_idl_column **columnp)
{
- die_if_error(get_column(table, column_name, columnp));
+ char *error = get_column(table, column_name, columnp);
+ if (error) {
+ return error;
+ }
ovsdb_idl_add_column(ctx->idl, *columnp);
+ return NULL;
}
-static const struct ovsdb_idl_table_class *
-pre_get_table(struct ctl_context *ctx, const char *table_name)
+static char * OVS_WARN_UNUSED_RESULT
+pre_get_table(struct ctl_context *ctx, const char *table_name,
+ const struct ovsdb_idl_table_class **tablep)
{
- const struct ovsdb_idl_table_class *table = get_table(table_name);
+ const struct ovsdb_idl_table_class *table;
+ char *error = get_table(table_name, &table);
+ if (error) {
+ return error;
+ }
ovsdb_idl_add_table(ctx->idl, table);
const struct ctl_table_class *ctl = &ctl_classes[table - idl_classes];
}
}
- return table;
+ if (tablep) {
+ *tablep = table;
+ }
+ return NULL;
}
static char *
return error;
}
-static const struct ovsdb_idl_column *
-pre_parse_column_key_value(struct ctl_context *ctx,
- const char *arg,
+static char * OVS_WARN_UNUSED_RESULT
+pre_parse_column_key_value(struct ctl_context *ctx, const char *arg,
const struct ovsdb_idl_table_class *table)
{
const struct ovsdb_idl_column *column;
const char *p;
- char *column_name;
+ char *column_name = NULL;
+ char *error;
p = arg;
- die_if_error(ovsdb_token_parse(&p, &column_name));
+ error = ovsdb_token_parse(&p, &column_name);
+ if (error) {
+ goto out;
+ }
if (column_name[0] == '\0') {
- ctl_fatal("%s: missing column name", arg);
+ error = xasprintf("%s: missing column name", arg);
+ goto out;
}
- pre_get_column(ctx, table, column_name, &column);
+ error = pre_get_column(ctx, table, column_name, &column);
+out:
free(column_name);
- return column;
+ return error;
}
-static void
+/* Checks if the 'column' is mutable. Returns NULL if it is mutable, or a
+ * malloc()'ed error message otherwise. */
+static char * OVS_WARN_UNUSED_RESULT
check_mutable(const struct ovsdb_idl_row *row,
const struct ovsdb_idl_column *column)
{
if (!ovsdb_idl_is_mutable(row, column)) {
- ctl_fatal("cannot modify read-only column %s in table %s",
- column->name, row->table->class_->name);
+ return xasprintf("cannot modify read-only column %s in table %s",
+ column->name, row->table->class_->name);
}
+ return NULL;
}
#define RELOPS \
RELOP(RELOP_SET_LT, "{<}") \
RELOP(RELOP_SET_GT, "{>}") \
RELOP(RELOP_SET_LE, "{<=}") \
- RELOP(RELOP_SET_GE, "{>=}")
+ RELOP(RELOP_SET_GE, "{>=}") \
+ RELOP(RELOP_SET_IN, "{in}") \
+ RELOP(RELOP_SET_NOT_IN, "{not-in}")
enum relop {
#define RELOP(ENUM, STRING) ENUM,
{
return (op == RELOP_SET_EQ || op == RELOP_SET_NE ||
op == RELOP_SET_LT || op == RELOP_SET_GT ||
- op == RELOP_SET_LE || op == RELOP_SET_GE);
+ op == RELOP_SET_LE || op == RELOP_SET_GE ||
+ op == RELOP_SET_IN || op == RELOP_SET_NOT_IN);
}
static bool
case RELOP_SET_GT:
return a->n > b->n && ovsdb_datum_includes_all(b, a, type);
case RELOP_SET_LE:
+ case RELOP_SET_IN:
return ovsdb_datum_includes_all(a, b, type);
case RELOP_SET_GE:
return ovsdb_datum_includes_all(b, a, type);
+ case RELOP_SET_NOT_IN:
+ return ovsdb_datum_excludes_all(a, b, type);
default:
OVS_NOT_REACHED();
}
}
-static bool
-is_condition_satisfied(const struct ovsdb_idl_table_class *table,
- const struct ovsdb_idl_row *row, const char *arg,
- struct ovsdb_symbol_table *symtab)
+/* Checks if given row satisfies the specified condition. Returns the result of
+ * evaluating the condition in 'satisfied' flag and NULL as a return value on
+ * success. On failure returns a malloc()'ed error message and 'satisfied'
+ * value is not modified. */
+static char * OVS_WARN_UNUSED_RESULT
+check_condition(const struct ovsdb_idl_table_class *table,
+ const struct ovsdb_idl_row *row, const char *arg,
+ struct ovsdb_symbol_table *symtab, bool *satisfied)
{
static const char *operators[] = {
#define RELOP(ENUM, STRING) STRING,
const struct ovsdb_idl_column *column;
const struct ovsdb_datum *have_datum;
- char *key_string, *value_string;
+ char *key_string = NULL;
+ char *value_string = NULL;
struct ovsdb_type type;
int operator;
bool retval;
char *error;
+ ovs_assert(satisfied);
+
error = parse_column_key_value(arg, table, &column, &key_string,
&operator, operators, ARRAY_SIZE(operators),
&value_string);
- die_if_error(error);
+ if (error) {
+ goto out;
+ }
if (!value_string) {
- ctl_fatal("%s: missing value", arg);
+ error = xasprintf("%s: missing value", arg);
+ goto out;
}
type = column->type;
unsigned int idx;
if (column->type.value.type == OVSDB_TYPE_VOID) {
- ctl_fatal("cannot specify key to check for non-map column %s",
- column->name);
+ error = xasprintf("cannot specify key to check for non-map column "
+ "%s", column->name);
+ goto out;
}
- die_if_error(ovsdb_atom_from_string(&want_key, NULL, &column->type.key,
- key_string, symtab));
+ error = ovsdb_atom_from_string(&want_key, NULL, &column->type.key,
+ key_string, symtab);
+ if (error) {
+ goto out;
+ }
type.key = type.value;
type.value.type = OVSDB_TYPE_VOID;
- die_if_error(ovsdb_datum_from_string(&b, &type, value_string, symtab));
+ error = ovsdb_datum_from_string(&b, &type, value_string, symtab);
+ if (error) {
+ goto out;
+ }
idx = ovsdb_datum_find_key(have_datum,
&want_key, column->type.key.type);
} else {
struct ovsdb_datum want_datum;
- die_if_error(ovsdb_datum_from_string(&want_datum, &column->type,
- value_string, symtab));
+ error = ovsdb_datum_from_string(&want_datum, &column->type,
+ value_string, symtab);
+ if (error) {
+ goto out;
+ }
retval = evaluate_relop(have_datum, &want_datum, &type, operator);
ovsdb_datum_destroy(&want_datum, &column->type);
}
+ *satisfied = retval;
+out:
free(key_string);
free(value_string);
- return retval;
+ return error;
}
static void
"possibly erroneous");
}
- table = pre_get_table(ctx, table_name);
+ ctx->error = pre_get_table(ctx, table_name, &table);
+ if (ctx->error) {
+ return;
+ }
for (i = 3; i < ctx->argc; i++) {
if (!strcasecmp(ctx->argv[i], "_uuid")
|| !strcasecmp(ctx->argv[i], "-uuid")) {
continue;
}
- pre_parse_column_key_value(ctx, ctx->argv[i], table);
+ ctx->error = pre_parse_column_key_value(ctx, ctx->argv[i], table);
+ if (ctx->error) {
+ return;
+ }
}
}
int i;
if (id && !must_exist) {
- ctl_fatal("--if-exists and --id may not be specified together");
+ ctl_error(ctx, "--if-exists and --id may not be specified together");
+ return;
}
- table = get_table(table_name);
- row = ctl_get_row(ctx, table, record_id, must_exist);
+ ctx->error = get_table(table_name, &table);
+ if (ctx->error) {
+ return;
+ }
+ ctx->error = ctl_get_row(ctx, table, record_id, must_exist, &row);
+ if (ctx->error) {
+ return;
+ }
if (!row) {
return;
}
if (id) {
- struct ovsdb_symbol *symbol;
- bool new;
+ struct ovsdb_symbol *symbol = NULL;
+ bool new = false;
- symbol = create_symbol(ctx->symtab, id, &new);
+ ctx->error = create_symbol(ctx->symtab, id, &symbol, &new);
+ if (ctx->error) {
+ return;
+ }
if (!new) {
- ctl_fatal("row id \"%s\" specified on \"get\" command was used "
- "before it was defined", id);
+ ctl_error(ctx, "row id \"%s\" specified on \"get\" command was "
+ "used before it was defined", id);
+ return;
}
symbol->uuid = row->uuid;
continue;
}
- die_if_error(parse_column_key_value(ctx->argv[i], table,
- &column, &key_string,
- NULL, NULL, 0, NULL));
+ ctx->error = parse_column_key_value(ctx->argv[i], table, &column,
+ &key_string, NULL, NULL, 0, NULL);
+ if (ctx->error) {
+ return;
+ }
ovsdb_idl_txn_verify(row, column);
datum = ovsdb_idl_read(row, column);
unsigned int idx;
if (column->type.value.type == OVSDB_TYPE_VOID) {
- ctl_fatal("cannot specify key to get for non-map column %s",
+ ctl_error(ctx,
+ "cannot specify key to get for non-map column %s",
column->name);
+ free(key_string);
+ return;
}
- die_if_error(ovsdb_atom_from_string(&key, NULL,
- &column->type.key,
- key_string, ctx->symtab));
+ ctx->error = ovsdb_atom_from_string(&key, NULL, &column->type.key,
+ key_string, ctx->symtab);
+ if (ctx->error) {
+ free(key_string);
+ return;
+ }
idx = ovsdb_datum_find_key(datum, &key,
column->type.key.type);
if (idx == UINT_MAX) {
if (must_exist) {
- ctl_fatal("no key \"%s\" in %s record \"%s\" column %s",
- key_string, table->name, record_id,
- column->name);
+ ctl_error(
+ ctx, "no key \"%s\" in %s record \"%s\" column %s",
+ key_string, table->name, record_id, column->name);
+ free(key_string);
+ ovsdb_atom_destroy(&key, column->type.key.type);
+ return;
}
} else {
ovsdb_atom_to_string(&datum->values[idx],
}
}
-static void
+/* Returns NULL on success or malloc()'ed error message on failure. */
+static char * OVS_WARN_UNUSED_RESULT
parse_column_names(const char *column_names,
const struct ovsdb_idl_table_class *table,
const struct ovsdb_idl_column ***columnsp,
if (!strcasecmp(column_name, "_uuid")) {
column = NULL;
} else {
- die_if_error(get_column(table, column_name, &column));
+ char *error = get_column(table, column_name, &column);
+ if (error) {
+ free(columns);
+ free(s);
+ return error;
+ }
}
if (n_columns >= allocated_columns) {
columns = x2nrealloc(columns, &allocated_columns,
free(s);
if (!n_columns) {
- ctl_fatal("must specify at least one column name");
+ return xstrdup("must specify at least one column name");
}
}
*columnsp = columns;
*n_columnsp = n_columns;
+ return NULL;
}
-static void
+static char * OVS_WARN_UNUSED_RESULT
pre_list_columns(struct ctl_context *ctx,
const struct ovsdb_idl_table_class *table,
const char *column_names)
const struct ovsdb_idl_column **columns;
size_t n_columns;
size_t i;
+ char *error;
- parse_column_names(column_names, table, &columns, &n_columns);
+ error = parse_column_names(column_names, table, &columns, &n_columns);
+ if (error) {
+ return error;
+ }
for (i = 0; i < n_columns; i++) {
if (columns[i]) {
ovsdb_idl_add_column(ctx->idl, columns[i]);
}
}
free(columns);
+ return NULL;
}
static void
const char *table_name = ctx->argv[1];
const struct ovsdb_idl_table_class *table;
- table = pre_get_table(ctx, table_name);
- pre_list_columns(ctx, table, column_names);
+ ctx->error = pre_get_table(ctx, table_name, &table);
+ if (ctx->error) {
+ return;
+ }
+ ctx->error = pre_list_columns(ctx, table, column_names);
+ if (ctx->error) {
+ return;
+ }
}
static struct table *
size_t n_columns;
int i;
- table = get_table(table_name);
- parse_column_names(column_names, table, &columns, &n_columns);
+ ctx->error = get_table(table_name, &table);
+ if (ctx->error) {
+ return;
+ }
+ ctx->error = parse_column_names(column_names, table, &columns, &n_columns);
+ if (ctx->error) {
+ return;
+ }
out = ctx->table = list_make_table(columns, n_columns);
if (ctx->argc > 2) {
for (i = 2; i < ctx->argc; i++) {
- list_record(ctl_get_row(ctx, table, ctx->argv[i], must_exist),
- columns, n_columns, out);
+ const struct ovsdb_idl_row *row;
+
+ ctx->error = ctl_get_row(ctx, table, ctx->argv[i], must_exist,
+ &row);
+ if (ctx->error) {
+ free(columns);
+ return;
+ }
+ list_record(row, columns, n_columns, out);
}
} else {
const struct ovsdb_idl_row *row;
free(columns);
}
-/* Finds and returns the "struct ovsdb_idl_table_class *" with 'table_name' by
- * searching the tables in these schema. */
-static const struct ovsdb_idl_table_class *
-get_table(const char *table_name)
+/* Finds the "struct ovsdb_idl_table_class *" with 'table_name' by searching
+ * the tables in these schema. Returns NULL and sets 'tablep' on success, or a
+ * malloc()'ed error message on failure. */
+static char * OVS_WARN_UNUSED_RESULT
+get_table(const char *table_name, const struct ovsdb_idl_table_class **tablep)
{
+ struct sset best_matches = SSET_INITIALIZER(&best_matches);
const struct ovsdb_idl_table_class *best_match = NULL;
unsigned int best_score = 0;
for (const struct ovsdb_idl_table_class *table = idl_classes;
table < &idl_classes[n_classes]; table++) {
unsigned int score = score_partial_match(table->name, table_name);
- if (score > best_score) {
+ if (score && score >= best_score) {
+ if (score > best_score) {
+ sset_clear(&best_matches);
+ }
+ sset_add(&best_matches, table->name);
best_match = table;
best_score = score;
- } else if (score == best_score) {
- best_match = NULL;
}
}
- if (best_match) {
- return best_match;
- } else if (best_score) {
- ctl_fatal("multiple table names match \"%s\"", table_name);
+
+ char *error = NULL;
+ if (!best_match) {
+ error = xasprintf("unknown table \"%s\"", table_name);
+ } else if (sset_count(&best_matches) == 1) {
+ *tablep = best_match;
} else {
- ctl_fatal("unknown table \"%s\"", table_name);
+ char *matches = sset_join(&best_matches, ", ", "");
+ error = xasprintf("\"%s\" matches multiple table names: %s",
+ table_name, matches);
+ free(matches);
}
- return NULL;
+ sset_destroy(&best_matches);
+ return error;
}
static void
const struct ovsdb_idl_table_class *table;
int i;
- table = pre_get_table(ctx, table_name);
- pre_list_columns(ctx, table, column_names);
+ ctx->error = pre_get_table(ctx, table_name, &table);
+ if (ctx->error) {
+ return;
+ }
+ ctx->error = pre_list_columns(ctx, table, column_names);
+ if (ctx->error) {
+ return;
+ }
for (i = 2; i < ctx->argc; i++) {
- pre_parse_column_key_value(ctx, ctx->argv[i], table);
+ ctx->error = pre_parse_column_key_value(ctx, ctx->argv[i], table);
+ if (ctx->error) {
+ return;
+ }
}
}
struct table *out;
size_t n_columns;
- table = get_table(table_name);
- parse_column_names(column_names, table, &columns, &n_columns);
+ ctx->error = get_table(table_name, &table);
+ if (ctx->error) {
+ return;
+ }
+ ctx->error = parse_column_names(column_names, table, &columns, &n_columns);
+ if (ctx->error) {
+ return;
+ }
out = ctx->table = list_make_table(columns, n_columns);
for (row = ovsdb_idl_first_row(ctx->idl, table); row;
row = ovsdb_idl_next_row(row)) {
int i;
for (i = 2; i < ctx->argc; i++) {
- if (!is_condition_satisfied(table, row, ctx->argv[i],
- ctx->symtab)) {
+ bool satisfied = false;
+
+ ctx->error = check_condition(table, row, ctx->argv[i],
+ ctx->symtab, &satisfied);
+ if (ctx->error) {
+ free(columns);
+ return;
+ }
+ if (!satisfied) {
goto next_row;
}
}
free(columns);
}
-/* Sets the column of 'row' in 'table'. */
-static void
+/* Sets the column of 'row' in 'table'. Returns NULL on success or a
+ * malloc()'ed error message on failure. */
+static char * OVS_WARN_UNUSED_RESULT
set_column(const struct ovsdb_idl_table_class *table,
const struct ovsdb_idl_row *row, const char *arg,
struct ovsdb_symbol_table *symtab)
{
const struct ovsdb_idl_column *column;
- char *key_string, *value_string;
+ char *key_string = NULL;
+ char *value_string = NULL;
char *error;
error = parse_column_key_value(arg, table, &column, &key_string,
NULL, NULL, 0, &value_string);
- die_if_error(error);
+ if (error) {
+ goto out;
+ }
if (!value_string) {
- ctl_fatal("%s: missing value", arg);
+ error = xasprintf("%s: missing value", arg);
+ goto out;
+ }
+ error = check_mutable(row, column);
+ if (error) {
+ goto out;
}
- check_mutable(row, column);
if (key_string) {
union ovsdb_atom key, value;
struct ovsdb_datum datum;
if (column->type.value.type == OVSDB_TYPE_VOID) {
- ctl_fatal("cannot specify key to set for non-map column %s",
- column->name);
+ error = xasprintf("cannot specify key to set for non-map column "
+ "%s", column->name);
+ goto out;
}
- die_if_error(ovsdb_atom_from_string(&key, NULL, &column->type.key,
- key_string, symtab));
- die_if_error(ovsdb_atom_from_string(&value, NULL, &column->type.value,
- value_string, symtab));
+ error = ovsdb_atom_from_string(&key, NULL, &column->type.key,
+ key_string, symtab);
+ if (error) {
+ goto out;
+ }
+ error = ovsdb_atom_from_string(&value, NULL, &column->type.value,
+ value_string, symtab);
+ if (error) {
+ goto out;
+ }
ovsdb_datum_init_empty(&datum);
ovsdb_datum_add_unsafe(&datum, &key, &value, &column->type, NULL);
} else {
struct ovsdb_datum datum;
- die_if_error(ovsdb_datum_from_string(&datum, &column->type,
- value_string, symtab));
+ error = ovsdb_datum_from_string(&datum, &column->type,
+ value_string, symtab);
+ if (error) {
+ goto out;
+ }
ovsdb_idl_txn_write(row, column, &datum);
}
+out:
free(key_string);
free(value_string);
+
+ return error;
}
static void
const struct ovsdb_idl_table_class *table;
int i;
- table = pre_get_table(ctx, table_name);
+ ctx->error = pre_get_table(ctx, table_name, &table);
+ if (ctx->error) {
+ return;
+ }
for (i = 3; i < ctx->argc; i++) {
- pre_parse_column_key_value(ctx, ctx->argv[i], table);
+ ctx->error = pre_parse_column_key_value(ctx, ctx->argv[i], table);
+ if (ctx->error) {
+ return;
+ }
}
}
const struct ovsdb_idl_row *row;
int i;
- table = get_table(table_name);
- row = ctl_get_row(ctx, table, record_id, must_exist);
+ ctx->error = get_table(table_name, &table);
+ if (ctx->error) {
+ return;
+ }
+ ctx->error = ctl_get_row(ctx, table, record_id, must_exist, &row);
+ if (ctx->error) {
+ return;
+ }
if (!row) {
return;
}
for (i = 3; i < ctx->argc; i++) {
- set_column(table, row, ctx->argv[i], ctx->symtab);
+ ctx->error = set_column(table, row, ctx->argv[i], ctx->symtab);
+ if (ctx->error) {
+ return;
+ }
}
invalidate_cache(ctx);
const struct ovsdb_idl_table_class *table;
const struct ovsdb_idl_column *column;
- table = pre_get_table(ctx, table_name);
- pre_get_column(ctx, table, column_name, &column);
+ ctx->error = pre_get_table(ctx, table_name, &table);
+ if (ctx->error) {
+ return;
+ }
+ ctx->error = pre_get_column(ctx, table, column_name, &column);
+ if (ctx->error) {
+ return;
+ }
}
static void
struct ovsdb_datum old;
int i;
- table = get_table(table_name);
- die_if_error(get_column(table, column_name, &column));
- row = ctl_get_row(ctx, table, record_id, must_exist);
+ ctx->error = get_table(table_name, &table);
+ if (ctx->error) {
+ return;
+ }
+ ctx->error = get_column(table, column_name, &column);
+ if (ctx->error) {
+ return;
+ }
+ ctx->error = ctl_get_row(ctx, table, record_id, must_exist, &row);
+ if (ctx->error) {
+ return;
+ }
if (!row) {
return;
}
- check_mutable(row, column);
+ ctx->error = check_mutable(row, column);
+ if (ctx->error) {
+ return;
+ }
type = &column->type;
ovsdb_datum_clone(&old, ovsdb_idl_read(row, column), &column->type);
add_type = *type;
add_type.n_min = 1;
add_type.n_max = UINT_MAX;
- die_if_error(ovsdb_datum_from_string(&add, &add_type, ctx->argv[i],
- ctx->symtab));
+ ctx->error = ovsdb_datum_from_string(&add, &add_type, ctx->argv[i],
+ ctx->symtab);
+ if (ctx->error) {
+ ovsdb_datum_destroy(&old, &column->type);
+ return;
+ }
ovsdb_datum_union(&old, &add, type, false);
ovsdb_datum_destroy(&add, type);
}
if (old.n > type->n_max) {
- ctl_fatal("\"add\" operation would put %u %s in column %s of "
+ ctl_error(ctx, "\"add\" operation would put %u %s in column %s of "
"table %s but the maximum number is %u",
old.n,
type->value.type == OVSDB_TYPE_VOID ? "values" : "pairs",
column->name, table->name, type->n_max);
+ ovsdb_datum_destroy(&old, &column->type);
+ return;
}
ovsdb_idl_txn_verify(row, column);
ovsdb_idl_txn_write(row, column, &old);
const struct ovsdb_idl_table_class *table;
const struct ovsdb_idl_column *column;
- table = pre_get_table(ctx, table_name);
- pre_get_column(ctx, table, column_name, &column);
+ ctx->error = pre_get_table(ctx, table_name, &table);
+ if (ctx->error) {
+ return;
+ }
+ ctx->error = pre_get_column(ctx, table, column_name, &column);
+ if (ctx->error) {
+ return;
+ }
}
static void
struct ovsdb_datum old;
int i;
- table = get_table(table_name);
- die_if_error(get_column(table, column_name, &column));
- row = ctl_get_row(ctx, table, record_id, must_exist);
+ ctx->error = get_table(table_name, &table);
+ if (ctx->error) {
+ return;
+ }
+ ctx->error = get_column(table, column_name, &column);
+ if (ctx->error) {
+ return;
+ }
+ ctx->error = ctl_get_row(ctx, table, record_id, must_exist, &row);
+ if (ctx->error) {
+ return;
+ }
if (!row) {
return;
}
- check_mutable(row, column);
+ ctx->error = check_mutable(row, column);
+ if (ctx->error) {
+ return;
+ }
type = &column->type;
ovsdb_datum_clone(&old, ovsdb_idl_read(row, column), &column->type);
if (ovsdb_type_is_map(&rm_type)) {
rm_type.value.type = OVSDB_TYPE_VOID;
free(error);
- die_if_error(ovsdb_datum_from_string(
- &rm, &rm_type, ctx->argv[i], ctx->symtab));
+ ctx->error = ovsdb_datum_from_string(&rm, &rm_type,
+ ctx->argv[i],
+ ctx->symtab);
+ if (ctx->error) {
+ ovsdb_datum_destroy(&old, &column->type);
+ return;
+ }
} else {
- ctl_fatal("%s", error);
+ ctx->error = error;
+ ovsdb_datum_destroy(&old, &column->type);
+ return;
}
}
ovsdb_datum_subtract(&old, type, &rm, &rm_type);
ovsdb_datum_destroy(&rm, &rm_type);
}
if (old.n < type->n_min) {
- ctl_fatal("\"remove\" operation would put %u %s in column %s of "
- "table %s but the minimum number is %u",
- old.n,
+ ctl_error(ctx, "\"remove\" operation would put %u %s in column %s of "
+ "table %s but the minimum number is %u", old.n,
type->value.type == OVSDB_TYPE_VOID ? "values" : "pairs",
column->name, table->name, type->n_min);
+ ovsdb_datum_destroy(&old, &column->type);
+ return;
}
ovsdb_idl_txn_verify(row, column);
ovsdb_idl_txn_write(row, column, &old);
const struct ovsdb_idl_table_class *table;
int i;
- table = pre_get_table(ctx, table_name);
+ ctx->error = pre_get_table(ctx, table_name, &table);
+ if (ctx->error) {
+ return;
+ }
for (i = 3; i < ctx->argc; i++) {
const struct ovsdb_idl_column *column;
- pre_get_column(ctx, table, ctx->argv[i], &column);
+ ctx->error = pre_get_column(ctx, table, ctx->argv[i], &column);
+ if (ctx->error) {
+ return;
+ }
}
}
const struct ovsdb_idl_row *row;
int i;
- table = get_table(table_name);
- row = ctl_get_row(ctx, table, record_id, must_exist);
+ ctx->error = get_table(table_name, &table);
+ if (ctx->error) {
+ return;
+ }
+ ctx->error = ctl_get_row(ctx, table, record_id, must_exist, &row);
+ if (ctx->error) {
+ return;
+ }
if (!row) {
return;
}
const struct ovsdb_type *type;
struct ovsdb_datum datum;
- die_if_error(get_column(table, ctx->argv[i], &column));
- check_mutable(row, column);
+ ctx->error = get_column(table, ctx->argv[i], &column);
+ if (ctx->error) {
+ return;
+ }
+ ctx->error = check_mutable(row, column);
+ if (ctx->error) {
+ return;
+ }
type = &column->type;
if (type->n_min > 0) {
- ctl_fatal("\"clear\" operation cannot be applied to column %s "
- "of table %s, which is not allowed to be empty",
+ ctl_error(ctx, "\"clear\" operation cannot be applied to column "
+ "%s of table %s, which is not allowed to be empty",
column->name, table->name);
+ return;
}
ovsdb_datum_init_empty(&datum);
const char *table_name = ctx->argv[1];
const struct ovsdb_idl_table_class *table;
- table = get_table(table_name);
+ ctx->error = get_table(table_name, &table);
+ if (ctx->error) {
+ return;
+ }
if (!id && !table->is_root) {
VLOG_WARN("applying \"create\" command to table %s without --id "
"option will have no effect", table->name);
{
const char *id = shash_find_data(&ctx->options, "--id");
const char *table_name = ctx->argv[1];
- const struct ovsdb_idl_table_class *table = get_table(table_name);
+ const struct ovsdb_idl_table_class *table;
const struct ovsdb_idl_row *row;
- const struct uuid *uuid;
+ const struct uuid *uuid = NULL;
int i;
+ ctx->error = get_table(table_name, &table);
+ if (ctx->error) {
+ return;
+ }
if (id) {
- struct ovsdb_symbol *symbol = create_symbol(ctx->symtab, id, NULL);
+ struct ovsdb_symbol *symbol = NULL;
+
+ ctx->error = create_symbol(ctx->symtab, id, &symbol, NULL);
+ if (ctx->error) {
+ return;
+ }
if (table->is_root) {
/* This table is in the root set, meaning that rows created in it
* won't disappear even if they are unreferenced, so disable
symbol->strong_ref = true;
}
uuid = &symbol->uuid;
- } else {
- uuid = NULL;
}
row = ovsdb_idl_txn_insert(ctx->txn, table, uuid);
for (i = 2; i < ctx->argc; i++) {
- set_column(table, row, ctx->argv[i], ctx->symtab);
+ ctx->error = set_column(table, row, ctx->argv[i], ctx->symtab);
+ if (ctx->error) {
+ return;
+ }
}
ds_put_format(&ctx->output, UUID_FMT, UUID_ARGS(&row->uuid));
}
{
const char *table_name = ctx->argv[1];
- pre_get_table(ctx, table_name);
+ ctx->error = pre_get_table(ctx, table_name, NULL);
+ if (ctx->error) {
+ return;
+ }
}
static void
const struct ovsdb_idl_table_class *table;
int i;
- table = get_table(table_name);
+ ctx->error = get_table(table_name, &table);
+ if (ctx->error) {
+ return;
+ }
if (delete_all && ctx->argc > 2) {
- ctl_fatal("--all and records argument should not be specified together");
+ ctl_error(ctx, "--all and records argument should not be specified "
+ "together");
+ return;
}
if (delete_all && !must_exist) {
- ctl_fatal("--all and --if-exists should not be specified together");
+ ctl_error(ctx, "--all and --if-exists should not be specified "
+ "together");
+ return;
}
if (delete_all) {
for (i = 2; i < ctx->argc; i++) {
const struct ovsdb_idl_row *row;
- row = ctl_get_row(ctx, table, ctx->argv[i], must_exist);
+ ctx->error = ctl_get_row(ctx, table, ctx->argv[i], must_exist,
+ &row);
+ if (ctx->error) {
+ return;
+ }
if (row) {
ovsdb_idl_txn_delete(row);
}
const struct ovsdb_idl_table_class *table;
int i;
- table = pre_get_table(ctx, table_name);
+ ctx->error = pre_get_table(ctx, table_name, &table);
+ if (ctx->error) {
+ return;
+ }
for (i = 3; i < ctx->argc; i++) {
- pre_parse_column_key_value(ctx, ctx->argv[i], table);
+ ctx->error = pre_parse_column_key_value(ctx, ctx->argv[i], table);
+ if (ctx->error) {
+ return;
+ }
}
}
const struct ovsdb_idl_row *row;
int i;
- table = get_table(table_name);
-
- row = ctl_get_row(ctx, table, record_id, false);
+ ctx->error = get_table(table_name, &table);
+ if (ctx->error) {
+ return;
+ }
+ ctx->error = ctl_get_row(ctx, table, record_id, false, &row);
+ if (ctx->error) {
+ return;
+ }
if (!row) {
ctx->try_again = true;
return;
}
for (i = 3; i < ctx->argc; i++) {
- if (!is_condition_satisfied(table, row, ctx->argv[i], ctx->symtab)) {
+ bool satisfied;
+
+ ctx->error = check_condition(table, row, ctx->argv[i], ctx->symtab,
+ &satisfied);
+ if (ctx->error) {
+ return;
+ }
+ if (!satisfied) {
ctx->try_again = true;
return;
}
}
/* Parses one command. */
-static void
+static char * OVS_WARN_UNUSED_RESULT
parse_command(int argc, char *argv[], struct shash *local_options,
struct ctl_command *command)
{
struct shash_node *node;
int n_arg;
int i;
+ char *error;
shash_init(&command->options);
shash_swap(local_options, &command->options);
}
if (shash_find(&command->options, key)) {
- ctl_fatal("'%s' option specified multiple times", argv[i]);
+ free(key);
+ free(value);
+ error = xasprintf("'%s' option specified multiple times", argv[i]);
+ goto error;
}
shash_add_nocopy(&command->options, key, value);
}
if (i == argc) {
- ctl_fatal("missing command name (use --help for help)");
+ error = xstrdup("missing command name (use --help for help)");
+ goto error;
}
p = shash_find_data(&all_commands, argv[i]);
if (!p) {
- ctl_fatal("unknown command '%s'; use --help for help", argv[i]);
+ error = xasprintf("unknown command '%s'; use --help for help",
+ argv[i]);
+ goto error;
}
SHASH_FOR_EACH (node, &command->options) {
int end = s ? s[strlen(node->name)] : EOF;
if (!strchr("=,? ", end)) {
- ctl_fatal("'%s' command has no '%s' option",
- argv[i], node->name);
+ error = xasprintf("'%s' command has no '%s' option",
+ argv[i], node->name);
+ goto error;
}
if (end != '?' && (end == '=') != (node->data != NULL)) {
if (end == '=') {
- ctl_fatal("missing argument to '%s' option on '%s' "
- "command", node->name, argv[i]);
+ error = xasprintf("missing argument to '%s' option on '%s' "
+ "command", node->name, argv[i]);
+ goto error;
} else {
- ctl_fatal("'%s' option on '%s' does not accept an "
- "argument", node->name, argv[i]);
+ error = xasprintf("'%s' option on '%s' does not accept an "
+ "argument", node->name, argv[i]);
+ goto error;
}
}
}
n_arg = argc - i - 1;
if (n_arg < p->min_args) {
- ctl_fatal("'%s' command requires at least %d arguments",
- p->name, p->min_args);
+ error = xasprintf("'%s' command requires at least %d arguments",
+ p->name, p->min_args);
+ goto error;
} else if (n_arg > p->max_args) {
int j;
for (j = i + 1; j < argc; j++) {
if (argv[j][0] == '-') {
- ctl_fatal("'%s' command takes at most %d arguments "
- "(note that options must precede command "
- "names and follow a \"--\" argument)",
- p->name, p->max_args);
+ error = xasprintf("'%s' command takes at most %d arguments "
+ "(note that options must precede command "
+ "names and follow a \"--\" argument)",
+ p->name, p->max_args);
+ goto error;
}
}
- ctl_fatal("'%s' command takes at most %d arguments",
- p->name, p->max_args);
+ error = xasprintf("'%s' command takes at most %d arguments",
+ p->name, p->max_args);
+ goto error;
}
command->syntax = p;
command->argc = n_arg + 1;
command->argv = &argv[i];
+ return NULL;
+
+error:
+ shash_destroy_free_data(&command->options);
+ return error;
}
static void
}
/* Parses command-line input for commands. */
-struct ctl_command *
+char *
ctl_parse_commands(int argc, char *argv[], struct shash *local_options,
- size_t *n_commandsp)
+ struct ctl_command **commandsp, size_t *n_commandsp)
{
struct ctl_command *commands;
size_t n_commands, allocated_commands;
int i, start;
+ char *error;
commands = NULL;
n_commands = allocated_commands = 0;
+ *commandsp = NULL;
+ *n_commandsp = 0;
+
for (start = i = 0; i <= argc; i++) {
if (i == argc || !strcmp(argv[i], "--")) {
if (i > start) {
shash_moved(&c->options);
}
}
- parse_command(i - start, &argv[start], local_options,
- &commands[n_commands++]);
+ error = parse_command(i - start, &argv[start], local_options,
+ &commands[n_commands]);
+ if (error) {
+ struct ctl_command *c;
+
+ for (c = commands; c < &commands[n_commands]; c++) {
+ shash_destroy_free_data(&c->options);
+ }
+ free(commands);
+
+ return error;
+ }
+
+ n_commands++;
} else if (!shash_is_empty(local_options)) {
- ctl_fatal("missing command name (use --help for help)");
+ return xstrdup("missing command name (use --help for help)");
}
start = i + 1;
}
}
if (!n_commands) {
- ctl_fatal("missing command name (use --help for help)");
+ return xstrdup("missing command name (use --help for help)");
}
+ *commandsp = commands;
*n_commandsp = n_commands;
- return commands;
+ return NULL;
}
/* Prints all registered commands. */
return false;
}
+/* Report an error while running in the command context. Caller should return
+ * to its caller immediately after reporting the error. */
+void
+ctl_error(struct ctl_context *ctx, const char *format, ...)
+{
+ va_list args;
+
+ ovs_assert(ctx);
+
+ if (ctx->error) {
+ VLOG_ERR("Discarding unhandled error: %s", ctx->error);
+ free(ctx->error);
+ }
+
+ va_start(args, format);
+ ctx->error = xvasprintf(format, args);
+ va_end(args);
+}
+
void
ctl_fatal(const char *format, ...)
{
ds_swap(&ctx->output, &command->output);
ctx->table = command->table;
ctx->try_again = false;
+ ctx->error = NULL;
}
/* Initializes the entire 'ctx'. */
{
ds_swap(&ctx->output, &command->output);
command->table = ctx->table;
+ free(ctx->error);
+ ctx->error = NULL;
}
/* Finishes up with 'ctx'.
invalidate_cache(ctx);
}
-void ctl_set_column(const char *table_name,
- const struct ovsdb_idl_row *row, const char *arg,
- struct ovsdb_symbol_table *symtab)
+char * OVS_WARN_UNUSED_RESULT
+ctl_set_column(const char *table_name, const struct ovsdb_idl_row *row,
+ const char *arg, struct ovsdb_symbol_table *symtab)
{
- set_column(get_table(table_name), row, arg, symtab);
+ const struct ovsdb_idl_table_class *table;
+ char *error;
+
+ error = get_table(table_name, &table);
+ if (error) {
+ return error;
+ }
+ error = set_column(table, row, arg, symtab);
+ if (error) {
+ return error;
+ }
+
+ return NULL;
}