Fredrik Hultin, gPXE Command Line Interface

The Project

I'm going to write a command line for gPXE which will let the user do some basic tasks interactively. Things like setting and getting an IP, pinging, specifying tftp-servers etc. I'm going to do this as a separate, custom made, but implementation independent command line library which will handle all the internals of the command line. For this lib a small driver in the underlying application (ie. gPXE) will send key pressings to the lib. Code in gPXE will be able to add any command in run time or link time to the lib using linker tables or doubly-linked lists. The command line lib will then call the registered service when a command successfully has been entered.

Design goals

Small, efficient and extensible code. It will be easy to add new commands and the command line will do as much data-processing and error checking it can before passing the presumably correct arguments to the underlying functions.


User interface

The plans for the user interface are so that it will look something like

Welcome to Etherboot


Built in commands:
  help               Command help, use "help help" for more info.
  exit, quit, boot   Exits the command line and boots

Compiled in commands:
  ip                     Views or sets current IP adress and netmask

?>help ip
ip - Views or sets current IP adress and netmask

Used for both IPv6 and IPv4.

Set the IP adress and netmask (IPv4)
  ip <ip> <netmask>


Set the IP adress (IPv6)
  ip <ip>

    ip 2001:0db8::1428:57ab

View the IP/netmask


Protocol version: 4

Command implementation

I've tried to minimize the code and work in/on the custom commands; the command line code will do most of the work. The ip command implementation could look something like this (without the parts actually doing anything)

#include "command.h"
/* Command description, table entry*/
struct command test_command __command = {
        .name = "ip",
        .extra_help = "Used for both IPv6 and IPv4.",
        .desc = "Views or sets current IP adress and netmask",
        .exec = cmd_test_exec,
        .get_param_list = cmd_ip_get_param_list;
/* Get the parameter specifications */
static cmdl_param_list *cmd_ip_get_param_list (void) {
  /* List set 1: set ipv4 adress and netmask */
  /* Create the list set */
  cmdl_param_list_set* ipv4;
  ipv4 = cmdl_param_list_set_create();
  /* Name and describe the list set */
  cmdl_param_list_set_name(ipv4, "ipv4");
  cmdl_param_list_set_desc(ipv4, "Set the IP adress and netmask (IPv4)");
  /* Add the parameters with name, type and example value */
  cmdl_param_list_set_add(ipv4, "ip", CMDL_IPV4, "");
  cmdl_param_list_set_add(ipv4, "netmask", CMDL_IPV4, "");
  /* List set 2: set ipv6 adress */
  /* Create the list set */
  cmdl_param_list_set* ipv6;
  ipv6 = cmdl_param_list_set_create();
  /* Name and describe the list set */
  cmdl_param_list_set_name(ipv6, "ipv6");
  cmdl_param_list_set_desc(ipv6, "Set the IP adress (IPv6)");
  cmdl_param_list_set_add(ipv6, "ip", CMDL_IPV6, "2001:0db8::1428:57ab");
  /* List set 3: view the IP adress and netmask */
  /* Create the list set */
  cmdl_param_list_set* view;
  view = cmdl_param_list_set_create();
  /* Name and describe the list set */
  cmdl_param_list_set_name(view, "view");
  cmdl_param_list_set_desc(view, "View the IP/netmask");
  /* (No parameters = view) */
  /* Create the parameter list */
  cmdl_param_list* param_list;
  param_list = cmdl_param_list_create();
  /* Add the three list sets to the parameter list */
  cmdl_param_list_add(param_list, ipv4);
  cmdl_param_list_add(param_list, ipv6);
  cmdl_param_list_add(param_list, view);
  /* Return the parameter list to the command line */
  return param_list;
static int cmd_ip_exec ( cmd_line* cmd, cmdl_params* params ) {
  /* View */
  if(strcmp("view", params->set_name) == 0){
    int protocolv;
    cmdl_ipv4 ip4, netmask;
    cmdl_ipv6 ip6;
    protocolv = ...();
    cmdl_printf(cmd, "Protocol version: %i\n");
    if(protocol != 4){
      ip4 = ...();
      netmask = ...();
      cmdl_printf(cmd, "Adress: %i.%i.%i.%i\nNetmask: %i.%i.%i.%i\n", 
                       ip4.octet[0],     ip4.octet[1],     ip4.octet[2],     ip4.octet[3], 
                   netmask.octet[0], netmask.octet[1], netmask.octet[2], netmask.octet[3] );
  /* ipv4 */
  if(strcmp("ipv4", params->set_name) == 0){
    if( !set_gpxe_ip_something( cmdl_param_get_ipv4(params->param[0]) ){
      return -1;
    if( !set_gpxe_netmask_something( cmdl_param_get_ipv4(params, 1) ){
      return -1;
  /* ipv6 */
  if(strcmp("ipv6", params->set_name) == 0){
    if( !set_gpxe_ip6_something( cmdl_param_get_ipv6(params, 0) ){
      return -1;
  return 0;

Observe that the output of “help ip” is generated from this implementation.


The main command structure

struct command {
  /* The name of the command */
  const char *name;
  /* Additional help, or help that can't be automatically generated */
  const char *extra_help;
  /* Short description of the command, what it does. */
  const char *desc;
  /* The command function, returns an error code, takes command line pointer and params specified in param_list */
  int ( *exec ) ( cmd_line*, cmdl_params*)
  /* Returns a list describing parameters the command accepts */
  cmdl_param_list *( *get_param_list )( void );


cmdl_param_list will contain parameter list sets, since a command might accept many different sets of parameters.

typedef struct {
  int num_sets; // The number of sets in the list
  cmdl_param_list_set** param_list_set; // An array of list set pointers
} cmdl_param_list;


A parameter list set will contain parameter descriptions

typedef struct {
  char* name;  // Name of the list set (used for identification)
  char* desc;  // Description of the list set (used for the automated help)
  int num_params;   // The number of parameters
  cmdl_param_desc** param; // An array of parameter description pointers
} cmdl_param_list_set;


A parameter description contains information about a single parameter

typedef struct {
  int type; // Parameter type
  char* name; // Parameter name
  char* example; // Example of possible value
} cmdl_param_desc;
  • name is the name of the input field, it will be used for the standardized automatic help function
  • example is an example value for the field, also for the help

type will be an enum with different types of inputs the command line will understand and parse, for example:



Contains one named parameter set

typedef struct{
  char* set_name; // Name of the set
  int num_params; // The number of parameters the set contains
  cmdl_param** param; // An array of param pointers


Container class for parameter values. Only one parameter type is valid per parameter.

typedef struct{
  int valid_type; // Defines the param type (which pointer is valid)
  // Pointers to available types
  int* integer;
  double* fp;
  char* str;
  cmdl_ipv4* ipv4;
  cmdl_ipv6* ipv6;
  // ...
// with supporting structures like
typedef struct{
  unsigned char octet[4];
typedef struct{
  unsigned short int part[
// ...etc


Functions for the command implementation interface


Allocates a new list set and returns it.

cmdl_param_list_set* cmdl_param_list_set_create();


Names a given list set with a given name.

void cmdl_param_list_set_name(cmdl_param_list_set* list_set, char* name);


Adds a description to a given list set.

void cmdl_param_list_set_desc(cmdl_param_list_set* list_set, char* desc);


Adds a parameter description to a given list set.

void cmdl_param_list_set_add(cmdl_param_list_set* list_set, char* name, int type, char* example);


Allocates and returns a parameter new parameter list.

cmdl_param_list* cmdl_param_list_create();


Adds a parameter list set to a given parameter list.

void cmdl_param_list_add(cmdl_param_list* param_list, cmdl_param


Prints formated text to a given command line.

int cmdl_printf(cmd_line* cmd, const char *format, ...);


Returns the given parameter from a cmdl_params as TYPE. Rturns NULL if the parameter isn't a TYPE.

int *cmdl_param_get_int(cmdl_params* params, int param_num);
cmdl_ipv4 *cmdl_param_get_ipv4(cmdl_params* params, int param_num);


Perhaps this implementation would be a bit over the top. I got an email from Michael last week and he suggested, without reading this, that I should use ordinary C-style command structures (int argc, char** argv). That might not be as grandiose, and it would be harder to generate standardized automated help output, but it'd be much easier to implement. With some nice parse helper functions available to the command implementations, then perhaps it wouldn't be so bad. Hmm…


The command line is working and accepts input from the user, which it parses and then, at the moment, disregards.

QR Code
QR Code soc:fredrikhultin (generated for current page)