Mike Waychison
2011-Aug-03 19:38 UTC
[klibc] [PATCH v3 0/2] Support drop directories directly from kinit
This patchset applies to klibc mainline.
This patchset introduces the ability to kinit to execute scripts or
executable files present in in the initramfs before switching over to
the root filesystem.
This functionality is implemented in a newly introduced run_parts()
call, which calls scandir() to iterate through files which in then
executes in sequence.
run_parts() is also available as a minimal run-parts executable (that
doesn't yet take any flags).
This patchset then introduces two different drop directories, though
this is of course subject to change and these are only presented in an
effort to put an example forward. I currently only have a requirement
to run stuff between the time we call do_mounts() and the time we call
run_init().
These are the directories:
/scripts/after-network: ipconfig is completed, but the root
filesystem isn't yet mounted.
/scripts/after-mount: the root filesystem has just been mounted at
/root.
I believe this would help both our use-case (where we'd like to do
customization of the early-bootup sequence without having to hack kinit
too much), and the use case for initramfs-tools, opening the door to
replace all the "core" shell there with kinit as a C implementation.
Thanks,
Mike Waychison
Changelog
========v3
- Renamed run_scripts() to run_parts().
- Introduced the build of a standalone run-parts binary.
- Dropped the alphasort and scandir parts of the series as they are
now merged.
v2
- Added __extern to alphasort declaration.
- Split alphasort() out into alphasort.c and its own patch.
Related discussions
================== - We recently had a discussion at:
http://www.zytor.com/pipermail/klibc/2011-July/003014.html
where I wanted to refactor bits of kinit out so that I could
invoke them from a shell script. This patchset is an alternative
solution that would allow users of kinit to add custom logic at
the intermediate stages without having to have the whole init
driven by a shell script.
- earlier sendouts:
v1: http://www.zytor.com/pipermail/klibc/2011-July/003032.html
v2: http://www.zytor.com/pipermail/klibc/2011-August/003055.html
Patchset summary
===============
kinit: Add run_parts()
kinit: Add callsites to execute files in drop-directories.
This patch implements support for executing files present in drop
directories. This is implemented by a new function called run_parts,
which takes the name of the base drop-directory to search for scripts.
It is implemented by simply doing a single-depth search for files
present in the directory using scandir(). It then will synchronously
execute each file in order. Failure to execute anything that looks like
a file is treated as a fatal error, which should be easy to catch as
these directories are essentially configuration directories and failure
to execute should be immediately visible to developers.
run_parts() is available as a standalone binary named run-parts, which
for the moment only ever accepts a single argument, the directory to
search for parts to run.
Signed-off-by: Mike Waychison <mikew at google.com>
---
usr/kinit/Kbuild | 11 ++-
usr/kinit/kinit.h | 2 +
usr/kinit/run-parts/Kbuild | 22 +++++++
usr/kinit/run-parts/run-parts.c | 125 +++++++++++++++++++++++++++++++++++++++
4 files changed, 155 insertions(+), 5 deletions(-)
create mode 100644 usr/kinit/run-parts/Kbuild
create mode 100644 usr/kinit/run-parts/run-parts.c
diff --git a/usr/kinit/Kbuild b/usr/kinit/Kbuild
index ff1d449..849cb02 100644
--- a/usr/kinit/Kbuild
+++ b/usr/kinit/Kbuild
@@ -16,6 +16,7 @@ kinit-y += nfsmount/
kinit-y += run-init/
kinit-y += fstype/
kinit-y += resume/
+kinit-y += run-parts/
static-y := kinit
shared-y := kinit.shared
@@ -24,14 +25,14 @@ kinit.shared-y := $(kinit-y)
# Additional include paths files
KLIBCCFLAGS += -I$(srctree)/$(src)/fstype \
-I$(srctree)/$(src)/ipconfig \
- -I$(srctree)/$(src)/nfsmount \
- -I$(srctree)/$(src)/resume \
- -I$(srctree)/$(src)/run-init
+ -I$(srctree)/$(src)/nfsmount \
+ -I$(srctree)/$(src)/resume \
+ -I$(srctree)/$(src)/run-init \
+ -I$(srctree)/$(src)/run-parts \
# Cleaning
targets += kinit kinit.g kinit.shared kinit.shared.g
-subdir- := fstype ipconfig nfsmount resume run-init
-
+subdir- := fstype ipconfig nfsmount resume run-init run-parts
# install binary
install-y := kinit kinit.shared
diff --git a/usr/kinit/kinit.h b/usr/kinit/kinit.h
index c2e67b7..a7a101e 100644
--- a/usr/kinit/kinit.h
+++ b/usr/kinit/kinit.h
@@ -65,4 +65,6 @@ static inline void dump_args(int argc, char *argv[])
}
#endif
+void run_parts(const char *, int, char **);
+
#endif /* KINIT_H */
diff --git a/usr/kinit/run-parts/Kbuild b/usr/kinit/run-parts/Kbuild
new file mode 100644
index 0000000..9d16a9e
--- /dev/null
+++ b/usr/kinit/run-parts/Kbuild
@@ -0,0 +1,22 @@
+#
+# Kbuild file for run-parts
+#
+
+static-y := static/run-parts
+shared-y := shared/run-parts
+
+# common .o files
+objs := run-parts.o
+
+# Create built-in.o with all object files (used by kinit)
+lib-y := $(objs)
+
+# .o files used to built executables
+static/run-parts-y := $(objs)
+shared/run-parts-y := $(objs)
+
+# Cleaning
+clean-dirs := static shared
+
+# install binary
+install-y := $(shared-y)
diff --git a/usr/kinit/run-parts/run-parts.c b/usr/kinit/run-parts/run-parts.c
new file mode 100644
index 0000000..4201d19
--- /dev/null
+++ b/usr/kinit/run-parts/run-parts.c
@@ -0,0 +1,125 @@
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <dirent.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static int is_file(const struct dirent *dirent)
+{
+ return (dirent->d_type == DT_REG);
+}
+
+static void run_part(const char *basepath, const char *filename,
+ int cmdc, char **cmdv)
+{
+ char *fullpath;
+ char **args;
+ pid_t pid;
+ int status;
+ int ret;
+ int i;
+
+ /* Figure out fullpath */
+ fullpath = malloc(strlen(basepath) + strlen(filename) + 2);
+ if (!fullpath) {
+ fprintf(stderr, "Ran out of memory!\n");
+ exit(1);
+ }
+ sprintf(fullpath, "%s/%s", basepath, filename);
+
+ /* Prepare args for call */
+ args = malloc(sizeof(*args) * (cmdc + 2)); /* NULL + ARGV[0} */
+ if (!args) {
+ fprintf(stderr, "Ran out of memory!\n");
+ free(fullpath);
+ exit(1);
+ }
+ args[0] = fullpath;
+ for (i = 0; i < cmdc; i++)
+ args[i + 1] = cmdv[i];
+ args[cmdc + 1] = NULL;
+
+ /* Run the child */
+ pid = fork();
+ if (pid == -1) {
+ fprintf(stderr, "Failed to fork in run-parts(%s)\n",
+ basepath);
+ exit(1);
+ }
+ if (pid == 0) {
+ /* Child */
+ execv(fullpath, args);
+ fprintf(stderr, "Failed to exec %s: %s\n", fullpath,
+ strerror(errno));
+ exit(1);
+ }
+
+ /* Parent */
+ while (1) {
+ ret = waitpid(pid, &status, 0);
+ if (ret == pid)
+ break;
+ if (ret == -1 && errno == EINTR)
+ continue;
+ fprintf(stderr, "failed to wait on child: ret=%d errno=%d\n",
+ ret, errno);
+ exit(1);
+ }
+
+ if (WIFEXITED(status)) {
+ if (WEXITSTATUS(status) != 0)
+ fprintf(stderr, "%s exited with status: %d\n",
+ fullpath, WEXITSTATUS(status));
+ } else if (WIFSIGNALED(status)) {
+ fprintf(stderr, "%s terminated by signal %d\n",
+ fullpath, WTERMSIG(status));
+ } else {
+ fprintf(stderr, "Failed to understand status code 0x%x\n",
+ status);
+ }
+
+ free(args);
+ free(fullpath);
+}
+
+void run_parts(const char *basepath, int cmdc, char **cmdv)
+{
+ struct dirent **namelist;
+ int i;
+ int count;
+
+ count = scandir(basepath, &namelist, is_file, alphasort);
+ if (count < 0) {
+ if (errno == ENOENT)
+ return;
+ fprintf(stderr, "WARNING: could not run-parts(%s): %s\n",
+ basepath, strerror(errno));
+ return;
+ }
+
+ for (i = 0; i < count; i++) {
+ run_part(basepath, namelist[i]->d_name, cmdc, cmdv);
+ free(namelist[i]);
+ }
+ free(namelist);
+}
+
+int main(int argc, char *argv[])
+ __attribute__ ((weak, alias("run_parts_main")));
+
+static int run_parts_main(int argc, char **argv)
+{
+ if (argc != 2) {
+ fprintf(stderr, "usage: run-parts DIRECTORY\n");
+ return EXIT_FAILURE;
+ }
+
+ /* TODO: Handle --arg and build an argument vector. */
+
+ run_parts(argv[1], 0, NULL);
+
+ return EXIT_SUCCESS;
+}
Mike Waychison
2011-Aug-03 19:38 UTC
[klibc] [PATCH v3 2/2] kinit: Add callsites to execute files in drop-directories.
This patch adds two callsites where kinit will go off and execute
executable files in hard-coded drop-directories.
We introduce a drop-directory at
/scripts/after-network: ipconfig is completed, but the root
filesystem isn't yet mounted.
/scripts/after-mount: the root filesystem is mounted at /root.
Each part executed is passed all of the command line flags in their
argv.
Signed-off-by: Mike Waychison <mikew at google.com>
---
usr/kinit/kinit.c | 4 ++++
1 files changed, 4 insertions(+), 0 deletions(-)
diff --git a/usr/kinit/kinit.c b/usr/kinit/kinit.c
index 4a1f40b..a824520 100644
--- a/usr/kinit/kinit.c
+++ b/usr/kinit/kinit.c
@@ -285,9 +285,13 @@ int main(int argc, char *argv[])
/* Initialize networking, if applicable */
do_ipconfig(cmdc, cmdv);
+ run_parts("/scripts/after-ipconfig", cmdc, cmdv);
+
check_path("/root");
do_mounts(cmdc, cmdv);
+ run_parts("/scripts/after-mount", cmdc, cmdv);
+
if (mnt_procfs) {
umount2("/proc", 0);
mnt_procfs = 0;