|
Packit |
34410b |
/*
|
|
Packit |
34410b |
*
|
|
Packit |
34410b |
* BlueZ - Bluetooth protocol stack for Linux
|
|
Packit |
34410b |
*
|
|
Packit |
34410b |
* Copyright (C) 2000-2001 Qualcomm Incorporated
|
|
Packit |
34410b |
* Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
|
|
Packit |
34410b |
* Copyright (C) 2002-2010 Marcel Holtmann <marcel@holtmann.org>
|
|
Packit |
34410b |
*
|
|
Packit |
34410b |
*
|
|
Packit |
34410b |
* This program is free software; you can redistribute it and/or modify
|
|
Packit |
34410b |
* it under the terms of the GNU General Public License as published by
|
|
Packit |
34410b |
* the Free Software Foundation; either version 2 of the License, or
|
|
Packit |
34410b |
* (at your option) any later version.
|
|
Packit |
34410b |
*
|
|
Packit |
34410b |
* This program is distributed in the hope that it will be useful,
|
|
Packit |
34410b |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
34410b |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit |
34410b |
* GNU General Public License for more details.
|
|
Packit |
34410b |
*
|
|
Packit |
34410b |
* You should have received a copy of the GNU General Public License
|
|
Packit |
34410b |
* along with this program; if not, write to the Free Software
|
|
Packit |
34410b |
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
Packit |
34410b |
*
|
|
Packit |
34410b |
*/
|
|
Packit |
34410b |
|
|
Packit |
34410b |
#ifdef HAVE_CONFIG_H
|
|
Packit |
34410b |
#include <config.h>
|
|
Packit |
34410b |
#endif
|
|
Packit |
34410b |
|
|
Packit |
34410b |
#define _GNU_SOURCE
|
|
Packit |
34410b |
#include <errno.h>
|
|
Packit |
34410b |
#include <stdio.h>
|
|
Packit |
34410b |
#include <unistd.h>
|
|
Packit |
34410b |
#include <stdlib.h>
|
|
Packit |
34410b |
#include <string.h>
|
|
Packit |
34410b |
#include <signal.h>
|
|
Packit |
34410b |
#include <stdbool.h>
|
|
Packit |
34410b |
#include <sys/signalfd.h>
|
|
Packit |
34410b |
#include <sys/types.h>
|
|
Packit |
34410b |
#include <sys/stat.h>
|
|
Packit |
34410b |
|
|
Packit |
34410b |
#include <glib.h>
|
|
Packit |
34410b |
|
|
Packit |
34410b |
#include <dbus/dbus.h>
|
|
Packit |
34410b |
|
|
Packit |
34410b |
#include "lib/bluetooth.h"
|
|
Packit |
34410b |
#include "lib/sdp.h"
|
|
Packit |
34410b |
|
|
Packit |
34410b |
#include "gdbus/gdbus.h"
|
|
Packit |
34410b |
|
|
Packit |
34410b |
#include "log.h"
|
|
Packit |
34410b |
#include "backtrace.h"
|
|
Packit |
34410b |
|
|
Packit |
34410b |
#include "shared/att-types.h"
|
|
Packit |
34410b |
#include "shared/mainloop.h"
|
|
Packit |
34410b |
#include "lib/uuid.h"
|
|
Packit |
34410b |
#include "hcid.h"
|
|
Packit |
34410b |
#include "sdpd.h"
|
|
Packit |
34410b |
#include "adapter.h"
|
|
Packit |
34410b |
#include "device.h"
|
|
Packit |
34410b |
#include "dbus-common.h"
|
|
Packit |
34410b |
#include "agent.h"
|
|
Packit |
34410b |
#include "profile.h"
|
|
Packit |
34410b |
|
|
Packit |
34410b |
#define BLUEZ_NAME "org.bluez"
|
|
Packit |
34410b |
|
|
Packit |
34410b |
#define DEFAULT_PAIRABLE_TIMEOUT 0 /* disabled */
|
|
Packit |
34410b |
#define DEFAULT_DISCOVERABLE_TIMEOUT 180 /* 3 minutes */
|
|
Packit |
34410b |
|
|
Packit |
34410b |
#define SHUTDOWN_GRACE_SECONDS 10
|
|
Packit |
34410b |
|
|
Packit |
34410b |
struct main_opts main_opts;
|
|
Packit |
34410b |
static GKeyFile *main_conf;
|
|
Packit |
34410b |
static char *main_conf_file_path;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static enum {
|
|
Packit |
34410b |
MPS_OFF,
|
|
Packit |
34410b |
MPS_SINGLE,
|
|
Packit |
34410b |
MPS_MULTIPLE,
|
|
Packit |
34410b |
} mps = MPS_OFF;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static const char *supported_options[] = {
|
|
Packit |
34410b |
"Name",
|
|
Packit |
34410b |
"Class",
|
|
Packit |
34410b |
"DiscoverableTimeout",
|
|
Packit |
34410b |
"AlwaysPairable"
|
|
Packit |
34410b |
"PairableTimeout",
|
|
Packit |
34410b |
"DeviceID",
|
|
Packit |
34410b |
"ReverseServiceDiscovery",
|
|
Packit |
34410b |
"NameResolving",
|
|
Packit |
34410b |
"DebugKeys",
|
|
Packit |
34410b |
"ControllerMode",
|
|
Packit |
34410b |
"MultiProfile",
|
|
Packit |
34410b |
"FastConnectable",
|
|
Packit |
34410b |
"Privacy",
|
|
Packit |
34410b |
NULL
|
|
Packit |
34410b |
};
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static const char *policy_options[] = {
|
|
Packit |
34410b |
"ReconnectUUIDs",
|
|
Packit |
34410b |
"ReconnectAttempts",
|
|
Packit |
34410b |
"ReconnectIntervals",
|
|
Packit |
34410b |
"AutoEnable",
|
|
Packit |
34410b |
NULL
|
|
Packit |
34410b |
};
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static const char *gatt_options[] = {
|
|
Packit |
34410b |
"Cache",
|
|
Packit |
34410b |
"KeySize",
|
|
Packit |
34410b |
"ExchangeMTU",
|
|
Packit |
34410b |
NULL
|
|
Packit |
34410b |
};
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static const struct group_table {
|
|
Packit |
34410b |
const char *name;
|
|
Packit |
34410b |
const char **options;
|
|
Packit |
34410b |
} valid_groups[] = {
|
|
Packit |
34410b |
{ "General", supported_options },
|
|
Packit |
34410b |
{ "Policy", policy_options },
|
|
Packit |
34410b |
{ "GATT", gatt_options },
|
|
Packit |
34410b |
{ }
|
|
Packit |
34410b |
};
|
|
Packit |
34410b |
|
|
Packit |
34410b |
|
|
Packit |
34410b |
GKeyFile *btd_get_main_conf(void)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
return main_conf;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static GKeyFile *load_config(const char *file)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
GError *err = NULL;
|
|
Packit |
34410b |
GKeyFile *keyfile;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
keyfile = g_key_file_new();
|
|
Packit |
34410b |
|
|
Packit |
34410b |
g_key_file_set_list_separator(keyfile, ',');
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (!g_key_file_load_from_file(keyfile, file, 0, &err)) {
|
|
Packit |
34410b |
if (!g_error_matches(err, G_FILE_ERROR, G_FILE_ERROR_NOENT))
|
|
Packit |
34410b |
error("Parsing %s failed: %s", file, err->message);
|
|
Packit |
34410b |
g_error_free(err);
|
|
Packit |
34410b |
g_key_file_free(keyfile);
|
|
Packit |
34410b |
return NULL;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
return keyfile;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static void parse_did(const char *did)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
int result;
|
|
Packit |
34410b |
uint16_t vendor, product, version , source;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
/* version and source are optional */
|
|
Packit |
34410b |
version = 0x0000;
|
|
Packit |
34410b |
source = 0x0002;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
result = sscanf(did, "bluetooth:%4hx:%4hx:%4hx",
|
|
Packit |
34410b |
&vendor, &product, &version);
|
|
Packit |
34410b |
if (result != EOF && result >= 2) {
|
|
Packit |
34410b |
source = 0x0001;
|
|
Packit |
34410b |
goto done;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
result = sscanf(did, "usb:%4hx:%4hx:%4hx",
|
|
Packit |
34410b |
&vendor, &product, &version);
|
|
Packit |
34410b |
if (result != EOF && result >= 2)
|
|
Packit |
34410b |
goto done;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
result = sscanf(did, "%4hx:%4hx:%4hx", &vendor, &product, &version);
|
|
Packit |
34410b |
if (result == EOF || result < 2)
|
|
Packit |
34410b |
return;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
done:
|
|
Packit |
34410b |
main_opts.did_source = source;
|
|
Packit |
34410b |
main_opts.did_vendor = vendor;
|
|
Packit |
34410b |
main_opts.did_product = product;
|
|
Packit |
34410b |
main_opts.did_version = version;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static bt_gatt_cache_t parse_gatt_cache(const char *cache)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
if (!strcmp(cache, "always")) {
|
|
Packit |
34410b |
return BT_GATT_CACHE_ALWAYS;
|
|
Packit |
34410b |
} else if (!strcmp(cache, "yes")) {
|
|
Packit |
34410b |
return BT_GATT_CACHE_YES;
|
|
Packit |
34410b |
} else if (!strcmp(cache, "no")) {
|
|
Packit |
34410b |
return BT_GATT_CACHE_NO;
|
|
Packit |
34410b |
} else {
|
|
Packit |
34410b |
DBG("Invalid value for KeepCache=%s", cache);
|
|
Packit |
34410b |
return BT_GATT_CACHE_ALWAYS;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static void check_options(GKeyFile *config, const char *group,
|
|
Packit |
34410b |
const char **options)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
char **keys;
|
|
Packit |
34410b |
int i;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
keys = g_key_file_get_keys(config, group, NULL, NULL);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
for (i = 0; keys != NULL && keys[i] != NULL; i++) {
|
|
Packit |
34410b |
bool found;
|
|
Packit |
34410b |
unsigned int j;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
found = false;
|
|
Packit |
34410b |
for (j = 0; options != NULL && options[j] != NULL; j++) {
|
|
Packit |
34410b |
if (g_str_equal(keys[i], options[j])) {
|
|
Packit |
34410b |
found = true;
|
|
Packit |
34410b |
break;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (!found)
|
|
Packit |
34410b |
warn("Unknown key %s for group %s in %s",
|
|
Packit |
34410b |
keys[i], group, main_conf_file_path);
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
g_strfreev(keys);
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static void check_config(GKeyFile *config)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
char **keys;
|
|
Packit |
34410b |
int i;
|
|
Packit |
34410b |
const struct group_table *group;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (!config)
|
|
Packit |
34410b |
return;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
keys = g_key_file_get_groups(config, NULL);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
for (i = 0; keys != NULL && keys[i] != NULL; i++) {
|
|
Packit |
34410b |
bool match = false;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
for (group = valid_groups; group && group->name ; group++) {
|
|
Packit |
34410b |
if (g_str_equal(keys[i], group->name)) {
|
|
Packit |
34410b |
match = true;
|
|
Packit |
34410b |
break;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (!match)
|
|
Packit |
34410b |
warn("Unknown group %s in %s", keys[i],
|
|
Packit |
34410b |
main_conf_file_path);
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
g_strfreev(keys);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
for (group = valid_groups; group && group->name; group++)
|
|
Packit |
34410b |
check_options(config, group->name, group->options);
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static int get_mode(const char *str)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
if (strcmp(str, "dual") == 0)
|
|
Packit |
34410b |
return BT_MODE_DUAL;
|
|
Packit |
34410b |
else if (strcmp(str, "bredr") == 0)
|
|
Packit |
34410b |
return BT_MODE_BREDR;
|
|
Packit |
34410b |
else if (strcmp(str, "le") == 0)
|
|
Packit |
34410b |
return BT_MODE_LE;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
error("Unknown controller mode \"%s\"", str);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
return BT_MODE_DUAL;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static void parse_config(GKeyFile *config)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
GError *err = NULL;
|
|
Packit |
34410b |
char *str;
|
|
Packit |
34410b |
int val;
|
|
Packit |
34410b |
gboolean boolean;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (!config)
|
|
Packit |
34410b |
return;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
check_config(config);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
DBG("parsing %s", main_conf_file_path);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
val = g_key_file_get_integer(config, "General",
|
|
Packit |
34410b |
"DiscoverableTimeout", &err;;
|
|
Packit |
34410b |
if (err) {
|
|
Packit |
34410b |
DBG("%s", err->message);
|
|
Packit |
34410b |
g_clear_error(&err;;
|
|
Packit |
34410b |
} else {
|
|
Packit |
34410b |
DBG("discovto=%d", val);
|
|
Packit |
34410b |
main_opts.discovto = val;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
boolean = g_key_file_get_boolean(config, "General",
|
|
Packit |
34410b |
"AlwaysPairable", &err;;
|
|
Packit |
34410b |
if (err) {
|
|
Packit |
34410b |
DBG("%s", err->message);
|
|
Packit |
34410b |
g_clear_error(&err;;
|
|
Packit |
34410b |
} else {
|
|
Packit |
34410b |
DBG("pairable=%s", boolean ? "true" : "false");
|
|
Packit |
34410b |
main_opts.pairable = boolean;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
val = g_key_file_get_integer(config, "General",
|
|
Packit |
34410b |
"PairableTimeout", &err;;
|
|
Packit |
34410b |
if (err) {
|
|
Packit |
34410b |
DBG("%s", err->message);
|
|
Packit |
34410b |
g_clear_error(&err;;
|
|
Packit |
34410b |
} else {
|
|
Packit |
34410b |
DBG("pairto=%d", val);
|
|
Packit |
34410b |
main_opts.pairto = val;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
str = g_key_file_get_string(config, "General", "Privacy", &err;;
|
|
Packit |
34410b |
if (err) {
|
|
Packit |
34410b |
DBG("%s", err->message);
|
|
Packit |
34410b |
g_clear_error(&err;;
|
|
Packit |
34410b |
main_opts.privacy = 0x00;
|
|
Packit |
34410b |
} else {
|
|
Packit |
34410b |
DBG("privacy=%s", str);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (!strcmp(str, "device"))
|
|
Packit |
34410b |
main_opts.privacy = 0x01;
|
|
Packit |
34410b |
else if (!strcmp(str, "off"))
|
|
Packit |
34410b |
main_opts.privacy = 0x00;
|
|
Packit |
34410b |
else {
|
|
Packit |
34410b |
DBG("Invalid privacy option: %s", str);
|
|
Packit |
34410b |
main_opts.privacy = 0x00;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
g_free(str);
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
str = g_key_file_get_string(config, "General", "Name", &err;;
|
|
Packit |
34410b |
if (err) {
|
|
Packit |
34410b |
DBG("%s", err->message);
|
|
Packit |
34410b |
g_clear_error(&err;;
|
|
Packit |
34410b |
} else {
|
|
Packit |
34410b |
DBG("name=%s", str);
|
|
Packit |
34410b |
g_free(main_opts.name);
|
|
Packit |
34410b |
main_opts.name = str;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
str = g_key_file_get_string(config, "General", "Class", &err;;
|
|
Packit |
34410b |
if (err) {
|
|
Packit |
34410b |
DBG("%s", err->message);
|
|
Packit |
34410b |
g_clear_error(&err;;
|
|
Packit |
34410b |
} else {
|
|
Packit |
34410b |
DBG("class=%s", str);
|
|
Packit |
34410b |
main_opts.class = strtol(str, NULL, 16);
|
|
Packit |
34410b |
g_free(str);
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
str = g_key_file_get_string(config, "General", "DeviceID", &err;;
|
|
Packit |
34410b |
if (err) {
|
|
Packit |
34410b |
DBG("%s", err->message);
|
|
Packit |
34410b |
g_clear_error(&err;;
|
|
Packit |
34410b |
} else {
|
|
Packit |
34410b |
DBG("deviceid=%s", str);
|
|
Packit |
34410b |
parse_did(str);
|
|
Packit |
34410b |
g_free(str);
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
boolean = g_key_file_get_boolean(config, "General",
|
|
Packit |
34410b |
"ReverseServiceDiscovery", &err;;
|
|
Packit |
34410b |
if (err) {
|
|
Packit |
34410b |
DBG("%s", err->message);
|
|
Packit |
34410b |
g_clear_error(&err;;
|
|
Packit |
34410b |
} else
|
|
Packit |
34410b |
main_opts.reverse_discovery = boolean;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
boolean = g_key_file_get_boolean(config, "General",
|
|
Packit |
34410b |
"NameResolving", &err;;
|
|
Packit |
34410b |
if (err)
|
|
Packit |
34410b |
g_clear_error(&err;;
|
|
Packit |
34410b |
else
|
|
Packit |
34410b |
main_opts.name_resolv = boolean;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
boolean = g_key_file_get_boolean(config, "General",
|
|
Packit |
34410b |
"DebugKeys", &err;;
|
|
Packit |
34410b |
if (err)
|
|
Packit |
34410b |
g_clear_error(&err;;
|
|
Packit |
34410b |
else
|
|
Packit |
34410b |
main_opts.debug_keys = boolean;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
str = g_key_file_get_string(config, "General", "ControllerMode", &err;;
|
|
Packit |
34410b |
if (err) {
|
|
Packit |
34410b |
g_clear_error(&err;;
|
|
Packit |
34410b |
} else {
|
|
Packit |
34410b |
DBG("ControllerMode=%s", str);
|
|
Packit |
34410b |
main_opts.mode = get_mode(str);
|
|
Packit |
34410b |
g_free(str);
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
str = g_key_file_get_string(config, "General", "MultiProfile", &err;;
|
|
Packit |
34410b |
if (err) {
|
|
Packit |
34410b |
g_clear_error(&err;;
|
|
Packit |
34410b |
} else {
|
|
Packit |
34410b |
DBG("MultiProfile=%s", str);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (!strcmp(str, "single"))
|
|
Packit |
34410b |
mps = MPS_SINGLE;
|
|
Packit |
34410b |
else if (!strcmp(str, "multiple"))
|
|
Packit |
34410b |
mps = MPS_MULTIPLE;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
g_free(str);
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
boolean = g_key_file_get_boolean(config, "General",
|
|
Packit |
34410b |
"FastConnectable", &err;;
|
|
Packit |
34410b |
if (err)
|
|
Packit |
34410b |
g_clear_error(&err;;
|
|
Packit |
34410b |
else
|
|
Packit |
34410b |
main_opts.fast_conn = boolean;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
str = g_key_file_get_string(config, "GATT", "Cache", &err;;
|
|
Packit |
34410b |
if (err) {
|
|
Packit |
34410b |
DBG("%s", err->message);
|
|
Packit |
34410b |
g_clear_error(&err;;
|
|
Packit |
34410b |
} else {
|
|
Packit |
34410b |
main_opts.gatt_cache = parse_gatt_cache(str);
|
|
Packit |
34410b |
g_free(str);
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
val = g_key_file_get_integer(config, "GATT", "KeySize", &err;;
|
|
Packit |
34410b |
if (err) {
|
|
Packit |
34410b |
DBG("%s", err->message);
|
|
Packit |
34410b |
g_clear_error(&err;;
|
|
Packit |
34410b |
} else {
|
|
Packit |
34410b |
DBG("KeySize=%d", val);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (val >=7 && val <= 16)
|
|
Packit |
34410b |
main_opts.key_size = val;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
val = g_key_file_get_integer(config, "GATT", "ExchangeMTU", &err;;
|
|
Packit |
34410b |
if (err) {
|
|
Packit |
34410b |
DBG("%s", err->message);
|
|
Packit |
34410b |
g_clear_error(&err;;
|
|
Packit |
34410b |
} else {
|
|
Packit |
34410b |
/* Ensure the mtu is within a valid range. */
|
|
Packit |
34410b |
val = MIN(val, BT_ATT_MAX_LE_MTU);
|
|
Packit |
34410b |
val = MAX(val, BT_ATT_DEFAULT_LE_MTU);
|
|
Packit |
34410b |
DBG("ExchangeMTU=%d", val);
|
|
Packit |
34410b |
main_opts.gatt_mtu = val;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static void init_defaults(void)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
uint8_t major, minor;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
/* Default HCId settings */
|
|
Packit |
34410b |
memset(&main_opts, 0, sizeof(main_opts));
|
|
Packit |
34410b |
main_opts.name = g_strdup_printf("BlueZ %s", VERSION);
|
|
Packit |
34410b |
main_opts.class = 0x000000;
|
|
Packit |
34410b |
main_opts.pairto = DEFAULT_PAIRABLE_TIMEOUT;
|
|
Packit |
34410b |
main_opts.discovto = DEFAULT_DISCOVERABLE_TIMEOUT;
|
|
Packit |
34410b |
main_opts.reverse_discovery = TRUE;
|
|
Packit |
34410b |
main_opts.name_resolv = TRUE;
|
|
Packit |
34410b |
main_opts.debug_keys = FALSE;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (sscanf(VERSION, "%hhu.%hhu", &major, &minor) != 2)
|
|
Packit |
34410b |
return;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
main_opts.did_source = 0x0002; /* USB */
|
|
Packit |
34410b |
main_opts.did_vendor = 0x1d6b; /* Linux Foundation */
|
|
Packit |
34410b |
main_opts.did_product = 0x0246; /* BlueZ */
|
|
Packit |
34410b |
main_opts.did_version = (major << 8 | minor);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
main_opts.gatt_cache = BT_GATT_CACHE_ALWAYS;
|
|
Packit |
34410b |
main_opts.gatt_mtu = BT_ATT_MAX_LE_MTU;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static void log_handler(const gchar *log_domain, GLogLevelFlags log_level,
|
|
Packit |
34410b |
const gchar *message, gpointer user_data)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
int priority;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (log_level & (G_LOG_LEVEL_ERROR |
|
|
Packit |
34410b |
G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING))
|
|
Packit |
34410b |
priority = 0x03;
|
|
Packit |
34410b |
else
|
|
Packit |
34410b |
priority = 0x06;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
btd_log(0xffff, priority, "GLib: %s", message);
|
|
Packit |
34410b |
btd_backtrace(0xffff);
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
void btd_exit(void)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
mainloop_quit();
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static gboolean quit_eventloop(gpointer user_data)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
btd_exit();
|
|
Packit |
34410b |
return FALSE;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static void signal_callback(int signum, void *user_data)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
static bool terminated = false;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
switch (signum) {
|
|
Packit |
34410b |
case SIGINT:
|
|
Packit |
34410b |
case SIGTERM:
|
|
Packit |
34410b |
if (!terminated) {
|
|
Packit |
34410b |
info("Terminating");
|
|
Packit |
34410b |
g_timeout_add_seconds(SHUTDOWN_GRACE_SECONDS,
|
|
Packit |
34410b |
quit_eventloop, NULL);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
mainloop_sd_notify("STATUS=Powering down");
|
|
Packit |
34410b |
adapter_shutdown();
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
terminated = true;
|
|
Packit |
34410b |
break;
|
|
Packit |
34410b |
case SIGUSR2:
|
|
Packit |
34410b |
__btd_toggle_debug();
|
|
Packit |
34410b |
break;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static char *option_debug = NULL;
|
|
Packit |
34410b |
static char *option_plugin = NULL;
|
|
Packit |
34410b |
static char *option_noplugin = NULL;
|
|
Packit |
34410b |
static char *option_configfile = NULL;
|
|
Packit |
34410b |
static gboolean option_compat = FALSE;
|
|
Packit |
34410b |
static gboolean option_detach = TRUE;
|
|
Packit |
34410b |
static gboolean option_version = FALSE;
|
|
Packit |
34410b |
static gboolean option_experimental = FALSE;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static void free_options(void)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
g_free(option_debug);
|
|
Packit |
34410b |
option_debug = NULL;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
g_free(option_plugin);
|
|
Packit |
34410b |
option_plugin = NULL;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
g_free(option_noplugin);
|
|
Packit |
34410b |
option_noplugin = NULL;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
g_free(option_configfile);
|
|
Packit |
34410b |
option_configfile = NULL;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static void disconnect_dbus(void)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
DBusConnection *conn = btd_get_dbus_connection();
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (!conn || !dbus_connection_get_is_connected(conn))
|
|
Packit |
34410b |
return;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
g_dbus_detach_object_manager(conn);
|
|
Packit |
34410b |
set_dbus_connection(NULL);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
dbus_connection_unref(conn);
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static void disconnected_dbus(DBusConnection *conn, void *data)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
info("Disconnected from D-Bus. Exiting.");
|
|
Packit |
34410b |
mainloop_quit();
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static int connect_dbus(void)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
DBusConnection *conn;
|
|
Packit |
34410b |
DBusError err;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
dbus_error_init(&err;;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, BLUEZ_NAME, &err;;
|
|
Packit |
34410b |
if (!conn) {
|
|
Packit |
34410b |
if (dbus_error_is_set(&err)) {
|
|
Packit |
34410b |
g_printerr("D-Bus setup failed: %s\n", err.message);
|
|
Packit |
34410b |
dbus_error_free(&err;;
|
|
Packit |
34410b |
return -EIO;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
return -EALREADY;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
set_dbus_connection(conn);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
g_dbus_set_disconnect_function(conn, disconnected_dbus, NULL, NULL);
|
|
Packit |
34410b |
g_dbus_attach_object_manager(conn);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
return 0;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static gboolean parse_debug(const char *key, const char *value,
|
|
Packit |
34410b |
gpointer user_data, GError **error)
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
if (value)
|
|
Packit |
34410b |
option_debug = g_strdup(value);
|
|
Packit |
34410b |
else
|
|
Packit |
34410b |
option_debug = g_strdup("*");
|
|
Packit |
34410b |
|
|
Packit |
34410b |
return TRUE;
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
static GOptionEntry options[] = {
|
|
Packit |
34410b |
{ "debug", 'd', G_OPTION_FLAG_OPTIONAL_ARG,
|
|
Packit |
34410b |
G_OPTION_ARG_CALLBACK, parse_debug,
|
|
Packit |
34410b |
"Specify debug options to enable", "DEBUG" },
|
|
Packit |
34410b |
{ "plugin", 'p', 0, G_OPTION_ARG_STRING, &option_plugin,
|
|
Packit |
34410b |
"Specify plugins to load", "NAME,..," },
|
|
Packit |
34410b |
{ "noplugin", 'P', 0, G_OPTION_ARG_STRING, &option_noplugin,
|
|
Packit |
34410b |
"Specify plugins not to load", "NAME,..." },
|
|
Packit |
34410b |
{ "configfile", 'f', 0, G_OPTION_ARG_STRING, &option_configfile,
|
|
Packit |
34410b |
"Specify an explicit path to the config file", "FILE"},
|
|
Packit |
34410b |
{ "compat", 'C', 0, G_OPTION_ARG_NONE, &option_compat,
|
|
Packit |
34410b |
"Provide deprecated command line interfaces" },
|
|
Packit |
34410b |
{ "experimental", 'E', 0, G_OPTION_ARG_NONE, &option_experimental,
|
|
Packit |
34410b |
"Enable experimental interfaces" },
|
|
Packit |
34410b |
{ "nodetach", 'n', G_OPTION_FLAG_REVERSE,
|
|
Packit |
34410b |
G_OPTION_ARG_NONE, &option_detach,
|
|
Packit |
34410b |
"Run with logging in foreground" },
|
|
Packit |
34410b |
{ "version", 'v', 0, G_OPTION_ARG_NONE, &option_version,
|
|
Packit |
34410b |
"Show version information and exit" },
|
|
Packit |
34410b |
{ NULL },
|
|
Packit |
34410b |
};
|
|
Packit |
34410b |
|
|
Packit |
34410b |
int main(int argc, char *argv[])
|
|
Packit |
34410b |
{
|
|
Packit |
34410b |
GOptionContext *context;
|
|
Packit |
34410b |
GError *err = NULL;
|
|
Packit |
34410b |
uint16_t sdp_mtu = 0;
|
|
Packit |
34410b |
uint32_t sdp_flags = 0;
|
|
Packit |
34410b |
int gdbus_flags = 0;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
init_defaults();
|
|
Packit |
34410b |
|
|
Packit |
34410b |
context = g_option_context_new(NULL);
|
|
Packit |
34410b |
g_option_context_add_main_entries(context, options, NULL);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (g_option_context_parse(context, &argc, &argv, &err) == FALSE) {
|
|
Packit |
34410b |
if (err != NULL) {
|
|
Packit |
34410b |
g_printerr("%s\n", err->message);
|
|
Packit |
34410b |
g_error_free(err);
|
|
Packit |
34410b |
} else
|
|
Packit |
34410b |
g_printerr("An unknown error occurred\n");
|
|
Packit |
34410b |
exit(1);
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
g_option_context_free(context);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (option_version == TRUE) {
|
|
Packit |
34410b |
printf("%s\n", VERSION);
|
|
Packit |
34410b |
exit(0);
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
umask(0077);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
btd_backtrace_init();
|
|
Packit |
34410b |
|
|
Packit |
34410b |
mainloop_init();
|
|
Packit |
34410b |
|
|
Packit |
34410b |
__btd_log_init(option_debug, option_detach);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
g_log_set_handler("GLib", G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL |
|
|
Packit |
34410b |
G_LOG_FLAG_RECURSION,
|
|
Packit |
34410b |
log_handler, NULL);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
mainloop_sd_notify("STATUS=Starting up");
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (option_configfile)
|
|
Packit |
34410b |
main_conf_file_path = option_configfile;
|
|
Packit |
34410b |
else
|
|
Packit |
34410b |
main_conf_file_path = CONFIGDIR "/main.conf";
|
|
Packit |
34410b |
|
|
Packit |
34410b |
main_conf = load_config(main_conf_file_path);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
parse_config(main_conf);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (connect_dbus() < 0) {
|
|
Packit |
34410b |
error("Unable to get on D-Bus");
|
|
Packit |
34410b |
exit(1);
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (option_experimental)
|
|
Packit |
34410b |
gdbus_flags = G_DBUS_FLAG_ENABLE_EXPERIMENTAL;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
g_dbus_set_flags(gdbus_flags);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (adapter_init() < 0) {
|
|
Packit |
34410b |
error("Adapter handling initialization failed");
|
|
Packit |
34410b |
exit(1);
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
btd_device_init();
|
|
Packit |
34410b |
btd_agent_init();
|
|
Packit |
34410b |
btd_profile_init();
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (main_opts.mode != BT_MODE_LE) {
|
|
Packit |
34410b |
if (option_compat == TRUE)
|
|
Packit |
34410b |
sdp_flags |= SDP_SERVER_COMPAT;
|
|
Packit |
34410b |
|
|
Packit |
34410b |
start_sdp_server(sdp_mtu, sdp_flags);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (main_opts.did_source > 0)
|
|
Packit |
34410b |
register_device_id(main_opts.did_source,
|
|
Packit |
34410b |
main_opts.did_vendor,
|
|
Packit |
34410b |
main_opts.did_product,
|
|
Packit |
34410b |
main_opts.did_version);
|
|
Packit |
34410b |
}
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (mps != MPS_OFF)
|
|
Packit |
34410b |
register_mps(mps == MPS_MULTIPLE);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
/* Loading plugins has to be done after D-Bus has been setup since
|
|
Packit |
34410b |
* the plugins might wanna expose some paths on the bus. However the
|
|
Packit |
34410b |
* best order of how to init various subsystems of the Bluetooth
|
|
Packit |
34410b |
* daemon needs to be re-worked. */
|
|
Packit |
34410b |
plugin_init(option_plugin, option_noplugin);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
/* no need to keep parsed option in memory */
|
|
Packit |
34410b |
free_options();
|
|
Packit |
34410b |
|
|
Packit |
34410b |
rfkill_init();
|
|
Packit |
34410b |
|
|
Packit |
34410b |
DBG("Entering main loop");
|
|
Packit |
34410b |
|
|
Packit |
34410b |
mainloop_sd_notify("STATUS=Running");
|
|
Packit |
34410b |
mainloop_sd_notify("READY=1");
|
|
Packit |
34410b |
|
|
Packit |
34410b |
mainloop_run_with_signal(signal_callback, NULL);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
mainloop_sd_notify("STATUS=Quitting");
|
|
Packit |
34410b |
|
|
Packit |
34410b |
plugin_cleanup();
|
|
Packit |
34410b |
|
|
Packit |
34410b |
btd_profile_cleanup();
|
|
Packit |
34410b |
btd_agent_cleanup();
|
|
Packit |
34410b |
btd_device_cleanup();
|
|
Packit |
34410b |
|
|
Packit |
34410b |
adapter_cleanup();
|
|
Packit |
34410b |
|
|
Packit |
34410b |
rfkill_exit();
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (main_opts.mode != BT_MODE_LE)
|
|
Packit |
34410b |
stop_sdp_server();
|
|
Packit |
34410b |
|
|
Packit |
34410b |
if (main_conf)
|
|
Packit |
34410b |
g_key_file_free(main_conf);
|
|
Packit |
34410b |
|
|
Packit |
34410b |
disconnect_dbus();
|
|
Packit |
34410b |
|
|
Packit |
34410b |
info("Exit");
|
|
Packit |
34410b |
|
|
Packit |
34410b |
__btd_log_cleanup();
|
|
Packit |
34410b |
|
|
Packit |
34410b |
return 0;
|
|
Packit |
34410b |
}
|