client: Use D-Bus helper functions for Technology enabling/disabling
[platform/upstream/connman.git] / client / commands.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2012-2013  Intel Corporation. All rights reserved.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  *
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <errno.h>
31 #include <getopt.h>
32 #include <stdbool.h>
33
34 #include <glib.h>
35 #include <gdbus.h>
36
37 #include "services.h"
38 #include "technology.h"
39 #include "data_manager.h"
40
41 #include "dbus_helpers.h"
42 #include "input.h"
43 #include "commands.h"
44
45 #define MANDATORY_ARGS 3
46
47 static DBusConnection *connection;
48
49 static char *ipv4[] = {
50         "Method",
51         "Address",
52         "Netmask",
53         "Gateway",
54         NULL
55 };
56
57 static char *ipv6[] = {
58         "Method",
59         "Address",
60         "PrefixLength",
61         "Gateway",
62         "Privacy",
63         NULL
64 };
65
66 static char *proxy_simple[] = {
67         "Method",
68         "URL",
69         NULL
70 };
71
72 static int cmd_help(char *args[], int num, struct option *options);
73
74 static int parse_args(char *arg, struct option *options)
75 {
76         int i;
77
78         if (arg == NULL)
79                 return -1;
80
81         for (i = 0; options[i].name != NULL; i++) {
82                 if (strcmp(options[i].name, arg) == 0 ||
83                                 (strncmp(arg, "--", 2) == 0 &&
84                                         strcmp(&arg[2], options[i].name) == 0))
85                         return options[i].val;
86         }
87
88         return '?';
89 }
90
91 static void enable_return(DBusMessageIter *iter, const char *error,
92                 void *user_data)
93 {
94         char *tech = user_data;
95         char *str;
96
97         str = strrchr(tech, '/');
98         if (str != NULL)
99                 str++;
100         else
101                 str = tech;
102
103         if (error == NULL) {
104                 fprintf(stdout, "Enabled %s\n", str);
105         } else
106                 fprintf(stderr, "Error %s: %s\n", str, error);
107
108         g_free(user_data);
109 }
110
111 static int cmd_enable(char *args[], int num, struct option *options)
112 {
113         char *tech;
114         dbus_bool_t b = TRUE;
115
116         if (num > 2)
117                 return -E2BIG;
118
119         if (num < 2)
120                 return -EINVAL;
121
122         if (strcmp(args[1], "offlinemode") == 0) {
123                 tech = g_strdup(args[1]);
124                 return __connmanctl_dbus_set_property(connection, "/",
125                                 "net.connman.Manager", enable_return, tech,
126                                 "OfflineMode", DBUS_TYPE_BOOLEAN, &b);
127         }
128
129         tech = g_strdup_printf("/net/connman/technology/%s", args[1]);
130         return __connmanctl_dbus_set_property(connection, tech,
131                                 "net.connman.Technology", enable_return, tech,
132                                 "Powered", DBUS_TYPE_BOOLEAN, &b);
133 }
134
135 static void disable_return(DBusMessageIter *iter, const char *error,
136                 void *user_data)
137 {
138         char *tech = user_data;
139         char *str;
140
141         str = strrchr(tech, '/');
142         if (str != NULL)
143                 str++;
144         else
145                 str = tech;
146
147         if (error == NULL) {
148                 fprintf(stdout, "Disabled %s\n", str);
149         } else
150                 fprintf(stderr, "Error %s: %s\n", str, error);
151
152         g_free(user_data);
153 }
154
155 static int cmd_disable(char *args[], int num, struct option *options)
156 {
157         char *tech;
158         dbus_bool_t b = FALSE;
159
160         if (num > 2)
161                 return -E2BIG;
162
163         if (num < 2)
164                 return -EINVAL;
165
166         if (strcmp(args[1], "offlinemode") == 0) {
167                 tech = g_strdup(args[1]);
168                 return __connmanctl_dbus_set_property(connection, "/",
169                                 "net.connman.Manager", disable_return, tech,
170                                 "OfflineMode", DBUS_TYPE_BOOLEAN, &b);
171         }
172
173         tech = g_strdup_printf("/net/connman/technology/%s", args[1]);
174         return __connmanctl_dbus_set_property(connection, tech,
175                                 "net.connman.Technology", disable_return, tech,
176                                 "Powered", DBUS_TYPE_BOOLEAN, &b);
177 }
178
179 static void state_print(DBusMessageIter *iter, const char *error,
180                 void *user_data)
181 {
182         DBusMessageIter entry;
183
184         if (error != NULL) {
185                 fprintf(stderr, "Error: %s", error);
186                 return;
187         }
188
189         dbus_message_iter_recurse(iter, &entry);
190         __connmanctl_dbus_print(&entry, "  ", " = ", "\n");
191         fprintf(stdout, "\n");
192 }
193
194 static int cmd_state(char *args[], int num, struct option *options)
195 {
196         if (num > 1)
197                 return -E2BIG;
198
199         return __connmanctl_dbus_method_call(connection, "/",
200                         "net.connman.Manager", "GetProperties",
201                         state_print, NULL, DBUS_TYPE_INVALID);
202 }
203
204 static int cmd_services(char *args[], int num, struct option *options)
205 {
206         char *service_name = NULL;
207         int err = 0;
208         int c;
209         DBusMessage *message;
210
211         if (num > 3)
212                 return -E2BIG;
213
214         c = parse_args(args[1], options);
215         switch (c) {
216         case -1:
217                 break;
218         case 'p':
219                 if (num < 3)
220                         return -EINVAL;
221                 service_name = args[2];
222                 break;
223         default:
224                 if (num > 2)
225                         return -E2BIG;
226                 service_name = args[1];
227                 break;
228         }
229
230         message = get_message(connection, "GetServices");
231         if (message == NULL)
232                 return -ENOMEM;
233
234         err = list_properties(connection, "GetServices", service_name);
235         dbus_message_unref(message);
236
237         return err;
238 }
239
240 static void technology_print(DBusMessageIter *iter, const char *error,
241                 void *user_data)
242 {
243         DBusMessageIter array;
244
245         if (error != NULL) {
246                 fprintf(stderr, "Error: %s\n", error);
247                 return;
248         }
249
250         dbus_message_iter_recurse(iter, &array);
251         while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRUCT) {
252                 DBusMessageIter entry, dict;
253                 const char *path;
254
255                 dbus_message_iter_recurse(&array, &entry);
256                 dbus_message_iter_get_basic(&entry, &path);
257                 fprintf(stdout, "%s\n", path);
258
259                 dbus_message_iter_next(&entry);
260
261                 dbus_message_iter_recurse(&entry, &dict);
262                 __connmanctl_dbus_print(&dict, "  ", " = ", "\n");
263                 fprintf(stdout, "\n");
264
265                 dbus_message_iter_next(&array);
266         }
267 }
268
269 static int cmd_technologies(char *args[], int num, struct option *options)
270 {
271         if (num > 1)
272                 return -E2BIG;
273
274         return __connmanctl_dbus_method_call(connection, "/",
275                         "net.connman.Manager", "GetTechnologies",
276                         technology_print, NULL, DBUS_TYPE_INVALID);
277 }
278
279 static void scan_return(DBusMessageIter *iter, const char *error,
280                 void *user_data)
281 {
282         char *path = user_data;
283
284         if (error == NULL) {
285                 char *str = strrchr(path, '/');
286                 str++;
287                 fprintf(stdout, "Scan completed for %s\n", str);
288         } else
289                 fprintf(stderr, "Error %s: %s", path, error);
290
291         g_free(user_data);
292 }
293
294 static int cmd_scan(char *args[], int num, struct option *options)
295 {
296         char *path;
297
298         if (num > 2)
299                 return -E2BIG;
300
301         if (num < 2)
302                 return -EINVAL;
303
304         path = g_strdup_printf("/net/connman/technology/%s", args[1]);
305         return __connmanctl_dbus_method_call(connection, path,
306                         "net.connman.Technology", "Scan",
307                         scan_return, path, DBUS_TYPE_INVALID);
308 }
309
310 static void connect_return(DBusMessageIter *iter, const char *error,
311                 void *user_data)
312 {
313         char *path = user_data;
314
315         if (error == NULL) {
316                 char *str = strrchr(path, '/');
317                 str++;
318                 fprintf(stdout, "Connected %s\n", str);
319         } else
320                 fprintf(stderr, "Error %s: %s\n", path, error);
321
322         g_free(user_data);
323 }
324
325 static int cmd_connect(char *args[], int num, struct option *options)
326 {
327         char *path;
328
329         if (num > 2)
330                 return -E2BIG;
331
332         if (num < 2)
333                 return -EINVAL;
334
335         path = g_strdup_printf("/net/connman/service/%s", args[1]);
336         return __connmanctl_dbus_method_call(connection, path,
337                         "net.connman.Service", "Connect",
338                         connect_return, path, DBUS_TYPE_INVALID);
339 }
340
341 static void disconnect_return(DBusMessageIter *iter, const char *error,
342                 void *user_data)
343 {
344         char *path = user_data;
345
346         if (error == NULL) {
347                 char *str = strrchr(path, '/');
348                 str++;
349                 fprintf(stdout, "Disconnected %s\n", str);
350         } else
351                 fprintf(stderr, "Error %s: %s\n", path, error);
352
353         g_free(user_data);
354 }
355
356 static int cmd_disconnect(char *args[], int num, struct option *options)
357 {
358         char *path;
359
360         if (num > 2)
361                 return -E2BIG;
362
363         if (num < 2)
364                 return -EINVAL;
365
366         path = g_strdup_printf("/net/connman/service/%s", args[1]);
367         return __connmanctl_dbus_method_call(connection, path,
368                         "net.connman.Service", "Disconnect",
369                         disconnect_return, path, DBUS_TYPE_INVALID);
370
371         return 0;
372 }
373
374 static int cmd_config(char *args[], int num, struct option *options)
375 {
376         int res = 0, index = 2, oldindex = 0;
377         int c;
378         char *service_name;
379         DBusMessage *message;
380         char **opt_start;
381         dbus_bool_t val;
382
383         service_name = args[1];
384         if (service_name == NULL)
385                 return -EINVAL;
386
387         while (index < num && args[index] != NULL) {
388                 c = parse_args(args[index], options);
389                 opt_start = &args[index + 1];
390                 res = 0;
391
392                 message = get_message(connection, "GetServices");
393                 if (message == NULL)
394                         return -ENOMEM;
395
396                 oldindex = index;
397
398                 switch (c) {
399                 case 'a':
400                         switch (parse_boolean(*opt_start)) {
401                         case 1:
402                                 val = TRUE;
403                                 break;
404                         case 0:
405                                 val = FALSE;
406                                 break;
407                         default:
408                                 res = -EINVAL;
409                                 break;
410                         }
411                         if (res == 0)
412                                 res = set_service_property(connection, message,
413                                                 service_name, "AutoConnect",
414                                                 NULL, &val, 0);
415                         break;
416                 case 'i':
417                         res = set_service_property(connection, message,
418                                         service_name, "IPv4.Configuration",
419                                         ipv4, opt_start, 0);
420                         if (res < 0)
421                                 index += 4;
422                         break;
423                 case 'v':
424                         res = set_service_property(connection, message,
425                                         service_name, "IPv6.Configuration",
426                                         ipv6, opt_start, 0);
427                         if (res < 0)
428                                 index += 5;
429                         break;
430                 case 'n':
431                         res = set_service_property(connection, message,
432                                         service_name,
433                                         "Nameservers.Configuration",
434                                         NULL, opt_start, 0);
435                         break;
436                 case 't':
437                         res = set_service_property(connection, message,
438                                         service_name,
439                                         "Timeservers.Configuration",
440                                         NULL, opt_start, 0);
441                         break;
442                 case 'd':
443                         res = set_service_property(connection, message,
444                                         service_name,
445                                         "Domains.Configuration",
446                                         NULL, opt_start, 0);
447                         break;
448                 case 'x':
449                         if (*opt_start == NULL) {
450                                 res = -EINVAL;
451                                 break;
452                         }
453
454                         if (strcmp(*opt_start, "direct") == 0) {
455                                 res = set_service_property(connection, message,
456                                                 service_name,
457                                                 "Proxy.Configuration",
458                                                 proxy_simple, opt_start, 1);
459                                 break;
460                         }
461
462                         if (strcmp(*opt_start, "auto") == 0) {
463                                 res = set_service_property(connection, message,
464                                                 service_name,
465                                                 "Proxy.Configuration",
466                                                 proxy_simple, opt_start, 1);
467                                 break;
468                         }
469
470                         if (strcmp(*opt_start, "manual") == 0) {
471                                         char **url_start = &args[index + 2];
472
473                                         if (*url_start != NULL &&
474                                                 strcmp(*url_start,
475                                                         "servers") == 0) {
476                                                 url_start = &args[index + 3];
477                                                 index++;
478                                         }
479                                         res = store_proxy_input(connection,
480                                                         message, service_name,
481                                                         0, url_start);
482                         }
483
484                         break;
485                 case 'r':
486                         res = remove_service(connection, message, service_name);
487                         break;
488                 default:
489                         res = -EINVAL;
490                         break;
491                 }
492
493                 dbus_message_unref(message);
494
495                 if (res < 0) {
496                         printf("Error '%s': %s\n", args[oldindex],
497                                         strerror(-res));
498                 } else
499                         index += res;
500
501                 index++;
502         }
503
504         return 0;
505 }
506
507 static DBusHandlerResult monitor_changed(DBusConnection *connection,
508                 DBusMessage *message, void *user_data)
509 {
510         DBusMessageIter iter;
511         const char *interface, *path;
512
513         interface = dbus_message_get_interface(message);
514         if (strncmp(interface, "net.connman.", 12) != 0)
515                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
516
517         interface = strrchr(interface, '.');
518         if (interface != NULL && *interface != '\0')
519                 interface++;
520
521         path = strrchr(dbus_message_get_path(message), '/');
522         if (path != NULL && *path != '\0')
523                 path++;
524
525         __connmanctl_save_rl();
526
527         if (dbus_message_is_signal(message, "net.connman.Manager",
528                                         "ServicesChanged") == TRUE) {
529
530                 fprintf(stdout, "%-12s %-20s = {\n", interface,
531                                 "ServicesChanged");
532                 dbus_message_iter_init(message, &iter);
533                 __connmanctl_services_list(&iter);
534                 fprintf(stdout, "\n}\n");
535
536                 __connmanctl_redraw_rl();
537
538                 return DBUS_HANDLER_RESULT_HANDLED;
539         }
540
541         if (dbus_message_is_signal(message, "net.connman.Manager",
542                                         "TechnologyAdded") == TRUE)
543                 path = "TechnologyAdded";
544
545         if (dbus_message_is_signal(message, "net.connman.Manager",
546                                         "TechnologyRemoved") == TRUE)
547                 path = "TechnologyRemoved";
548
549         fprintf(stdout, "%-12s %-20s ", interface, path);
550         dbus_message_iter_init(message, &iter);
551
552         __connmanctl_dbus_print(&iter, "", " = ", " = ");
553         fprintf(stdout, "\n");
554
555         __connmanctl_redraw_rl();
556
557         return DBUS_HANDLER_RESULT_HANDLED;
558 }
559
560 static bool monitor_s = false;
561 static bool monitor_t = false;
562 static bool monitor_m = false;
563
564 static void monitor_add(char *interface)
565 {
566         char *rule;
567         DBusError err;
568
569         if (monitor_s == false && monitor_t == false && monitor_m == false)
570                 dbus_connection_add_filter(connection, monitor_changed,
571                                 NULL, NULL);
572
573         if (g_strcmp0(interface, "Service") == 0) {
574                 if (monitor_s == true)
575                         return;
576                 monitor_s = true;
577         } else if (g_strcmp0(interface, "Technology") == 0) {
578                 if (monitor_t == true)
579                         return;
580                 monitor_t = true;
581         } else if (g_strcmp0(interface, "Manager") == 0) {
582                 if (monitor_m == true)
583                         return;
584                 monitor_m = true;
585         } else
586                 return;
587
588         dbus_error_init(&err);
589         rule  = g_strdup_printf("type='signal',interface='net.connman.%s'",
590                         interface);
591         dbus_bus_add_match(connection, rule, &err);
592         g_free(rule);
593
594         if (dbus_error_is_set(&err))
595                 fprintf(stderr, "Error: %s\n", err.message);
596 }
597
598 static void monitor_del(char *interface)
599 {
600         char *rule;
601
602         if (g_strcmp0(interface, "Service") == 0) {
603                 if (monitor_s == false)
604                         return;
605                 monitor_s = false;
606         } else if (g_strcmp0(interface, "Technology") == 0) {
607                 if (monitor_t == false)
608                         return;
609                 monitor_t = false;
610         } else if (g_strcmp0(interface, "Manager") == 0) {
611                 if (monitor_m == false)
612                         return;
613                 monitor_m = false;
614         } else
615                 return;
616
617         rule  = g_strdup_printf("type='signal',interface='net.connman.%s'",
618                         interface);
619         dbus_bus_remove_match(connection, rule, NULL);
620         g_free(rule);
621
622         if (monitor_s == false && monitor_t == false && monitor_m == false)
623                 dbus_connection_remove_filter(connection, monitor_changed,
624                                 NULL);
625 }
626
627 static int cmd_monitor(char *args[], int num, struct option *options)
628 {
629         bool add = true;
630         int c;
631
632         if (num > 3)
633                 return -E2BIG;
634
635         if (num == 3) {
636                 switch (parse_boolean(args[2])) {
637                 case 0:
638                         add = false;
639                         break;
640
641                 default:
642                         break;
643                 }
644         }
645
646         c = parse_args(args[1], options);
647         switch (c) {
648         case -1:
649                 monitor_add("Service");
650                 monitor_add("Technology");
651                 monitor_add("Manager");
652                 break;
653
654         case 's':
655                 if (add == true)
656                         monitor_add("Service");
657                 else
658                         monitor_del("Service");
659                 break;
660
661         case 'c':
662                 if (add == true)
663                         monitor_add("Technology");
664                 else
665                         monitor_del("Technology");
666                 break;
667
668         case 'm':
669                 if (add == true)
670                         monitor_add("Manager");
671                 else
672                         monitor_del("Manager");
673                 break;
674
675         default:
676                 switch(parse_boolean(args[1])) {
677                 case 0:
678                         monitor_del("Service");
679                         monitor_del("Technology");
680                         monitor_del("Manager");
681                         break;
682
683                 case 1:
684                         monitor_add("Service");
685                         monitor_add("Technology");
686                         monitor_add("Manager");
687                         break;
688
689                 default:
690                         return -EINVAL;
691                 }
692         }
693
694         if (add == true)
695                 return -EINPROGRESS;
696
697         return 0;
698 }
699
700 static int cmd_exit(char *args[], int num, struct option *options)
701 {
702         return 1;
703 }
704
705 static struct option service_options[] = {
706         {"properties", required_argument, 0, 'p'},
707         { NULL, }
708 };
709
710 static const char *service_desc[] = {
711         "[<service>]      (obsolete)",
712         NULL
713 };
714
715 static struct option config_options[] = {
716         {"nameservers", required_argument, 0, 'n'},
717         {"timeservers", required_argument, 0, 't'},
718         {"domains", required_argument, 0, 'd'},
719         {"ipv6", required_argument, 0, 'v'},
720         {"proxy", required_argument, 0, 'x'},
721         {"autoconnect", required_argument, 0, 'a'},
722         {"ipv4", required_argument, 0, 'i'},
723         {"remove", 0, 0, 'r'},
724         { NULL, }
725 };
726
727 static const char *config_desc[] = {
728         "<dns1> [<dns2>] [<dns3>]",
729         "<ntp1> [<ntp2>] [...]",
730         "<domain1> [<domain2>] [...]",
731         "off|auto|manual <address> <prefixlength> <gateway> <privacy>",
732         "direct|auto <URL>|manual <URL1> [<URL2>] [...]\n"
733         "                   [exclude <exclude1> [<exclude2>] [...]]",
734         "yes|no",
735         "off|dhcp|manual <address> <prefixlength> <gateway>",
736         "                 Remove service",
737         NULL
738 };
739
740 static struct option monitor_options[] = {
741         {"services", no_argument, 0, 's'},
742         {"tech", no_argument, 0, 'c'},
743         {"manager", no_argument, 0, 'm'},
744         { NULL, }
745 };
746
747 static const char *monitor_desc[] = {
748         "[off]            Monitor only services",
749         "[off]            Monitor only technologies",
750         "[off]            Monitor only manager interface",
751         NULL
752 };
753
754 static const struct {
755         const char *cmd;
756         const char *argument;
757         struct option *options;
758         const char **options_desc;
759         int (*func) (char *args[], int num, struct option *options);
760         const char *desc;
761 } cmd_table[] = {
762         { "enable",       "<technology>|offline", NULL,    NULL,
763           cmd_enable, "Enables given technology or offline mode" },
764         { "disable",      "<technology>|offline", NULL,    NULL,
765           cmd_disable, "Disables given technology or offline mode"},
766         { "state",        NULL,           NULL,            NULL,
767           cmd_state, "Shows if the system is online or offline" },
768         { "services",     "[<service>]",  service_options, &service_desc[0],
769           cmd_services, "Display services" },
770         { "technologies", NULL,           NULL,            NULL,
771           cmd_technologies, "Display technologies" },
772         { "scan",         "<technology>", NULL,            NULL,
773           cmd_scan, "Scans for new services for given technology" },
774         { "connect",      "<service>",    NULL,            NULL,
775           cmd_connect, "Connect a given service" },
776         { "disconnect",   "<service>",    NULL,            NULL,
777           cmd_disconnect, "Disconnect a given service" },
778         { "config",       "<service>",    config_options,  &config_desc[0],
779           cmd_config, "Set service configuration options" },
780         { "monitor",      "[off]",        monitor_options, &monitor_desc[0],
781           cmd_monitor, "Monitor signals from interfaces" },
782         { "help",         NULL,           NULL,            NULL,
783           cmd_help, "Show help" },
784         { "exit",         NULL,           NULL,            NULL,
785           cmd_exit,       "Exit" },
786         { "quit",         NULL,           NULL,            NULL,
787           cmd_exit,       "Quit" },
788         {  NULL, },
789 };
790
791 static int cmd_help(char *args[], int num, struct option *options)
792 {
793         bool interactive = __connmanctl_is_interactive();
794         int i, j;
795
796         if (interactive == false)
797                 fprintf(stdout, "Usage: connmanctl [[command] [args]]\n");
798
799         for (i = 0; cmd_table[i].cmd != NULL; i++) {
800                 const char *cmd = cmd_table[i].cmd;
801                 const char *argument = cmd_table[i].argument;
802                 const char *desc = cmd_table[i].desc;
803
804                 printf("%-12s%-22s%s\n", cmd != NULL? cmd: "",
805                                 argument != NULL? argument: "",
806                                 desc != NULL? desc: "");
807
808                 if (cmd_table[i].options != NULL) {
809                         for (j = 0; cmd_table[i].options[j].name != NULL;
810                              j++) {
811                                 const char *options_desc =
812                                         cmd_table[i].options_desc != NULL ?
813                                         cmd_table[i].options_desc[j]: "";
814
815                                 printf("   --%-12s%s\n",
816                                                 cmd_table[i].options[j].name,
817                                                 options_desc);
818                         }
819                 }
820         }
821
822         if (interactive == false)
823                 fprintf(stdout, "\nNote: arguments and output are considered "
824                                 "EXPERIMENTAL for now.\n");
825
826         return 0;
827 }
828
829 int commands(DBusConnection *dbus_conn, char *argv[], int argc)
830 {
831         int i, result;
832
833         connection = dbus_conn;
834
835         for (i = 0; cmd_table[i].cmd != NULL; i++) {
836                 if (g_strcmp0(cmd_table[i].cmd, argv[0]) == 0 &&
837                                 cmd_table[i].func != NULL) {
838                         result = cmd_table[i].func(argv, argc,
839                                         cmd_table[i].options);
840                         if (result < 0 && result != -EINPROGRESS)
841                                 fprintf(stderr, "Error '%s': %s\n", argv[0],
842                                                 strerror(-result));
843                         return result;
844                 }
845         }
846
847         fprintf(stderr, "Error '%s': Unknown command\n", argv[0]);
848         return -EINVAL;
849 }