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