virhook: support hooks placed in $driver.d/
It is easier for management software (and subsequently distributions) to install hook script under /etc/libvirt/hooks/$driver.d/ and have libvirt execute them in alphabetical order. To maintain backwards compatibility, /etc/libvirt/hooks/$driver hook script is executed the first followed by scripts from the $driver.d directory. The stdio is chained between the scripts. The output of the first script is input of the second and so on. Signed-off-by: Dmitry Nesterenko <dmitry.nesterenko@virtuozzo.com> Signed-off-by: Michal Privoznik <mprivozn@redhat.com> Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
This commit is contained in:
parent
841910b5de
commit
feb83c1e71
@ -33,6 +33,7 @@
|
|||||||
#include "virfile.h"
|
#include "virfile.h"
|
||||||
#include "configmake.h"
|
#include "configmake.h"
|
||||||
#include "vircommand.h"
|
#include "vircommand.h"
|
||||||
|
#include "virstring.h"
|
||||||
|
|
||||||
#define VIR_FROM_THIS VIR_FROM_HOOK
|
#define VIR_FROM_THIS VIR_FROM_HOOK
|
||||||
|
|
||||||
@ -141,7 +142,11 @@ static int virHooksFound = -1;
|
|||||||
static int
|
static int
|
||||||
virHookCheck(int no, const char *driver)
|
virHookCheck(int no, const char *driver)
|
||||||
{
|
{
|
||||||
|
int ret;
|
||||||
|
DIR *dir;
|
||||||
|
struct dirent *entry;
|
||||||
g_autofree char *path = NULL;
|
g_autofree char *path = NULL;
|
||||||
|
g_autofree char *dir_path = NULL;
|
||||||
|
|
||||||
if (driver == NULL) {
|
if (driver == NULL) {
|
||||||
virReportError(VIR_ERR_INTERNAL_ERROR,
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
||||||
@ -158,16 +163,39 @@ virHookCheck(int no, const char *driver)
|
|||||||
|
|
||||||
if (!virFileExists(path)) {
|
if (!virFileExists(path)) {
|
||||||
VIR_DEBUG("No hook script %s", path);
|
VIR_DEBUG("No hook script %s", path);
|
||||||
return 0;
|
} else if (!virFileIsExecutable(path)) {
|
||||||
}
|
|
||||||
|
|
||||||
if (!virFileIsExecutable(path)) {
|
|
||||||
VIR_WARN("Non-executable hook script %s", path);
|
VIR_WARN("Non-executable hook script %s", path);
|
||||||
|
} else {
|
||||||
|
VIR_DEBUG("Found hook script %s", path);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
dir_path = g_strdup_printf("%s.d", path);
|
||||||
|
if ((ret = virDirOpenIfExists(&dir, dir_path)) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (!ret) {
|
||||||
|
VIR_DEBUG("No hook script dir %s", dir_path);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
VIR_DEBUG("Found hook script %s", path);
|
while ((ret = virDirRead(dir, &entry, dir_path)) > 0) {
|
||||||
return 1;
|
g_autofree char *entry_path = g_build_filename(dir_path,
|
||||||
|
entry->d_name,
|
||||||
|
NULL);
|
||||||
|
if (!virFileIsExecutable(entry_path)) {
|
||||||
|
VIR_WARN("Non-executable hook script %s", entry_path);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
VIR_DEBUG("Found hook script %s", entry_path);
|
||||||
|
ret = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
VIR_DIR_CLOSE(dir);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -282,12 +310,18 @@ virRunScript(const char *path,
|
|||||||
* @input: extra input given to the script on stdin
|
* @input: extra input given to the script on stdin
|
||||||
* @output: optional address of variable to store malloced result buffer
|
* @output: optional address of variable to store malloced result buffer
|
||||||
*
|
*
|
||||||
* Implement a hook call, where the external script for the driver is
|
* Implement a hook call, where the external scripts for the driver are
|
||||||
* called with the given information. This is a synchronous call, we wait for
|
* called with the given information. This is a synchronous call, we wait for
|
||||||
* execution completion. If @output is non-NULL, *output is guaranteed to be
|
* execution completion. If @output is non-NULL, *output is guaranteed to be
|
||||||
* allocated after successful virHookCall, and is best-effort allocated after
|
* allocated after successful virHookCall, and is best-effort allocated after
|
||||||
* failed virHookCall; the caller is responsible for freeing *output.
|
* failed virHookCall; the caller is responsible for freeing *output.
|
||||||
*
|
*
|
||||||
|
* The script from LIBVIRT_HOOK_DIR is executed the first, followed by scripts
|
||||||
|
* found under "$driver.d/" directory (sorted alphabetically. If output from
|
||||||
|
* the hook script is expected, then the output produced by LIBVIRT_HOOK_DIR
|
||||||
|
* script is fed as input to the first script from the "$driver.d/" directory
|
||||||
|
* and its output is fed as input to the second and so on.
|
||||||
|
*
|
||||||
* Returns: 0 if the execution succeeded, 1 if the script was not found or
|
* Returns: 0 if the execution succeeded, 1 if the script was not found or
|
||||||
* invalid parameters, and -1 if script returned an error
|
* invalid parameters, and -1 if script returned an error
|
||||||
*/
|
*/
|
||||||
@ -300,11 +334,16 @@ virHookCall(int driver,
|
|||||||
const char *input,
|
const char *input,
|
||||||
char **output)
|
char **output)
|
||||||
{
|
{
|
||||||
|
int ret, script_ret;
|
||||||
|
DIR *dir;
|
||||||
|
struct dirent *entry;
|
||||||
g_autofree char *path = NULL;
|
g_autofree char *path = NULL;
|
||||||
g_autoptr(virCommand) cmd = NULL;
|
g_autofree char *dir_path = NULL;
|
||||||
|
VIR_AUTOSTRINGLIST entries = NULL;
|
||||||
const char *drvstr;
|
const char *drvstr;
|
||||||
const char *opstr;
|
const char *opstr;
|
||||||
const char *subopstr;
|
const char *subopstr;
|
||||||
|
size_t i, nentries;
|
||||||
|
|
||||||
if (output)
|
if (output)
|
||||||
*output = NULL;
|
*output = NULL;
|
||||||
@ -368,6 +407,61 @@ virHookCall(int driver,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return virRunScript(path, id, opstr, subopstr, extra,
|
script_ret = 1;
|
||||||
input, output);
|
|
||||||
|
if (virFileIsExecutable(path)) {
|
||||||
|
script_ret = virRunScript(path, id, opstr, subopstr, extra,
|
||||||
|
input, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
dir_path = g_strdup_printf("%s.d", path);
|
||||||
|
if ((ret = virDirOpenIfExists(&dir, dir_path)) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (!ret)
|
||||||
|
return script_ret;
|
||||||
|
|
||||||
|
while ((ret = virDirRead(dir, &entry, dir_path)) > 0) {
|
||||||
|
g_autofree char *entry_path = g_build_filename(dir_path,
|
||||||
|
entry->d_name,
|
||||||
|
NULL);
|
||||||
|
if (!virFileIsExecutable(entry_path))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
virStringListAdd(&entries, entry_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
VIR_DIR_CLOSE(dir);
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (!entries)
|
||||||
|
return script_ret;
|
||||||
|
|
||||||
|
nentries = virStringListLength((const char **)entries);
|
||||||
|
qsort(entries, nentries, sizeof(*entries), virStringSortCompare);
|
||||||
|
|
||||||
|
for (i = 0; i < nentries; i++) {
|
||||||
|
int entry_ret;
|
||||||
|
const char *entry_input;
|
||||||
|
g_autofree char *entry_output = NULL;
|
||||||
|
|
||||||
|
/* Get input from previous output */
|
||||||
|
entry_input = (!script_ret && output &&
|
||||||
|
!virStringIsEmpty(*output)) ? *output : input;
|
||||||
|
entry_ret = virRunScript(entries[i], id, opstr,
|
||||||
|
subopstr, extra, entry_input,
|
||||||
|
(output) ? &entry_output : NULL);
|
||||||
|
if (entry_ret < script_ret)
|
||||||
|
script_ret = entry_ret;
|
||||||
|
|
||||||
|
/* Replace output to new output from item */
|
||||||
|
if (!entry_ret && output && !virStringIsEmpty(entry_output)) {
|
||||||
|
g_free(*output);
|
||||||
|
*output = g_steal_pointer(&entry_output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return script_ret;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user