mirror of
https://github.com/ostreedev/ostree.git
synced 2024-12-22 17:35:55 +03:00
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`.
This commit is contained in:
parent
365559eaa8
commit
513b3c09a5
@ -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 \
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -23,6 +23,7 @@
|
||||
|
||||
#include <gio/gio.h>
|
||||
|
||||
#include <locale.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/statvfs.h>
|
||||
@ -140,6 +141,102 @@ message_handler (const gchar *log_domain,
|
||||
g_printerr ("%s: %s\n", g_get_prgname (), message);
|
||||
}
|
||||
|
||||
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,
|
||||
|
@ -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,
|
||||
|
27
tests/test-cli-extensions.sh
Executable file
27
tests/test-cli-extensions.sh
Executable file
@ -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'
|
Loading…
Reference in New Issue
Block a user