1.. SPDX-License-Identifier: BSD-3-Clause 2 Copyright(c) 2023 Intel Corporation. 3 4Command-line Library 5==================== 6 7Since its earliest versions, DPDK has included a command-line library - 8primarily for internal use by, for example, ``dpdk-testpmd`` and the ``dpdk-test`` binaries, 9but the library is also exported on install and can be used by any end application. 10This chapter covers the basics of the command-line library and how to use it in an application. 11 12Library Features 13---------------- 14 15The DPDK command-line library supports the following features: 16 17* Tab-completion available for interactive terminal sessions 18 19* Ability to read and process commands taken from an input file, e.g. startup script 20 21* Parameterized commands able to take multiple parameters with different datatypes: 22 23 * Strings 24 * Signed/unsigned 16/32/64-bit integers 25 * IP Addresses 26 * Ethernet Addresses 27 28* Ability to multiplex multiple commands to a single callback function 29 30Adding Command-line to an Application 31------------------------------------- 32 33Adding a command-line instance to an application involves a number of coding steps. 34 35#. Define the result structure for the command, specifying the command parameters 36 37#. Provide an initializer for each field in the result 38 39#. Define the callback function for the command 40 41#. Provide a parse result structure instance for the command, linking the callback to the command 42 43#. Add the parse result structure to a command-line context 44 45#. Within your main application code, create a new command-line instance passing in the context. 46 47Many of these steps can be automated using the script ``dpdk-cmdline-gen.py`` installed by DPDK, 48and found in the ``buildtools`` folder in the source tree. 49This section covers adding a command-line using this script to generate the boiler plate, 50while the following section, 51`Worked Example of Adding Command-line to an Application`_ covers the steps to do so manually. 52 53Creating a Command List File 54~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 55 56The ``dpdk-cmdline-gen.py`` script takes as input a list of commands to be used by the application. 57While these can be piped to it via standard input, using a list file is probably best. 58 59The format of the list file must be: 60 61* Comment lines start with '#' as first non-whitespace character 62 63* One command per line 64 65* Variable fields are prefixed by the type-name in angle-brackets, for example: 66 67 * ``<STRING>message`` 68 69 * ``<UINT16>port_id`` 70 71 * ``<IP>src_ip`` 72 73 * ``<IPv4>dst_ip4`` 74 75 * ``<IPv6>dst_ip6`` 76 77* Variable fields, which take their values from a list of options, 78 have the comma-separated option list placed in braces, rather than a the type name. 79 For example, 80 81 * ``<(rx,tx,rxtx)>mode`` 82 83* The help text for a command is given in the form of a comment on the same line as the command 84 85An example list file, with a variety of (unrelated) commands, is shown below:: 86 87 # example list file 88 list # show all entries 89 add <UINT16>x <UINT16>y # add x and y 90 echo <STRING>message # print message to screen 91 add socket <STRING>path # add unix socket with the given path 92 set mode <(rx,tx)>rxtx # set Rx-only or Tx-only mode 93 quit # close the application 94 95Running the Generator Script 96~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 97 98To generate the necessary definitions for a command-line, run ``dpdk-cmdline-gen.py`` passing the list file as parameter. 99The script will output the generated C code to standard output, 100the contents of which are in the form of a C header file. 101Optionally, an output filename may be specified via the ``-o/--output-file`` argument. 102 103The generated content includes: 104 105* The result structure definitions for each command 106 107* The token initializers for each structure field 108 109* An "extern" function prototype for the callback for each command 110 111* A parse context for each command, including the per-command comments as help string 112 113* A command-line context array definition, suitable for passing to ``cmdline_new`` 114 115If so desired, the script can also output function stubs for the callback functions for each command. 116This behaviour is triggered by passing the ``--stubs`` flag to the script. 117In this case, an output file must be provided with a filename ending in ".h", 118and the callback stubs will be written to an equivalent ".c" file. 119 120.. note:: 121 122 The stubs are written to a separate file, 123 to allow continuous use of the script to regenerate the command-line header, 124 without overwriting any code the user has added to the callback functions. 125 This makes it easy to incrementally add new commands to an existing application. 126 127Providing the Function Callbacks 128~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 129 130As discussed above, the script output is a header file, containing structure definitions, 131but the callback functions themselves obviously have to be provided by the user. 132These callback functions must be provided as non-static functions in a C file, 133and named ``cmd_<cmdname>_parsed``. 134The function prototypes can be seen in the generated output header. 135 136The "cmdname" part of the function name is built up by combining the non-variable initial tokens in the command. 137So, given the commands in our worked example below: ``quit`` and ``show port stats <n>``, 138the callback functions would be: 139 140.. code:: c 141 142 void 143 cmd_quit_parsed(void *parsed_result, struct cmdline *cl, void *data) 144 { 145 ... 146 } 147 148 void 149 cmd_show_port_stats_parsed(void *parsed_result, struct cmdline *cl, void *data) 150 { 151 ... 152 } 153 154These functions must be provided by the developer, but, as stated above, 155stub functions may be generated by the script automatically using the ``--stubs`` parameter. 156 157The same "cmdname" stem is used in the naming of the generated structures too. 158To get at the results structure for each command above, 159the ``parsed_result`` parameter should be cast to ``struct cmd_quit_result`` 160or ``struct cmd_show_port_stats_result`` respectively. 161 162.. note:: 163 164 In some cases, the user may want to have an optional variable parameter at the end of a command. 165 Such a variable parameter would not normally be included in the "cmdname" string, 166 leading to duplicate definition errors. 167 To work around this, 168 any variable token with a name prefixed by ``'__'`` will be included in the "cmdname" string, 169 with the prefix removed. 170 Using this, it is possible to have commands, such as: 171 ``start tx_first`` and ``start tx_first <UINT16>__n``, without them conflicting. 172 The resulting code generated will expect functions called ``cmd_start_tx_first_parsed`` 173 and ``cmd_start_tx_first_n_parsed`` respectively. 174 175Integrating with the Application 176~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 177 178To integrate the script output with the application, 179we must ``#include`` the generated header into our applications C file, 180and then have the command-line created via either ``cmdline_new`` or ``cmdline_stdin_new``. 181The first parameter to the function call should be the context array in the generated header file, 182``ctx`` by default. (Modifiable via script parameter). 183 184The callback functions may be in this same file, or in a separate one - 185they just need to be available to the linker at build-time. 186 187Limitations of the Script Approach 188~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 189 190The script approach works for most commands that a user may wish to add to an application. 191However, it does not support the full range of functions possible with the DPDK command-line library. 192For example, 193it is not possible using the script to multiplex multiple commands into a single callback function. 194To use this functionality, the user should follow the instructions in the next section 195`Worked Example of Adding Command-line to an Application`_ to manually configure a command-line instance. 196 197Worked Example of Adding Command-line to an Application 198------------------------------------------------------- 199 200The next few subsections will cover each of the steps listed in `Adding Command-line to an Application`_ in more detail, 201working through an example to add two commands to a command-line instance. 202Those two commands will be: 203 204#. ``quit`` - as the name suggests, to close the application 205 206#. ``show port stats <n>`` - to display on-screen the statistics for a given ethernet port 207 208.. note:: 209 210 For further examples of use of the command-line, see 211 :doc:`cmdline example application <../sample_app_ug/cmd_line>` 212 213Defining Command Result Structure 214~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 215 216The first structure to be defined is the structure which will be created on successful parse of a command. 217This structure contains one member field for each token, or word, in the command. 218The simplest case is for a one-word command, like ``quit``. 219For this, we only need to define a structure with a single string parameter to contain that word. 220 221.. code-block:: c 222 223 struct cmd_quit_result { 224 cmdline_fixed_string_t quit; 225 }; 226 227For readability, the name of the struct member should match that of the token in the command. 228 229For our second command, we need a structure with four member fields in it, 230as there are four words/tokens in our command. 231The first three are strings, and the final one is a 16-bit numeric value. 232The resulting struct looks like: 233 234.. code-block:: c 235 236 struct cmd_show_port_stats_result { 237 cmdline_fixed_string_t show; 238 cmdline_fixed_string_t port; 239 cmdline_fixed_string_t stats; 240 uint16_t n; 241 }; 242 243As before, we choose names to match the tokens in the command. 244Since our numeric parameter is a 16-bit value, we use ``uint16_t`` type for it. 245Any of the standard sized integer types can be used as parameters, depending on the desired result. 246 247Beyond the standard integer types, 248the library also allows variable parameters to be of a number of other types, 249as called out in the feature list above. 250 251* For variable string parameters, 252 the type should be ``cmdline_fixed_string_t`` - the same as for fixed tokens, 253 but these will be initialized differently (as described below). 254 255* For ethernet addresses use type ``struct rte_ether_addr`` 256 257* For IP addresses use type ``cmdline_ipaddr_t`` 258 259Providing Field Initializers 260~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 261 262Each field of our result structure needs an initializer. 263For fixed string tokens, like "quit", "show" and "port", the initializer will be the string itself. 264 265.. code-block:: c 266 267 static cmdline_parse_token_string_t cmd_quit_quit_tok = 268 TOKEN_STRING_INITIALIZER(struct cmd_quit_result, quit, "quit"); 269 270The convention for naming used here is to include the base name of the overall result structure - 271``cmd_quit`` in this case, 272as well as the name of the field within that structure - ``quit`` in this case, followed by ``_tok``. 273(This is why there is a double ``quit`` in the name above). 274 275This naming convention is seen in our second example, 276which also demonstrates how to define a numeric initializer. 277 278 279.. code-block:: c 280 281 static cmdline_parse_token_string_t cmd_show_port_stats_show_tok = 282 TOKEN_STRING_INITIALIZER(struct cmd_show_port_stats_result, show, "show"); 283 static cmdline_parse_token_string_t cmd_show_port_stats_port_tok = 284 TOKEN_STRING_INITIALIZER(struct cmd_show_port_stats_result, port, "port"); 285 static cmdline_parse_token_string_t cmd_show_port_stats_stats_tok = 286 TOKEN_STRING_INITIALIZER(struct cmd_show_port_stats_result, stats, "stats"); 287 static cmdline_parse_token_num_t cmd_show_port_stats_n_tok = 288 TOKEN_NUM_INITIALIZER(struct cmd_show_port_stats_result, n, RTE_UINT16); 289 290For variable string tokens, the same ``TOKEN_STRING_INITIALIZER`` macro should be used. 291However, the final parameter should be ``NULL`` rather than a hard-coded token string. 292 293For numeric parameters, the final parameter to the ``TOKEN_NUM_INITIALIZER`` macro should be the 294cmdline type matching the variable type defined in the result structure, 295e.g. RTE_UINT8, RTE_UINT32, etc. 296 297For IP addresses, the macro ``TOKEN_IPADDR_INITIALIZER`` should be used. 298 299For ethernet addresses, the macro ``TOKEN_ETHERADDR_INITIALIZER`` should be used. 300 301Defining Callback Function 302~~~~~~~~~~~~~~~~~~~~~~~~~~ 303 304For each command, we need to define a function to be called once the command has been recognised. 305The callback function should have type: 306 307.. code:: c 308 309 void (*f)(void *, struct cmdline *, void *) 310 311where the first parameter is a pointer to the result structure defined above, 312the second parameter is the command-line instance, 313and the final parameter is a user-defined pointer provided when we associate the callback with the command. 314Most callback functions only use the first parameter, or none at all, 315but the additional two parameters provide some extra flexibility, 316to allow the callback to work with non-global state in your application. 317 318For our two example commands, the relevant callback functions would look very similar in definition. 319However, within the function body, 320we assume that the user would need to reference the result structure to extract the port number in 321the second case. 322 323.. code:: c 324 325 void 326 cmd_quit_parsed(void *parsed_result, struct cmdline *cl, void *data) 327 { 328 quit = 1; 329 } 330 void 331 cmd_show_port_stats_parsed(void *parsed_result, struct cmdline *cl, void *data) 332 { 333 struct cmd_show_port_stats_result *res = parsed_result; 334 uint16_t port_id = res->n; 335 ... 336 } 337 338 339Associating Callback and Command 340~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 341 342The ``cmdline_parse_inst_t`` type defines a "parse instance", 343i.e. a sequence of tokens to be matched and then an associated function to be called. 344Also included in the instance type are a field for help text for the command, 345and any additional user-defined parameter to be passed to the callback functions referenced above. 346For example, for our simple "quit" command: 347 348.. code-block:: c 349 350 static cmdline_parse_inst_t cmd_quit = { 351 .f = cmd_quit_parsed, 352 .data = NULL, 353 .help_str = "Close the application", 354 .tokens = { 355 (void *)&cmd_quit_quit_tok, 356 NULL 357 } 358 }; 359 360In this case, we firstly identify the callback function to be called, 361then set the user-defined parameter to NULL, 362provide a help message to be given, on request, to the user explaining the command, 363before finally listing out the single token to be matched for this command instance. 364 365For our second, port stats, example, 366as well as making things a little more complicated by having multiple tokens to be matched, 367we can also demonstrate passing in a parameter to the function. 368Let us suppose that our application does not always use all the ports available to it, 369but instead only uses a subset of the ports, stored in an array called ``active_ports``. 370Our stats command, therefore, should only display stats for the currently in-use ports, 371so we pass this ``active_ports`` array. 372(For simplicity of illustration, we shall assume that the array uses a terminating marker, 373e.g. -1 for the end of the port list, so we don't need to pass in a length parameter too.) 374 375.. code-block:: c 376 377 extern int16_t active_ports[]; 378 ... 379 static cmdline_parse_inst_t cmd_show_port_stats = { 380 .f = cmd_show_port_stats_parsed, 381 .data = active_ports, 382 .help_str = "Show statistics for active network ports", 383 .tokens = { 384 (void *)&cmd_show_port_stats_show_tok, 385 (void *)&cmd_show_port_stats_port_tok, 386 (void *)&cmd_show_port_stats_stats_tok, 387 (void *)&cmd_show_port_stats_n_tok, 388 NULL 389 } 390 }; 391 392 393Adding Command to Command-line Context 394~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 395 396Now that we have configured each individual command and callback, 397we need to merge these into a single array of command-line "contexts". 398This context array will be used to create the actual command-line instance in the application. 399Thankfully, each context entry is the same as each parse instance, 400so our array is defined by simply listing out the previously defined command parse instances. 401 402.. code-block:: c 403 404 static cmdline_parse_ctx_t ctx[] = { 405 &cmd_quit, 406 &cmd_show_port_stats, 407 NULL 408 }; 409 410The context list must be terminated by a NULL entry. 411 412Creating a Command-line Instance 413~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 414 415Once we have our ``ctx`` variable defined, 416we now just need to call the API to create the new command-line instance in our application. 417The basic API is ``cmdline_new`` which will create an interactive command-line with all commands available. 418However, if additional features for interactive use - such as tab-completion - 419are desired, it is recommended that ``cmdline_new_stdin`` be used instead. 420 421A pattern that can be used in applications is to use ``cmdline_new`` for processing any startup commands, 422either from file or from the environment (as is done in the "dpdk-test" application), 423and then using ``cmdline_stdin_new`` thereafter to handle the interactive part. 424For example, to handle a startup file and then provide an interactive prompt: 425 426.. code-block:: c 427 428 struct cmdline *cl; 429 int fd = open(startup_file, O_RDONLY); 430 431 if (fd >= 0) { 432 cl = cmdline_new(ctx, "", fd, STDOUT_FILENO); 433 if (cl == NULL) { 434 /* error handling */ 435 } 436 cmdline_interact(cl); 437 cmdline_quit(cl); 438 close(fd); 439 } 440 441 cl = cmdline_stdin_new(ctx, "Proxy>> "); 442 if (cl == NULL) { 443 /* error handling */ 444 } 445 cmdline_interact(cl); 446 cmdline_stdin_exit(cl); 447 448 449Multiplexing Multiple Commands to a Single Function 450~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 451 452To reduce the amount of boiler-plate code needed when creating a command-line for an application, 453it is possible to merge a number of commands together to have them call a separate function. 454This can be done in a number of different ways: 455 456* A callback function can be used as the target for a number of different commands. 457 Which command was used for entry to the function can be determined by examining the first parameter, 458 ``parsed_result`` in our examples above. 459 460* For simple string commands, multiple options can be concatenated using the "#" character. 461 For example: ``exit#quit``, specified as a token initializer, 462 will match either on the string "exit" or the string "quit". 463 464As a concrete example, 465these two techniques are used in the DPDK unit test application ``dpdk-test``, 466where a single command ``cmdline_parse_t`` instance is used for all the "dump_<item>" test cases. 467 468.. literalinclude:: ../../../app/test/commands.c 469 :language: c 470 :start-after: Add the dump_* tests cases 8< 471 :end-before: >8 End of add the dump_* tests cases 472 473 474Examples of Command-line Use in DPDK 475------------------------------------ 476 477To help the user follow the steps provided above, 478the following DPDK files can be consulted for examples of command-line use. 479 480.. note:: 481 482 This is not an exhaustive list of examples of command-line use in DPDK. 483 It is simply a list of a few files that may be of use to the application developer. 484 Some of these referenced files contain more complex examples of use that others. 485 486* ``commands.c/.h`` in ``examples/cmdline`` 487 488* ``mp_commands.c/.h`` in ``examples/multi_process/simple_mp`` 489 490* ``commands.c`` in ``app/test`` 491