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