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;