stktest: Rework state logic
[platform/upstream/ofono.git] / tools / stktest.c
1 /*
2  *
3  *  oFono - Open Source Telephony
4  *
5  *  Copyright (C) 2008-2011  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 version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdio.h>
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <unistd.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <signal.h>
33 #include <netinet/in.h>
34 #include <arpa/inet.h>
35
36 #include <gdbus.h>
37 #include <gatchat/gatserver.h>
38
39 #define OFONO_SERVICE   "org.ofono"
40 #define STKTEST_PATH    "/stktest"
41 #define OFONO_MANAGER_INTERFACE         OFONO_SERVICE ".Manager"
42 #define OFONO_MODEM_INTERFACE           OFONO_SERVICE ".Modem"
43 #define OFONO_STK_INTERFACE             OFONO_SERVICE ".SimToolkit"
44
45 #define LISTEN_PORT     12765
46
47 enum test_state {
48         TEST_STATE_POWERING_UP = 1,
49         TEST_STATE_REGISTERING_AGENT,
50         TEST_STATE_RUNNING,
51         TEST_STATE_POWERING_DOWN,
52 };
53
54 static GMainLoop *main_loop = NULL;
55 static volatile sig_atomic_t __terminated = 0;
56
57 /* DBus related */
58 static DBusConnection *conn;
59 static gboolean ofono_running = FALSE;
60 static guint modem_changed_watch;
61 enum test_state state;
62
63 /* Emulator setup */
64 static guint server_watch;
65 static GAtServer *emulator;
66
67 /* Emulated modem state variables */
68 static int modem_mode = 0;
69
70 static gboolean create_tcp(void);
71
72 static void server_debug(const char *str, void *data)
73 {
74         g_print("%s: %s\n", (char *) data, str);
75 }
76
77 static void cgmi_cb(GAtServer *server, GAtServerRequestType type,
78                         GAtResult *cmd, gpointer user)
79 {
80         switch (type) {
81         case G_AT_SERVER_REQUEST_TYPE_COMMAND_ONLY:
82                 g_at_server_send_info(server, "oFono", TRUE);
83                 g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
84                 break;
85         case G_AT_SERVER_REQUEST_TYPE_SUPPORT:
86                 g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
87                 break;
88         default:
89                 g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
90         };
91 }
92
93 static void cgmm_cb(GAtServer *server, GAtServerRequestType type,
94                         GAtResult *cmd, gpointer user)
95 {
96         switch (type) {
97         case G_AT_SERVER_REQUEST_TYPE_COMMAND_ONLY:
98                 g_at_server_send_info(server, "oFono pre-1.0", TRUE);
99                 g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
100                 break;
101         case G_AT_SERVER_REQUEST_TYPE_SUPPORT:
102                 g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
103                 break;
104         default:
105                 g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
106         };
107 }
108
109 static void cgmr_cb(GAtServer *server, GAtServerRequestType type,
110                         GAtResult *cmd, gpointer user)
111 {
112         char buf[256];
113
114         switch (type) {
115         case G_AT_SERVER_REQUEST_TYPE_COMMAND_ONLY:
116                 sprintf(buf, "oFono pre-1.0 version: %s", VERSION);
117                 g_at_server_send_info(server, buf, TRUE);
118                 g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
119                 break;
120         case G_AT_SERVER_REQUEST_TYPE_SUPPORT:
121                 g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
122                 break;
123         default:
124                 g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
125         };
126 }
127
128 static void cgsn_cb(GAtServer *server, GAtServerRequestType type,
129                         GAtResult *cmd, gpointer user)
130 {
131         switch (type) {
132         case G_AT_SERVER_REQUEST_TYPE_COMMAND_ONLY:
133                 g_at_server_send_info(server, "123456789", TRUE);
134                 g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
135                 break;
136         case G_AT_SERVER_REQUEST_TYPE_SUPPORT:
137                 g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
138                 break;
139         default:
140                 g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
141         };
142 }
143
144 static gboolean send_ok(gpointer user)
145 {
146         GAtServer *server = user;
147
148         g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
149
150         return FALSE;
151 }
152
153 static void cfun_cb(GAtServer *server, GAtServerRequestType type,
154                         GAtResult *cmd, gpointer user)
155 {
156         char buf[12];
157
158         switch (type) {
159         case G_AT_SERVER_REQUEST_TYPE_SUPPORT:
160                 g_at_server_send_info(server, "+CFUN: (0-1,4)", TRUE);
161                 g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
162                 break;
163         case G_AT_SERVER_REQUEST_TYPE_QUERY:
164                 snprintf(buf, sizeof(buf), "+CFUN: %d", modem_mode);
165                 g_at_server_send_info(server, buf, TRUE);
166                 g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
167                 break;
168         case G_AT_SERVER_REQUEST_TYPE_SET:
169         {
170                 GAtResultIter iter;
171                 int mode;
172
173                 g_at_result_iter_init(&iter, cmd);
174                 g_at_result_iter_next(&iter, "");
175
176                 if (g_at_result_iter_next_number(&iter, &mode) == FALSE)
177                         goto error;
178
179                 if (mode != 0 && mode != 1)
180                         goto error;
181
182                 if (modem_mode == mode) {
183                         g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
184                         break;
185                 }
186
187                 modem_mode = mode;
188                 g_timeout_add_seconds(1, send_ok, server);
189                 break;
190         }
191         default:
192                 goto error;
193         };
194
195         return;
196
197 error:
198         g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
199 }
200
201 static void listen_again(gpointer user_data)
202 {
203         if (create_tcp() == TRUE)
204                 return;
205
206         g_print("Error listening to socket\n");
207         g_main_loop_quit(main_loop);
208 }
209
210 static void setup_emulator(GAtServer *server)
211 {
212         g_at_server_set_debug(server, server_debug, "Server");
213
214         g_at_server_register(server, "+CGMI",    cgmi_cb,    NULL, NULL);
215         g_at_server_register(server, "+CGMM",    cgmm_cb,    NULL, NULL);
216         g_at_server_register(server, "+CGMR",    cgmr_cb,    NULL, NULL);
217         g_at_server_register(server, "+CGSN",    cgsn_cb,    NULL, NULL);
218         g_at_server_register(server, "+CFUN",    cfun_cb,    NULL, NULL);
219
220         g_at_server_set_disconnect_function(server, listen_again, NULL);
221 }
222
223 static gboolean on_socket_connected(GIOChannel *chan, GIOCondition cond,
224                                                         gpointer user)
225 {
226         struct sockaddr saddr;
227         unsigned int len = sizeof(saddr);
228         int fd;
229         GIOChannel *client_io = NULL;
230
231         if (cond != G_IO_IN)
232                 goto error;
233
234         fd = accept(g_io_channel_unix_get_fd(chan), &saddr, &len);
235         if (fd == -1)
236                 goto error;
237
238         client_io = g_io_channel_unix_new(fd);
239
240         emulator = g_at_server_new(client_io);
241         g_io_channel_unref(client_io);
242
243         if (emulator == NULL)
244                 goto error;
245
246         setup_emulator(emulator);
247
248 error:
249         server_watch = 0;
250         return FALSE;
251 }
252
253 static gboolean create_tcp(void)
254 {
255         struct sockaddr_in addr;
256         int sk;
257         int reuseaddr = 1;
258         GIOChannel *server_io;
259
260         sk = socket(PF_INET, SOCK_STREAM, 0);
261         if (sk < 0) {
262                 g_print("Can't create tcp/ip socket: %s (%d)\n",
263                                                 strerror(errno), errno);
264                 return FALSE;
265         }
266
267         memset(&addr, 0, sizeof(addr));
268
269         addr.sin_family = AF_INET;
270         addr.sin_addr.s_addr = INADDR_ANY;
271         addr.sin_port = htons(LISTEN_PORT);
272
273         setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr));
274         if (bind(sk, (struct sockaddr *) &addr, sizeof(struct sockaddr)) < 0) {
275                 g_print("Can't bind socket: %s (%d)", strerror(errno), errno);
276                 close(sk);
277                 return FALSE;
278         }
279
280         if (listen(sk, 1) < 0) {
281                 g_print("Can't listen on socket: %s (%d)",
282                                                 strerror(errno), errno);
283                 close(sk);
284                 return FALSE;
285         }
286
287         g_print("new tcp is created at tcp port %d\n", LISTEN_PORT);
288
289         server_io = g_io_channel_unix_new(sk);
290         g_io_channel_set_close_on_unref(server_io, TRUE);
291
292         server_watch = g_io_add_watch_full(server_io,
293                                 G_PRIORITY_DEFAULT,
294                                 G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
295                                 on_socket_connected, NULL, NULL);
296
297         g_io_channel_unref(server_io);
298
299         return TRUE;
300 }
301
302 static gboolean has_stk_interface(DBusMessageIter *iter)
303 {
304         DBusMessageIter entry;
305
306         dbus_message_iter_recurse(iter, &entry);
307
308         while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) {
309                 const char *interface;
310
311                 dbus_message_iter_get_basic(&entry, &interface);
312
313                 if (g_str_equal(interface, OFONO_STK_INTERFACE) == TRUE)
314                         return TRUE;
315
316                 dbus_message_iter_next(&entry);
317         }
318
319         return FALSE;
320 }
321
322 static void set_property_reply(DBusPendingCall *call, void *user_data)
323 {
324         DBusMessage *reply = dbus_pending_call_steal_reply(call);
325         DBusError err;
326
327         dbus_error_init(&err);
328
329         if (dbus_set_error_from_message(&err, reply) == TRUE) {
330                 g_printerr("%s: %s\n", err.name, err.message);
331                 dbus_error_free(&err);
332         }
333
334         dbus_message_unref(reply);
335 }
336
337 static int set_property(const char *path, const char *interface,
338                         const char *key, int type, const void *val,
339                         DBusPendingCallNotifyFunction notify,
340                         gpointer user_data,
341                         DBusFreeFunction destroy)
342 {
343         DBusMessage *msg;
344         DBusMessageIter iter, value;
345         DBusPendingCall *call;
346         const char *signature;
347
348         msg = dbus_message_new_method_call(OFONO_SERVICE, path, interface,
349                                                 "SetProperty");
350         if (msg == NULL)
351                 return -ENOMEM;
352
353         dbus_message_set_auto_start(msg, FALSE);
354
355         dbus_message_iter_init_append(msg, &iter);
356
357         dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &key);
358
359         switch (type) {
360         case DBUS_TYPE_BOOLEAN:
361                 signature = DBUS_TYPE_BOOLEAN_AS_STRING;
362                 break;
363         default:
364                 dbus_message_unref(msg);
365                 return -EINVAL;
366         }
367
368         dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
369                                                         signature, &value);
370         dbus_message_iter_append_basic(&value, type, val);
371         dbus_message_iter_close_container(&iter, &value);
372
373         if (dbus_connection_send_with_reply(conn, msg, &call, -1) == FALSE) {
374                 dbus_message_unref(msg);
375                 return -EIO;
376         }
377
378         dbus_message_unref(msg);
379
380         if (call == NULL)
381                 return -EINVAL;
382
383         dbus_pending_call_set_notify(call, notify, user_data, destroy);
384
385         dbus_pending_call_unref(call);
386
387         return 0;
388 }
389
390 static void register_agent()
391 {
392         state = TEST_STATE_REGISTERING_AGENT;
393         g_print("Gained STK interface, registering agent...\n");
394 }
395
396 static gboolean modem_changed(DBusConnection *conn,
397                                 DBusMessage *msg, void *user_data)
398 {
399         DBusMessageIter iter, value;
400         const char *path, *key;
401         gboolean has_stk;
402
403         if (dbus_message_iter_init(msg, &iter) == FALSE)
404                 return TRUE;
405
406         path = dbus_message_get_path(msg);
407
408         if (g_str_equal(STKTEST_PATH, path) == FALSE)
409                 return TRUE;
410
411         dbus_message_iter_get_basic(&iter, &key);
412
413         dbus_message_iter_next(&iter);
414         dbus_message_iter_recurse(&iter, &value);
415
416         if (g_str_equal(key, "Interfaces") == FALSE)
417                 return TRUE;
418
419         has_stk = has_stk_interface(&value);
420
421         switch (state) {
422         case TEST_STATE_POWERING_UP:
423                 if (has_stk)
424                         register_agent();
425                 break;
426         case TEST_STATE_REGISTERING_AGENT:
427         case TEST_STATE_RUNNING:
428                 if (has_stk == FALSE)
429                         g_printerr("Unexpectedly lost STK interface\n");
430                 /* Fall through */
431         case TEST_STATE_POWERING_DOWN:
432                 break;
433         };
434
435         return TRUE;
436 }
437
438 static void powerup(void)
439 {
440         dbus_bool_t powered = TRUE;
441
442         state = TEST_STATE_POWERING_UP;
443         set_property(STKTEST_PATH, OFONO_MODEM_INTERFACE, "Powered",
444                         DBUS_TYPE_BOOLEAN, &powered,
445                         set_property_reply, NULL, NULL);
446 }
447
448 static void get_modems_reply(DBusPendingCall *call, void *user_data)
449 {
450         DBusMessage *reply = dbus_pending_call_steal_reply(call);
451         DBusMessageIter iter, list;
452         DBusError err;
453         gboolean found = FALSE;
454
455         dbus_error_init(&err);
456
457         if (dbus_set_error_from_message(&err, reply) == TRUE) {
458                 g_printerr("%s: %s\n", err.name, err.message);
459                 dbus_error_free(&err);
460                 goto done;
461         }
462
463         if (dbus_message_has_signature(reply, "a(oa{sv})") == FALSE)
464                 goto done;
465
466         if (dbus_message_iter_init(reply, &iter) == FALSE)
467                 goto done;
468
469         dbus_message_iter_recurse(&iter, &list);
470
471         while (dbus_message_iter_get_arg_type(&list) == DBUS_TYPE_STRUCT) {
472                 DBusMessageIter entry;
473                 const char *path;
474
475                 dbus_message_iter_recurse(&list, &entry);
476                 dbus_message_iter_get_basic(&entry, &path);
477
478                 if (g_str_equal(path, STKTEST_PATH))
479                         found = TRUE;
480
481                 dbus_message_iter_next(&list);
482         }
483
484 done:
485         dbus_message_unref(reply);
486
487         if (found == FALSE) {
488                 g_printerr("STK Test modem not found\n");
489                 g_main_loop_quit(main_loop);
490                 return;
491         }
492
493         g_print("Test modem found\n");
494
495         modem_changed_watch = g_dbus_add_signal_watch(conn, OFONO_SERVICE,
496                                                         STKTEST_PATH,
497                                                         OFONO_MODEM_INTERFACE,
498                                                         "PropertyChanged",
499                                                         modem_changed,
500                                                         NULL, NULL);
501
502         if (create_tcp() == FALSE) {
503                 g_printerr("Unable to listen on modem emulator socket\n");
504                 g_main_loop_quit(main_loop);
505         }
506
507         powerup();
508 }
509
510 static int get_modems(DBusConnection *conn)
511 {
512         DBusMessage *msg;
513         DBusPendingCall *call;
514
515         msg = dbus_message_new_method_call(OFONO_SERVICE, "/",
516                                         OFONO_MANAGER_INTERFACE, "GetModems");
517         if (msg == NULL)
518                 return -ENOMEM;
519
520         dbus_message_set_auto_start(msg, FALSE);
521
522         g_print("getting modems\n");
523
524         if (dbus_connection_send_with_reply(conn, msg, &call, -1) == FALSE) {
525                 dbus_message_unref(msg);
526                 return -EIO;
527         }
528
529         dbus_message_unref(msg);
530
531         if (call == NULL)
532                 return -EINVAL;
533
534         dbus_pending_call_set_notify(call, get_modems_reply, conn, NULL);
535
536         dbus_pending_call_unref(call);
537
538         return 0;
539 }
540
541 static void ofono_connect(DBusConnection *conn, void *user_data)
542 {
543         g_print("starting telephony interface\n");
544
545         ofono_running = TRUE;
546
547         get_modems(conn);
548 }
549
550 static void ofono_disconnect(DBusConnection *conn, void *user_data)
551 {
552         g_print("stopping telephony interface\n");
553
554         ofono_running = FALSE;
555
556         g_dbus_remove_watch(conn, modem_changed_watch);
557         modem_changed_watch = 0;
558
559         if (server_watch) {
560                 g_source_remove(server_watch);
561                 server_watch = 0;
562         }
563
564         g_at_server_unref(emulator);
565         emulator = NULL;
566 }
567
568 static void sig_term(int sig)
569 {
570         if (__terminated > 0)
571                 return;
572
573         __terminated = 1;
574
575         g_print("Terminating\n");
576
577         g_main_loop_quit(main_loop);
578 }
579
580 static void disconnect_callback(DBusConnection *conn, void *user_data)
581 {
582         g_printerr("D-Bus disconnect\n");
583
584         g_main_loop_quit(main_loop);
585 }
586
587 static gboolean option_version = FALSE;
588
589 static GOptionEntry options[] = {
590         { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version,
591                                 "Show version information and exit" },
592         { NULL },
593 };
594
595 int main(int argc, char **argv)
596 {
597         GOptionContext *context;
598         GError *error = NULL;
599         DBusError err;
600         guint watch;
601         struct sigaction sa;
602
603         context = g_option_context_new(NULL);
604         g_option_context_add_main_entries(context, options, NULL);
605
606         if (g_option_context_parse(context, &argc, &argv, &error) == FALSE) {
607                 if (error != NULL) {
608                         g_printerr("%s\n", error->message);
609                         g_error_free(error);
610                 } else
611                         g_printerr("An unknown error occurred\n");
612                 exit(1);
613         }
614
615         g_option_context_free(context);
616
617         if (option_version == TRUE) {
618                 printf("%s\n", VERSION);
619                 exit(0);
620         }
621
622         main_loop = g_main_loop_new(NULL, FALSE);
623
624         dbus_error_init(&err);
625
626         conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, &err);
627         if (conn == NULL) {
628                 if (dbus_error_is_set(&err) == TRUE) {
629                         fprintf(stderr, "%s\n", err.message);
630                         dbus_error_free(&err);
631                 } else
632                         fprintf(stderr, "Can't register with system bus\n");
633                 exit(1);
634         }
635
636         g_dbus_set_disconnect_function(conn, disconnect_callback, NULL, NULL);
637
638         memset(&sa, 0, sizeof(sa));
639         sa.sa_handler = sig_term;
640         sigaction(SIGINT, &sa, NULL);
641         sigaction(SIGTERM, &sa, NULL);
642
643         watch = g_dbus_add_service_watch(conn, OFONO_SERVICE,
644                                 ofono_connect, ofono_disconnect, NULL, NULL);
645
646         g_main_loop_run(main_loop);
647
648         g_dbus_remove_watch(conn, watch);
649
650         if (ofono_running == TRUE)
651                 ofono_disconnect(conn, NULL);
652
653         dbus_connection_unref(conn);
654
655         g_main_loop_unref(main_loop);
656
657         return 0;
658 }