X-Git-Url: http://git.annexia.org/?a=blobdiff_plain;f=helper%2Fkernel.c;h=6a68e5551e719bc528cb9c97840bb47db33ab0ea;hb=7189eb48dd8f46049d982f1d99e158609409019f;hp=33018263de5bbf0ab3a252d31143d5dc54ad7450;hpb=e23a9c8f05e3646feb826d5db36d8656a80a27ab;p=febootstrap.git diff --git a/helper/kernel.c b/helper/kernel.c index 3301826..6a68e55 100644 --- a/helper/kernel.c +++ b/helper/kernel.c @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -77,6 +78,9 @@ has_modpath (const char *kernel_name) } } +static const char *create_kernel_archlinux (const char *hostcpu, const char *kernel); +static const char *create_kernel_from_env (const char *hostcpu, const char *kernel, const char *kernel_env, const char *modpath_env); + /* Create the kernel. This chooses an appropriate kernel and makes a * symlink to it. * @@ -94,7 +98,16 @@ has_modpath (const char *kernel_name) const char * create_kernel (const char *hostcpu, const char *kernel) { - char **all_files = read_dir (KERNELDIR); + /* Override kernel selection using environment variables? */ + char *kernel_env = getenv ("FEBOOTSTRAP_KERNEL"); + if (kernel_env) { + char *modpath_env = getenv ("FEBOOTSTRAP_MODULES"); + return create_kernel_from_env (hostcpu, kernel, kernel_env, modpath_env); + } + + /* In ArchLinux, kernel is always named /boot/vmlinuz26. */ + if (access ("/boot/vmlinuz26", F_OK) == 0) + return create_kernel_archlinux (hostcpu, kernel); /* In original: ls -1dvr /boot/vmlinuz-*.$arch* 2>/dev/null | grep -v xen */ const char *patt; @@ -104,6 +117,7 @@ create_kernel (const char *hostcpu, const char *kernel) else patt = xasprintf ("vmlinuz-*.%s*", hostcpu); + char **all_files = read_dir (KERNELDIR); char **candidates; candidates = filter_fnmatch (all_files, patt, FNM_NOESCAPE); candidates = filter_notmatching_substring (candidates, "xen"); @@ -122,16 +136,18 @@ create_kernel (const char *hostcpu, const char *kernel) sort (candidates, reverse_filevercmp); - /* Choose the first candidate. */ - char *tmp = xasprintf (KERNELDIR "/%s", candidates[0]); + if (kernel) { + /* Choose the first candidate. */ + char *tmp = xasprintf (KERNELDIR "/%s", candidates[0]); - if (verbose) - fprintf (stderr, "creating symlink %s -> %s\n", kernel, tmp); + if (verbose >= 2) + fprintf (stderr, "creating symlink %s -> %s\n", kernel, tmp); - if (symlink (tmp, kernel) == -1) - error (EXIT_FAILURE, errno, "symlink kernel"); + if (symlink (tmp, kernel) == -1) + error (EXIT_FAILURE, errno, "symlink kernel"); - free (tmp); + free (tmp); + } return get_modpath (candidates[0]); @@ -146,3 +162,117 @@ create_kernel (const char *hostcpu, const char *kernel) "febootstrap use, you shouldn't boot the Xen guest with it).\n"); exit (EXIT_FAILURE); } + +/* In ArchLinux, kernel is always named /boot/vmlinuz26, and we have + * to use the 'file' command to work out what version it is. + */ +static const char * +create_kernel_archlinux (const char *hostcpu, const char *kernel) +{ + const char *file_cmd = "file /boot/vmlinuz26 | awk '{print $9}'"; + FILE *pp; + char modversion[256]; + char *modpath; + size_t len; + + pp = popen (file_cmd, "r"); + if (pp == NULL) { + error: + fprintf (stderr, "febootstrap-supermin-helper: %s: command failed\n", + file_cmd); + exit (EXIT_FAILURE); + } + + if (fgets (modversion, sizeof modversion, pp) == NULL) + goto error; + + if (pclose (pp) == -1) + goto error; + + /* Chomp final \n */ + len = strlen (modversion); + if (len > 0 && modversion[len-1] == '\n') { + modversion[len-1] = '\0'; + len--; + } + + /* Generate module path. */ + modpath = xasprintf (MODULESDIR "/%s", modversion); + + /* Check module path is a directory. */ + if (!isdir (modpath)) { + fprintf (stderr, "febootstrap-supermin-helper: /boot/vmlinuz26 kernel exists but %s is not a valid module path\n", + modpath); + exit (EXIT_FAILURE); + } + + if (kernel) { + /* Symlink from kernel to /boot/vmlinuz26. */ + if (symlink ("/boot/vmlinuz26", kernel) == -1) + error (EXIT_FAILURE, errno, "symlink kernel"); + } + + /* Return module path. */ + return modpath; +} + +/* Select the kernel from environment variables set by the user. + * modpath_env may be NULL, in which case we attempt to work it out + * from kernel_env. + */ +static const char * +create_kernel_from_env (const char *hostcpu, const char *kernel, + const char *kernel_env, const char *modpath_env) +{ + if (verbose) { + fprintf (stderr, + "febootstrap-supermin-helper: using environment variable(s) FEBOOTSTRAP_* to\n" + "select kernel %s", kernel_env); + if (modpath_env) + fprintf (stderr, " and module path %s", modpath_env); + fprintf (stderr, "\n"); + } + + if (!isfile (kernel_env)) { + fprintf (stderr, + "febootstrap-supermin-helper: %s: not a regular file\n" + "(what is $FEBOOTSTRAP_KERNEL set to?)\n", kernel_env); + exit (EXIT_FAILURE); + } + + if (!modpath_env) { + /* Try to guess modpath from kernel path. */ + const char *p = strrchr (kernel_env, '/'); + if (p) p++; else p = kernel_env; + + /* NB: We need the extra test to ensure calling get_modpath is safe. */ + if (strncmp (p, "vmlinuz-", 8) != 0) { + fprintf (stderr, + "febootstrap-supermin-helper: cannot guess module path.\n" + "Set $FEBOOTSTRAP_MODULES to the modules directory corresponding to\n" + "kernel %s, or unset $FEBOOTSTRAP_KERNEL to autoselect a kernel.\n", + kernel_env); + exit (EXIT_FAILURE); + } + + modpath_env = get_modpath (p); + } + + if (!isdir (modpath_env)) { + fprintf (stderr, + "febootstrap-supermin-helper: %s: not a directory\n" + "(what is $FEBOOTSTRAP_MODULES set to?)\n", modpath_env); + exit (EXIT_FAILURE); + } + + /* Create the symlink. */ + if (kernel) { + if (verbose >= 2) + fprintf (stderr, "creating symlink %s -> %s\n", kernel_env, kernel); + + if (symlink (kernel_env, kernel) == -1) + error (EXIT_FAILURE, errno, "symlink kernel"); + } + + return modpath_env; +}