#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include <fnmatch.h>
#include <unistd.h>
#include <errno.h>
}
}
+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.
*
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;
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");
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]);
"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;
+}