5aa234473f
Resolves: RHEL-11456, LPM hooks
220 lines
5.7 KiB
Diff
220 lines
5.7 KiB
Diff
commit e0928dc5e5375591a4cff6ffabc6063771288f59
|
|
Author: Laurent Dufour <ldufour@linux.ibm.com>
|
|
Date: Fri Sep 16 18:39:16 2022 +0200
|
|
|
|
drmgr: introducing the hook framework
|
|
|
|
The hook framework run in a sequence any executable file found in the
|
|
relevant directory (/etc/drmgr.d/<DRC TYPE NAME>/)
|
|
|
|
The hook are run according to the versionsort()'s output order.
|
|
|
|
The hook inherits from drmgr its standard I/O streams. All others file
|
|
descriptor should have the close on exec flag set to ensure they will be
|
|
closed when executing an hook.
|
|
|
|
The hooks are run with no arguments, arguments are passed through
|
|
environment variable.
|
|
|
|
The inherited environment is cleaned and 2 environment variables
|
|
are set:
|
|
- DRC_TYPE containing the DRC type string
|
|
- PHASE containing the current phase
|
|
|
|
There are 4 known phases: check, undocheck, pre and post.
|
|
|
|
The hook's run is recorded in the drmgr's log, so blocking hook could be
|
|
identified.
|
|
|
|
Signed-off-by: Laurent Dufour <ldufour@linux.ibm.com>
|
|
Signed-off-by: Tyrel Datwyler <tyreld@linux.ibm.com>
|
|
|
|
diff --git a/src/drmgr/common.c b/src/drmgr/common.c
|
|
index 12af756..9cd91d1 100644
|
|
--- a/src/drmgr/common.c
|
|
+++ b/src/drmgr/common.c
|
|
@@ -49,6 +49,8 @@ char *remove_slot_fname = REMOVE_SLOT_FNAME;
|
|
|
|
#define SYSFS_DLPAR_FILE "/sys/kernel/dlpar"
|
|
|
|
+#define DR_SCRIPT_DIR "/etc/drmgr.d"
|
|
+
|
|
static int dr_lock_fd = 0;
|
|
static long dr_timeout;
|
|
|
|
@@ -67,6 +69,13 @@ static char *drc_type_str[] = {
|
|
[DRC_TYPE_ACC] = "acc",
|
|
};
|
|
|
|
+static char *hook_phase_name[] = {
|
|
+ [HOOK_CHECK] = "check",
|
|
+ [HOOK_UNDOCHECK] = "undocheck",
|
|
+ [HOOK_PRE] = "pre",
|
|
+ [HOOK_POST] = "post",
|
|
+};
|
|
+
|
|
/**
|
|
* set_output level
|
|
* @brief Common routine to set the output level
|
|
@@ -1546,3 +1555,138 @@ enum drc_type to_drc_type(const char *arg)
|
|
return DRC_TYPE_NONE;
|
|
}
|
|
|
|
+static int run_one_hook(enum drc_type drc_type, enum hook_phase phase,
|
|
+ const char *name)
|
|
+{
|
|
+ int rc;
|
|
+ pid_t child;
|
|
+
|
|
+ fflush(NULL);
|
|
+ child = fork();
|
|
+ if (child == -1) {
|
|
+ say(ERROR, "Can't fork to run a hook: %s\n", strerror(errno));
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ if (child) {
|
|
+ /* Father side */
|
|
+ while (waitpid(child, &rc, 0) == -1) {
|
|
+ if (errno == EINTR)
|
|
+ continue;
|
|
+ say(ERROR, "waitpid error: %s\n", strerror(errno));
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ if (WIFSIGNALED(rc)) {
|
|
+ say(INFO, "hook '%s' terminated by signal %d\n",
|
|
+ name, WTERMSIG(rc));
|
|
+ rc = 1;
|
|
+ } else {
|
|
+ rc = WEXITSTATUS(rc);
|
|
+ say(INFO, "hook '%s' exited with status %d\n",
|
|
+ name, rc);
|
|
+ }
|
|
+ return rc;
|
|
+ }
|
|
+
|
|
+
|
|
+ /* Child side */
|
|
+ say(DEBUG, "Running hook '%s' for phase %s (PID=%d)\n",
|
|
+ name, hook_phase_name[phase], getpid());
|
|
+
|
|
+ if (chdir("/")) {
|
|
+ say(ERROR, "Can't change working directory to / : %s\n",
|
|
+ strerror(errno));
|
|
+ exit(255);
|
|
+ }
|
|
+
|
|
+ if (clearenv() ||
|
|
+ setenv("DRC_TYPE", drc_type_str[drc_type], 1) ||
|
|
+ setenv("PHASE", hook_phase_name[phase], 1)) {
|
|
+ say(ERROR, "Can't set environment variables: %s\n",
|
|
+ strerror(errno));
|
|
+ exit(255);
|
|
+ }
|
|
+
|
|
+ execl(name, name, (char *)NULL);
|
|
+ say(ERROR, "Can't exec hook %s : %s\n", strerror(errno));
|
|
+ exit(255);
|
|
+}
|
|
+
|
|
+static int is_file_or_link(const struct dirent *entry)
|
|
+{
|
|
+ if ((entry->d_type == DT_REG) || (entry->d_type == DT_LNK))
|
|
+ return 1;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Run all executable hooks found in a given directory.
|
|
+ * Return 0 if all run script have returned 0 status.
|
|
+ */
|
|
+int run_hooks(enum drc_type drc_type, enum hook_phase phase)
|
|
+{
|
|
+ int rc = 0, fdd, num, i;
|
|
+ DIR *dir;
|
|
+ struct dirent **entries = NULL;
|
|
+
|
|
+ /* Sanity check */
|
|
+ if (drc_type <= DRC_TYPE_NONE || drc_type >= ARRAY_SIZE(drc_type_str)) {
|
|
+ say(ERROR, "Invalid DRC TYPE detected (%d)\n", drc_type);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ if (phase < HOOK_CHECK || phase > HOOK_POST) {
|
|
+ say(ERROR, "Invalid hook phase %d\n", phase);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ dir = opendir(DR_SCRIPT_DIR);
|
|
+ if (dir == NULL) {
|
|
+ if (errno == ENOENT)
|
|
+ return 0;
|
|
+ say(ERROR, "Can't open %s: %s\n", DR_SCRIPT_DIR,
|
|
+ strerror(errno));
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ fdd = dirfd(dir);
|
|
+ num = scandirat(fdd, drc_type_str[drc_type], &entries,
|
|
+ is_file_or_link, versionsort);
|
|
+ closedir(dir);
|
|
+
|
|
+ for (i = 0; i < num; i++) {
|
|
+ struct stat st;
|
|
+ struct dirent *entry = entries[i];
|
|
+ char *name;
|
|
+
|
|
+ if (asprintf(&name, "%s/%s/%s", DR_SCRIPT_DIR,
|
|
+ drc_type_str[drc_type], entry->d_name) == -1) {
|
|
+ say(ERROR,
|
|
+ "Can't allocate filename string (%zd bytes)\n",
|
|
+ strlen(DR_SCRIPT_DIR) + 1 +
|
|
+ strlen(drc_type_str[drc_type]) + 1 +
|
|
+ strlen(entry->d_name) + 1);
|
|
+ rc = 1;
|
|
+ free(entry);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Report error only in the case the hook itself fails.
|
|
+ * Any other error (file is not executable etc.) is ignored.
|
|
+ */
|
|
+ if (stat(name, &st))
|
|
+ say(WARN, "Can't stat file %s: %s\n",
|
|
+ name, strerror(errno));
|
|
+ else if (S_ISREG(st.st_mode) && (st.st_mode & S_IXUSR) &&
|
|
+ run_one_hook(drc_type, phase, name))
|
|
+ rc = 1;
|
|
+
|
|
+ free(name);
|
|
+ free(entry);
|
|
+ }
|
|
+
|
|
+ free(entries);
|
|
+ return rc;
|
|
+}
|
|
diff --git a/src/drmgr/dr.h b/src/drmgr/dr.h
|
|
index 58fdb5c..5526c29 100644
|
|
--- a/src/drmgr/dr.h
|
|
+++ b/src/drmgr/dr.h
|
|
@@ -70,6 +70,8 @@ enum drc_type {DRC_TYPE_NONE, DRC_TYPE_PCI, DRC_TYPE_SLOT, DRC_TYPE_PHB,
|
|
DRC_TYPE_CPU, DRC_TYPE_MEM, DRC_TYPE_PORT,
|
|
DRC_TYPE_HIBERNATE, DRC_TYPE_MIGRATION, DRC_TYPE_ACC};
|
|
|
|
+enum hook_phase {HOOK_CHECK, HOOK_UNDOCHECK, HOOK_PRE, HOOK_POST};
|
|
+
|
|
extern enum drmgr_action usr_action;
|
|
extern int display_capabilities;
|
|
extern int usr_slot_identification;
|
|
@@ -133,6 +135,8 @@ void print_dlpar_capabilities(void);
|
|
|
|
void set_output_level(int);
|
|
|
|
+int run_hooks(enum drc_type drc_type, enum hook_phase phase);
|
|
+
|
|
#define DR_BUF_SZ 256
|
|
|
|
int drslot_chrp_slot(void);
|