xref: /dpdk/doc/guides/prog_guide/cmdline.rst (revision 620b269600244c269e1f3698b18c47c84d9d2a25)
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