This is an old revision of the document!
====== 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. ===== Design ===== ==== User interface ==== The plans for the user interface are so that it will look something like <file> Welcome to Etherboot ?>help 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> Example: ip 192.168.1.42 255.255.255.0 Set the IP adress (IPv6) ip <ip> Example: ip 2001:0db8::1428:57ab View the IP/netmask ip Example: ip ?>ip 10.0.0.4 255.0.0.0 ?>ip Protocol version: 4 Adress: 10.0.0.4 Netmask: 255.0.0.0 ?>boot Booting... </file> ==== 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) <code c> #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_descr(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, "192.168.1.42"); cmdl_param_list_set_add(ipv4, "netmask", CMDL_IPV4, "255.255.255.0"); /* 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_descr(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_descr(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){ }else{ 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] ); }else{ ... } } /* 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; } </code> ==== Structures ==== === The main command structure === <code c> 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 ); }; </code> === cmdl_param_list === cmdl_param_list will contain parameter list sets, since a command might accept many different sets of parameters. <code c> 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; </code> === cmdl_param_list_set === A parameter list set will contain parameter descriptions <code c> 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; </code> === cmdl_param_desc === A parameter description contains information about a single parameter <code c> typedef struct { int type; // Parameter type char* name; // Parameter name char* example; // Example of possible value } cmdl_param_desc; </code> * 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: <code c> enum{ CMDL_INT=0, CMDL_FP, CMDL_STR, CMDL_IPV4, CMDL_IPV6 }; </code> === cmdl_params === Contains one named parameter set <code c> 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 }cmdl_params; </code> === cmdl_param === Container class for parameter values. Only one parameter type is valid per parameter. <code c> 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; // ... }cmdl_param; // with supporting structures like typedef struct{ unsigned char octet[4]; }cmdl_ipv4; typedef struct{ unsigned short int part[ 8]; }cmdl_ipv6; // ...etc </code> ==== Functions ==== Functions for the command implementation interface === cmdl_param_list_set_create === Allocates a new list set and returns it. <code> cmdl_param_list_set* cmdl_param_list_set_create() </code> === cmdl_param_list_set_name === Names a given list set with a given name. <code> void cmdl_param_list_set_name(cmdl_param_list_set* list_set, char* name); </code> === cmdl_param_list_set_desc === Adds a description to a given list set. <code> void cmdl_param_list_set_desc(cmdl_param_list_set* list_set, char* desc); </code> === cmdl_param_list_set_add === Adds a parameter description to a given list set. <code> void cmdl_param_list_set_add(cmdl_param_list_set* list_set, char* name, int type, char* example); </code> === cmdl_param_list_create === Allocates and returns a parameter new parameter list. <code> cmdl_param_list* cmdl_param_list_create(); </code> === cmdl_param_list_add === Adds a parameter list set to a given parameter list. <code> void cmdl_param_list_add(cmdl_param_list* param_list, cmdl_param </code> === cmdl_printf === Prints formated text to a given command line. <code> int cmdl_printf(cmd_line* cmd, const char *format, ...); </code> === cmdl_param_get_TYPE === Returns the given parameter from a cmdl_params as TYPE. Rturns NULL if the parameter isn't a TYPE. <code> int *cmdl_param_get_int(cmdl_params* params, int param_num); cmdl_ipv4 *cmdl_param_get_ipv4(cmdl_params* params, int param_num); ... </code> ter_list_set* list_set); </code> ===== Status ===== The command line is working and accepts input from the user, which it parses and then, at the moment, disregards.