1 1.6 thorpej /* $NetBSD: db.c,v 1.6 2020/06/07 00:12:00 thorpej Exp $ */ 2 1.1 plunky 3 1.1 plunky /*- 4 1.1 plunky * Copyright (c) 2006 Itronix Inc. 5 1.1 plunky * All rights reserved. 6 1.1 plunky * 7 1.1 plunky * Written by Iain Hibbert for Itronix Inc. 8 1.1 plunky * 9 1.1 plunky * Redistribution and use in source and binary forms, with or without 10 1.1 plunky * modification, are permitted provided that the following conditions 11 1.1 plunky * are met: 12 1.1 plunky * 1. Redistributions of source code must retain the above copyright 13 1.1 plunky * notice, this list of conditions and the following disclaimer. 14 1.1 plunky * 2. Redistributions in binary form must reproduce the above copyright 15 1.1 plunky * notice, this list of conditions and the following disclaimer in the 16 1.1 plunky * documentation and/or other materials provided with the distribution. 17 1.1 plunky * 3. The name of Itronix Inc. may not be used to endorse 18 1.1 plunky * or promote products derived from this software without specific 19 1.1 plunky * prior written permission. 20 1.1 plunky * 21 1.1 plunky * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND 22 1.1 plunky * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 23 1.1 plunky * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 24 1.1 plunky * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY 25 1.1 plunky * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 1.1 plunky * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 1.1 plunky * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 28 1.1 plunky * ON ANY THEORY OF LIABILITY, WHETHER IN 29 1.1 plunky * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 1.1 plunky * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 1.1 plunky * POSSIBILITY OF SUCH DAMAGE. 32 1.1 plunky */ 33 1.1 plunky 34 1.1 plunky #include <sys/cdefs.h> 35 1.6 thorpej __RCSID("$NetBSD: db.c,v 1.6 2020/06/07 00:12:00 thorpej Exp $"); 36 1.1 plunky 37 1.1 plunky #include <bluetooth.h> 38 1.1 plunky #include <err.h> 39 1.1 plunky #include <stdlib.h> 40 1.4 pavel #include <stdbool.h> 41 1.1 plunky 42 1.1 plunky #include <prop/proplib.h> 43 1.1 plunky 44 1.1 plunky #include <dev/bluetooth/btdev.h> 45 1.3 plunky #include <dev/bluetooth/bthidev.h> 46 1.1 plunky #include <dev/bluetooth/btsco.h> 47 1.1 plunky 48 1.1 plunky #include "btdevctl.h" 49 1.1 plunky 50 1.1 plunky #define BTDEVCTL_PLIST "/var/db/btdevctl.plist" 51 1.3 plunky #define BTDEVCTL_VERSION 2 52 1.1 plunky 53 1.1 plunky static prop_dictionary_t db = NULL; 54 1.4 pavel static bool db_flush = true; /* write db on set */ 55 1.1 plunky 56 1.1 plunky static void db_update0(void); 57 1.3 plunky static void db_update1(void); 58 1.1 plunky 59 1.1 plunky /* 60 1.1 plunky * lookup laddr/raddr/service in database and return dictionary 61 1.1 plunky */ 62 1.1 plunky prop_dictionary_t 63 1.1 plunky db_get(bdaddr_t *laddr, bdaddr_t *raddr, const char *service) 64 1.1 plunky { 65 1.1 plunky prop_dictionary_t ldev, rdev, dev; 66 1.1 plunky prop_object_t obj; 67 1.1 plunky 68 1.1 plunky if (db == NULL) { 69 1.1 plunky db = prop_dictionary_internalize_from_file(BTDEVCTL_PLIST); 70 1.1 plunky if (db == NULL) { 71 1.1 plunky db = prop_dictionary_create(); 72 1.1 plunky if (db == NULL) 73 1.1 plunky err(EXIT_FAILURE, "prop_dictionary_create"); 74 1.1 plunky 75 1.1 plunky return NULL; 76 1.1 plunky } else { 77 1.1 plunky obj = prop_dictionary_get(db, "btdevctl-version"); 78 1.6 thorpej switch(prop_number_signed_value(obj)) { 79 1.3 plunky case 0: db_update0(); 80 1.5 mrg /* FALLTHROUGH */ 81 1.3 plunky case 1: db_update1(); 82 1.5 mrg /* FALLTHROUGH */ 83 1.1 plunky case BTDEVCTL_VERSION: 84 1.1 plunky break; 85 1.1 plunky 86 1.1 plunky default: 87 1.1 plunky errx(EXIT_FAILURE, "unknown btdevctl-version"); 88 1.1 plunky } 89 1.1 plunky } 90 1.1 plunky } 91 1.1 plunky 92 1.1 plunky ldev = prop_dictionary_get(db, bt_ntoa(laddr, NULL)); 93 1.1 plunky if (prop_object_type(ldev) != PROP_TYPE_DICTIONARY) 94 1.1 plunky return NULL; 95 1.1 plunky 96 1.1 plunky rdev = prop_dictionary_get(ldev, bt_ntoa(raddr, NULL)); 97 1.1 plunky if (prop_object_type(rdev) != PROP_TYPE_DICTIONARY) 98 1.1 plunky return NULL; 99 1.1 plunky 100 1.1 plunky dev = prop_dictionary_get(rdev, service); 101 1.1 plunky if (prop_object_type(dev) != PROP_TYPE_DICTIONARY) 102 1.1 plunky return NULL; 103 1.1 plunky 104 1.1 plunky return dev; 105 1.1 plunky } 106 1.1 plunky 107 1.1 plunky /* 108 1.1 plunky * store dictionary in database at laddr/raddr/service 109 1.1 plunky */ 110 1.1 plunky int 111 1.1 plunky db_set(prop_dictionary_t dev, bdaddr_t *laddr, bdaddr_t *raddr, const char *service) 112 1.1 plunky { 113 1.1 plunky prop_dictionary_t ldev, rdev; 114 1.1 plunky 115 1.1 plunky ldev = prop_dictionary_get(db, bt_ntoa(laddr, NULL)); 116 1.1 plunky if (ldev == NULL) { 117 1.1 plunky ldev = prop_dictionary_create(); 118 1.1 plunky if (ldev == NULL) 119 1.1 plunky return 0; 120 1.1 plunky 121 1.1 plunky if (!prop_dictionary_set(db, bt_ntoa(laddr, NULL), ldev)) 122 1.1 plunky return 0; 123 1.1 plunky 124 1.1 plunky prop_object_release(ldev); 125 1.1 plunky } 126 1.1 plunky 127 1.1 plunky rdev = prop_dictionary_get(ldev, bt_ntoa(raddr, NULL)); 128 1.1 plunky if (rdev == NULL) { 129 1.1 plunky rdev = prop_dictionary_create(); 130 1.1 plunky if (rdev == NULL) 131 1.1 plunky return 0; 132 1.1 plunky 133 1.1 plunky if (!prop_dictionary_set(ldev, bt_ntoa(raddr, NULL), rdev)) 134 1.1 plunky return 0; 135 1.1 plunky 136 1.1 plunky prop_object_release(rdev); 137 1.1 plunky } 138 1.1 plunky 139 1.1 plunky if (!prop_dictionary_set(rdev, service, dev)) 140 1.1 plunky return 0; 141 1.1 plunky 142 1.4 pavel if (db_flush == true) { 143 1.6 thorpej if (!prop_dictionary_set_int(db, "btdevctl-version", 144 1.6 thorpej BTDEVCTL_VERSION)) 145 1.6 thorpej err(EXIT_FAILURE, "prop_dictionary_set_int"); 146 1.1 plunky 147 1.1 plunky if (!prop_dictionary_externalize_to_file(db, BTDEVCTL_PLIST)) 148 1.1 plunky warn("%s", BTDEVCTL_PLIST); 149 1.1 plunky } 150 1.1 plunky 151 1.1 plunky return 1; 152 1.1 plunky } 153 1.1 plunky 154 1.1 plunky /* 155 1.1 plunky * update database from version 0. This was a flat file using 156 1.1 plunky * btdevN as an index, and local-bdaddr and remote-bdaddr stored 157 1.1 plunky * as data objects. Step through and add them to the new dictionary. 158 1.1 plunky * We have to generate the service, but only HID, HF and HSET 159 1.1 plunky * were supported, so thats not too difficult. 160 1.1 plunky */ 161 1.1 plunky static void 162 1.1 plunky db_update0(void) 163 1.1 plunky { 164 1.1 plunky prop_dictionary_t old, dev; 165 1.1 plunky prop_dictionary_keysym_t key; 166 1.1 plunky prop_object_iterator_t iter; 167 1.1 plunky prop_object_t obj; 168 1.1 plunky bdaddr_t laddr, raddr; 169 1.2 plunky const char *service; 170 1.1 plunky 171 1.4 pavel db_flush = false; /* no write on set */ 172 1.1 plunky old = db; 173 1.1 plunky 174 1.1 plunky db = prop_dictionary_create(); 175 1.1 plunky if (db == NULL) 176 1.1 plunky err(EXIT_FAILURE, "prop_dictionary_create"); 177 1.1 plunky 178 1.1 plunky iter = prop_dictionary_iterator(old); 179 1.1 plunky if (iter == NULL) 180 1.1 plunky err(EXIT_FAILURE, "prop_dictionary_iterator"); 181 1.1 plunky 182 1.1 plunky while ((key = prop_object_iterator_next(iter)) != NULL) { 183 1.1 plunky dev = prop_dictionary_get_keysym(old, key); 184 1.1 plunky if (prop_object_type(dev) != PROP_TYPE_DICTIONARY) 185 1.1 plunky errx(EXIT_FAILURE, "invalid device dictionary"); 186 1.1 plunky 187 1.1 plunky obj = prop_dictionary_get(dev, BTDEVladdr); 188 1.1 plunky if (prop_data_size(obj) != sizeof(laddr)) 189 1.1 plunky errx(EXIT_FAILURE, "invalid %s", BTDEVladdr); 190 1.1 plunky 191 1.6 thorpej bdaddr_copy(&laddr, prop_data_value(obj)); 192 1.1 plunky prop_dictionary_remove(dev, BTDEVladdr); 193 1.1 plunky 194 1.1 plunky obj = prop_dictionary_get(dev, BTDEVraddr); 195 1.1 plunky if (prop_data_size(obj) != sizeof(raddr)) 196 1.1 plunky errx(EXIT_FAILURE, "invalid %s", BTDEVraddr); 197 1.1 plunky 198 1.6 thorpej bdaddr_copy(&raddr, prop_data_value(obj)); 199 1.1 plunky prop_dictionary_remove(dev, BTDEVraddr); 200 1.1 plunky 201 1.1 plunky obj = prop_dictionary_get(dev, BTDEVtype); 202 1.6 thorpej if (prop_string_equals_string(obj, "bthidev")) 203 1.1 plunky service = "HID"; 204 1.6 thorpej else if (prop_string_equals_string(obj, "btsco")) { 205 1.1 plunky obj = prop_dictionary_get(dev, BTSCOlisten); 206 1.1 plunky if (prop_bool_true(obj)) 207 1.1 plunky service = "HF"; 208 1.1 plunky else 209 1.1 plunky service = "HSET"; 210 1.1 plunky } else 211 1.1 plunky errx(EXIT_FAILURE, "invalid %s", BTDEVtype); 212 1.1 plunky 213 1.1 plunky if (!db_set(dev, &laddr, &raddr, service)) 214 1.1 plunky err(EXIT_FAILURE, "service store failed"); 215 1.1 plunky } 216 1.1 plunky 217 1.1 plunky prop_object_iterator_release(iter); 218 1.1 plunky prop_object_release(old); 219 1.1 plunky 220 1.4 pavel db_flush = true; /* write on set */ 221 1.1 plunky } 222 1.3 plunky 223 1.3 plunky /* 224 1.3 plunky * update database from version 1. Link Mode capability was added. 225 1.3 plunky * By default, we request authentication for HIDs, and encryption 226 1.3 plunky * is enabled for keyboards. 227 1.3 plunky */ 228 1.3 plunky static void 229 1.3 plunky db_update1(void) 230 1.3 plunky { 231 1.3 plunky prop_dictionary_t ldev, rdev, srv; 232 1.3 plunky prop_object_iterator_t iter0, iter1; 233 1.3 plunky prop_dictionary_keysym_t key; 234 1.3 plunky prop_object_t obj; 235 1.3 plunky bdaddr_t bdaddr; 236 1.3 plunky 237 1.3 plunky iter0 = prop_dictionary_iterator(db); 238 1.3 plunky if (iter0 == NULL) 239 1.3 plunky err(EXIT_FAILURE, "prop_dictionary_iterator"); 240 1.3 plunky 241 1.3 plunky while ((key = prop_object_iterator_next(iter0)) != NULL) { 242 1.3 plunky ldev = prop_dictionary_get_keysym(db, key); 243 1.3 plunky if (prop_object_type(ldev) != PROP_TYPE_DICTIONARY 244 1.6 thorpej || !bt_aton(prop_dictionary_keysym_value(key), &bdaddr)) 245 1.3 plunky continue; 246 1.3 plunky 247 1.3 plunky iter1 = prop_dictionary_iterator(ldev); 248 1.3 plunky if (iter1 == NULL) 249 1.3 plunky err(EXIT_FAILURE, "prop_dictionary_iterator"); 250 1.3 plunky 251 1.3 plunky while ((key = prop_object_iterator_next(iter1)) != NULL) { 252 1.3 plunky rdev = prop_dictionary_get_keysym(ldev, key); 253 1.3 plunky if (prop_object_type(rdev) != PROP_TYPE_DICTIONARY 254 1.6 thorpej || !bt_aton(prop_dictionary_keysym_value(key), &bdaddr)) 255 1.3 plunky continue; 256 1.3 plunky 257 1.3 plunky srv = prop_dictionary_get(rdev, "HID"); 258 1.3 plunky if (prop_object_type(srv) != PROP_TYPE_DICTIONARY) 259 1.3 plunky continue; 260 1.3 plunky 261 1.3 plunky obj = prop_dictionary_get(srv, BTHIDEVdescriptor); 262 1.3 plunky if (prop_object_type(obj) != PROP_TYPE_DATA) 263 1.3 plunky continue; 264 1.3 plunky 265 1.6 thorpej if (!prop_dictionary_set_string_nocopy(srv, BTDEVmode, 266 1.6 thorpej hid_mode(obj))) 267 1.3 plunky err(EXIT_FAILURE, "Cannot set %s", BTDEVmode); 268 1.3 plunky } 269 1.3 plunky 270 1.3 plunky prop_object_iterator_release(iter1); 271 1.3 plunky } 272 1.3 plunky 273 1.3 plunky prop_object_iterator_release(iter0); 274 1.3 plunky } 275