3 * BlueZ - Bluetooth protocol stack for Linux
5 * Copyright (C) 2000-2001 Qualcomm Incorporated
6 * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
7 * Copyright (C) 2002-2010 Marcel Holtmann <marcel@holtmann.org>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
37 #include <sys/signalfd.h>
38 #include <sys/types.h>
43 #include <dbus/dbus.h>
45 #include "lib/bluetooth.h"
48 #include "gdbus/gdbus.h"
57 #include "dbus-common.h"
62 #ifdef __TIZEN_PATCH__
66 #define BLUEZ_NAME "org.bluez"
68 #define DEFAULT_PAIRABLE_TIMEOUT 0 /* disabled */
69 #define DEFAULT_DISCOVERABLE_TIMEOUT 180 /* 3 minutes */
71 #define SHUTDOWN_GRACE_SECONDS 10
73 struct main_opts main_opts;
74 static GKeyFile *main_conf;
82 static const char * const supported_options[] = {
85 "DiscoverableTimeout",
90 "ReverseServiceDiscovery",
95 #ifdef __TIZEN_PATCH__
100 GKeyFile *btd_get_main_conf(void)
105 static GKeyFile *load_config(const char *file)
110 keyfile = g_key_file_new();
112 g_key_file_set_list_separator(keyfile, ',');
114 if (!g_key_file_load_from_file(keyfile, file, 0, &err)) {
115 if (!g_error_matches(err, G_FILE_ERROR, G_FILE_ERROR_NOENT))
116 error("Parsing %s failed: %s", file, err->message);
118 g_key_file_free(keyfile);
125 static void parse_did(const char *did)
128 uint16_t vendor, product, version , source;
130 /* version and source are optional */
134 result = sscanf(did, "bluetooth:%4hx:%4hx:%4hx",
135 &vendor, &product, &version);
136 if (result != EOF && result >= 2) {
141 result = sscanf(did, "usb:%4hx:%4hx:%4hx",
142 &vendor, &product, &version);
143 if (result != EOF && result >= 2)
146 result = sscanf(did, "%4hx:%4hx:%4hx", &vendor, &product, &version);
147 if (result == EOF || result < 2)
151 main_opts.did_source = source;
152 main_opts.did_vendor = vendor;
153 main_opts.did_product = product;
154 main_opts.did_version = version;
157 static void check_config(GKeyFile *config)
159 const char *valid_groups[] = { "General", "Policy", NULL };
166 keys = g_key_file_get_groups(config, NULL);
168 for (i = 0; keys != NULL && keys[i] != NULL; i++) {
172 for (group = valid_groups; *group; group++) {
173 if (g_str_equal(keys[i], *group)) {
180 warn("Unknown group %s in main.conf", keys[i]);
185 keys = g_key_file_get_keys(config, "General", NULL, NULL);
187 for (i = 0; keys != NULL && keys[i] != NULL; i++) {
192 for (j = 0; j < G_N_ELEMENTS(supported_options); j++) {
193 if (g_str_equal(keys[i], supported_options[j])) {
200 warn("Unknown key %s in main.conf", keys[i]);
206 static int get_mode(const char *str)
208 if (strcmp(str, "dual") == 0)
210 else if (strcmp(str, "bredr") == 0)
211 return BT_MODE_BREDR;
212 else if (strcmp(str, "le") == 0)
215 error("Unknown controller mode \"%s\"", str);
220 static void parse_config(GKeyFile *config)
230 check_config(config);
232 DBG("parsing main.conf");
234 val = g_key_file_get_integer(config, "General",
235 "DiscoverableTimeout", &err);
237 DBG("%s", err->message);
240 DBG("discovto=%d", val);
241 main_opts.discovto = val;
244 val = g_key_file_get_integer(config, "General",
245 "PairableTimeout", &err);
247 DBG("%s", err->message);
250 DBG("pairto=%d", val);
251 main_opts.pairto = val;
254 val = g_key_file_get_integer(config, "General", "AutoConnectTimeout",
257 DBG("%s", err->message);
260 DBG("auto_to=%d", val);
261 main_opts.autoto = val;
264 str = g_key_file_get_string(config, "General", "Name", &err);
266 DBG("%s", err->message);
270 g_free(main_opts.name);
271 main_opts.name = str;
274 str = g_key_file_get_string(config, "General", "Class", &err);
276 DBG("%s", err->message);
279 DBG("class=%s", str);
280 main_opts.class = strtol(str, NULL, 16);
284 str = g_key_file_get_string(config, "General", "DeviceID", &err);
286 DBG("%s", err->message);
289 DBG("deviceid=%s", str);
294 boolean = g_key_file_get_boolean(config, "General",
295 "ReverseServiceDiscovery", &err);
297 DBG("%s", err->message);
300 main_opts.reverse_sdp = boolean;
302 boolean = g_key_file_get_boolean(config, "General",
303 "NameResolving", &err);
307 main_opts.name_resolv = boolean;
309 boolean = g_key_file_get_boolean(config, "General",
314 main_opts.debug_keys = boolean;
316 str = g_key_file_get_string(config, "General", "ControllerMode", &err);
320 DBG("ControllerMode=%s", str);
321 main_opts.mode = get_mode(str);
325 str = g_key_file_get_string(config, "General", "MultiProfile", &err);
329 DBG("MultiProfile=%s", str);
331 if (!strcmp(str, "single"))
333 else if (!strcmp(str, "multiple"))
338 #ifdef __TIZEN_PATCH__
339 boolean = g_key_file_get_boolean(config, "General",
340 "EnableLEPrivacy", &err);
344 main_opts.le_privacy = boolean;
348 static void init_defaults(void)
350 uint8_t major, minor;
352 /* Default HCId settings */
353 memset(&main_opts, 0, sizeof(main_opts));
354 main_opts.name = g_strdup_printf("BlueZ %s", VERSION);
355 main_opts.class = 0x000000;
356 main_opts.pairto = DEFAULT_PAIRABLE_TIMEOUT;
357 main_opts.discovto = DEFAULT_DISCOVERABLE_TIMEOUT;
358 main_opts.reverse_sdp = TRUE;
359 main_opts.name_resolv = TRUE;
360 main_opts.debug_keys = FALSE;
361 #ifdef __TIZEN_PATCH__
362 main_opts.le_privacy = FALSE;
365 if (sscanf(VERSION, "%hhu.%hhu", &major, &minor) != 2)
368 main_opts.did_source = 0x0002; /* USB */
369 main_opts.did_vendor = 0x1d6b; /* Linux Foundation */
370 main_opts.did_product = 0x0246; /* BlueZ */
371 main_opts.did_version = (major << 8 | minor);
374 static GMainLoop *event_loop;
378 g_main_loop_quit(event_loop);
381 static gboolean quit_eventloop(gpointer user_data)
387 static gboolean signal_handler(GIOChannel *channel, GIOCondition cond,
390 static unsigned int __terminated = 0;
391 struct signalfd_siginfo si;
395 if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP))
398 fd = g_io_channel_unix_get_fd(channel);
400 result = read(fd, &si, sizeof(si));
401 if (result != sizeof(si))
404 switch (si.ssi_signo) {
407 if (__terminated == 0) {
409 g_timeout_add_seconds(SHUTDOWN_GRACE_SECONDS,
410 quit_eventloop, NULL);
412 sd_notify(0, "STATUS=Powering down");
419 __btd_toggle_debug();
426 static guint setup_signalfd(void)
434 sigaddset(&mask, SIGINT);
435 sigaddset(&mask, SIGTERM);
436 sigaddset(&mask, SIGUSR2);
438 if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) {
439 perror("Failed to set signal mask");
443 fd = signalfd(-1, &mask, 0);
445 perror("Failed to create signal descriptor");
449 channel = g_io_channel_unix_new(fd);
451 g_io_channel_set_close_on_unref(channel, TRUE);
452 g_io_channel_set_encoding(channel, NULL, NULL);
453 g_io_channel_set_buffered(channel, FALSE);
455 source = g_io_add_watch(channel,
456 G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
457 signal_handler, NULL);
459 g_io_channel_unref(channel);
464 static char *option_debug = NULL;
465 static char *option_plugin = NULL;
466 static char *option_noplugin = NULL;
467 static gboolean option_compat = FALSE;
468 static gboolean option_detach = TRUE;
469 static gboolean option_version = FALSE;
470 static gboolean option_experimental = FALSE;
472 static void free_options(void)
474 g_free(option_debug);
477 g_free(option_plugin);
478 option_plugin = NULL;
480 g_free(option_noplugin);
481 option_noplugin = NULL;
484 static void disconnect_dbus(void)
486 DBusConnection *conn = btd_get_dbus_connection();
488 if (!conn || !dbus_connection_get_is_connected(conn))
491 g_dbus_detach_object_manager(conn);
492 set_dbus_connection(NULL);
494 dbus_connection_unref(conn);
497 static void disconnected_dbus(DBusConnection *conn, void *data)
499 info("Disconnected from D-Bus. Exiting.");
500 g_main_loop_quit(event_loop);
503 static int connect_dbus(void)
505 DBusConnection *conn;
508 dbus_error_init(&err);
510 conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, BLUEZ_NAME, &err);
512 if (dbus_error_is_set(&err)) {
513 g_printerr("D-Bus setup failed: %s\n", err.message);
514 dbus_error_free(&err);
520 set_dbus_connection(conn);
522 g_dbus_set_disconnect_function(conn, disconnected_dbus, NULL, NULL);
523 g_dbus_attach_object_manager(conn);
528 static gboolean watchdog_callback(gpointer user_data)
530 sd_notify(0, "WATCHDOG=1");
535 static gboolean parse_debug(const char *key, const char *value,
536 gpointer user_data, GError **error)
539 option_debug = g_strdup(value);
541 option_debug = g_strdup("*");
546 static GOptionEntry options[] = {
547 { "debug", 'd', G_OPTION_FLAG_OPTIONAL_ARG,
548 G_OPTION_ARG_CALLBACK, parse_debug,
549 "Specify debug options to enable", "DEBUG" },
550 { "plugin", 'p', 0, G_OPTION_ARG_STRING, &option_plugin,
551 "Specify plugins to load", "NAME,..," },
552 { "noplugin", 'P', 0, G_OPTION_ARG_STRING, &option_noplugin,
553 "Specify plugins not to load", "NAME,..." },
554 { "compat", 'C', 0, G_OPTION_ARG_NONE, &option_compat,
555 "Provide deprecated command line interfaces" },
556 { "experimental", 'E', 0, G_OPTION_ARG_NONE, &option_experimental,
557 "Enable experimental interfaces" },
558 { "nodetach", 'n', G_OPTION_FLAG_REVERSE,
559 G_OPTION_ARG_NONE, &option_detach,
560 "Run with logging in foreground" },
561 { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version,
562 "Show version information and exit" },
566 int main(int argc, char *argv[])
568 GOptionContext *context;
570 uint16_t sdp_mtu = 0;
571 uint32_t sdp_flags = 0;
573 guint signal, watchdog;
574 const char *watchdog_usec;
578 context = g_option_context_new(NULL);
579 g_option_context_add_main_entries(context, options, NULL);
581 if (g_option_context_parse(context, &argc, &argv, &err) == FALSE) {
583 g_printerr("%s\n", err->message);
586 g_printerr("An unknown error occurred\n");
590 g_option_context_free(context);
592 if (option_version == TRUE) {
593 printf("%s\n", VERSION);
599 event_loop = g_main_loop_new(NULL, FALSE);
601 signal = setup_signalfd();
603 __btd_log_init(option_debug, option_detach);
605 sd_notify(0, "STATUS=Starting up");
607 main_conf = load_config(CONFIGDIR "/main.conf");
609 parse_config(main_conf);
611 if (connect_dbus() < 0) {
612 error("Unable to get on D-Bus");
616 if (option_experimental)
617 gdbus_flags = G_DBUS_FLAG_ENABLE_EXPERIMENTAL;
619 g_dbus_set_flags(gdbus_flags);
621 #ifdef __TIZEN_PATCH__
625 if (adapter_init() < 0) {
626 error("Adapter handling initialization failed");
634 if (option_compat == TRUE)
635 sdp_flags |= SDP_SERVER_COMPAT;
637 start_sdp_server(sdp_mtu, sdp_flags);
639 if (main_opts.did_source > 0)
640 register_device_id(main_opts.did_source, main_opts.did_vendor,
641 main_opts.did_product, main_opts.did_version);
644 register_mps(mps == MPS_MULTIPLE);
646 /* Loading plugins has to be done after D-Bus has been setup since
647 * the plugins might wanna expose some paths on the bus. However the
648 * best order of how to init various subsystems of the Bluetooth
649 * daemon needs to be re-worked. */
650 plugin_init(option_plugin, option_noplugin);
652 /* no need to keep parsed option in memory */
657 DBG("Entering main loop");
659 sd_notify(0, "STATUS=Running");
660 sd_notify(0, "READY=1");
662 watchdog_usec = getenv("WATCHDOG_USEC");
664 unsigned int seconds;
666 seconds = atoi(watchdog_usec) / (1000 * 1000);
667 info("Watchdog timeout is %d seconds", seconds);
669 watchdog = g_timeout_add_seconds_full(G_PRIORITY_HIGH,
676 g_main_loop_run(event_loop);
678 sd_notify(0, "STATUS=Quitting");
680 g_source_remove(signal);
684 btd_profile_cleanup();
686 btd_device_cleanup();
690 #ifdef __TIZEN_PATCH__
698 g_main_loop_unref(event_loop);
701 g_key_file_free(main_conf);
708 g_source_remove(watchdog);