1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387
|
/* libguestfs
* Copyright (C) 2010-2020 Red Hat Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* This file deals with building the libguestfs appliance.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <libintl.h>
#include "ignore-value.h"
#include "guestfs.h"
#include "guestfs-internal.h"
struct appliance_files {
char *kernel;
char *initrd;
char *image;
};
/* Old-style appliance is going to be obsoleted. */
static const char kernel_name[] = "vmlinuz." host_cpu;
static const char initrd_name[] = "initramfs." host_cpu ".img";
static int search_appliance (guestfs_h *g, struct appliance_files *appliance);
static int dir_contains_file (guestfs_h *g, const char *dir, const char *file);
static int dir_contains_files (guestfs_h *g, const char *dir, ...);
static int contains_old_style_appliance (guestfs_h *g, const char *path, void *data);
static int contains_fixed_appliance (guestfs_h *g, const char *path, void *data);
static int contains_supermin_appliance (guestfs_h *g, const char *path, void *data);
static int build_supermin_appliance (guestfs_h *g, const char *supermin_path, struct appliance_files *appliance);
static int run_supermin_build (guestfs_h *g, const char *lockfile, const char *appliancedir, const char *supermin_path);
/**
* Locate or build the appliance.
*
* This function locates or builds the appliance as necessary,
* handling the supermin appliance, caching of supermin-built
* appliances, or using either a fixed or old-style appliance.
*
* The return value is C<0> = good, C<-1> = error. Returned in
* C<appliance.kernel> will be the name of the kernel to use,
* C<appliance.initrd> the name of the initrd, C<appliance.image> the name of
* the ext2 root filesystem. C<appliance.image> can be C<NULL>, meaning that
* we are using an old-style (non-ext2) appliance. All three strings must be
* freed by the caller. However the referenced files themselves must I<not> be
* deleted.
*
* The process is as follows:
*
* =over 4
*
* =item 1.
*
* Look in C<path> which contains a supermin appliance skeleton. If no element
* has this, skip straight to step 3.
*
* =item 2.
*
* Call C<supermin --build> to build the full appliance (if it needs
* to be rebuilt). If this is successful, return the full appliance.
*
* =item 3.
*
* Check C<path>, looking for a fixed appliance. If one is found, return it.
*
* =item 4.
*
* Check C<path>, looking for an old-style appliance. If one is found,
* return it.
*
* =back
*
* The supermin appliance cache directory lives in
* F<$TMPDIR/.guestfs-$UID/> and consists of up to four files:
*
* $TMPDIR/.guestfs-$UID/lock - the supermin lock file
* $TMPDIR/.guestfs-$UID/appliance.d/kernel - the kernel
* $TMPDIR/.guestfs-$UID/appliance.d/initrd - the supermin initrd
* $TMPDIR/.guestfs-$UID/appliance.d/root - the appliance
*
* Multiple instances of libguestfs with the same UID may be racing to
* create an appliance. However (since supermin E<ge> 5) supermin
* provides a I<--lock> flag and atomic update of the F<appliance.d>
* subdirectory.
*/
int
guestfs_int_build_appliance (guestfs_h *g,
char **kernel_rtn,
char **initrd_rtn,
char **appliance_rtn)
{
struct appliance_files appliance;
memset (&appliance, 0, sizeof appliance);
if (search_appliance (g, &appliance) != 1)
return -1;
/* Don't assign these until we know we're going to succeed, to avoid
* the caller double-freeing (RHBZ#983218).
*/
*kernel_rtn = appliance.kernel;
*initrd_rtn = appliance.initrd;
*appliance_rtn = appliance.image;
return 0;
}
/**
* Check C<path>, looking for one of appliances: supermin appliance,
* fixed appliance or old-style appliance. If one of the fixed appliances is
* found, return it. If the supermin appliance skeleton is found, build and
* return appliance.
*
* Return values:
*
* 1 = appliance is found, returns C<appliance>,
* 0 = appliance not found,
* -1 = error which aborts the launch process.
*/
static int
locate_or_build_appliance (guestfs_h *g,
struct appliance_files *appliance,
const char *path)
{
int r;
/* Step (1). */
r = contains_supermin_appliance(g, path, NULL);
if (r == 1) {
/* Step (2): build supermin appliance. */
r = build_supermin_appliance (g, path, appliance);
return r < 0 ? r : 1;
}
/* Step (3). */
r = contains_fixed_appliance (g, path, NULL);
if (r == 1) {
appliance->kernel = safe_asprintf (g, "%s/kernel", path);
appliance->initrd = safe_asprintf (g, "%s/initrd", path);
appliance->image = safe_asprintf(g, "%s/root", path);
return 1;
}
/* Step (4). */
r = contains_old_style_appliance (g, path, NULL);
if (r == 1) {
appliance->kernel = safe_asprintf(g, "%s/%s", path, kernel_name);
appliance->initrd = safe_asprintf(g, "%s/%s", path, initrd_name);
appliance->image = NULL;
return 1;
}
return 0;
}
/**
* Search elements of C<g-E<gt>path>, returning the first C<appliance> element
* which matches the predicate function C<locate_or_build_appliance>.
*
* Return values:
*
* 1 = a path element matched, returns C<appliance>,
* 0 = no path element matched,
* -1 = error which aborts the launch process.
*/
static int
search_appliance (guestfs_h *g, struct appliance_files *appliance)
{
const char *pelem = g->path;
/* Note that if g->path is an empty string, we want to check the
* current directory (for backwards compatibility with
* libguestfs < 1.5.4).
*/
do {
size_t len = strcspn (pelem, PATH_SEPARATOR);
/* Empty element or "." means current directory. */
CLEANUP_FREE char *path = (len == 0) ? safe_strdup (g, ".") :
safe_strndup (g, pelem, len);
int r = locate_or_build_appliance (g, appliance, path);
if (r != 0)
return r; /* error or predicate matched */
if (pelem[len] == PATH_SEPARATOR[0])
pelem += len + 1;
else
pelem += len;
} while (*pelem);
/* Predicate didn't match on any path element. */
error (g, _("cannot find any suitable libguestfs supermin, fixed or old-style appliance on LIBGUESTFS_PATH (search path: %s)"),
g->path);
return 0;
}
static int
contains_old_style_appliance (guestfs_h *g, const char *path, void *data)
{
return dir_contains_files (g, path, kernel_name, initrd_name, NULL);
}
static int
contains_fixed_appliance (guestfs_h *g, const char *path, void *data)
{
return dir_contains_files (g, path,
"README.fixed",
"kernel", "initrd", "root", NULL);
}
static int
contains_supermin_appliance (guestfs_h *g, const char *path, void *data)
{
return dir_contains_files (g, path,
"supermin.d/base.tar.gz",
"supermin.d/packages", NULL);
}
/**
* Build supermin appliance from C<supermin_path> to
* F<$TMPDIR/.guestfs-$UID>.
*
* Returns: C<0> = built or C<-1> = error (aborts launch).
*/
static int
build_supermin_appliance (guestfs_h *g,
const char *supermin_path,
struct appliance_files *appliance)
{
CLEANUP_FREE char *cachedir = NULL, *lockfile = NULL, *appliancedir = NULL;
cachedir = guestfs_int_lazy_make_supermin_appliance_dir (g);
if (cachedir == NULL)
return -1;
appliancedir = safe_asprintf (g, "%s/appliance.d", cachedir);
lockfile = safe_asprintf (g, "%s/lock", cachedir);
debug (g, "begin building supermin appliance");
/* Build the appliance if it needs to be built. */
debug (g, "run supermin");
if (run_supermin_build (g, lockfile, appliancedir, supermin_path) == -1)
return -1;
debug (g, "finished building supermin appliance");
/* Return the appliance filenames. */
appliance->kernel = safe_asprintf (g, "%s/kernel", appliancedir);
appliance->initrd = safe_asprintf (g, "%s/initrd", appliancedir);
appliance->image = safe_asprintf (g, "%s/root", appliancedir);
/* Touch the files so they don't get deleted (as they are in /var/tmp). */
(void) utimes (appliance->kernel, NULL);
(void) utimes (appliance->initrd, NULL);
/* Checking backend != "uml" is a big hack. UML encodes the mtime
* of the original backing file (in this case, the appliance) in the
* COW file, and checks it when adding it to the VM. If there are
* multiple threads running and one touches the appliance here, it
* will disturb the mtime and UML will give an error.
*
* We can get rid of this hack as soon as UML fixes the
* ubdN=cow,original parsing bug, since we won't need to run
* uml_mkcow separately, so there is no possible race.
*
* XXX
*/
if (STRNEQ (g->backend, "uml"))
(void) utimes (appliance->image, NULL);
return 0;
}
/**
* Run C<supermin --build> and tell it to generate the appliance.
*/
static int
run_supermin_build (guestfs_h *g,
const char *lockfile,
const char *appliancedir,
const char *supermin_path)
{
CLEANUP_CMD_CLOSE struct command *cmd = guestfs_int_new_command (g);
int r;
#if 0 /* not supported in supermin 5 yet XXX */
const uid_t uid = getuid ();
const uid_t euid = geteuid ();
const gid_t gid = getgid ();
const gid_t egid = getegid ();
const int pass_u_g_args = uid != euid || gid != egid;
#endif
guestfs_int_cmd_add_arg (cmd, SUPERMIN);
guestfs_int_cmd_add_arg (cmd, "--build");
if (g->verbose)
guestfs_int_cmd_add_arg (cmd, "--verbose");
guestfs_int_cmd_add_arg (cmd, "--if-newer");
guestfs_int_cmd_add_arg (cmd, "--lock");
guestfs_int_cmd_add_arg (cmd, lockfile);
#if 0
if (pass_u_g_args) {
guestfs_int_cmd_add_arg (cmd, "-u");
guestfs_int_cmd_add_arg_format (cmd, "%d", euid);
guestfs_int_cmd_add_arg (cmd, "-g");
guestfs_int_cmd_add_arg_format (cmd, "%d", egid);
}
#endif
guestfs_int_cmd_add_arg (cmd, "--copy-kernel");
guestfs_int_cmd_add_arg (cmd, "-f");
guestfs_int_cmd_add_arg (cmd, "ext2");
guestfs_int_cmd_add_arg (cmd, "--host-cpu");
guestfs_int_cmd_add_arg (cmd, host_cpu);
guestfs_int_cmd_add_arg_format (cmd, "%s/supermin.d", supermin_path);
guestfs_int_cmd_add_arg (cmd, "-o");
guestfs_int_cmd_add_arg (cmd, appliancedir);
r = guestfs_int_cmd_run (cmd);
if (r == -1)
return -1;
if (!WIFEXITED (r) || WEXITSTATUS (r) != 0) {
guestfs_int_external_command_failed (g, r, SUPERMIN, NULL);
return -1;
}
return 0;
}
/**
* Returns true iff C<file> is contained in C<dir>.
*/
static int
dir_contains_file (guestfs_h *g, const char *dir, const char *file)
{
CLEANUP_FREE char *path = NULL;
path = safe_asprintf (g, "%s/%s", dir, file);
return access (path, F_OK) == 0;
}
/**
* Returns true iff every listed file is contained in C<dir>.
*/
static int
dir_contains_files (guestfs_h *g, const char *dir, ...)
{
va_list args;
const char *file;
va_start (args, dir);
while ((file = va_arg (args, const char *)) != NULL) {
if (!dir_contains_file (g, dir, file)) {
va_end (args);
return 0;
}
}
va_end (args);
return 1;
}
|