tizen 2.3 release
[framework/connectivity/bluez.git] / profiles / iap / main.c
1 /*
2  *
3  *  BlueZ - Bluetooth protocol stack for Linux
4  *
5  *  Copyright (C) 2012  Marcel Holtmann <marcel@holtmann.org>
6  *
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program; if not, write to the Free Software
20  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21  *
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <stdio.h>
29 #include <errno.h>
30 #include <ctype.h>
31 #include <unistd.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <signal.h>
35 #include <sys/signalfd.h>
36
37 #include <glib.h>
38 #include <gdbus.h>
39
40 #define IAP_PATH "/org/bluez/iap"
41
42 #define IAP_UUID "00000000-deca-fade-deca-deafdecacafe"
43
44 #define IAP_RECORD                                                      \
45         "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>                    \
46         <record>                                                        \
47                 <attribute id=\"0x0001\">                               \
48                         <sequence>                                      \
49                                 <uuid value=\"%s\" />                   \
50                         </sequence>                                     \
51                 </attribute>                                            \
52                 <attribute id=\"0x0002\">                               \
53                         <uint32 value=\"0x00000000\" />                 \
54                 </attribute>                                            \
55                 <attribute id=\"0x0004\">                               \
56                         <sequence>                                      \
57                                 <sequence>                              \
58                                         <uuid value=\"0x0100\" />       \
59                                 </sequence>                             \
60                                 <sequence>                              \
61                                         <uuid value=\"0x0003\" />       \
62                                         <uint8 value=\"0x%02x\" />      \
63                                 </sequence>                             \
64                         </sequence>                                     \
65                 </attribute>                                            \
66                 <attribute id=\"0x0005\">                               \
67                         <sequence>                                      \
68                                 <uuid value=\"0x1002\" />               \
69                         </sequence>                                     \
70                 </attribute>                                            \
71                 <attribute id=\"0x0006\">                               \
72                         <sequence>                                      \
73                                 <uint16 value=\"0x656e\" />             \
74                                 <uint16 value=\"0x006a\" />             \
75                                 <uint16 value=\"0x0100\" />             \
76                                 <uint16 value=\"0x6672\" />             \
77                                 <uint16 value=\"0x006a\" />             \
78                                 <uint16 value=\"0x0110\" />             \
79                                 <uint16 value=\"0x6465\" />             \
80                                 <uint16 value=\"0x006a\" />             \
81                                 <uint16 value=\"0x0120\" />             \
82                                 <uint16 value=\"0x6a61\" />             \
83                                 <uint16 value=\"0x006a\" />             \
84                                 <uint16 value=\"0x0130\" />             \
85                         </sequence>                                     \
86                 </attribute>                                            \
87                 <attribute id=\"0x0008\">                               \
88                         <uint8 value=\"0xff\" />                        \
89                 </attribute>                                            \
90                 <attribute id=\"0x0009\">                               \
91                         <sequence>                                      \
92                                 <sequence>                              \
93                                         <uuid value=\"0x1101\" />       \
94                                         <uint16 value=\"0x0100\" />     \
95                                 </sequence>                             \
96                         </sequence>                                     \
97                 </attribute>                                            \
98                 <attribute id=\"0x0100\">                               \
99                         <text value=\"Wireless iAP\" />                 \
100                 </attribute>                                            \
101         </record>"
102
103 static GMainLoop *main_loop;
104
105 static guint iap_source = 0;
106
107 static gboolean iap_handler(GIOChannel *channel, GIOCondition condition,
108                                                         gpointer user_data)
109 {
110         unsigned char buf[512];
111         ssize_t len;
112         int fd;
113
114         if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
115                 iap_source = 0;
116                 return FALSE;
117         }
118
119         fd = g_io_channel_unix_get_fd(channel);
120
121         len = read(fd, buf, sizeof(buf));
122         if (len < 0) {
123                 iap_source = 0;
124                 return FALSE;
125         }
126
127         return TRUE;
128 }
129
130 static guint create_source(int fd, GIOFunc func)
131 {
132         GIOChannel *channel;
133         guint source;
134
135         channel = g_io_channel_unix_new(fd);
136
137         g_io_channel_set_close_on_unref(channel, TRUE);
138         g_io_channel_set_encoding(channel, NULL, NULL);
139         g_io_channel_set_buffered(channel, FALSE);
140
141         source = g_io_add_watch(channel,
142                         G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, func, NULL);
143
144         g_io_channel_unref(channel);
145
146         return source;
147 }
148
149 static void remove_source(const char *path)
150 {
151         if (iap_source > 0) {
152                 g_source_remove(iap_source);
153                 iap_source = 0;
154         }
155 }
156
157 static DBusMessage *release_profile(DBusConnection *conn,
158                                         DBusMessage *msg, void *user_data)
159 {
160         g_print("Profile released\n");
161
162         remove_source(IAP_PATH);
163
164         return dbus_message_new_method_return(msg);
165 }
166
167 static DBusMessage *new_connection(DBusConnection *conn,
168                                         DBusMessage *msg, void *user_data)
169 {
170         const char *path, *device;
171         int fd;
172
173         g_print("New connection\n");
174
175         path = dbus_message_get_path(msg);
176
177         dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device,
178                                 DBUS_TYPE_UNIX_FD, &fd, DBUS_TYPE_INVALID);
179
180         g_print("  from %s\n", path);
181         g_print("  for device %s with fd %d\n", device, fd);
182
183         if (iap_source == 0)
184                 iap_source = create_source(fd, iap_handler);
185         else
186                 close(fd);
187
188         return dbus_message_new_method_return(msg);
189 }
190
191 static DBusMessage *request_disconnection(DBusConnection *conn,
192                                         DBusMessage *msg, void *user_data)
193 {
194         DBusMessageIter iter;
195         const char *path, *device;
196
197         g_print("Request disconnection\n");
198
199         path = dbus_message_get_path(msg);
200
201         dbus_message_iter_init(msg, &iter);
202         dbus_message_iter_get_basic(&iter, &device);
203
204         g_print("  from %s\n", path);
205         g_print("  for device %s\n", device);
206
207         remove_source(path);
208
209         return dbus_message_new_method_return(msg);
210 }
211
212 static DBusMessage *cancel_request(DBusConnection *conn,
213                                         DBusMessage *msg, void *user_data)
214 {
215         const char *path;
216
217         g_print("Request canceled\n");
218
219         path = dbus_message_get_path(msg);
220
221         g_print("  from %s\n", path);
222
223         remove_source(path);
224
225         return dbus_message_new_method_return(msg);
226 }
227
228 static const GDBusMethodTable methods[] = {
229         { GDBUS_METHOD("Release", NULL, NULL, release_profile) },
230         { GDBUS_METHOD("NewConnection",
231                         GDBUS_ARGS({ "device", "o" },
232                                         { "fd", "h"}, { "opts", "a{sv}"}),
233                         NULL, new_connection) },
234         { GDBUS_METHOD("RequestDisconnection",
235                         GDBUS_ARGS({ "device", "o" }),
236                         NULL, request_disconnection) },
237         { GDBUS_METHOD("Cancel", NULL, NULL, cancel_request) },
238         { }
239 };
240
241 static void register_profile_setup(DBusMessageIter *iter, void *user_data)
242 {
243         const char *path = IAP_PATH;
244         const char *uuid = IAP_UUID;
245         DBusMessageIter dict, entry, value;
246         dbus_uint16_t channel;
247         char *record;
248         const char *str;
249
250         dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
251         dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid);
252
253         dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
254                                 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
255                                 DBUS_TYPE_STRING_AS_STRING
256                                 DBUS_TYPE_VARIANT_AS_STRING
257                                 DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
258
259         dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY,
260                                                         NULL, &entry);
261         str = "Role";
262         dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &str);
263         dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
264                                         DBUS_TYPE_STRING_AS_STRING, &value);
265         str = "server";
266         dbus_message_iter_append_basic(&value, DBUS_TYPE_STRING, &str);
267         dbus_message_iter_close_container(&entry, &value);
268         dbus_message_iter_close_container(&dict, &entry);
269
270         dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY,
271                                                         NULL, &entry);
272         str = "Channel";
273         dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &str);
274         dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
275                                         DBUS_TYPE_UINT16_AS_STRING, &value);
276         channel = 23;
277         dbus_message_iter_append_basic(&value, DBUS_TYPE_UINT16, &channel);
278         dbus_message_iter_close_container(&entry, &value);
279         dbus_message_iter_close_container(&dict, &entry);
280
281         dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY,
282                                                         NULL, &entry);
283         str = "ServiceRecord";
284         dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &str);
285         dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
286                                         DBUS_TYPE_STRING_AS_STRING, &value);
287         record = g_strdup_printf(IAP_RECORD, IAP_UUID, channel);
288         dbus_message_iter_append_basic(&value, DBUS_TYPE_STRING, &record);
289         g_free(record);
290         dbus_message_iter_close_container(&entry, &value);
291         dbus_message_iter_close_container(&dict, &entry);
292
293         dbus_message_iter_close_container(iter, &dict);
294 }
295
296 static void register_profile_reply(DBusMessage *message, void *user_data)
297 {
298         DBusError error;
299
300         dbus_error_init(&error);
301
302         if (dbus_set_error_from_message(&error, message) == TRUE) {
303                 g_print("Failed to register profile\n");
304                 return;
305         }
306
307         g_print("Profile registered\n");
308 }
309
310 static void connect_handler(DBusConnection *connection, void *user_data)
311 {
312         GDBusClient *client = user_data;
313         GDBusProxy *proxy;
314
315         g_print("Bluetooth connected\n");
316
317         proxy = g_dbus_proxy_new(client, "/org/bluez",
318                                         "org.bluez.ProfileManager1");
319         if (!proxy)
320                 return;
321
322         g_dbus_register_interface(connection, IAP_PATH,
323                                         "org.bluez.Profile1",
324                                         methods, NULL, NULL, NULL, NULL);
325
326         g_dbus_proxy_method_call(proxy, "RegisterProfile", 
327                                         register_profile_setup,
328                                         register_profile_reply, NULL, NULL);
329
330         g_dbus_proxy_unref(proxy);
331 }
332
333 static void disconnect_handler(DBusConnection *connection, void *user_data)
334 {
335         g_print("Bluetooth disconnected\n");
336
337         g_dbus_unregister_interface(connection, IAP_PATH,
338                                                 "org.bluez.Profile1");
339 }
340
341 static gboolean signal_handler(GIOChannel *channel, GIOCondition condition,
342                                                         gpointer user_data)
343 {
344         static unsigned int __terminated = 0;
345         struct signalfd_siginfo si;
346         ssize_t result;
347         int fd;
348
349         if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
350                 g_main_loop_quit(main_loop);
351                 return FALSE;
352         }
353
354         fd = g_io_channel_unix_get_fd(channel);
355
356         result = read(fd, &si, sizeof(si));
357         if (result != sizeof(si))
358                 return FALSE;
359
360         switch (si.ssi_signo) {
361         case SIGINT:
362         case SIGTERM:
363                 if (__terminated == 0)
364                         g_main_loop_quit(main_loop);
365
366                 __terminated = 1;
367                 break;
368         }
369
370         return TRUE;
371 }
372
373 static guint setup_signalfd(void)
374 {
375         GIOChannel *channel;
376         guint source;
377         sigset_t mask;
378         int fd;
379
380         sigemptyset(&mask);
381         sigaddset(&mask, SIGINT);
382         sigaddset(&mask, SIGTERM);
383
384         if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) {
385                 perror("Failed to set signal mask");
386                 return 0;
387         }
388
389         fd = signalfd(-1, &mask, 0);
390         if (fd < 0) {
391                 perror("Failed to create signal descriptor");
392                 return 0;
393         }
394
395         channel = g_io_channel_unix_new(fd);
396
397         g_io_channel_set_close_on_unref(channel, TRUE);
398         g_io_channel_set_encoding(channel, NULL, NULL);
399         g_io_channel_set_buffered(channel, FALSE);
400
401         source = g_io_add_watch(channel,
402                                 G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
403                                 signal_handler, NULL);
404
405         g_io_channel_unref(channel);
406
407         return source;
408 }
409
410 static gboolean option_version = FALSE;
411
412 static GOptionEntry options[] = {
413         { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version,
414                                 "Show version information and exit" },
415         { NULL },
416 };
417
418 int main(int argc, char *argv[])
419 {
420         GOptionContext *context;
421         GError *error = NULL;
422         DBusConnection *dbus_conn;
423         GDBusClient *client;
424         guint signal;
425
426         context = g_option_context_new(NULL);
427         g_option_context_add_main_entries(context, options, NULL);
428
429         if (g_option_context_parse(context, &argc, &argv, &error) == FALSE) {
430                 if (error != NULL) {
431                         g_printerr("%s\n", error->message);
432                         g_error_free(error);
433                 } else
434                         g_printerr("An unknown error occurred\n");
435                 exit(1);
436         }
437
438         g_option_context_free(context);
439
440         if (option_version == TRUE) {
441                 g_print("%s\n", VERSION);
442                 exit(0);
443         }
444
445         main_loop = g_main_loop_new(NULL, FALSE);
446         dbus_conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
447
448         signal = setup_signalfd();
449
450         client = g_dbus_client_new(dbus_conn, "org.bluez", "/org/bluez");
451
452         g_dbus_client_set_connect_watch(client, connect_handler, client);
453         g_dbus_client_set_disconnect_watch(client, disconnect_handler, NULL);
454
455         g_main_loop_run(main_loop);
456
457         g_dbus_client_unref(client);
458
459         g_source_remove(signal);
460
461         dbus_connection_unref(dbus_conn);
462         g_main_loop_unref(main_loop);
463
464         return 0;
465 }