Files
ipxe/src/hci/commands/image_cmd.c
Michael Brown c4a8d90387 [image] Generalise concept of selected image
Most image flags are independent values: any combination of flags may
be set for any image, and the flags for one image are independent of
the flags for any other image.  The "selected" flag does not follow
this pattern: at most one image may be marked as selected at any time.

When invoking a kernel via the UEFI shim, there will be multiple
"special" images: the selected kernel itself, the shim image, and
potentially a shim-signed GRUB binary to be used as a crutch to assist
shim in loading the kernel (since current versions of the UEFI shim
are not capable of directly loading a Linux kernel).

Remove the "selected" image flag and replace it with a general concept
of an image tag with the same semantics: a given tag may be assigned
to at most one image, an image may be found by its tag only while the
image is currently registered, and a tag will survive unregistration
and reregistration of an image (if it has not already been assigned to
a new image).  For visual consistency, also replace the current image
pointer with a current image tag.

The image pointer stored within the image tag holds only a weak
reference to the image, since the selection of an image should not
prevent that image from being freed.  (The strong reference to the
currently executing image is held locally within the execution scope
of image_exec(), and is logically separate from the current image
pointer.)

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-05-17 14:42:03 +01:00

446 lines
11 KiB
C

/*
* Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <getopt.h>
#include <ipxe/image.h>
#include <ipxe/command.h>
#include <ipxe/parseopt.h>
#include <ipxe/shell.h>
#include <usr/imgmgmt.h>
/** @file
*
* Image management commands
*
*/
/** "img{single}" options */
struct imgsingle_options {
/** Image name */
char *name;
/** Download timeout */
unsigned long timeout;
/** Replace image */
int replace;
/** Free image after execution */
int autofree;
};
/** "img{single}" option list */
static union {
/* "imgexec" takes all three options */
struct option_descriptor imgexec[4];
/* Other "img{single}" commands take only --name, --timeout,
* and --autofree
*/
struct option_descriptor imgsingle[3];
} opts = {
.imgexec = {
OPTION_DESC ( "name", 'n', required_argument,
struct imgsingle_options, name, parse_string ),
OPTION_DESC ( "timeout", 't', required_argument,
struct imgsingle_options, timeout, parse_timeout),
OPTION_DESC ( "autofree", 'a', no_argument,
struct imgsingle_options, autofree, parse_flag ),
OPTION_DESC ( "replace", 'r', no_argument,
struct imgsingle_options, replace, parse_flag ),
},
};
/** An "img{single}" family command descriptor */
struct imgsingle_descriptor {
/** Command descriptor */
struct command_descriptor *cmd;
/** Function to use to acquire the image */
int ( * acquire ) ( const char *name, unsigned long timeout,
struct image **image );
/** Pre-action to take upon image, or NULL */
void ( * preaction ) ( struct image *image );
/** Action to take upon image, or NULL */
int ( * action ) ( struct image *image,
struct imgsingle_options *opts );
/** Verb to describe action */
const char *verb;
};
/**
* The "img{single}" family of commands
*
* @v argc Argument count
* @v argv Argument list
* @v desc "img{single}" command descriptor
* @v action_name Action name (for error messages)
* @v action Action to take upon image
* @ret rc Return status code
*/
static int imgsingle_exec ( int argc, char **argv,
struct imgsingle_descriptor *desc ) {
struct imgsingle_options opts;
char *name_uri = NULL;
char *cmdline = NULL;
struct image *image;
int rc;
/* Parse options */
if ( ( rc = parse_options ( argc, argv, desc->cmd, &opts ) ) != 0 )
goto err_parse_options;
/* Parse name/URI string and command line, if present */
if ( optind < argc ) {
name_uri = argv[optind];
if ( argv[ optind + 1 ] != NULL ) {
cmdline = concat_args ( &argv[ optind + 1 ] );
if ( ! cmdline ) {
rc = -ENOMEM;
goto err_parse_cmdline;
}
}
}
/* Acquire the image */
if ( name_uri ) {
if ( ( rc = desc->acquire ( name_uri, opts.timeout,
&image ) ) != 0 )
goto err_acquire;
} else {
image = find_image_tag ( &selected_image );
if ( ! image ) {
printf ( "No image selected\n" );
goto err_acquire;
}
}
/* Carry out command pre-action, if applicable */
if ( desc->preaction )
desc->preaction ( image );
/* Set the image name, if applicable */
if ( opts.name ) {
if ( ( rc = image_set_name ( image, opts.name ) ) != 0 ) {
printf ( "Could not name image: %s\n",
strerror ( rc ) );
goto err_set_name;
}
}
/* Set the command-line arguments, if applicable */
if ( cmdline ) {
if ( ( rc = image_set_cmdline ( image, cmdline ) ) != 0 ) {
printf ( "Could not set arguments: %s\n",
strerror ( rc ) );
goto err_set_cmdline;
}
}
/* Set the auto-unregister flag, if applicable */
if ( opts.autofree )
image->flags |= IMAGE_AUTO_UNREGISTER;
/* Carry out command action, if applicable */
if ( desc->action ) {
if ( ( rc = desc->action ( image, &opts ) ) != 0 ) {
printf ( "Could not %s: %s\n",
desc->verb, strerror ( rc ) );
goto err_action;
}
}
/* Success */
rc = 0;
err_action:
err_set_cmdline:
err_set_name:
err_acquire:
free ( cmdline );
err_parse_cmdline:
err_parse_options:
return rc;
}
/** "imgfetch" command descriptor */
static struct command_descriptor imgfetch_cmd =
COMMAND_DESC ( struct imgsingle_options, opts.imgsingle,
1, MAX_ARGUMENTS, "<uri> [<arguments>...]" );
/** "imgfetch" family command descriptor */
struct imgsingle_descriptor imgfetch_desc = {
.cmd = &imgfetch_cmd,
.acquire = imgdownload_string,
};
/**
* The "imgfetch" command
*
* @v argc Argument count
* @v argv Argument list
* @ret rc Return status code
*/
static int imgfetch_exec ( int argc, char **argv ) {
return imgsingle_exec ( argc, argv, &imgfetch_desc );
}
/**
* "imgselect" command action
*
* @v image Image
* @v opts Options
* @ret rc Return status code
*/
static int imgselect ( struct image *image,
struct imgsingle_options *opts __unused ) {
return image_select ( image );
}
/** "imgselect" command descriptor */
static struct command_descriptor imgselect_cmd =
COMMAND_DESC ( struct imgsingle_options, opts.imgsingle,
1, MAX_ARGUMENTS, "<uri|image> [<arguments>...]" );
/** "imgselect" family command descriptor */
struct imgsingle_descriptor imgselect_desc = {
.cmd = &imgselect_cmd,
.acquire = imgacquire,
.action = imgselect,
.verb = "select",
};
/**
* The "imgselect" command
*
* @v argc Argument count
* @v argv Argument list
* @ret rc Return status code
*/
static int imgselect_exec ( int argc, char **argv ) {
return imgsingle_exec ( argc, argv, &imgselect_desc );
}
/** "imgexec" command descriptor */
static struct command_descriptor imgexec_cmd =
COMMAND_DESC ( struct imgsingle_options, opts.imgexec,
0, MAX_ARGUMENTS, "[<uri|image> [<arguments>...]]" );
/**
* "imgexec" command action
*
* @v image Image
* @v opts Options
* @ret rc Return status code
*/
static int imgexec ( struct image *image, struct imgsingle_options *opts ) {
int rc;
/* Perform replacement or execution as applicable */
if ( opts->replace ) {
/* Try to replace image */
if ( ( rc = image_replace ( image ) ) != 0 )
return rc;
/* Stop script and tail-recurse into replacement image */
shell_stop ( SHELL_STOP_COMMAND_SEQUENCE );
} else {
/* Try to execute image */
if ( ( rc = image_exec ( image ) ) != 0 )
return rc;
}
return 0;
}
/** "imgexec" family command descriptor */
struct imgsingle_descriptor imgexec_desc = {
.cmd = &imgexec_cmd,
.acquire = imgacquire,
.action = imgexec,
.verb = "boot",
};
/**
* The "imgexec" command
*
* @v argc Argument count
* @v argv Argument list
* @ret rc Return status code
*/
static int imgexec_exec ( int argc, char **argv) {
return imgsingle_exec ( argc, argv, &imgexec_desc );
}
/** "imgargs" command descriptor */
static struct command_descriptor imgargs_cmd =
COMMAND_DESC ( struct imgsingle_options, opts.imgsingle,
1, MAX_ARGUMENTS, "<uri|image> [<arguments>...]" );
/** "imgargs" family command descriptor */
struct imgsingle_descriptor imgargs_desc = {
.cmd = &imgargs_cmd,
.acquire = imgacquire,
.preaction = image_clear_cmdline,
};
/**
* The "imgargs" command body
*
* @v argc Argument count
* @v argv Argument list
* @ret rc Return status code
*/
static int imgargs_exec ( int argc, char **argv ) {
return imgsingle_exec ( argc, argv, &imgargs_desc );
}
/** "img{multi}" options */
struct imgmulti_options {};
/** "img{multi}" option list */
static struct option_descriptor imgmulti_opts[] = {};
/** "img{multi}" command descriptor */
static struct command_descriptor imgmulti_cmd =
COMMAND_DESC ( struct imgmulti_options, imgmulti_opts, 0, MAX_ARGUMENTS,
"[<image>...]" );
/**
* The "img{multi}" family of commands
*
* @v argc Argument count
* @v argv Argument list
* @v payload Function to execute on each image
* @ret rc Return status code
*/
static int imgmulti_exec ( int argc, char **argv,
void ( * payload ) ( struct image *image ) ) {
struct imgmulti_options opts;
struct image *image;
struct image *tmp;
int i;
int rc;
/* Parse options */
if ( ( rc = parse_options ( argc, argv, &imgmulti_cmd, &opts ) ) != 0 )
return rc;
/* If no images are explicitly specified, process all images */
if ( optind == argc ) {
for_each_image_safe ( image, tmp )
payload ( image );
return 0;
}
/* Otherwise, process specified images */
for ( i = optind ; i < argc ; i++ ) {
image = find_image ( argv[i] );
if ( ! image ) {
printf ( "\"%s\": no such image\n", argv[i] );
return -ENOENT;
}
payload ( image );
}
return 0;
}
/**
* The "imgstat" command
*
* @v argc Argument count
* @v argv Argument list
* @ret rc Return status code
*/
static int imgstat_exec ( int argc, char **argv ) {
return imgmulti_exec ( argc, argv, imgstat );
}
/**
* The "imgfree" command
*
* @v argc Argument count
* @v argv Argument list
* @ret rc Return status code
*/
static int imgfree_exec ( int argc, char **argv ) {
return imgmulti_exec ( argc, argv, unregister_image );
}
/** Image management commands */
struct command image_commands[] __command = {
{
.name = "imgfetch",
.exec = imgfetch_exec,
},
{
.name = "module",
.exec = imgfetch_exec, /* synonym for "imgfetch" */
},
{
.name = "initrd",
.exec = imgfetch_exec, /* synonym for "imgfetch" */
},
{
.name = "kernel",
.exec = imgselect_exec, /* synonym for "imgselect" */
},
{
.name = "chain",
.exec = imgexec_exec, /* synonym for "imgexec" */
},
{
.name = "imgselect",
.exec = imgselect_exec,
},
{
.name = "imgload",
.exec = imgselect_exec, /* synonym for "imgselect" */
},
{
.name = "imgargs",
.exec = imgargs_exec,
},
{
.name = "imgexec",
.exec = imgexec_exec,
},
{
.name = "boot", /* synonym for "imgexec" */
.exec = imgexec_exec,
},
{
.name = "imgstat",
.exec = imgstat_exec,
},
{
.name = "imgfree",
.exec = imgfree_exec,
},
};