1*449dc649SBruce Richardson.. SPDX-License-Identifier: BSD-3-Clause 2*449dc649SBruce Richardson Copyright(c) 2023 Intel Corporation. 3*449dc649SBruce Richardson 4*449dc649SBruce RichardsonCommand-line Library 5*449dc649SBruce Richardson==================== 6*449dc649SBruce Richardson 7*449dc649SBruce RichardsonSince its earliest versions, DPDK has included a command-line library - 8*449dc649SBruce Richardsonprimarily for internal use by, for example, ``dpdk-testpmd`` and the ``dpdk-test`` binaries, 9*449dc649SBruce Richardsonbut the library is also exported on install and can be used by any end application. 10*449dc649SBruce RichardsonThis chapter covers the basics of the command-line library and how to use it in an application. 11*449dc649SBruce Richardson 12*449dc649SBruce RichardsonLibrary Features 13*449dc649SBruce Richardson---------------- 14*449dc649SBruce Richardson 15*449dc649SBruce RichardsonThe DPDK command-line library supports the following features: 16*449dc649SBruce Richardson 17*449dc649SBruce Richardson* Tab-completion available for interactive terminal sessions 18*449dc649SBruce Richardson 19*449dc649SBruce Richardson* Ability to read and process commands taken from an input file, e.g. startup script 20*449dc649SBruce Richardson 21*449dc649SBruce Richardson* Parameterized commands able to take multiple parameters with different datatypes: 22*449dc649SBruce Richardson 23*449dc649SBruce Richardson * Strings 24*449dc649SBruce Richardson * Signed/unsigned 16/32/64-bit integers 25*449dc649SBruce Richardson * IP Addresses 26*449dc649SBruce Richardson * Ethernet Addresses 27*449dc649SBruce Richardson 28*449dc649SBruce Richardson* Ability to multiplex multiple commands to a single callback function 29*449dc649SBruce Richardson 30*449dc649SBruce RichardsonAdding Command-line to an Application 31*449dc649SBruce Richardson------------------------------------- 32*449dc649SBruce Richardson 33*449dc649SBruce RichardsonAdding a command-line instance to an application involves a number of coding steps. 34*449dc649SBruce Richardson 35*449dc649SBruce Richardson#. Define the result structure for the command, specifying the command parameters 36*449dc649SBruce Richardson 37*449dc649SBruce Richardson#. Provide an initializer for each field in the result 38*449dc649SBruce Richardson 39*449dc649SBruce Richardson#. Define the callback function for the command 40*449dc649SBruce Richardson 41*449dc649SBruce Richardson#. Provide a parse result structure instance for the command, linking the callback to the command 42*449dc649SBruce Richardson 43*449dc649SBruce Richardson#. Add the parse result structure to a command-line context 44*449dc649SBruce Richardson 45*449dc649SBruce Richardson#. Within your main application code, create a new command-line instance passing in the context. 46*449dc649SBruce Richardson 47*449dc649SBruce RichardsonThe next few subsections will cover each of these steps in more detail, 48*449dc649SBruce Richardsonworking through an example to add two commands to a command-line instance. 49*449dc649SBruce RichardsonThose two commands will be: 50*449dc649SBruce Richardson 51*449dc649SBruce Richardson#. ``quit`` - as the name suggests, to close the application 52*449dc649SBruce Richardson 53*449dc649SBruce Richardson#. ``show port stats <n>`` - to display on-screen the statistics for a given ethernet port 54*449dc649SBruce Richardson 55*449dc649SBruce Richardson.. note:: 56*449dc649SBruce Richardson 57*449dc649SBruce Richardson For further examples of use of the command-line, see 58*449dc649SBruce Richardson :doc:`cmdline example application <../sample_app_ug/cmd_line>` 59*449dc649SBruce Richardson 60*449dc649SBruce RichardsonDefining Command Result Structure 61*449dc649SBruce Richardson~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 62*449dc649SBruce Richardson 63*449dc649SBruce RichardsonThe first structure to be defined is the structure which will be created on successful parse of a command. 64*449dc649SBruce RichardsonThis structure contains one member field for each token, or word, in the command. 65*449dc649SBruce RichardsonThe simplest case is for a one-word command, like ``quit``. 66*449dc649SBruce RichardsonFor this, we only need to define a structure with a single string parameter to contain that word. 67*449dc649SBruce Richardson 68*449dc649SBruce Richardson.. code-block:: c 69*449dc649SBruce Richardson 70*449dc649SBruce Richardson struct cmd_quit_result { 71*449dc649SBruce Richardson cmdline_fixed_string_t quit; 72*449dc649SBruce Richardson }; 73*449dc649SBruce Richardson 74*449dc649SBruce RichardsonFor readability, the name of the struct member should match that of the token in the command. 75*449dc649SBruce Richardson 76*449dc649SBruce RichardsonFor our second command, we need a structure with four member fields in it, 77*449dc649SBruce Richardsonas there are four words/tokens in our command. 78*449dc649SBruce RichardsonThe first three are strings, and the final one is a 16-bit numeric value. 79*449dc649SBruce RichardsonThe resulting struct looks like: 80*449dc649SBruce Richardson 81*449dc649SBruce Richardson.. code-block:: c 82*449dc649SBruce Richardson 83*449dc649SBruce Richardson struct cmd_show_port_stats_result { 84*449dc649SBruce Richardson cmdline_fixed_string_t show; 85*449dc649SBruce Richardson cmdline_fixed_string_t port; 86*449dc649SBruce Richardson cmdline_fixed_string_t stats; 87*449dc649SBruce Richardson uint16_t n; 88*449dc649SBruce Richardson }; 89*449dc649SBruce Richardson 90*449dc649SBruce RichardsonAs before, we choose names to match the tokens in the command. 91*449dc649SBruce RichardsonSince our numeric parameter is a 16-bit value, we use ``uint16_t`` type for it. 92*449dc649SBruce RichardsonAny of the standard sized integer types can be used as parameters, depending on the desired result. 93*449dc649SBruce Richardson 94*449dc649SBruce RichardsonBeyond the standard integer types, 95*449dc649SBruce Richardsonthe library also allows variable parameters to be of a number of other types, 96*449dc649SBruce Richardsonas called out in the feature list above. 97*449dc649SBruce Richardson 98*449dc649SBruce Richardson* For variable string parameters, 99*449dc649SBruce Richardson the type should be ``cmdline_fixed_string_t`` - the same as for fixed tokens, 100*449dc649SBruce Richardson but these will be initialized differently (as described below). 101*449dc649SBruce Richardson 102*449dc649SBruce Richardson* For ethernet addresses use type ``struct rte_ether_addr`` 103*449dc649SBruce Richardson 104*449dc649SBruce Richardson* For IP addresses use type ``cmdline_ipaddr_t`` 105*449dc649SBruce Richardson 106*449dc649SBruce RichardsonProviding Field Initializers 107*449dc649SBruce Richardson~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 108*449dc649SBruce Richardson 109*449dc649SBruce RichardsonEach field of our result structure needs an initializer. 110*449dc649SBruce RichardsonFor fixed string tokens, like "quit", "show" and "port", the initializer will be the string itself. 111*449dc649SBruce Richardson 112*449dc649SBruce Richardson.. code-block:: c 113*449dc649SBruce Richardson 114*449dc649SBruce Richardson static cmdline_parse_token_string_t cmd_quit_quit_tok = 115*449dc649SBruce Richardson TOKEN_STRING_INITIALIZER(struct cmd_quit_result, quit, "quit"); 116*449dc649SBruce Richardson 117*449dc649SBruce RichardsonThe convention for naming used here is to include the base name of the overall result structure - 118*449dc649SBruce Richardson``cmd_quit`` in this case, 119*449dc649SBruce Richardsonas well as the name of the field within that structure - ``quit`` in this case, followed by ``_tok``. 120*449dc649SBruce Richardson(This is why there is a double ``quit`` in the name above). 121*449dc649SBruce Richardson 122*449dc649SBruce RichardsonThis naming convention is seen in our second example, 123*449dc649SBruce Richardsonwhich also demonstrates how to define a numeric initializer. 124*449dc649SBruce Richardson 125*449dc649SBruce Richardson 126*449dc649SBruce Richardson.. code-block:: c 127*449dc649SBruce Richardson 128*449dc649SBruce Richardson static cmdline_parse_token_string_t cmd_show_port_stats_show_tok = 129*449dc649SBruce Richardson TOKEN_STRING_INITIALIZER(struct cmd_show_port_stats_result, show, "show"); 130*449dc649SBruce Richardson static cmdline_parse_token_string_t cmd_show_port_stats_port_tok = 131*449dc649SBruce Richardson TOKEN_STRING_INITIALIZER(struct cmd_show_port_stats_result, port, "port"); 132*449dc649SBruce Richardson static cmdline_parse_token_string_t cmd_show_port_stats_stats_tok = 133*449dc649SBruce Richardson TOKEN_STRING_INITIALIZER(struct cmd_show_port_stats_result, stats, "stats"); 134*449dc649SBruce Richardson static cmdline_parse_token_num_t cmd_show_port_stats_n_tok = 135*449dc649SBruce Richardson TOKEN_NUM_INITIALIZER(struct cmd_show_port_stats_result, n, RTE_UINT16); 136*449dc649SBruce Richardson 137*449dc649SBruce RichardsonFor variable string tokens, the same ``TOKEN_STRING_INITIALIZER`` macro should be used. 138*449dc649SBruce RichardsonHowever, the final parameter should be ``NULL`` rather than a hard-coded token string. 139*449dc649SBruce Richardson 140*449dc649SBruce RichardsonFor numeric parameters, the final parameter to the ``TOKEN_NUM_INITIALIZER`` macro should be the 141*449dc649SBruce Richardsoncmdline type matching the variable type defined in the result structure, 142*449dc649SBruce Richardsone.g. RTE_UINT8, RTE_UINT32, etc. 143*449dc649SBruce Richardson 144*449dc649SBruce RichardsonFor IP addresses, the macro ``TOKEN_IPADDR_INITIALIZER`` should be used. 145*449dc649SBruce Richardson 146*449dc649SBruce RichardsonFor ethernet addresses, the macro ``TOKEN_ETHERADDR_INITIALIZER`` should be used. 147*449dc649SBruce Richardson 148*449dc649SBruce RichardsonDefining Callback Function 149*449dc649SBruce Richardson~~~~~~~~~~~~~~~~~~~~~~~~~~ 150*449dc649SBruce Richardson 151*449dc649SBruce RichardsonFor each command, we need to define a function to be called once the command has been recognised. 152*449dc649SBruce RichardsonThe callback function should have type: 153*449dc649SBruce Richardson 154*449dc649SBruce Richardson.. code:: c 155*449dc649SBruce Richardson 156*449dc649SBruce Richardson void (*f)(void *, struct cmdline *, void *) 157*449dc649SBruce Richardson 158*449dc649SBruce Richardsonwhere the first parameter is a pointer to the result structure defined above, 159*449dc649SBruce Richardsonthe second parameter is the command-line instance, 160*449dc649SBruce Richardsonand the final parameter is a user-defined pointer provided when we associate the callback with the command. 161*449dc649SBruce RichardsonMost callback functions only use the first parameter, or none at all, 162*449dc649SBruce Richardsonbut the additional two parameters provide some extra flexibility, 163*449dc649SBruce Richardsonto allow the callback to work with non-global state in your application. 164*449dc649SBruce Richardson 165*449dc649SBruce RichardsonFor our two example commands, the relevant callback functions would look very similar in definition. 166*449dc649SBruce RichardsonHowever, within the function body, 167*449dc649SBruce Richardsonwe assume that the user would need to reference the result structure to extract the port number in 168*449dc649SBruce Richardsonthe second case. 169*449dc649SBruce Richardson 170*449dc649SBruce Richardson.. code:: c 171*449dc649SBruce Richardson 172*449dc649SBruce Richardson void 173*449dc649SBruce Richardson cmd_quit_parsed(void *parsed_result, struct cmdline *cl, void *data) 174*449dc649SBruce Richardson { 175*449dc649SBruce Richardson quit = 1; 176*449dc649SBruce Richardson } 177*449dc649SBruce Richardson void 178*449dc649SBruce Richardson cmd_show_port_stats_parsed(void *parsed_result, struct cmdline *cl, void *data) 179*449dc649SBruce Richardson { 180*449dc649SBruce Richardson struct cmd_show_port_stats_result *res = parsed_result; 181*449dc649SBruce Richardson uint16_t port_id = res->n; 182*449dc649SBruce Richardson ... 183*449dc649SBruce Richardson } 184*449dc649SBruce Richardson 185*449dc649SBruce Richardson 186*449dc649SBruce RichardsonAssociating Callback and Command 187*449dc649SBruce Richardson~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 188*449dc649SBruce Richardson 189*449dc649SBruce RichardsonThe ``cmdline_parse_inst_t`` type defines a "parse instance", 190*449dc649SBruce Richardsoni.e. a sequence of tokens to be matched and then an associated function to be called. 191*449dc649SBruce RichardsonAlso included in the instance type are a field for help text for the command, 192*449dc649SBruce Richardsonand any additional user-defined parameter to be passed to the callback functions referenced above. 193*449dc649SBruce RichardsonFor example, for our simple "quit" command: 194*449dc649SBruce Richardson 195*449dc649SBruce Richardson.. code-block:: c 196*449dc649SBruce Richardson 197*449dc649SBruce Richardson static cmdline_parse_inst_t cmd_quit = { 198*449dc649SBruce Richardson .f = cmd_quit_parsed, 199*449dc649SBruce Richardson .data = NULL, 200*449dc649SBruce Richardson .help_str = "Close the application", 201*449dc649SBruce Richardson .tokens = { 202*449dc649SBruce Richardson (void *)&cmd_quit_quit_tok, 203*449dc649SBruce Richardson NULL 204*449dc649SBruce Richardson } 205*449dc649SBruce Richardson }; 206*449dc649SBruce Richardson 207*449dc649SBruce RichardsonIn this case, we firstly identify the callback function to be called, 208*449dc649SBruce Richardsonthen set the user-defined parameter to NULL, 209*449dc649SBruce Richardsonprovide a help message to be given, on request, to the user explaining the command, 210*449dc649SBruce Richardsonbefore finally listing out the single token to be matched for this command instance. 211*449dc649SBruce Richardson 212*449dc649SBruce RichardsonFor our second, port stats, example, 213*449dc649SBruce Richardsonas well as making things a little more complicated by having multiple tokens to be matched, 214*449dc649SBruce Richardsonwe can also demonstrate passing in a parameter to the function. 215*449dc649SBruce RichardsonLet us suppose that our application does not always use all the ports available to it, 216*449dc649SBruce Richardsonbut instead only uses a subset of the ports, stored in an array called ``active_ports``. 217*449dc649SBruce RichardsonOur stats command, therefore, should only display stats for the currently in-use ports, 218*449dc649SBruce Richardsonso we pass this ``active_ports`` array. 219*449dc649SBruce Richardson(For simplicity of illustration, we shall assume that the array uses a terminating marker, 220*449dc649SBruce Richardsone.g. -1 for the end of the port list, so we don't need to pass in a length parameter too.) 221*449dc649SBruce Richardson 222*449dc649SBruce Richardson.. code-block:: c 223*449dc649SBruce Richardson 224*449dc649SBruce Richardson extern int16_t active_ports[]; 225*449dc649SBruce Richardson ... 226*449dc649SBruce Richardson static cmdline_parse_inst_t cmd_show_port_stats = { 227*449dc649SBruce Richardson .f = cmd_show_port_stats_parsed, 228*449dc649SBruce Richardson .data = active_ports, 229*449dc649SBruce Richardson .help_str = "Show statistics for active network ports", 230*449dc649SBruce Richardson .tokens = { 231*449dc649SBruce Richardson (void *)&cmd_show_port_stats_show_tok, 232*449dc649SBruce Richardson (void *)&cmd_show_port_stats_port_tok, 233*449dc649SBruce Richardson (void *)&cmd_show_port_stats_stats_tok, 234*449dc649SBruce Richardson (void *)&cmd_show_port_stats_n_tok, 235*449dc649SBruce Richardson NULL 236*449dc649SBruce Richardson } 237*449dc649SBruce Richardson }; 238*449dc649SBruce Richardson 239*449dc649SBruce Richardson 240*449dc649SBruce RichardsonAdding Command to Command-line Context 241*449dc649SBruce Richardson~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 242*449dc649SBruce Richardson 243*449dc649SBruce RichardsonNow that we have configured each individual command and callback, 244*449dc649SBruce Richardsonwe need to merge these into a single array of command-line "contexts". 245*449dc649SBruce RichardsonThis context array will be used to create the actual command-line instance in the application. 246*449dc649SBruce RichardsonThankfully, each context entry is the same as each parse instance, 247*449dc649SBruce Richardsonso our array is defined by simply listing out the previously defined command parse instances. 248*449dc649SBruce Richardson 249*449dc649SBruce Richardson.. code-block:: c 250*449dc649SBruce Richardson 251*449dc649SBruce Richardson static cmdline_parse_ctx_t ctx[] = { 252*449dc649SBruce Richardson &cmd_quit, 253*449dc649SBruce Richardson &cmd_show_port_stats, 254*449dc649SBruce Richardson NULL 255*449dc649SBruce Richardson }; 256*449dc649SBruce Richardson 257*449dc649SBruce RichardsonThe context list must be terminated by a NULL entry. 258*449dc649SBruce Richardson 259*449dc649SBruce RichardsonCreating a Command-line Instance 260*449dc649SBruce Richardson~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 261*449dc649SBruce Richardson 262*449dc649SBruce RichardsonOnce we have our ``ctx`` variable defined, 263*449dc649SBruce Richardsonwe now just need to call the API to create the new command-line instance in our application. 264*449dc649SBruce RichardsonThe basic API is ``cmdline_new`` which will create an interactive command-line with all commands available. 265*449dc649SBruce RichardsonHowever, if additional features for interactive use - such as tab-completion - 266*449dc649SBruce Richardsonare desired, it is recommended that ``cmdline_new_stdin`` be used instead. 267*449dc649SBruce Richardson 268*449dc649SBruce RichardsonA pattern that can be used in applications is to use ``cmdline_new`` for processing any startup commands, 269*449dc649SBruce Richardsoneither from file or from the environment (as is done in the "dpdk-test" application), 270*449dc649SBruce Richardsonand then using ``cmdline_stdin_new`` thereafter to handle the interactive part. 271*449dc649SBruce RichardsonFor example, to handle a startup file and then provide an interactive prompt: 272*449dc649SBruce Richardson 273*449dc649SBruce Richardson.. code-block:: c 274*449dc649SBruce Richardson 275*449dc649SBruce Richardson struct cmdline *cl; 276*449dc649SBruce Richardson int fd = open(startup_file, O_RDONLY); 277*449dc649SBruce Richardson 278*449dc649SBruce Richardson if (fd >= 0) { 279*449dc649SBruce Richardson cl = cmdline_new(ctx, "", fd, STDOUT_FILENO); 280*449dc649SBruce Richardson if (cl == NULL) { 281*449dc649SBruce Richardson /* error handling */ 282*449dc649SBruce Richardson } 283*449dc649SBruce Richardson cmdline_interact(cl); 284*449dc649SBruce Richardson cmdline_quit(cl); 285*449dc649SBruce Richardson close(fd); 286*449dc649SBruce Richardson } 287*449dc649SBruce Richardson 288*449dc649SBruce Richardson cl = cmdline_stdin_new(ctx, "Proxy>> "); 289*449dc649SBruce Richardson if (cl == NULL) { 290*449dc649SBruce Richardson /* error handling */ 291*449dc649SBruce Richardson } 292*449dc649SBruce Richardson cmdline_interact(cl); 293*449dc649SBruce Richardson cmdline_stdin_exit(cl); 294*449dc649SBruce Richardson 295*449dc649SBruce Richardson 296*449dc649SBruce RichardsonMultiplexing Multiple Commands to a Single Function 297*449dc649SBruce Richardson~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 298*449dc649SBruce Richardson 299*449dc649SBruce RichardsonTo reduce the amount of boiler-plate code needed when creating a command-line for an application, 300*449dc649SBruce Richardsonit is possible to merge a number of commands together to have them call a separate function. 301*449dc649SBruce RichardsonThis can be done in a number of different ways: 302*449dc649SBruce Richardson 303*449dc649SBruce Richardson* A callback function can be used as the target for a number of different commands. 304*449dc649SBruce Richardson Which command was used for entry to the function can be determined by examining the first parameter, 305*449dc649SBruce Richardson ``parsed_result`` in our examples above. 306*449dc649SBruce Richardson 307*449dc649SBruce Richardson* For simple string commands, multiple options can be concatenated using the "#" character. 308*449dc649SBruce Richardson For example: ``exit#quit``, specified as a token initializer, 309*449dc649SBruce Richardson will match either on the string "exit" or the string "quit". 310*449dc649SBruce Richardson 311*449dc649SBruce RichardsonAs a concrete example, 312*449dc649SBruce Richardsonthese two techniques are used in the DPDK unit test application ``dpdk-test``, 313*449dc649SBruce Richardsonwhere a single command ``cmdline_parse_t`` instance is used for all the "dump_<item>" test cases. 314*449dc649SBruce Richardson 315*449dc649SBruce Richardson.. literalinclude:: ../../../app/test/commands.c 316*449dc649SBruce Richardson :language: c 317*449dc649SBruce Richardson :start-after: Add the dump_* tests cases 8< 318*449dc649SBruce Richardson :end-before: >8 End of add the dump_* tests cases 319*449dc649SBruce Richardson 320*449dc649SBruce Richardson 321*449dc649SBruce RichardsonExamples of Command-line Use in DPDK 322*449dc649SBruce Richardson------------------------------------ 323*449dc649SBruce Richardson 324*449dc649SBruce RichardsonTo help the user follow the steps provided above, 325*449dc649SBruce Richardsonthe following DPDK files can be consulted for examples of command-line use. 326*449dc649SBruce Richardson 327*449dc649SBruce Richardson.. note:: 328*449dc649SBruce Richardson 329*449dc649SBruce Richardson This is not an exhaustive list of examples of command-line use in DPDK. 330*449dc649SBruce Richardson It is simply a list of a few files that may be of use to the application developer. 331*449dc649SBruce Richardson Some of these referenced files contain more complex examples of use that others. 332*449dc649SBruce Richardson 333*449dc649SBruce Richardson* ``commands.c/.h`` in ``examples/cmdline`` 334*449dc649SBruce Richardson 335*449dc649SBruce Richardson* ``mp_commands.c/.h`` in ``examples/multi_process/simple_mp`` 336*449dc649SBruce Richardson 337*449dc649SBruce Richardson* ``commands.c`` in ``app/test`` 338