main.c revision 20ccc578c85111ca8790bd773b6fa337e80b4c71
1/* 2 * 3 * BlueZ - Bluetooth protocol stack for Linux 4 * 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> 8 * 9 * 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. 14 * 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. 19 * 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 23 * 24 */ 25 26#ifdef HAVE_CONFIG_H 27#include <config.h> 28#endif 29 30#include <errno.h> 31#include <stdio.h> 32#include <unistd.h> 33#include <stdlib.h> 34#include <string.h> 35#include <signal.h> 36#include <sys/stat.h> 37#include <sys/ioctl.h> 38#include <sys/socket.h> 39#include <sys/types.h> 40 41#include <bluetooth/bluetooth.h> 42 43#include <glib.h> 44 45#include <dbus/dbus.h> 46 47#include <gdbus.h> 48 49#include "log.h" 50 51#include "hcid.h" 52#include "sdpd.h" 53#include "attrib-server.h" 54#include "adapter.h" 55#include "event.h" 56#include "dbus-common.h" 57#include "agent.h" 58#include "manager.h" 59 60#ifdef HAVE_CAPNG 61#include <cap-ng.h> 62#endif 63 64#define BLUEZ_NAME "org.bluez" 65 66#define LAST_ADAPTER_EXIT_TIMEOUT 30 67 68#define DEFAULT_DISCOVERABLE_TIMEOUT 180 /* 3 minutes */ 69 70struct main_opts main_opts; 71 72static GKeyFile *load_config(const char *file) 73{ 74 GError *err = NULL; 75 GKeyFile *keyfile; 76 77 keyfile = g_key_file_new(); 78 79 g_key_file_set_list_separator(keyfile, ','); 80 81 if (!g_key_file_load_from_file(keyfile, file, 0, &err)) { 82 error("Parsing %s failed: %s", file, err->message); 83 g_error_free(err); 84 g_key_file_free(keyfile); 85 return NULL; 86 } 87 88 return keyfile; 89} 90 91static void parse_config(GKeyFile *config) 92{ 93 GError *err = NULL; 94 char *str; 95 int val; 96 gboolean boolean; 97 98 if (!config) 99 return; 100 101 DBG("parsing main.conf"); 102 103 val = g_key_file_get_integer(config, "General", 104 "DiscoverableTimeout", &err); 105 if (err) { 106 DBG("%s", err->message); 107 g_clear_error(&err); 108 } else { 109 DBG("discovto=%d", val); 110 main_opts.discovto = val; 111 main_opts.flags |= 1 << HCID_SET_DISCOVTO; 112 } 113 114 val = g_key_file_get_integer(config, "General", 115 "PairableTimeout", &err); 116 if (err) { 117 DBG("%s", err->message); 118 g_clear_error(&err); 119 } else { 120 DBG("pairto=%d", val); 121 main_opts.pairto = val; 122 } 123 124 val = g_key_file_get_integer(config, "General", "PageTimeout", &err); 125 if (err) { 126 DBG("%s", err->message); 127 g_clear_error(&err); 128 } else { 129 DBG("pageto=%d", val); 130 main_opts.pageto = val; 131 main_opts.flags |= 1 << HCID_SET_PAGETO; 132 } 133 134 str = g_key_file_get_string(config, "General", "Name", &err); 135 if (err) { 136 DBG("%s", err->message); 137 g_clear_error(&err); 138 } else { 139 DBG("name=%s", str); 140 g_free(main_opts.name); 141 main_opts.name = g_strdup(str); 142 main_opts.flags |= 1 << HCID_SET_NAME; 143 g_free(str); 144 } 145 146 str = g_key_file_get_string(config, "General", "Class", &err); 147 if (err) { 148 DBG("%s", err->message); 149 g_clear_error(&err); 150 } else { 151 DBG("class=%s", str); 152 main_opts.class = strtol(str, NULL, 16); 153 main_opts.flags |= 1 << HCID_SET_CLASS; 154 g_free(str); 155 } 156 157 val = g_key_file_get_integer(config, "General", 158 "DiscoverSchedulerInterval", &err); 159 if (err) { 160 DBG("%s", err->message); 161 g_clear_error(&err); 162 } else { 163 DBG("discov_interval=%d", val); 164 main_opts.discov_interval = val; 165 } 166 167 boolean = g_key_file_get_boolean(config, "General", 168 "InitiallyPowered", &err); 169 if (err) { 170 DBG("%s", err->message); 171 g_clear_error(&err); 172 } else if (boolean == FALSE) 173 main_opts.mode = MODE_OFF; 174 175 boolean = g_key_file_get_boolean(config, "General", 176 "RememberPowered", &err); 177 if (err) { 178 DBG("%s", err->message); 179 g_clear_error(&err); 180 } else 181 main_opts.remember_powered = boolean; 182 183 str = g_key_file_get_string(config, "General", "DeviceID", &err); 184 if (err) { 185 DBG("%s", err->message); 186 g_clear_error(&err); 187 } else { 188 DBG("deviceid=%s", str); 189 strncpy(main_opts.deviceid, str, 190 sizeof(main_opts.deviceid) - 1); 191 g_free(str); 192 } 193 194 boolean = g_key_file_get_boolean(config, "General", 195 "ReverseServiceDiscovery", &err); 196 if (err) { 197 DBG("%s", err->message); 198 g_clear_error(&err); 199 } else 200 main_opts.reverse_sdp = boolean; 201 202 boolean = g_key_file_get_boolean(config, "General", 203 "NameResolving", &err); 204 if (err) 205 g_clear_error(&err); 206 else 207 main_opts.name_resolv = boolean; 208 209 boolean = g_key_file_get_boolean(config, "General", 210 "DebugKeys", &err); 211 if (err) 212 g_clear_error(&err); 213 else 214 main_opts.debug_keys = boolean; 215 216 boolean = g_key_file_get_boolean(config, "General", 217 "AttributeServer", &err); 218 if (err) 219 g_clear_error(&err); 220 else 221 main_opts.attrib_server = boolean; 222 223 boolean = g_key_file_get_boolean(config, "General", 224 "EnableLE", &err); 225 if (err) 226 g_clear_error(&err); 227 else 228 main_opts.le = boolean; 229 230 main_opts.link_mode = HCI_LM_ACCEPT; 231 232 main_opts.link_policy = HCI_LP_RSWITCH | HCI_LP_SNIFF | 233 HCI_LP_HOLD | HCI_LP_PARK; 234} 235 236static void init_defaults(void) 237{ 238 /* Default HCId settings */ 239 memset(&main_opts, 0, sizeof(main_opts)); 240 main_opts.scan = SCAN_PAGE; 241 main_opts.mode = MODE_CONNECTABLE; 242 main_opts.name = g_strdup("BlueZ"); 243 main_opts.discovto = DEFAULT_DISCOVERABLE_TIMEOUT; 244 main_opts.remember_powered = TRUE; 245 main_opts.reverse_sdp = TRUE; 246 main_opts.name_resolv = TRUE; 247 248 if (gethostname(main_opts.host_name, sizeof(main_opts.host_name) - 1) < 0) 249 strcpy(main_opts.host_name, "noname"); 250} 251 252static GMainLoop *event_loop; 253 254static void sig_term(int sig) 255{ 256 g_main_loop_quit(event_loop); 257} 258 259static void sig_debug(int sig) 260{ 261 __btd_toggle_debug(); 262} 263 264static gchar *option_debug = NULL; 265static gchar *option_plugin = NULL; 266static gchar *option_noplugin = NULL; 267static gboolean option_detach = TRUE; 268static gboolean option_version = FALSE; 269static gboolean option_udev = FALSE; 270 271static guint last_adapter_timeout = 0; 272 273static gboolean exit_timeout(gpointer data) 274{ 275 g_main_loop_quit(event_loop); 276 last_adapter_timeout = 0; 277 return FALSE; 278} 279 280void btd_start_exit_timer(void) 281{ 282 if (option_udev == FALSE) 283 return; 284 285 if (last_adapter_timeout > 0) 286 g_source_remove(last_adapter_timeout); 287 288 last_adapter_timeout = g_timeout_add_seconds(LAST_ADAPTER_EXIT_TIMEOUT, 289 exit_timeout, NULL); 290} 291 292void btd_stop_exit_timer(void) 293{ 294 if (last_adapter_timeout == 0) 295 return; 296 297 g_source_remove(last_adapter_timeout); 298 last_adapter_timeout = 0; 299} 300 301static void disconnect_dbus(void) 302{ 303 DBusConnection *conn = get_dbus_connection(); 304 305 if (!conn || !dbus_connection_get_is_connected(conn)) 306 return; 307 308 manager_cleanup(conn, "/"); 309 310 set_dbus_connection(NULL); 311 312 dbus_connection_unref(conn); 313} 314 315static int connect_dbus(void) 316{ 317 DBusConnection *conn; 318 DBusError err; 319 320 dbus_error_init(&err); 321 322 conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, BLUEZ_NAME, &err); 323 if (!conn) { 324 if (dbus_error_is_set(&err)) { 325 dbus_error_free(&err); 326 return -EIO; 327 } 328 return -EALREADY; 329 } 330 331 if (!manager_init(conn, "/")) 332 return -EIO; 333 334 set_dbus_connection(conn); 335 336 return 0; 337} 338 339static gboolean parse_debug(const char *key, const char *value, 340 gpointer user_data, GError **error) 341{ 342 if (value) 343 option_debug = g_strdup(value); 344 else 345 option_debug = g_strdup("*"); 346 347 return TRUE; 348} 349 350static GOptionEntry options[] = { 351 { "debug", 'd', G_OPTION_FLAG_OPTIONAL_ARG, 352 G_OPTION_ARG_CALLBACK, parse_debug, 353 "Specify debug options to enable", "DEBUG" }, 354 { "plugin", 'p', 0, G_OPTION_ARG_STRING, &option_plugin, 355 "Specify plugins to load", "NAME,..," }, 356 { "noplugin", 'P', 0, G_OPTION_ARG_STRING, &option_noplugin, 357 "Specify plugins not to load", "NAME,..." }, 358 { "nodetach", 'n', G_OPTION_FLAG_REVERSE, 359 G_OPTION_ARG_NONE, &option_detach, 360 "Don't run as daemon in background" }, 361 { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version, 362 "Show version information and exit" }, 363 { "udev", 'u', 0, G_OPTION_ARG_NONE, &option_udev, 364 "Run from udev mode of operation" }, 365 { NULL }, 366}; 367 368int main(int argc, char *argv[]) 369{ 370 GOptionContext *context; 371 GError *err = NULL; 372 struct sigaction sa; 373 uint16_t mtu = 0; 374 GKeyFile *config; 375 376 init_defaults(); 377 378#ifdef HAVE_CAPNG 379 /* Drop capabilities */ 380 capng_clear(CAPNG_SELECT_BOTH); 381 capng_updatev(CAPNG_ADD, CAPNG_EFFECTIVE | CAPNG_PERMITTED, 382 CAP_NET_BIND_SERVICE, CAP_NET_ADMIN, 383 CAP_NET_RAW, CAP_IPC_LOCK, -1); 384 capng_apply(CAPNG_SELECT_BOTH); 385#endif 386 387 context = g_option_context_new(NULL); 388 g_option_context_add_main_entries(context, options, NULL); 389 390 if (g_option_context_parse(context, &argc, &argv, &err) == FALSE) { 391 if (err != NULL) { 392 g_printerr("%s\n", err->message); 393 g_error_free(err); 394 } else 395 g_printerr("An unknown error occurred\n"); 396 exit(1); 397 } 398 399 g_option_context_free(context); 400 401 if (option_version == TRUE) { 402 printf("%s\n", VERSION); 403 exit(0); 404 } 405 406 if (option_udev == TRUE) { 407 int err; 408 409 option_detach = TRUE; 410 err = connect_dbus(); 411 if (err < 0) { 412 if (err == -EALREADY) 413 exit(0); 414 exit(1); 415 } 416 } 417 418 if (option_detach == TRUE && option_udev == FALSE) { 419 if (daemon(0, 0)) { 420 perror("Can't start daemon"); 421 exit(1); 422 } 423 } 424 425 umask(0077); 426 427 __btd_log_init(option_debug, option_detach); 428 429 memset(&sa, 0, sizeof(sa)); 430 sa.sa_flags = SA_NOCLDSTOP; 431 sa.sa_handler = sig_term; 432 sigaction(SIGTERM, &sa, NULL); 433 sigaction(SIGINT, &sa, NULL); 434 435 sa.sa_handler = sig_debug; 436 sigaction(SIGUSR2, &sa, NULL); 437 438 sa.sa_handler = SIG_IGN; 439 sigaction(SIGPIPE, &sa, NULL); 440 441 config = load_config(CONFIGDIR "/main.conf"); 442 443 parse_config(config); 444 445 agent_init(); 446 447 if (option_udev == FALSE) { 448 if (connect_dbus() < 0) { 449 error("Unable to get on D-Bus"); 450 exit(1); 451 } 452 } else { 453 if (daemon(0, 0)) { 454 perror("Can't start daemon"); 455 exit(1); 456 } 457 } 458 459 start_sdp_server(mtu, main_opts.deviceid, SDP_SERVER_COMPAT); 460 461 if (main_opts.attrib_server) { 462 if (attrib_server_init() < 0) 463 error("Can't initialize attribute server"); 464 } 465 466 /* Loading plugins has to be done after D-Bus has been setup since 467 * the plugins might wanna expose some paths on the bus. However the 468 * best order of how to init various subsystems of the Bluetooth 469 * daemon needs to be re-worked. */ 470 plugin_init(config, option_plugin, option_noplugin); 471 472 event_loop = g_main_loop_new(NULL, FALSE); 473 474 if (adapter_ops_setup() < 0) { 475 error("adapter_ops_setup failed"); 476 exit(1); 477 } 478 479 rfkill_init(); 480 481 DBG("Entering main loop"); 482 483 g_main_loop_run(event_loop); 484 485 disconnect_dbus(); 486 487 rfkill_exit(); 488 489 plugin_cleanup(); 490 491 if (main_opts.attrib_server) 492 attrib_server_exit(); 493 494 stop_sdp_server(); 495 496 agent_exit(); 497 498 g_main_loop_unref(event_loop); 499 500 if (config) 501 g_key_file_free(config); 502 503 info("Exit"); 504 505 __btd_log_cleanup(); 506 507 return 0; 508} 509