client: Use D-Bus helper function to implement services command
[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 void services_list(DBusMessageIter *iter, const char *error,
205                 void *user_data)
206 {
207         if (error == NULL) {
208                 __connmanctl_services_list(iter);
209                 fprintf(stdout, "\n");
210         } else {
211                 fprintf(stderr, "Error: %s\n", error);
212         }
213 }
214
215 static void services_properties(DBusMessageIter *iter, const char *error,
216                 void *user_data)
217 {
218         char *path = user_data;
219         char *str;
220         DBusMessageIter dict;
221
222         if (error == NULL) {
223                 fprintf(stdout, "%s\n", path);
224
225                 dbus_message_iter_recurse(iter, &dict);
226                 __connmanctl_dbus_print(&dict, "  ", " = ", "\n");
227
228                 fprintf(stdout, "\n");
229
230         } else {
231                 str = strrchr(path, '/');
232                 if (str != NULL)
233                         str++;
234                 else
235                         str = path;
236
237                 fprintf(stderr, "Error %s: %s\n", str, error);
238         }
239
240         g_free(user_data);
241 }
242
243 static int cmd_services(char *args[], int num, struct option *options)
244 {
245         char *service_name = NULL;
246         char *path;
247         int c;
248
249         if (num > 3)
250                 return -E2BIG;
251
252         c = parse_args(args[1], options);
253         switch (c) {
254         case -1:
255                 break;
256         case 'p':
257                 if (num < 3)
258                         return -EINVAL;
259                 service_name = args[2];
260                 break;
261         default:
262                 if (num > 2)
263                         return -E2BIG;
264                 service_name = args[1];
265                 break;
266         }
267
268         if (service_name == NULL) {
269                 return __connmanctl_dbus_method_call(connection, "/",
270                         "net.connman.Manager", "GetServices",
271                         services_list, NULL, DBUS_TYPE_INVALID);
272         }
273
274         path = g_strdup_printf("/net/connman/service/%s", service_name);
275         return __connmanctl_dbus_method_call(connection, path,
276                         "net.connman.Service", "GetProperties",
277                         services_properties, path, DBUS_TYPE_INVALID);
278 }
279
280 static void technology_print(DBusMessageIter *iter, const char *error,
281                 void *user_data)
282 {
283         DBusMessageIter array;
284
285         if (error != NULL) {
286                 fprintf(stderr, "Error: %s\n", error);
287                 return;
288         }
289
290         dbus_message_iter_recurse(iter, &array);
291         while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRUCT) {
292                 DBusMessageIter entry, dict;
293                 const char *path;
294
295                 dbus_message_iter_recurse(&array, &entry);
296                 dbus_message_iter_get_basic(&entry, &path);
297                 fprintf(stdout, "%s\n", path);
298
299                 dbus_message_iter_next(&entry);
300
301                 dbus_message_iter_recurse(&entry, &dict);
302                 __connmanctl_dbus_print(&dict, "  ", " = ", "\n");
303                 fprintf(stdout, "\n");
304
305                 dbus_message_iter_next(&array);
306         }
307 }
308
309 static int cmd_technologies(char *args[], int num, struct option *options)
310 {
311         if (num > 1)
312                 return -E2BIG;
313
314         return __connmanctl_dbus_method_call(connection, "/",
315                         "net.connman.Manager", "GetTechnologies",
316                         technology_print, NULL, DBUS_TYPE_INVALID);
317 }
318
319 static void scan_return(DBusMessageIter *iter, const char *error,
320                 void *user_data)
321 {
322         char *path = user_data;
323
324         if (error == NULL) {
325                 char *str = strrchr(path, '/');
326                 str++;
327                 fprintf(stdout, "Scan completed for %s\n", str);
328         } else
329                 fprintf(stderr, "Error %s: %s", path, error);
330
331         g_free(user_data);
332 }
333
334 static int cmd_scan(char *args[], int num, struct option *options)
335 {
336         char *path;
337
338         if (num > 2)
339                 return -E2BIG;
340
341         if (num < 2)
342                 return -EINVAL;
343
344         path = g_strdup_printf("/net/connman/technology/%s", args[1]);
345         return __connmanctl_dbus_method_call(connection, path,
346                         "net.connman.Technology", "Scan",
347                         scan_return, path, DBUS_TYPE_INVALID);
348 }
349
350 static void connect_return(DBusMessageIter *iter, const char *error,
351                 void *user_data)
352 {
353         char *path = user_data;
354
355         if (error == NULL) {
356                 char *str = strrchr(path, '/');
357                 str++;
358                 fprintf(stdout, "Connected %s\n", str);
359         } else
360                 fprintf(stderr, "Error %s: %s\n", path, error);
361
362         g_free(user_data);
363 }
364
365 static int cmd_connect(char *args[], int num, struct option *options)
366 {
367         char *path;
368
369         if (num > 2)
370                 return -E2BIG;
371
372         if (num < 2)
373                 return -EINVAL;
374
375         path = g_strdup_printf("/net/connman/service/%s", args[1]);
376         return __connmanctl_dbus_method_call(connection, path,
377                         "net.connman.Service", "Connect",
378                         connect_return, path, DBUS_TYPE_INVALID);
379 }
380
381 static void disconnect_return(DBusMessageIter *iter, const char *error,
382                 void *user_data)
383 {
384         char *path = user_data;
385
386         if (error == NULL) {
387                 char *str = strrchr(path, '/');
388                 str++;
389                 fprintf(stdout, "Disconnected %s\n", str);
390         } else
391                 fprintf(stderr, "Error %s: %s\n", path, error);
392
393         g_free(user_data);
394 }
395
396 static int cmd_disconnect(char *args[], int num, struct option *options)
397 {
398         char *path;
399
400         if (num > 2)
401                 return -E2BIG;
402
403         if (num < 2)
404                 return -EINVAL;
405
406         path = g_strdup_printf("/net/connman/service/%s", args[1]);
407         return __connmanctl_dbus_method_call(connection, path,
408                         "net.connman.Service", "Disconnect",
409                         disconnect_return, path, DBUS_TYPE_INVALID);
410
411         return 0;
412 }
413
414 static int cmd_config(char *args[], int num, struct option *options)
415 {
416         int res = 0, index = 2, oldindex = 0;
417         int c;
418         char *service_name;
419         DBusMessage *message;
420         char **opt_start;
421         dbus_bool_t val;
422
423         service_name = args[1];
424         if (service_name == NULL)
425                 return -EINVAL;
426
427         while (index < num && args[index] != NULL) {
428                 c = parse_args(args[index], options);
429                 opt_start = &args[index + 1];
430                 res = 0;
431
432                 message = get_message(connection, "GetServices");
433                 if (message == NULL)
434                         return -ENOMEM;
435
436                 oldindex = index;
437
438                 switch (c) {
439                 case 'a':
440                         switch (parse_boolean(*opt_start)) {
441                         case 1:
442                                 val = TRUE;
443                                 break;
444                         case 0:
445                                 val = FALSE;
446                                 break;
447                         default:
448                                 res = -EINVAL;
449                                 break;
450                         }
451                         if (res == 0)
452                                 res = set_service_property(connection, message,
453                                                 service_name, "AutoConnect",
454                                                 NULL, &val, 0);
455                         break;
456                 case 'i':
457                         res = set_service_property(connection, message,
458                                         service_name, "IPv4.Configuration",
459                                         ipv4, opt_start, 0);
460                         if (res < 0)
461                                 index += 4;
462                         break;
463                 case 'v':
464                         res = set_service_property(connection, message,
465                                         service_name, "IPv6.Configuration",
466                                         ipv6, opt_start, 0);
467                         if (res < 0)
468                                 index += 5;
469                         break;
470                 case 'n':
471                         res = set_service_property(connection, message,
472                                         service_name,
473                                         "Nameservers.Configuration",
474                                         NULL, opt_start, 0);
475                         break;
476                 case 't':
477                         res = set_service_property(connection, message,
478                                         service_name,
479                                         "Timeservers.Configuration",
480                                         NULL, opt_start, 0);
481                         break;
482                 case 'd':
483                         res = set_service_property(connection, message,
484                                         service_name,
485                                         "Domains.Configuration",
486                                         NULL, opt_start, 0);
487                         break;
488                 case 'x':
489                         if (*opt_start == NULL) {
490                                 res = -EINVAL;
491                                 break;
492                         }
493
494                         if (strcmp(*opt_start, "direct") == 0) {
495                                 res = set_service_property(connection, message,
496                                                 service_name,
497                                                 "Proxy.Configuration",
498                                                 proxy_simple, opt_start, 1);
499                                 break;
500                         }
501
502                         if (strcmp(*opt_start, "auto") == 0) {
503                                 res = set_service_property(connection, message,
504                                                 service_name,
505                                                 "Proxy.Configuration",
506                                                 proxy_simple, opt_start, 1);
507                                 break;
508                         }
509
510                         if (strcmp(*opt_start, "manual") == 0) {
511                                         char **url_start = &args[index + 2];
512
513                                         if (*url_start != NULL &&
514                                                 strcmp(*url_start,
515                                                         "servers") == 0) {
516                                                 url_start = &args[index + 3];
517                                                 index++;
518                                         }
519                                         res = store_proxy_input(connection,
520                                                         message, service_name,
521                                                         0, url_start);
522                         }
523
524                         break;
525                 case 'r':
526                         res = remove_service(connection, message, service_name);
527                         break;
528                 default:
529                         res = -EINVAL;
530                         break;
531                 }
532
533                 dbus_message_unref(message);
534
535                 if (res < 0) {
536                         printf("Error '%s': %s\n", args[oldindex],
537                                         strerror(-res));
538                 } else
539                         index += res;
540
541                 index++;
542         }
543
544         return 0;
545 }
546
547 static DBusHandlerResult monitor_changed(DBusConnection *connection,
548                 DBusMessage *message, void *user_data)
549 {
550         DBusMessageIter iter;
551         const char *interface, *path;
552
553         interface = dbus_message_get_interface(message);
554         if (strncmp(interface, "net.connman.", 12) != 0)
555                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
556
557         interface = strrchr(interface, '.');
558         if (interface != NULL && *interface != '\0')
559                 interface++;
560
561         path = strrchr(dbus_message_get_path(message), '/');
562         if (path != NULL && *path != '\0')
563                 path++;
564
565         __connmanctl_save_rl();
566
567         if (dbus_message_is_signal(message, "net.connman.Manager",
568                                         "ServicesChanged") == TRUE) {
569
570                 fprintf(stdout, "%-12s %-20s = {\n", interface,
571                                 "ServicesChanged");
572                 dbus_message_iter_init(message, &iter);
573                 __connmanctl_services_list(&iter);
574                 fprintf(stdout, "\n}\n");
575
576                 __connmanctl_redraw_rl();
577
578                 return DBUS_HANDLER_RESULT_HANDLED;
579         }
580
581         if (dbus_message_is_signal(message, "net.connman.Manager",
582                                         "TechnologyAdded") == TRUE)
583                 path = "TechnologyAdded";
584
585         if (dbus_message_is_signal(message, "net.connman.Manager",
586                                         "TechnologyRemoved") == TRUE)
587                 path = "TechnologyRemoved";
588
589         fprintf(stdout, "%-12s %-20s ", interface, path);
590         dbus_message_iter_init(message, &iter);
591
592         __connmanctl_dbus_print(&iter, "", " = ", " = ");
593         fprintf(stdout, "\n");
594
595         __connmanctl_redraw_rl();
596
597         return DBUS_HANDLER_RESULT_HANDLED;
598 }
599
600 static bool monitor_s = false;
601 static bool monitor_t = false;
602 static bool monitor_m = false;
603
604 static void monitor_add(char *interface)
605 {
606         char *rule;
607         DBusError err;
608
609         if (monitor_s == false && monitor_t == false && monitor_m == false)
610                 dbus_connection_add_filter(connection, monitor_changed,
611                                 NULL, NULL);
612
613         if (g_strcmp0(interface, "Service") == 0) {
614                 if (monitor_s == true)
615                         return;
616                 monitor_s = true;
617         } else if (g_strcmp0(interface, "Technology") == 0) {
618                 if (monitor_t == true)
619                         return;
620                 monitor_t = true;
621         } else if (g_strcmp0(interface, "Manager") == 0) {
622                 if (monitor_m == true)
623                         return;
624                 monitor_m = true;
625         } else
626                 return;
627
628         dbus_error_init(&err);
629         rule  = g_strdup_printf("type='signal',interface='net.connman.%s'",
630                         interface);
631         dbus_bus_add_match(connection, rule, &err);
632         g_free(rule);
633
634         if (dbus_error_is_set(&err))
635                 fprintf(stderr, "Error: %s\n", err.message);
636 }
637
638 static void monitor_del(char *interface)
639 {
640         char *rule;
641
642         if (g_strcmp0(interface, "Service") == 0) {
643                 if (monitor_s == false)
644                         return;
645                 monitor_s = false;
646         } else if (g_strcmp0(interface, "Technology") == 0) {
647                 if (monitor_t == false)
648                         return;
649                 monitor_t = false;
650         } else if (g_strcmp0(interface, "Manager") == 0) {
651                 if (monitor_m == false)
652                         return;
653                 monitor_m = false;
654         } else
655                 return;
656
657         rule  = g_strdup_printf("type='signal',interface='net.connman.%s'",
658                         interface);
659         dbus_bus_remove_match(connection, rule, NULL);
660         g_free(rule);
661
662         if (monitor_s == false && monitor_t == false && monitor_m == false)
663                 dbus_connection_remove_filter(connection, monitor_changed,
664                                 NULL);
665 }
666
667 static int cmd_monitor(char *args[], int num, struct option *options)
668 {
669         bool add = true;
670         int c;
671
672         if (num > 3)
673                 return -E2BIG;
674
675         if (num == 3) {
676                 switch (parse_boolean(args[2])) {
677                 case 0:
678                         add = false;
679                         break;
680
681                 default:
682                         break;
683                 }
684         }
685
686         c = parse_args(args[1], options);
687         switch (c) {
688         case -1:
689                 monitor_add("Service");
690                 monitor_add("Technology");
691                 monitor_add("Manager");
692                 break;
693
694         case 's':
695                 if (add == true)
696                         monitor_add("Service");
697                 else
698                         monitor_del("Service");
699                 break;
700
701         case 'c':
702                 if (add == true)
703                         monitor_add("Technology");
704                 else
705                         monitor_del("Technology");
706                 break;
707
708         case 'm':
709                 if (add == true)
710                         monitor_add("Manager");
711                 else
712                         monitor_del("Manager");
713                 break;
714
715         default:
716                 switch(parse_boolean(args[1])) {
717                 case 0:
718                         monitor_del("Service");
719                         monitor_del("Technology");
720                         monitor_del("Manager");
721                         break;
722
723                 case 1:
724                         monitor_add("Service");
725                         monitor_add("Technology");
726                         monitor_add("Manager");
727                         break;
728
729                 default:
730                         return -EINVAL;
731                 }
732         }
733
734         if (add == true)
735                 return -EINPROGRESS;
736
737         return 0;
738 }
739
740 static int cmd_exit(char *args[], int num, struct option *options)
741 {
742         return 1;
743 }
744
745 static struct option service_options[] = {
746         {"properties", required_argument, 0, 'p'},
747         { NULL, }
748 };
749
750 static const char *service_desc[] = {
751         "[<service>]      (obsolete)",
752         NULL
753 };
754
755 static struct option config_options[] = {
756         {"nameservers", required_argument, 0, 'n'},
757         {"timeservers", required_argument, 0, 't'},
758         {"domains", required_argument, 0, 'd'},
759         {"ipv6", required_argument, 0, 'v'},
760         {"proxy", required_argument, 0, 'x'},
761         {"autoconnect", required_argument, 0, 'a'},
762         {"ipv4", required_argument, 0, 'i'},
763         {"remove", 0, 0, 'r'},
764         { NULL, }
765 };
766
767 static const char *config_desc[] = {
768         "<dns1> [<dns2>] [<dns3>]",
769         "<ntp1> [<ntp2>] [...]",
770         "<domain1> [<domain2>] [...]",
771         "off|auto|manual <address> <prefixlength> <gateway> <privacy>",
772         "direct|auto <URL>|manual <URL1> [<URL2>] [...]\n"
773         "                   [exclude <exclude1> [<exclude2>] [...]]",
774         "yes|no",
775         "off|dhcp|manual <address> <prefixlength> <gateway>",
776         "                 Remove service",
777         NULL
778 };
779
780 static struct option monitor_options[] = {
781         {"services", no_argument, 0, 's'},
782         {"tech", no_argument, 0, 'c'},
783         {"manager", no_argument, 0, 'm'},
784         { NULL, }
785 };
786
787 static const char *monitor_desc[] = {
788         "[off]            Monitor only services",
789         "[off]            Monitor only technologies",
790         "[off]            Monitor only manager interface",
791         NULL
792 };
793
794 static const struct {
795         const char *cmd;
796         const char *argument;
797         struct option *options;
798         const char **options_desc;
799         int (*func) (char *args[], int num, struct option *options);
800         const char *desc;
801 } cmd_table[] = {
802         { "enable",       "<technology>|offline", NULL,    NULL,
803           cmd_enable, "Enables given technology or offline mode" },
804         { "disable",      "<technology>|offline", NULL,    NULL,
805           cmd_disable, "Disables given technology or offline mode"},
806         { "state",        NULL,           NULL,            NULL,
807           cmd_state, "Shows if the system is online or offline" },
808         { "services",     "[<service>]",  service_options, &service_desc[0],
809           cmd_services, "Display services" },
810         { "technologies", NULL,           NULL,            NULL,
811           cmd_technologies, "Display technologies" },
812         { "scan",         "<technology>", NULL,            NULL,
813           cmd_scan, "Scans for new services for given technology" },
814         { "connect",      "<service>",    NULL,            NULL,
815           cmd_connect, "Connect a given service" },
816         { "disconnect",   "<service>",    NULL,            NULL,
817           cmd_disconnect, "Disconnect a given service" },
818         { "config",       "<service>",    config_options,  &config_desc[0],
819           cmd_config, "Set service configuration options" },
820         { "monitor",      "[off]",        monitor_options, &monitor_desc[0],
821           cmd_monitor, "Monitor signals from interfaces" },
822         { "help",         NULL,           NULL,            NULL,
823           cmd_help, "Show help" },
824         { "exit",         NULL,           NULL,            NULL,
825           cmd_exit,       "Exit" },
826         { "quit",         NULL,           NULL,            NULL,
827           cmd_exit,       "Quit" },
828         {  NULL, },
829 };
830
831 static int cmd_help(char *args[], int num, struct option *options)
832 {
833         bool interactive = __connmanctl_is_interactive();
834         int i, j;
835
836         if (interactive == false)
837                 fprintf(stdout, "Usage: connmanctl [[command] [args]]\n");
838
839         for (i = 0; cmd_table[i].cmd != NULL; i++) {
840                 const char *cmd = cmd_table[i].cmd;
841                 const char *argument = cmd_table[i].argument;
842                 const char *desc = cmd_table[i].desc;
843
844                 printf("%-12s%-22s%s\n", cmd != NULL? cmd: "",
845                                 argument != NULL? argument: "",
846                                 desc != NULL? desc: "");
847
848                 if (cmd_table[i].options != NULL) {
849                         for (j = 0; cmd_table[i].options[j].name != NULL;
850                              j++) {
851                                 const char *options_desc =
852                                         cmd_table[i].options_desc != NULL ?
853                                         cmd_table[i].options_desc[j]: "";
854
855                                 printf("   --%-12s%s\n",
856                                                 cmd_table[i].options[j].name,
857                                                 options_desc);
858                         }
859                 }
860         }
861
862         if (interactive == false)
863                 fprintf(stdout, "\nNote: arguments and output are considered "
864                                 "EXPERIMENTAL for now.\n");
865
866         return 0;
867 }
868
869 int commands(DBusConnection *dbus_conn, char *argv[], int argc)
870 {
871         int i, result;
872
873         connection = dbus_conn;
874
875         for (i = 0; cmd_table[i].cmd != NULL; i++) {
876                 if (g_strcmp0(cmd_table[i].cmd, argv[0]) == 0 &&
877                                 cmd_table[i].func != NULL) {
878                         result = cmd_table[i].func(argv, argc,
879                                         cmd_table[i].options);
880                         if (result < 0 && result != -EINPROGRESS)
881                                 fprintf(stderr, "Error '%s': %s\n", argv[0],
882                                                 strerror(-result));
883                         return result;
884                 }
885         }
886
887         fprintf(stderr, "Error '%s': Unknown command\n", argv[0]);
888         return -EINVAL;
889 }