summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuca BRUNO <luca.bruno@coreos.com>2021-12-20 10:00:02 +0000
committerLuca BRUNO <luca.bruno@coreos.com>2021-12-20 10:00:02 +0000
commit513b3c09a54af31ffd1b0eb9b3c47849816483be (patch)
treed8fc230e2dd16fb0e88f0d4789f951a74a9cd234
parent365559eaa8126d60366b3f69585268dd89ce3a3a (diff)
downloadostree-513b3c09a54af31ffd1b0eb9b3c47849816483be.tar.gz
main: add support for CLI extensions via external binaries
This adds some logic to detect and dispatch unknown subcommands to extensions available in `$PATH`. Additional commands can be implemented by adding relevant `ostree-$verb` binaries to the system. As an example, if a `/usr/bin/ostree-extcommand` extension is provided, the execution of `ostree extcommand --help` will be dispatched to that as `ostree-extcommand extcommand --help`.
-rw-r--r--Makefile-tests.am1
-rw-r--r--src/ostree/main.c25
-rw-r--r--src/ostree/ot-main.c97
-rw-r--r--src/ostree/ot-main.h6
-rwxr-xr-xtests/test-cli-extensions.sh27
5 files changed, 140 insertions, 16 deletions
diff --git a/Makefile-tests.am b/Makefile-tests.am
index 69d3035d..6bae65cf 100644
--- a/Makefile-tests.am
+++ b/Makefile-tests.am
@@ -62,6 +62,7 @@ _installed_or_uninstalled_test_scripts = \
tests/test-basic-user.sh \
tests/test-basic-user-only.sh \
tests/test-basic-root.sh \
+ tests/test-cli-extensions.sh \
tests/test-pull-subpath.sh \
tests/test-archivez.sh \
tests/test-remote-add.sh \
diff --git a/src/ostree/main.c b/src/ostree/main.c
index 0e47ede3..7d17080c 100644
--- a/src/ostree/main.c
+++ b/src/ostree/main.c
@@ -26,7 +26,6 @@
#include <errno.h>
#include <string.h>
#include <unistd.h>
-#include <locale.h>
#include "ot-main.h"
#include "ot-builtins.h"
@@ -131,22 +130,16 @@ int
main (int argc,
char **argv)
{
- g_autoptr(GError) error = NULL;
- int ret;
+ g_assert (argc > 0);
- setlocale (LC_ALL, "");
-
- g_set_prgname (argv[0]);
-
- ret = ostree_run (argc, argv, commands, &error);
-
- if (error != NULL)
+ g_autofree gchar *ext_command = ostree_command_lookup_external (argc, argv, commands);
+ if (ext_command != NULL)
{
- g_printerr ("%s%serror:%s%s %s\n",
- ot_get_red_start (), ot_get_bold_start (),
- ot_get_bold_end (), ot_get_red_end (),
- error->message);
+ argv[0] = ext_command;
+ return ostree_command_exec_external (argv);
+ }
+ else
+ {
+ return ostree_main (argc, argv, commands);
}
-
- return ret;
}
diff --git a/src/ostree/ot-main.c b/src/ostree/ot-main.c
index 017a65a1..8ee73038 100644
--- a/src/ostree/ot-main.c
+++ b/src/ostree/ot-main.c
@@ -23,6 +23,7 @@
#include <gio/gio.h>
+#include <locale.h>
#include <stdlib.h>
#include <string.h>
#include <sys/statvfs.h>
@@ -141,6 +142,102 @@ message_handler (const gchar *log_domain,
}
int
+ostree_main (int argc,
+ char **argv,
+ OstreeCommand *commands)
+{
+ g_autoptr(GError) error = NULL;
+
+ setlocale (LC_ALL, "");
+
+ g_set_prgname (argv[0]);
+
+ int ret = ostree_run (argc, argv, commands, &error);
+
+ if (error != NULL)
+ {
+ g_printerr ("%s%serror:%s%s %s\n",
+ ot_get_red_start (), ot_get_bold_start (),
+ ot_get_bold_end (), ot_get_red_end (),
+ error->message);
+ }
+
+ return ret;
+}
+
+
+/**
+ * ostree_command_lookup_external:
+ * @argc: number of entries in @argv
+ * @argv: array of command-line arguments
+ * @commands: array of hardcoded internal commands
+ *
+ * Search for a relevant ostree extension binary in $PATH. Given a verb
+ * from argv, if it is not an internal command, it tries to locate a
+ * corresponding 'ostree-$verb' executable on the system.
+ *
+ * Returns: (transfer full) (nullable): callname (i.e. argv[0]) for the
+ * external command if found, or %NULL otherwise.
+ */
+gchar *
+ostree_command_lookup_external (int argc,
+ char **argv,
+ OstreeCommand *commands)
+{
+ g_assert (commands != NULL);
+
+ // Find the first verb (ignoring all earlier flags), then
+ // check if it is a known native command. Otherwise, try to look it
+ // up in $PATH.
+ // We ignore argv[0] here, the ostree binary itself is not multicall.
+ for (guint arg_index = 1; arg_index < argc; arg_index++)
+ {
+ char *current_arg = argv[arg_index];
+ if (current_arg == NULL ||
+ g_str_has_prefix (current_arg, "-") ||
+ g_strcmp0 (current_arg, "") == 0)
+ continue;
+
+ for (guint cmd_index = 0; commands[cmd_index].name != NULL; cmd_index++)
+ {
+ if (g_strcmp0 (current_arg, (commands[cmd_index]).name) == 0)
+ return NULL;
+ }
+
+ g_autofree gchar *ext_command = g_strdup_printf ("ostree-%s", current_arg);
+ if (g_find_program_in_path (ext_command) == NULL)
+ return NULL;
+
+ return g_steal_pointer (&ext_command);
+ }
+
+ return NULL;
+}
+
+/**
+ * ostree_command_exec_external:
+ * @argv: array of command-line arguments
+ *
+ * Execute an ostree extension binary.
+ *
+ * Returns: diverge on proper execution, otherwise return 1.
+ */
+int
+ostree_command_exec_external (char **argv)
+{
+ int r = execvp(argv[0], argv);
+ g_assert (r == -1);
+
+ setlocale (LC_ALL, "");
+ g_printerr ("%s%serror:%s%s: Executing %s: %s\n",
+ ot_get_red_start (), ot_get_bold_start (),
+ ot_get_bold_end (), ot_get_red_end (),
+ argv[0],
+ g_strerror (errno));
+ return 1;
+}
+
+int
ostree_run (int argc,
char **argv,
OstreeCommand *commands,
diff --git a/src/ostree/ot-main.h b/src/ostree/ot-main.h
index ed06e621..b369deb8 100644
--- a/src/ostree/ot-main.h
+++ b/src/ostree/ot-main.h
@@ -58,10 +58,16 @@ struct OstreeCommandInvocation {
OstreeCommand *command;
};
+int ostree_main (int argc, char **argv, OstreeCommand *commands);
+
int ostree_run (int argc, char **argv, OstreeCommand *commands, GError **error);
int ostree_usage (OstreeCommand *commands, gboolean is_error);
+char* ostree_command_lookup_external (int argc, char **argv, OstreeCommand *commands);
+
+int ostree_command_exec_external (char **argv);
+
gboolean ostree_parse_sysroot_or_repo_option (GOptionContext *context,
const char *sysroot_path,
const char *repo_path,
diff --git a/tests/test-cli-extensions.sh b/tests/test-cli-extensions.sh
new file mode 100755
index 00000000..6e483c5e
--- /dev/null
+++ b/tests/test-cli-extensions.sh
@@ -0,0 +1,27 @@
+#!/usr/bin/env bash
+#
+# Copyright (C) 2021 Red Hat Inc.
+# SPDX-License-Identifier: LGPL-2.0+
+
+set -euo pipefail
+
+. $(dirname $0)/libtest.sh
+
+echo '1..2'
+
+mkdir -p ./localbin
+export PATH="./localbin/:${PATH}"
+ln -s /usr/bin/env ./localbin/ostree-env
+${CMD_PREFIX} ostree env --help >out.txt
+assert_file_has_content out.txt "with an empty environment"
+rm -rf -- localbin
+
+echo 'ok CLI extension localbin ostree-env'
+
+if ${CMD_PREFIX} ostree nosuchcommand 2>err.txt; then
+ assert_not_reached "missing CLI extension ostree-nosuchcommand succeeded"
+fi
+assert_file_has_content err.txt "Unknown command 'nosuchcommand'"
+rm -f -- err.txt
+
+echo 'ok CLI extension unknown ostree-nosuchcommand'