powerpc-utils/SOURCES/powerpc-utils-e0928dc5e5375...

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);