Bug Summary

File:.build-ci/../src/plugin.c
Warning:line 430, column 12
Potential leak of memory pointed to by 'help_argv'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-redhat-linux-gnu -O3 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name plugin.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model static -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/__w/nvme-cli/nvme-cli/.build-ci -fcoverage-compilation-dir=/__w/nvme-cli/nvme-cli/.build-ci -resource-dir /usr/bin/../lib/clang/22 -include /__w/nvme-cli/nvme-cli/.build-ci/nvme-config.h -I nvme.p -I . -I .. -I src -I ../src -I ccan -I ../ccan -I libnvme/src -I ../libnvme/src -I /usr/include/json-c -D _FILE_OFFSET_BITS=64 -D _GNU_SOURCE -U NDEBUG -internal-isystem /usr/bin/../lib/clang/22/include -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/16/../../../../x86_64-redhat-linux/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -std=gnu11 -ferror-limit 19 -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fcolor-diagnostics -vectorize-loops -vectorize-slp -analyzer-opt-analyze-headers -analyzer-output=html -faddrsig -fdwarf2-cfi-asm -o /__w/nvme-cli/nvme-cli/.build-ci/scan-results/2026-09-23-073103-589-1 -x c ../src/plugin.c
1// SPDX-License-Identifier: GPL-2.0-or-later
2#include <errno(*__errno_location ()).h>
3#include <stdio.h>
4#include <stdlib.h>
5#include <string.h>
6#include <unistd.h>
7
8#include <libnvme.h>
9
10#include <shared/wrap-util.h>
11
12#include "argconfig.h"
13#include "args.h"
14#include "cleanup.h"
15#include "plugin.h"
16
17static int version_cmd(struct plugin *plugin)
18{
19 struct program *prog = plugin->parent;
20
21 if (plugin->name) {
22 printf("%s %s version %s (git %s)\n",
23 prog->name, plugin->name, plugin->version, GIT_VERSION"v3.1-24-ge924f82");
24 } else {
25 printf("%s version %s (git %s)\n",
26 prog->name, prog->version, GIT_VERSION"v3.1-24-ge924f82");
27 }
28 printf("libnvme version %s (git %s)\n",
29 libnvme_get_version(LIBNVME_VERSION_PROJECT),
30 libnvme_get_version(LIBNVME_VERSION_GIT));
31 return 0;
32}
33
34static int help(int argc, char **argv, struct plugin *plugin)
35{
36 char man[0x100];
37 struct program *prog = plugin->parent;
38 char *str;
39 int i;
40
41 if (argc == 1) {
42 general_help(plugin, NULL((void*)0));
43 return 0;
44 }
45
46 str = argv[1];
47 for (i = 0; plugin->commands[i]; i++) {
48 struct command *command = plugin->commands[i];
49
50 if (strcmp(str, command->name))
51 if (!command->alias ||
52 (command->alias && strcmp(str, command->alias)))
53 continue;
54
55 if (plugin->name)
56 snprintf(man, sizeof(man), "%s-%s-%s", prog->name,
57 plugin->name, command->name);
58 else
59 snprintf(man, sizeof(man), "%s-%s", prog->name, command->name);
60 if (execlp("man", "man", man, (char *)NULL((void*)0)))
61 perror(argv[1]);
62 }
63
64 general_help(plugin, str);
65
66 return 0;
67}
68
69enum ext_filter {
70 EXT_GROUP, /* extension->group matches the given title */
71 EXT_CORE_ONLY, /* core, not shown next to a built-in group */
72 EXT_VENDOR, /* vendor (non-core) */
73};
74
75static bool_Bool extension_matches(struct plugin *extension, enum ext_filter filter,
76 const char *group_title)
77{
78 switch (filter) {
79 case EXT_GROUP:
80 return extension->group && !strcmp(extension->group, group_title);
81 case EXT_CORE_ONLY:
82 return extension->core && !extension->group;
83 case EXT_VENDOR:
84 return !extension->core;
85 }
86 return false0;
87}
88
89static int plugin_name_cmp(const void *a, const void *b)
90{
91 struct plugin *const *pa = a;
92 struct plugin *const *pb = b;
93
94 return strcmp((*pa)->name, (*pb)->name);
95}
96
97/*
98 * Collect extensions matching filter into a name-sorted, NULL-terminated
99 * array so the plugin listings in general_help() print alphabetically
100 * regardless of registration order. Caller must free() the result.
101 */
102static struct plugin **sorted_extensions(struct plugin *extensions, enum ext_filter filter,
103 const char *group_title)
104{
105 struct plugin *extension;
106 struct plugin **list;
107 size_t count = 0, i = 0;
108
109 for (extension = extensions; extension; extension = extension->next)
110 if (extension_matches(extension, filter, group_title))
111 count++;
112
113 list = malloc((count + 1) * sizeof(*list));
114 if (!list)
115 return NULL((void*)0);
116
117 for (extension = extensions; extension; extension = extension->next)
118 if (extension_matches(extension, filter, group_title))
119 list[i++] = extension;
120 list[i] = NULL((void*)0);
121
122 qsort(list, count, sizeof(*list), plugin_name_cmp);
123
124 return list;
125}
126
127static void usage_cmd(struct plugin *plugin)
128{
129 struct program *prog = plugin->parent;
130
131 if (plugin->name)
132 printf("usage: %s %s %s\n", prog->name, plugin->name, prog->usage);
133 else
134 printf("usage: %s %s\n", prog->name, prog->usage);
135}
136
137static void build_command_usage(char *use, size_t size, struct program *prog,
138 struct plugin *plugin, struct command *cmd)
139{
140 const char *device = cmd->no_device ? "" : " <device>";
141
142 if (!plugin->name)
143 snprintf(use, size, "%s %s%s [OPTIONS]", prog->name, cmd->name, device);
144 else
145 snprintf(use, size, "%s %s %s%s [OPTIONS]", prog->name, plugin->name,
146 cmd->name, device);
147}
148
149void plugin_add_group(struct plugin *plugin, const char *title,
150 struct command **commands)
151{
152 struct command_group *group, **tail;
153 struct command **merged;
154 size_t old_count = 0, new_count = 0;
155
156 group = malloc(sizeof(*group));
157 if (!group)
158 return;
159 group->title = title;
160 group->commands = commands;
161 group->next = NULL((void*)0);
162
163 for (tail = &plugin->groups; *tail; tail = &(*tail)->next)
164 ;
165 *tail = group;
166
167 if (plugin->commands)
168 while (plugin->commands[old_count])
169 old_count++;
170 while (commands[new_count])
171 new_count++;
172
173 merged = realloc(plugin->commands, (old_count + new_count + 1) * sizeof(*merged));
174 if (!merged)
175 return;
176
177 memcpy(&merged[old_count], commands, (new_count + 1) * sizeof(*merged));
178 plugin->commands = merged;
179}
180
181static void print_device_desc(void)
182{
183 printf("'<device>' is one of:\n");
184 printf(" - an NVMe controller device (ex: /dev/nvme0)\n");
185 printf(" - an NVMe namespace device (ex: /dev/nvme0n1)\n");
186#ifdef CONFIG_MI
187 printf(" - a mctp address (ex: mctp:<net>,<eid>[:ctrl-id])\n");
188#endif
189}
190
191void general_help(struct plugin *plugin, char *str)
192{
193 struct program *prog = plugin->parent;
194 struct plugin *extension;
195 unsigned int i = 0;
196 unsigned int padding = 15;
197 unsigned int curr_length = 0;
198 bool_Bool have_deprecated = false0;
199 bool_Bool needs_device = false0;
200
201 for (i = 0; plugin->commands[i]; i++) {
202 if (!plugin->commands[i]->no_device) {
203 needs_device = true1;
204 break;
205 }
206 }
207
208 printf("%s-%s\n", prog->name, prog->version);
209
210 usage_cmd(plugin);
211
212 if (prog->desc) {
213 printf("\n");
214 shr_print_word_wrapped(prog->desc, 0, 0, stdoutstdout);
215 printf("\n");
216 }
217
218 if (needs_device) {
219 printf("\n");
220 print_device_desc();
221 }
222
223 if (plugin->desc) {
224 printf("\n");
225 shr_print_word_wrapped(plugin->desc, 0, 0, stdoutstdout);
226 printf("\n");
227 }
228
229 printf("\nThe following are all implemented sub-commands:\n");
230 if (str)
231 printf("Note: Only sub-commands including %s\n", str);
232
233 /*
234 * iterate through all commands to get maximum length
235 * Still need to handle the case of ultra long strings, help messages, etc
236 */
237 for (i = 0; plugin->commands[i]; i++) {
238 curr_length = 2 + strlen(plugin->commands[i]->name);
239 if (padding < curr_length)
240 padding = curr_length;
241 if (plugin->commands[i]->deprecated)
242 have_deprecated = true1;
243 }
244
245 if (plugin->groups) {
246 struct command_group *group;
247
248 for (group = plugin->groups; group; group = group->next) {
249 bool_Bool header_printed = false0;
250
251 for (i = 0; group->commands[i]; i++) {
252 struct command *command = group->commands[i];
253
254 if (command->deprecated)
255 continue;
256 if (str && !strstr(command->name, str)_Generic (0 ? (command->name) : (void *) 1, const void *: (
const char *) (strstr (command->name, str)), default: strstr
(command->name, str))
)
257 continue;
258 if (!header_printed && group->title) {
259 printf("\n\033[1m%s:\033[0m\n", group->title);
260 header_printed = true1;
261 }
262 printf(" %-*s %s\n", padding, command->name, command->help);
263 }
264
265 /*
266 * Core plugins tagged with .group are shown next to the
267 * built-in group they relate to instead of the flat
268 * "core NVMe/NVMeoF plugins" list further down.
269 */
270 if (!plugin->name && group->title) {
271 __cleanup_free__attribute__((cleanup(shr_freep))) struct plugin **sorted =
272 sorted_extensions(prog->extensions->next, EXT_GROUP,
273 group->title);
274 size_t j;
275
276 for (j = 0; sorted && sorted[j]; j++) {
277 extension = sorted[j];
278
279 if (str && !strstr(extension->name, str)_Generic (0 ? (extension->name) : (void *) 1, const void *
: (const char *) (strstr (extension->name, str)), default:
strstr (extension->name, str))
)
280 continue;
281 if (!header_printed) {
282 printf("\n\033[1m%s:\033[0m\n", group->title);
283 header_printed = true1;
284 }
285 printf(" %-*s %s\n", padding, extension->name,
286 extension->desc);
287 }
288 }
289 }
290 } else {
291 /* Not yet migrated to plugin_add_group(): flat listing. */
292 for (i = 0; plugin->commands[i]; i++) {
293 if (plugin->commands[i]->deprecated)
294 continue;
295 if (!str || strstr(plugin->commands[i]->name, str)_Generic (0 ? (plugin->commands[i]->name) : (void *) 1,
const void *: (const char *) (strstr (plugin->commands[i]
->name, str)), default: strstr (plugin->commands[i]->
name, str))
)
296 printf(" %-*s %s\n", padding, plugin->commands[i]->name,
297 plugin->commands[i]->help);
298 }
299 }
300
301 printf("\n");
302 if (!str || strstr("version", str)_Generic (0 ? ("version") : (void *) 1, const void *: (const char
*) (strstr ("version", str)), default: strstr ("version", str
))
)
303 printf(" %-*s %s\n", padding, "version", "Shows the program version");
304 if (!str || strstr("help", str)_Generic (0 ? ("help") : (void *) 1, const void *: (const char
*) (strstr ("help", str)), default: strstr ("help", str))
)
305 printf(" %-*s %s\n", padding, "help", "Display this help");
306 printf("\n");
307
308 if (plugin->name)
309 printf("See '%s %s help <command>' for more information on a specific command\n",
310 prog->name, plugin->name);
311 else
312 printf("See '%s help <command>' for more information on a specific command\n",
313 prog->name);
314
315 /*
316 * The first plugin is the built-in. If we're not showing help for the
317 * built-in, don't show the program's other extensions
318 */
319 if (!plugin->name) {
320 bool_Bool have_core = false0, have_vendor = false0;
321
322 extension = prog->extensions->next;
323 while (extension) {
324 if (extension->core && !extension->group)
325 have_core = true1;
326 else if (!extension->core)
327 have_vendor = true1;
328 extension = extension->next;
329 }
330
331 if (have_core) {
332 __cleanup_free__attribute__((cleanup(shr_freep))) struct plugin **sorted =
333 sorted_extensions(prog->extensions->next, EXT_CORE_ONLY, NULL((void*)0));
334 size_t j;
335
336 printf("\nThe following are core NVMe/NVMeoF plugins:\n");
337 if (str)
338 printf("Note: Only extensions including %s\n", str);
339
340 for (j = 0; sorted && sorted[j]; j++) {
341 if (!str || strstr(sorted[j]->name, str)_Generic (0 ? (sorted[j]->name) : (void *) 1, const void *
: (const char *) (strstr (sorted[j]->name, str)), default:
strstr (sorted[j]->name, str))
)
342 printf(" %-*s %s\n", 15, sorted[j]->name, sorted[j]->desc);
343 }
344 }
345
346 if (have_vendor) {
347 __cleanup_free__attribute__((cleanup(shr_freep))) struct plugin **sorted =
348 sorted_extensions(prog->extensions->next, EXT_VENDOR, NULL((void*)0));
349 size_t j;
350
351 printf("\nThe following are vendor specific plugins:\n");
352 if (str)
353 printf("Note: Only extensions including %s\n", str);
354
355 for (j = 0; sorted && sorted[j]; j++) {
356 if (!str || strstr(sorted[j]->name, str)_Generic (0 ? (sorted[j]->name) : (void *) 1, const void *
: (const char *) (strstr (sorted[j]->name, str)), default:
strstr (sorted[j]->name, str))
)
357 printf(" %-*s %s\n", 15, sorted[j]->name, sorted[j]->desc);
358 }
359 }
360
361 if (have_core || have_vendor)
362 printf("\nSee '%s <plugin> help' for more information on a plugin\n",
363 prog->name);
364 }
365
366 if (have_deprecated) {
367 printf("\nThe following sub-commands are deprecated and will be removed in the next major version:\n");
368 if (str)
369 printf("Note: Only sub-commands including %s\n", str);
370
371 i = 0;
372 for (; plugin->commands[i]; i++) {
373 if (!plugin->commands[i]->deprecated)
374 continue;
375 if (!str || strstr(plugin->commands[i]->name, str)_Generic (0 ? (plugin->commands[i]->name) : (void *) 1,
const void *: (const char *) (strstr (plugin->commands[i]
->name, str)), default: strstr (plugin->commands[i]->
name, str))
)
376 printf(" %-*s %s\n", padding, plugin->commands[i]->name,
377 plugin->commands[i]->help);
378 }
379 }
380}
381
382int handle_plugin(int argc, char **argv, struct plugin *plugin)
383{
384 char use[0x100];
385 struct plugin *extension;
386 struct program *prog = plugin->parent;
387 struct command **cmd = plugin->commands;
388 struct command *cr = NULL((void*)0);
389 bool_Bool opt_help = false0, opt_version = false0;
390 bool_Bool cr_valid = false0;
391 char *str;
392 int err;
393
394 if (!argc) {
1
Assuming 'argc' is not equal to 0
2
Taking false branch
395 general_help(plugin, NULL((void*)0));
396 return 0;
397 }
398
399 /*
400 * look for global options before the sub command parser and
401 * pre-fill the global nvme_args variable.
402 */
403 NVME_ARGS(global_opts,nvme_args.supported_output_formats = (NORMAL | JSON | BINARY)
; struct argconfig_commandline_options global_opts[] = { {"",
0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options"
, 0, ((void*)0)}, {"help", 'h', ((void*)0), CFG_FLAG, &opt_help
, 0, "show help text", 0, }, {"version", 'V', ((void*)0), CFG_FLAG
, &opt_version, 0, "show version", 0, }, {"", 0, ((void*)
0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Global options", 0, (
(void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT, &nvme_args
.verbose, 0, "Increase output verbosity", 0, }, {"quiet", 0, (
(void*)0), CFG_FLAG, &nvme_args.quiet, 0, "suppress informational output"
, 0, }, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args
.output_format, 1, "Output format: normal|json|binary", 0, },
{"timeout", 0, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1
, "timeout value, in milliseconds", 0, }, {"dry-run", 0, ((void
*)0), CFG_FLAG, &nvme_args.dry_run, 0, "show command instead of executing"
, 0, }, {"no-retries", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_retries, 0, "disable retry logic on errors", 0, }, {"no-ioctl-probing"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_ioctl_probing, 0
, "disable 64-bit IOCTL support probing", 0, }, {"output-format-version"
, 0, "NUM", CFG_POSITIVE, &nvme_args.output_format_ver, 1
, "output format version: 1|2", 0, }, {"human-readable", 'H',
((void*)0), CFG_FLAG, &nvme_args.verbose, 0, ((void*)0),
0, ((void*)0), 1}, {"set-options", 0, "KEY=VALUE", CFG_STRING
, &nvme_args.set_options, 1, "set a libnvme library option (key=value[,key=value,...]);"
, 0, }, { ((void*)0) } }
404 OPT_FLAG("help", 'h', &opt_help, "show help text"),nvme_args.supported_output_formats = (NORMAL | JSON | BINARY)
; struct argconfig_commandline_options global_opts[] = { {"",
0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options"
, 0, ((void*)0)}, {"help", 'h', ((void*)0), CFG_FLAG, &opt_help
, 0, "show help text", 0, }, {"version", 'V', ((void*)0), CFG_FLAG
, &opt_version, 0, "show version", 0, }, {"", 0, ((void*)
0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Global options", 0, (
(void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT, &nvme_args
.verbose, 0, "Increase output verbosity", 0, }, {"quiet", 0, (
(void*)0), CFG_FLAG, &nvme_args.quiet, 0, "suppress informational output"
, 0, }, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args
.output_format, 1, "Output format: normal|json|binary", 0, },
{"timeout", 0, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1
, "timeout value, in milliseconds", 0, }, {"dry-run", 0, ((void
*)0), CFG_FLAG, &nvme_args.dry_run, 0, "show command instead of executing"
, 0, }, {"no-retries", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_retries, 0, "disable retry logic on errors", 0, }, {"no-ioctl-probing"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_ioctl_probing, 0
, "disable 64-bit IOCTL support probing", 0, }, {"output-format-version"
, 0, "NUM", CFG_POSITIVE, &nvme_args.output_format_ver, 1
, "output format version: 1|2", 0, }, {"human-readable", 'H',
((void*)0), CFG_FLAG, &nvme_args.verbose, 0, ((void*)0),
0, ((void*)0), 1}, {"set-options", 0, "KEY=VALUE", CFG_STRING
, &nvme_args.set_options, 1, "set a libnvme library option (key=value[,key=value,...]);"
, 0, }, { ((void*)0) } }
405 OPT_FLAG("version", 'V', &opt_version, "show version"))nvme_args.supported_output_formats = (NORMAL | JSON | BINARY)
; struct argconfig_commandline_options global_opts[] = { {"",
0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options"
, 0, ((void*)0)}, {"help", 'h', ((void*)0), CFG_FLAG, &opt_help
, 0, "show help text", 0, }, {"version", 'V', ((void*)0), CFG_FLAG
, &opt_version, 0, "show version", 0, }, {"", 0, ((void*)
0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Global options", 0, (
(void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT, &nvme_args
.verbose, 0, "Increase output verbosity", 0, }, {"quiet", 0, (
(void*)0), CFG_FLAG, &nvme_args.quiet, 0, "suppress informational output"
, 0, }, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args
.output_format, 1, "Output format: normal|json|binary", 0, },
{"timeout", 0, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1
, "timeout value, in milliseconds", 0, }, {"dry-run", 0, ((void
*)0), CFG_FLAG, &nvme_args.dry_run, 0, "show command instead of executing"
, 0, }, {"no-retries", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_retries, 0, "disable retry logic on errors", 0, }, {"no-ioctl-probing"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_ioctl_probing, 0
, "disable 64-bit IOCTL support probing", 0, }, {"output-format-version"
, 0, "NUM", CFG_POSITIVE, &nvme_args.output_format_ver, 1
, "output format version: 1|2", 0, }, {"human-readable", 'H',
((void*)0), CFG_FLAG, &nvme_args.verbose, 0, ((void*)0),
0, ((void*)0), 1}, {"set-options", 0, "KEY=VALUE", CFG_STRING
, &nvme_args.set_options, 1, "set a libnvme library option (key=value[,key=value,...]);"
, 0, }, { ((void*)0) } }
;
406 err = argconfig_parse_global(argc, argv, global_opts);
407 if (err) {
3
Assuming 'err' is 0
4
Taking false branch
408 general_help(plugin, NULL((void*)0));
409 return err;
410 }
411
412 argc -= optind;
413 argv += optind;
414
415 if (opt_help) {
5
Assuming 'opt_help' is true
6
Taking true branch
416 __cleanup_free__attribute__((cleanup(shr_freep))) char **help_argv = NULL((void*)0);
417 __cleanup_free__attribute__((cleanup(shr_freep))) char *help_name = NULL((void*)0);
418
419 if (argc <= 0) {
7
Assuming 'argc' is > 0
8
Taking false branch
420 general_help(plugin, NULL((void*)0));
421 return 0;
422 }
423
424 help_argv = malloc((argc + 1) * sizeof(*help_argv));
9
Memory is allocated
425 if (!help_argv)
10
Assuming 'help_argv' is non-null
11
Taking false branch
426 return -ENOMEM12;
427
428 help_name = strdup("help");
429 if (!help_name)
12
Assuming 'help_name' is null
13
Taking true branch
430 return -ENOMEM12;
14
Potential leak of memory pointed to by 'help_argv'
431
432 help_argv[0] = help_name;
433 memcpy(&help_argv[1], argv, argc * sizeof(*argv));
434 return help(argc + 1, help_argv, plugin);
435 }
436
437 if (opt_version)
438 return version_cmd(plugin);
439
440 if (!argc) {
441 general_help(plugin, NULL((void*)0));
442 return 0;
443 }
444
445 str = argv[0];
446
447 if (!strcmp(str, "help"))
448 return help(argc, argv, plugin);
449 if (!strcmp(str, "version"))
450 return version_cmd(plugin);
451
452 while (*cmd) {
453 if (!strcmp(str, (*cmd)->name) ||
454 ((*cmd)->alias && !strcmp(str, (*cmd)->alias))) {
455 build_command_usage(use, sizeof(use), prog, plugin, *cmd);
456 argconfig_append_usage(use);
457 return (*cmd)->fn(argc, argv, *cmd, plugin);
458 }
459 if (!strncmp(str, (*cmd)->name, strlen(str))) {
460 if (cr) {
461 cr_valid = false0;
462 } else {
463 cr = *cmd;
464 cr_valid = true1;
465 }
466 }
467 cmd++;
468 }
469
470 if (cr && cr_valid) {
471 build_command_usage(use, sizeof(use), prog, plugin, cr);
472 argconfig_append_usage(use);
473 return cr->fn(argc, argv, cr, plugin);
474 }
475
476 /* Check extensions only if this is running the built-in plugin */
477 if (plugin->name) {
478 fprintf(stderrstderr, "ERROR: Invalid sub-command '%s' for plugin %s\n", str, plugin->name);
479 return -ENOTTY25;
480 }
481
482 extension = plugin->next;
483 while (extension) {
484 if (!strcmp(str, extension->name))
485 return handle_plugin(argc, argv, extension);
486 extension = extension->next;
487 }
488
489 /*
490 * If the command is executed with the extension name and
491 * command together ("plugin-command"), run the plug in
492 */
493 extension = plugin->next;
494 while (extension) {
495 if (!strncmp(str, extension->name, strlen(extension->name))) {
496 __cleanup_free__attribute__((cleanup(shr_freep))) char **sub_argv = NULL((void*)0);
497 __cleanup_free__attribute__((cleanup(shr_freep))) char *name_copy = NULL((void*)0);
498
499 sub_argv = malloc((argc + 1) * sizeof(*sub_argv));
500 if (!sub_argv)
501 return -ENOMEM12;
502
503 argv[0] += strlen(extension->name);
504 while (*argv[0] == '-')
505 argv[0]++;
506
507 name_copy = strdup(extension->name);
508 if (!name_copy)
509 return -ENOMEM12;
510
511 sub_argv[0] = name_copy;
512 memcpy(&sub_argv[1], argv, argc * sizeof(*argv));
513
514 return handle_plugin(argc + 1, sub_argv, extension);
515 }
516 extension = extension->next;
517 }
518 fprintf(stderrstderr, "ERROR: Invalid sub-command '%s'\n", str);
519 return -ENOTTY25;
520}