main_b0.c 77 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. *
  4. * BlueZ - Bluetooth protocol stack for Linux
  5. *
  6. * Copyright (C) 2012 Intel Corporation. All rights reserved.
  7. *
  8. *
  9. */
  10. #ifdef HAVE_CONFIG_H
  11. #include <config.h>
  12. #endif
  13. #define _GNU_SOURCE
  14. #include <stdio.h>
  15. #include <errno.h>
  16. #include <unistd.h>
  17. #include <stdlib.h>
  18. #include <stdbool.h>
  19. #include <wordexp.h>
  20. #include <glib.h>
  21. #include "src/shared/shell.h"
  22. #include "src/shared/util.h"
  23. #include "gdbus/gdbus.h"
  24. #include "agent.h"
  25. #include "gatt.h"
  26. #include "advertising.h"
  27. #include "adv_monitor.h"
  28. #include "admin.h"
  29. /* String display constants */
  30. #define COLORED_NEW COLOR_GREEN "NEW" COLOR_OFF
  31. #define COLORED_CHG COLOR_YELLOW "CHG" COLOR_OFF
  32. #define COLORED_DEL COLOR_RED "DEL" COLOR_OFF
  33. #define PROMPT_ON COLOR_BLUE "[bluetooth]" COLOR_OFF "# "
  34. #define PROMPT_OFF "Waiting to connect to bluetoothd..."
  35. static DBusConnection *dbus_conn;
  36. static GDBusProxy *agent_manager;
  37. static char *auto_register_agent = NULL;
  38. struct adapter {
  39. GDBusProxy *proxy;
  40. GDBusProxy *ad_proxy;
  41. GDBusProxy *adv_monitor_proxy;
  42. GList *devices;
  43. };
  44. static struct adapter *default_ctrl;
  45. static GDBusProxy *default_dev;
  46. static GDBusProxy *default_attr;
  47. static GList *ctrl_list;
  48. static GList *battery_proxies;
  49. static GList *admin_devices_proxies;
  50. static const char *agent_arguments[] = {
  51. "on",
  52. "off",
  53. "DisplayOnly",
  54. "DisplayYesNo",
  55. "KeyboardDisplay",
  56. "KeyboardOnly",
  57. "NoInputNoOutput",
  58. NULL
  59. };
  60. static const char *ad_arguments[] = {
  61. "on",
  62. "off",
  63. "peripheral",
  64. "broadcast",
  65. NULL
  66. };
  67. static void proxy_leak(gpointer data)
  68. {
  69. printf("Leaking proxy %p\n", data);
  70. }
  71. static void setup_standard_input(void)
  72. {
  73. bt_shell_attach(fileno(stdin));
  74. }
  75. static void connect_handler(DBusConnection *connection, void *user_data)
  76. {
  77. bt_shell_set_prompt(PROMPT_ON);
  78. }
  79. static void disconnect_handler(DBusConnection *connection, void *user_data)
  80. {
  81. bt_shell_detach();
  82. bt_shell_set_prompt(PROMPT_OFF);
  83. g_list_free_full(ctrl_list, proxy_leak);
  84. g_list_free_full(battery_proxies, proxy_leak);
  85. ctrl_list = NULL;
  86. battery_proxies = NULL;
  87. default_ctrl = NULL;
  88. }
  89. static void print_adapter(GDBusProxy *proxy, const char *description)
  90. {
  91. DBusMessageIter iter;
  92. const char *address, *name;
  93. if (g_dbus_proxy_get_property(proxy, "Address", &iter) == FALSE)
  94. return;
  95. dbus_message_iter_get_basic(&iter, &address);
  96. if (g_dbus_proxy_get_property(proxy, "Alias", &iter) == TRUE)
  97. dbus_message_iter_get_basic(&iter, &name);
  98. else
  99. name = "<unknown>";
  100. bt_shell_printf("%s%s%sController %s %s %s\n",
  101. description ? "[" : "",
  102. description ? : "",
  103. description ? "] " : "",
  104. address, name,
  105. default_ctrl &&
  106. default_ctrl->proxy == proxy ?
  107. "[default]" : "");
  108. }
  109. static void print_device(GDBusProxy *proxy, const char *description)
  110. {
  111. DBusMessageIter iter;
  112. const char *address, *name;
  113. if (g_dbus_proxy_get_property(proxy, "Address", &iter) == FALSE)
  114. return;
  115. dbus_message_iter_get_basic(&iter, &address);
  116. if (g_dbus_proxy_get_property(proxy, "Alias", &iter) == TRUE)
  117. dbus_message_iter_get_basic(&iter, &name);
  118. else
  119. name = "<unknown>";
  120. bt_shell_printf("%s%s%s-Device %s %s\n",
  121. description ? "[" : "",
  122. description ? : "",
  123. description ? "] " : "",
  124. address, name);
  125. }
  126. static void print_fixed_iter(const char *label, const char *name,
  127. DBusMessageIter *iter)
  128. {
  129. dbus_bool_t *valbool;
  130. dbus_uint32_t *valu32;
  131. dbus_uint16_t *valu16;
  132. dbus_int16_t *vals16;
  133. unsigned char *byte;
  134. int len;
  135. switch (dbus_message_iter_get_arg_type(iter)) {
  136. case DBUS_TYPE_BOOLEAN:
  137. dbus_message_iter_get_fixed_array(iter, &valbool, &len);
  138. if (len <= 0)
  139. return;
  140. bt_shell_printf("%s%s:\n", label, name);
  141. bt_shell_hexdump((void *)valbool, len * sizeof(*valbool));
  142. break;
  143. case DBUS_TYPE_UINT32:
  144. dbus_message_iter_get_fixed_array(iter, &valu32, &len);
  145. if (len <= 0)
  146. return;
  147. bt_shell_printf("%s%s:\n", label, name);
  148. bt_shell_hexdump((void *)valu32, len * sizeof(*valu32));
  149. break;
  150. case DBUS_TYPE_UINT16:
  151. dbus_message_iter_get_fixed_array(iter, &valu16, &len);
  152. if (len <= 0)
  153. return;
  154. bt_shell_printf("%s%s:\n", label, name);
  155. bt_shell_hexdump((void *)valu16, len * sizeof(*valu16));
  156. break;
  157. case DBUS_TYPE_INT16:
  158. dbus_message_iter_get_fixed_array(iter, &vals16, &len);
  159. if (len <= 0)
  160. return;
  161. bt_shell_printf("%s%s:\n", label, name);
  162. bt_shell_hexdump((void *)vals16, len * sizeof(*vals16));
  163. break;
  164. case DBUS_TYPE_BYTE:
  165. dbus_message_iter_get_fixed_array(iter, &byte, &len);
  166. if (len <= 0)
  167. return;
  168. bt_shell_printf("%s%s:\n", label, name);
  169. bt_shell_hexdump((void *)byte, len * sizeof(*byte));
  170. break;
  171. default:
  172. return;
  173. };
  174. }
  175. static void print_iter(const char *label, const char *name,
  176. DBusMessageIter *iter)
  177. {
  178. dbus_bool_t valbool;
  179. dbus_uint32_t valu32;
  180. dbus_uint16_t valu16;
  181. dbus_int16_t vals16;
  182. unsigned char byte;
  183. const char *valstr;
  184. DBusMessageIter subiter;
  185. char *entry;
  186. if (iter == NULL) {
  187. bt_shell_printf("%s%s is nil\n", label, name);
  188. return;
  189. }
  190. switch (dbus_message_iter_get_arg_type(iter)) {
  191. case DBUS_TYPE_INVALID:
  192. bt_shell_printf("%s%s is invalid\n", label, name);
  193. break;
  194. case DBUS_TYPE_STRING:
  195. case DBUS_TYPE_OBJECT_PATH:
  196. dbus_message_iter_get_basic(iter, &valstr);
  197. bt_shell_printf("%s%s: %s\n", label, name, valstr);
  198. break;
  199. case DBUS_TYPE_BOOLEAN:
  200. dbus_message_iter_get_basic(iter, &valbool);
  201. bt_shell_printf("%s%s: %s\n", label, name,
  202. valbool == TRUE ? "yes" : "no");
  203. break;
  204. case DBUS_TYPE_UINT32:
  205. dbus_message_iter_get_basic(iter, &valu32);
  206. bt_shell_printf("%s%s: 0x%08x\n", label, name, valu32);
  207. break;
  208. case DBUS_TYPE_UINT16:
  209. dbus_message_iter_get_basic(iter, &valu16);
  210. bt_shell_printf("%s%s: 0x%04x\n", label, name, valu16);
  211. break;
  212. case DBUS_TYPE_INT16:
  213. dbus_message_iter_get_basic(iter, &vals16);
  214. bt_shell_printf("%s%s: %d\n", label, name, vals16);
  215. break;
  216. case DBUS_TYPE_BYTE:
  217. dbus_message_iter_get_basic(iter, &byte);
  218. bt_shell_printf("%s%s: 0x%02x (%d)\n", label, name, byte, byte);
  219. break;
  220. case DBUS_TYPE_VARIANT:
  221. dbus_message_iter_recurse(iter, &subiter);
  222. print_iter(label, name, &subiter);
  223. break;
  224. case DBUS_TYPE_ARRAY:
  225. dbus_message_iter_recurse(iter, &subiter);
  226. if (dbus_type_is_fixed(
  227. dbus_message_iter_get_arg_type(&subiter))) {
  228. print_fixed_iter(label, name, &subiter);
  229. break;
  230. }
  231. while (dbus_message_iter_get_arg_type(&subiter) !=
  232. DBUS_TYPE_INVALID) {
  233. print_iter(label, name, &subiter);
  234. dbus_message_iter_next(&subiter);
  235. }
  236. break;
  237. case DBUS_TYPE_DICT_ENTRY:
  238. dbus_message_iter_recurse(iter, &subiter);
  239. entry = g_strconcat(name, " Key", NULL);
  240. print_iter(label, entry, &subiter);
  241. g_free(entry);
  242. entry = g_strconcat(name, " Value", NULL);
  243. dbus_message_iter_next(&subiter);
  244. print_iter(label, entry, &subiter);
  245. g_free(entry);
  246. break;
  247. default:
  248. bt_shell_printf("%s%s has unsupported type\n", label, name);
  249. break;
  250. }
  251. }
  252. static void print_property_with_label(GDBusProxy *proxy, const char *name,
  253. const char *label)
  254. {
  255. DBusMessageIter iter;
  256. if (g_dbus_proxy_get_property(proxy, name, &iter) == FALSE)
  257. return;
  258. print_iter("\t", label ? label : name, &iter);
  259. }
  260. static void print_property(GDBusProxy *proxy, const char *name)
  261. {
  262. print_property_with_label(proxy, name, NULL);
  263. }
  264. static void print_uuid(const char *label, const char *uuid)
  265. {
  266. const char *text;
  267. text = bt_uuidstr_to_str(uuid);
  268. if (text) {
  269. char str[26];
  270. unsigned int n;
  271. str[sizeof(str) - 1] = '\0';
  272. n = snprintf(str, sizeof(str), "%s", text);
  273. if (n > sizeof(str) - 1) {
  274. str[sizeof(str) - 2] = '.';
  275. str[sizeof(str) - 3] = '.';
  276. if (str[sizeof(str) - 4] == ' ')
  277. str[sizeof(str) - 4] = '.';
  278. n = sizeof(str) - 1;
  279. }
  280. bt_shell_printf("\t%s: %s%*c(%s)\n", label, str, 26 - n, ' ',
  281. uuid);
  282. } else
  283. bt_shell_printf("\t%s: %*c(%s)\n", label, 26, ' ', uuid);
  284. }
  285. static void print_uuids(GDBusProxy *proxy)
  286. {
  287. DBusMessageIter iter, value;
  288. if (g_dbus_proxy_get_property(proxy, "UUIDs", &iter) == FALSE)
  289. return;
  290. dbus_message_iter_recurse(&iter, &value);
  291. while (dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_STRING) {
  292. const char *uuid;
  293. dbus_message_iter_get_basic(&value, &uuid);
  294. print_uuid("UUID", uuid);
  295. dbus_message_iter_next(&value);
  296. }
  297. }
  298. static void print_experimental(GDBusProxy *proxy)
  299. {
  300. DBusMessageIter iter, value;
  301. if (g_dbus_proxy_get_property(proxy, "ExperimentalFeatures",
  302. &iter) == FALSE)
  303. return;
  304. dbus_message_iter_recurse(&iter, &value);
  305. while (dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_STRING) {
  306. const char *uuid;
  307. dbus_message_iter_get_basic(&value, &uuid);
  308. print_uuid("ExperimentalFeatures", uuid);
  309. dbus_message_iter_next(&value);
  310. }
  311. }
  312. static gboolean device_is_child(GDBusProxy *device, GDBusProxy *parent)
  313. {
  314. DBusMessageIter iter;
  315. const char *adapter, *path;
  316. if (!parent)
  317. return FALSE;
  318. if (g_dbus_proxy_get_property(device, "Adapter", &iter) == FALSE)
  319. return FALSE;
  320. dbus_message_iter_get_basic(&iter, &adapter);
  321. path = g_dbus_proxy_get_path(parent);
  322. if (!strcmp(path, adapter))
  323. return TRUE;
  324. return FALSE;
  325. }
  326. static gboolean service_is_child(GDBusProxy *service)
  327. {
  328. DBusMessageIter iter;
  329. const char *device;
  330. if (g_dbus_proxy_get_property(service, "Device", &iter) == FALSE)
  331. return FALSE;
  332. dbus_message_iter_get_basic(&iter, &device);
  333. if (!default_ctrl)
  334. return FALSE;
  335. return g_dbus_proxy_lookup(default_ctrl->devices, NULL, device,
  336. "org.bluez.Device1") != NULL;
  337. }
  338. static struct adapter *find_parent(GDBusProxy *device)
  339. {
  340. GList *list;
  341. for (list = g_list_first(ctrl_list); list; list = g_list_next(list)) {
  342. struct adapter *adapter = list->data;
  343. if (device_is_child(device, adapter->proxy) == TRUE)
  344. return adapter;
  345. }
  346. return NULL;
  347. }
  348. static void set_default_device(GDBusProxy *proxy, const char *attribute)
  349. {
  350. char *desc = NULL;
  351. DBusMessageIter iter;
  352. const char *path;
  353. default_dev = proxy;
  354. if (proxy == NULL) {
  355. default_attr = NULL;
  356. goto done;
  357. }
  358. if (!g_dbus_proxy_get_property(proxy, "Alias", &iter)) {
  359. if (!g_dbus_proxy_get_property(proxy, "Address", &iter))
  360. goto done;
  361. }
  362. path = g_dbus_proxy_get_path(proxy);
  363. dbus_message_iter_get_basic(&iter, &desc);
  364. desc = g_strdup_printf(COLOR_BLUE "[%s%s%s]" COLOR_OFF "# ", desc,
  365. attribute ? ":" : "",
  366. attribute ? attribute + strlen(path) : "");
  367. done:
  368. bt_shell_set_prompt(desc ? desc : PROMPT_ON);
  369. g_free(desc);
  370. }
  371. static void battery_added(GDBusProxy *proxy)
  372. {
  373. battery_proxies = g_list_append(battery_proxies, proxy);
  374. }
  375. static void battery_removed(GDBusProxy *proxy)
  376. {
  377. battery_proxies = g_list_remove(battery_proxies, proxy);
  378. }
  379. static void device_added(GDBusProxy *proxy)
  380. {
  381. DBusMessageIter iter;
  382. struct adapter *adapter = find_parent(proxy);
  383. if (!adapter) {
  384. /* TODO: Error */
  385. return;
  386. }
  387. adapter->devices = g_list_append(adapter->devices, proxy);
  388. print_device(proxy, COLORED_NEW);
  389. bt_shell_set_env(g_dbus_proxy_get_path(proxy), proxy);
  390. if (default_dev)
  391. return;
  392. if (g_dbus_proxy_get_property(proxy, "Connected", &iter)) {
  393. dbus_bool_t connected;
  394. dbus_message_iter_get_basic(&iter, &connected);
  395. if (connected)
  396. set_default_device(proxy, NULL);
  397. }
  398. }
  399. static struct adapter *find_ctrl(GList *source, const char *path);
  400. static struct adapter *adapter_new(GDBusProxy *proxy)
  401. {
  402. struct adapter *adapter = g_malloc0(sizeof(struct adapter));
  403. ctrl_list = g_list_append(ctrl_list, adapter);
  404. if (!default_ctrl)
  405. default_ctrl = adapter;
  406. return adapter;
  407. }
  408. static void adapter_added(GDBusProxy *proxy)
  409. {
  410. struct adapter *adapter;
  411. adapter = find_ctrl(ctrl_list, g_dbus_proxy_get_path(proxy));
  412. if (!adapter)
  413. adapter = adapter_new(proxy);
  414. adapter->proxy = proxy;
  415. print_adapter(proxy, COLORED_NEW);
  416. bt_shell_set_env(g_dbus_proxy_get_path(proxy), proxy);
  417. }
  418. static void ad_manager_added(GDBusProxy *proxy)
  419. {
  420. struct adapter *adapter;
  421. adapter = find_ctrl(ctrl_list, g_dbus_proxy_get_path(proxy));
  422. if (!adapter)
  423. adapter = adapter_new(proxy);
  424. adapter->ad_proxy = proxy;
  425. }
  426. static void admon_manager_added(GDBusProxy *proxy)
  427. {
  428. struct adapter *adapter;
  429. adapter = find_ctrl(ctrl_list, g_dbus_proxy_get_path(proxy));
  430. if (!adapter)
  431. adapter = adapter_new(proxy);
  432. adapter->adv_monitor_proxy = proxy;
  433. adv_monitor_add_manager(dbus_conn, proxy);
  434. adv_monitor_register_app(dbus_conn);
  435. }
  436. static void admin_policy_set_added(GDBusProxy *proxy)
  437. {
  438. admin_policy_set_set_proxy(proxy);
  439. }
  440. static void admin_policy_status_added(GDBusProxy *proxy)
  441. {
  442. struct adapter *adapter;
  443. adapter = find_ctrl(ctrl_list, g_dbus_proxy_get_path(proxy));
  444. if (!adapter) {
  445. admin_devices_proxies = g_list_append(admin_devices_proxies,
  446. proxy);
  447. return;
  448. }
  449. admin_policy_set_status_proxy(proxy);
  450. }
  451. static void proxy_added(GDBusProxy *proxy, void *user_data)
  452. {
  453. const char *interface;
  454. interface = g_dbus_proxy_get_interface(proxy);
  455. if (!strcmp(interface, "org.bluez.Device1")) {
  456. device_added(proxy);
  457. } else if (!strcmp(interface, "org.bluez.Adapter1")) {
  458. adapter_added(proxy);
  459. } else if (!strcmp(interface, "org.bluez.AgentManager1")) {
  460. if (!agent_manager) {
  461. agent_manager = proxy;
  462. if (auto_register_agent &&
  463. !bt_shell_get_env("NON_INTERACTIVE"))
  464. agent_register(dbus_conn, agent_manager,
  465. auto_register_agent);
  466. }
  467. } else if (!strcmp(interface, "org.bluez.GattService1")) {
  468. if (service_is_child(proxy))
  469. gatt_add_service(proxy);
  470. } else if (!strcmp(interface, "org.bluez.GattCharacteristic1")) {
  471. gatt_add_characteristic(proxy);
  472. } else if (!strcmp(interface, "org.bluez.GattDescriptor1")) {
  473. gatt_add_descriptor(proxy);
  474. } else if (!strcmp(interface, "org.bluez.GattManager1")) {
  475. gatt_add_manager(proxy);
  476. } else if (!strcmp(interface, "org.bluez.LEAdvertisingManager1")) {
  477. ad_manager_added(proxy);
  478. } else if (!strcmp(interface, "org.bluez.Battery1")) {
  479. battery_added(proxy);
  480. } else if (!strcmp(interface,
  481. "org.bluez.AdvertisementMonitorManager1")) {
  482. admon_manager_added(proxy);
  483. } else if (!strcmp(interface, "org.bluez.AdminPolicySet1")) {
  484. admin_policy_set_added(proxy);
  485. } else if (!strcmp(interface, "org.bluez.AdminPolicyStatus1")) {
  486. admin_policy_status_added(proxy);
  487. }
  488. }
  489. static void set_default_attribute(GDBusProxy *proxy)
  490. {
  491. const char *path;
  492. default_attr = proxy;
  493. path = g_dbus_proxy_get_path(proxy);
  494. set_default_device(default_dev, path);
  495. }
  496. static void device_removed(GDBusProxy *proxy)
  497. {
  498. struct adapter *adapter = find_parent(proxy);
  499. if (!adapter) {
  500. /* TODO: Error */
  501. return;
  502. }
  503. adapter->devices = g_list_remove(adapter->devices, proxy);
  504. print_device(proxy, COLORED_DEL);
  505. bt_shell_set_env(g_dbus_proxy_get_path(proxy), NULL);
  506. if (default_dev == proxy)
  507. set_default_device(NULL, NULL);
  508. }
  509. static void adapter_removed(GDBusProxy *proxy)
  510. {
  511. GList *ll;
  512. for (ll = g_list_first(ctrl_list); ll; ll = g_list_next(ll)) {
  513. struct adapter *adapter = ll->data;
  514. if (adapter->proxy == proxy) {
  515. print_adapter(proxy, COLORED_DEL);
  516. bt_shell_set_env(g_dbus_proxy_get_path(proxy), NULL);
  517. if (default_ctrl && default_ctrl->proxy == proxy) {
  518. default_ctrl = NULL;
  519. set_default_device(NULL, NULL);
  520. }
  521. ctrl_list = g_list_remove_link(ctrl_list, ll);
  522. g_list_free(adapter->devices);
  523. g_free(adapter);
  524. g_list_free(ll);
  525. return;
  526. }
  527. }
  528. }
  529. static void admin_policy_set_removed(GDBusProxy *proxy)
  530. {
  531. admin_policy_set_set_proxy(NULL);
  532. }
  533. static void admin_policy_status_removed(GDBusProxy *proxy)
  534. {
  535. struct adapter *adapter;
  536. adapter = find_ctrl(ctrl_list, g_dbus_proxy_get_path(proxy));
  537. if (!adapter) {
  538. admin_devices_proxies = g_list_remove(admin_devices_proxies,
  539. proxy);
  540. return;
  541. }
  542. admin_policy_set_status_proxy(NULL);
  543. }
  544. static void proxy_removed(GDBusProxy *proxy, void *user_data)
  545. {
  546. const char *interface;
  547. interface = g_dbus_proxy_get_interface(proxy);
  548. if (!strcmp(interface, "org.bluez.Device1")) {
  549. device_removed(proxy);
  550. } else if (!strcmp(interface, "org.bluez.Adapter1")) {
  551. adapter_removed(proxy);
  552. } else if (!strcmp(interface, "org.bluez.AgentManager1")) {
  553. if (agent_manager == proxy) {
  554. agent_manager = NULL;
  555. if (auto_register_agent)
  556. agent_unregister(dbus_conn, NULL);
  557. }
  558. } else if (!strcmp(interface, "org.bluez.GattService1")) {
  559. gatt_remove_service(proxy);
  560. if (default_attr == proxy)
  561. set_default_attribute(NULL);
  562. } else if (!strcmp(interface, "org.bluez.GattCharacteristic1")) {
  563. gatt_remove_characteristic(proxy);
  564. if (default_attr == proxy)
  565. set_default_attribute(NULL);
  566. } else if (!strcmp(interface, "org.bluez.GattDescriptor1")) {
  567. gatt_remove_descriptor(proxy);
  568. if (default_attr == proxy)
  569. set_default_attribute(NULL);
  570. } else if (!strcmp(interface, "org.bluez.GattManager1")) {
  571. gatt_remove_manager(proxy);
  572. } else if (!strcmp(interface, "org.bluez.LEAdvertisingManager1")) {
  573. ad_unregister(dbus_conn, NULL);
  574. } else if (!strcmp(interface, "org.bluez.Battery1")) {
  575. battery_removed(proxy);
  576. } else if (!strcmp(interface,
  577. "org.bluez.AdvertisementMonitorManager1")) {
  578. adv_monitor_remove_manager(dbus_conn);
  579. } else if (!strcmp(interface, "org.bluez.AdminPolicySet1")) {
  580. admin_policy_set_removed(proxy);
  581. } else if (!strcmp(interface, "org.bluez.AdminPolicyStatus1")) {
  582. admin_policy_status_removed(proxy);
  583. }
  584. }
  585. static struct adapter *find_ctrl(GList *source, const char *path)
  586. {
  587. GList *list;
  588. for (list = g_list_first(source); list; list = g_list_next(list)) {
  589. struct adapter *adapter = list->data;
  590. if (!strcasecmp(g_dbus_proxy_get_path(adapter->proxy), path))
  591. return adapter;
  592. }
  593. return NULL;
  594. }
  595. static void property_changed(GDBusProxy *proxy, const char *name,
  596. DBusMessageIter *iter, void *user_data)
  597. {
  598. const char *interface;
  599. struct adapter *ctrl;
  600. interface = g_dbus_proxy_get_interface(proxy);
  601. if (!strcmp(interface, "org.bluez.Device1")) {
  602. if (default_ctrl && device_is_child(proxy,
  603. default_ctrl->proxy) == TRUE) {
  604. DBusMessageIter addr_iter;
  605. char *str;
  606. if (g_dbus_proxy_get_property(proxy, "Address",
  607. &addr_iter) == TRUE) {
  608. const char *address;
  609. dbus_message_iter_get_basic(&addr_iter,
  610. &address);
  611. str = g_strdup_printf("[" COLORED_CHG
  612. "] Device %s ", address);
  613. } else
  614. str = g_strdup("");
  615. if (strcmp(name, "Connected") == 0) {
  616. dbus_bool_t connected;
  617. dbus_message_iter_get_basic(iter, &connected);
  618. if (connected && default_dev == NULL)
  619. set_default_device(proxy, NULL);
  620. else if (!connected && default_dev == proxy)
  621. set_default_device(NULL, NULL);
  622. }
  623. print_iter(str, name, iter);
  624. g_free(str);
  625. }
  626. } else if (!strcmp(interface, "org.bluez.Adapter1")) {
  627. DBusMessageIter addr_iter;
  628. char *str;
  629. if (g_dbus_proxy_get_property(proxy, "Address",
  630. &addr_iter) == TRUE) {
  631. const char *address;
  632. dbus_message_iter_get_basic(&addr_iter, &address);
  633. str = g_strdup_printf("[" COLORED_CHG
  634. "] Controller %s ", address);
  635. } else
  636. str = g_strdup("");
  637. print_iter(str, name, iter);
  638. g_free(str);
  639. } else if (!strcmp(interface, "org.bluez.LEAdvertisingManager1")) {
  640. DBusMessageIter addr_iter;
  641. char *str;
  642. ctrl = find_ctrl(ctrl_list, g_dbus_proxy_get_path(proxy));
  643. if (!ctrl)
  644. return;
  645. if (g_dbus_proxy_get_property(ctrl->proxy, "Address",
  646. &addr_iter) == TRUE) {
  647. const char *address;
  648. dbus_message_iter_get_basic(&addr_iter, &address);
  649. str = g_strdup_printf("[" COLORED_CHG
  650. "] Controller %s ",
  651. address);
  652. } else
  653. str = g_strdup("");
  654. print_iter(str, name, iter);
  655. g_free(str);
  656. } else if (proxy == default_attr) {
  657. char *str;
  658. str = g_strdup_printf("[" COLORED_CHG "] Attribute %s ",
  659. g_dbus_proxy_get_path(proxy));
  660. print_iter(str, name, iter);
  661. g_free(str);
  662. }
  663. }
  664. static void message_handler(DBusConnection *connection,
  665. DBusMessage *message, void *user_data)
  666. {
  667. bt_shell_printf("[SIGNAL] %s.%s\n", dbus_message_get_interface(message),
  668. dbus_message_get_member(message));
  669. }
  670. static struct adapter *find_ctrl_by_address(GList *source, const char *address)
  671. {
  672. GList *list;
  673. for (list = g_list_first(source); list; list = g_list_next(list)) {
  674. struct adapter *adapter = list->data;
  675. DBusMessageIter iter;
  676. const char *str;
  677. if (g_dbus_proxy_get_property(adapter->proxy,
  678. "Address", &iter) == FALSE)
  679. continue;
  680. dbus_message_iter_get_basic(&iter, &str);
  681. if (!strcasecmp(str, address))
  682. return adapter;
  683. }
  684. return NULL;
  685. }
  686. static GDBusProxy *find_proxies_by_path(GList *source, const char *path)
  687. {
  688. GList *list;
  689. for (list = g_list_first(source); list; list = g_list_next(list)) {
  690. GDBusProxy *proxy = list->data;
  691. if (strcmp(g_dbus_proxy_get_path(proxy), path) == 0)
  692. return proxy;
  693. }
  694. return NULL;
  695. }
  696. static GDBusProxy *find_proxy_by_address(GList *source, const char *address)
  697. {
  698. GList *list;
  699. for (list = g_list_first(source); list; list = g_list_next(list)) {
  700. GDBusProxy *proxy = list->data;
  701. DBusMessageIter iter;
  702. const char *str;
  703. if (g_dbus_proxy_get_property(proxy, "Address", &iter) == FALSE)
  704. continue;
  705. dbus_message_iter_get_basic(&iter, &str);
  706. if (!strcasecmp(str, address))
  707. return proxy;
  708. }
  709. return NULL;
  710. }
  711. static gboolean check_default_ctrl(void)
  712. {
  713. if (!default_ctrl) {
  714. bt_shell_printf("No default controller available\n");
  715. return FALSE;
  716. }
  717. return TRUE;
  718. }
  719. static gboolean parse_argument(int argc, char *argv[], const char **arg_table,
  720. const char *msg, dbus_bool_t *value,
  721. const char **option)
  722. {
  723. const char **opt;
  724. if (!strcmp(argv[1], "help")) {
  725. for (opt = arg_table; opt && *opt; opt++)
  726. bt_shell_printf("%s\n", *opt);
  727. bt_shell_noninteractive_quit(EXIT_SUCCESS);
  728. return FALSE;
  729. }
  730. if (!strcmp(argv[1], "on") || !strcmp(argv[1], "yes")) {
  731. *value = TRUE;
  732. if (option)
  733. *option = "";
  734. return TRUE;
  735. }
  736. if (!strcmp(argv[1], "off") || !strcmp(argv[1], "no")) {
  737. *value = FALSE;
  738. return TRUE;
  739. }
  740. for (opt = arg_table; opt && *opt; opt++) {
  741. if (strcmp(argv[1], *opt) == 0) {
  742. *value = TRUE;
  743. *option = *opt;
  744. return TRUE;
  745. }
  746. }
  747. bt_shell_printf("Invalid argument %s\n", argv[1]);
  748. return FALSE;
  749. }
  750. static void cmd_list(int argc, char *argv[])
  751. {
  752. GList *list;
  753. for (list = g_list_first(ctrl_list); list; list = g_list_next(list)) {
  754. struct adapter *adapter = list->data;
  755. print_adapter(adapter->proxy, NULL);
  756. }
  757. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  758. }
  759. static void cmd_show(int argc, char *argv[])
  760. {
  761. struct adapter *adapter;
  762. DBusMessageIter iter;
  763. const char *address;
  764. if (argc < 2 || !strlen(argv[1])) {
  765. if (check_default_ctrl() == FALSE)
  766. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  767. adapter = default_ctrl;
  768. } else {
  769. adapter = find_ctrl_by_address(ctrl_list, argv[1]);
  770. if (!adapter) {
  771. bt_shell_printf("Controller %s not available\n",
  772. argv[1]);
  773. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  774. }
  775. }
  776. if (!g_dbus_proxy_get_property(adapter->proxy, "Address", &iter))
  777. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  778. dbus_message_iter_get_basic(&iter, &address);
  779. if (g_dbus_proxy_get_property(adapter->proxy, "AddressType", &iter)) {
  780. const char *type;
  781. dbus_message_iter_get_basic(&iter, &type);
  782. bt_shell_printf("Controller %s (%s)\n", address, type);
  783. } else {
  784. bt_shell_printf("Controller %s\n", address);
  785. }
  786. print_property(adapter->proxy, "Name");
  787. print_property(adapter->proxy, "Alias");
  788. print_property(adapter->proxy, "Class");
  789. print_property(adapter->proxy, "Powered");
  790. print_property(adapter->proxy, "Discoverable");
  791. print_property(adapter->proxy, "DiscoverableTimeout");
  792. print_property(adapter->proxy, "Pairable");
  793. print_uuids(adapter->proxy);
  794. print_property(adapter->proxy, "Modalias");
  795. print_property(adapter->proxy, "Discovering");
  796. print_property(adapter->proxy, "Roles");
  797. print_experimental(adapter->proxy);
  798. if (adapter->ad_proxy) {
  799. bt_shell_printf("Advertising Features:\n");
  800. print_property(adapter->ad_proxy, "ActiveInstances");
  801. print_property(adapter->ad_proxy, "SupportedInstances");
  802. print_property(adapter->ad_proxy, "SupportedIncludes");
  803. print_property(adapter->ad_proxy, "SupportedSecondaryChannels");
  804. print_property(adapter->ad_proxy, "SupportedCapabilities");
  805. print_property(adapter->ad_proxy, "SupportedFeatures");
  806. }
  807. if (adapter->adv_monitor_proxy) {
  808. bt_shell_printf("Advertisement Monitor Features:\n");
  809. print_property(adapter->adv_monitor_proxy,
  810. "SupportedMonitorTypes");
  811. print_property(adapter->adv_monitor_proxy, "SupportedFeatures");
  812. }
  813. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  814. }
  815. static void cmd_select(int argc, char *argv[])
  816. {
  817. struct adapter *adapter;
  818. adapter = find_ctrl_by_address(ctrl_list, argv[1]);
  819. if (!adapter) {
  820. bt_shell_printf("Controller %s not available\n", argv[1]);
  821. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  822. }
  823. if (default_ctrl && default_ctrl->proxy == adapter->proxy)
  824. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  825. default_ctrl = adapter;
  826. print_adapter(adapter->proxy, NULL);
  827. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  828. }
  829. static void cmd_devices(int argc, char *argv[])
  830. {
  831. GList *ll;
  832. if (check_default_ctrl() == FALSE)
  833. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  834. for (ll = g_list_first(default_ctrl->devices);
  835. ll; ll = g_list_next(ll)) {
  836. GDBusProxy *proxy = ll->data;
  837. print_device(proxy, NULL);
  838. }
  839. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  840. }
  841. static void cmd_paired_devices(int argc, char *argv[])
  842. {
  843. GList *ll;
  844. if (check_default_ctrl() == FALSE)
  845. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  846. for (ll = g_list_first(default_ctrl->devices);
  847. ll; ll = g_list_next(ll)) {
  848. GDBusProxy *proxy = ll->data;
  849. DBusMessageIter iter;
  850. dbus_bool_t paired;
  851. if (g_dbus_proxy_get_property(proxy, "Paired", &iter) == FALSE)
  852. continue;
  853. dbus_message_iter_get_basic(&iter, &paired);
  854. if (!paired)
  855. continue;
  856. print_device(proxy, NULL);
  857. }
  858. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  859. }
  860. static void generic_callback(const DBusError *error, void *user_data)
  861. {
  862. char *str = user_data;
  863. if (dbus_error_is_set(error)) {
  864. bt_shell_printf("Failed to set %s: %s\n", str, error->name);
  865. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  866. } else {
  867. bt_shell_printf("Changing %s succeeded\n", str);
  868. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  869. }
  870. }
  871. static void cmd_system_alias(int argc, char *argv[])
  872. {
  873. char *name;
  874. if (check_default_ctrl() == FALSE)
  875. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  876. name = g_strdup(argv[1]);
  877. if (g_dbus_proxy_set_property_basic(default_ctrl->proxy, "Alias",
  878. DBUS_TYPE_STRING, &name,
  879. generic_callback, name, g_free) == TRUE)
  880. return;
  881. g_free(name);
  882. }
  883. static void cmd_reset_alias(int argc, char *argv[])
  884. {
  885. char *name;
  886. if (check_default_ctrl() == FALSE)
  887. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  888. name = g_strdup("");
  889. if (g_dbus_proxy_set_property_basic(default_ctrl->proxy, "Alias",
  890. DBUS_TYPE_STRING, &name,
  891. generic_callback, name, g_free) == TRUE)
  892. return;
  893. g_free(name);
  894. }
  895. static void cmd_power(int argc, char *argv[])
  896. {
  897. dbus_bool_t powered;
  898. char *str;
  899. if (!parse_argument(argc, argv, NULL, NULL, &powered, NULL))
  900. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  901. if (check_default_ctrl() == FALSE)
  902. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  903. str = g_strdup_printf("power %s", powered == TRUE ? "on" : "off");
  904. if (g_dbus_proxy_set_property_basic(default_ctrl->proxy, "Powered",
  905. DBUS_TYPE_BOOLEAN, &powered,
  906. generic_callback, str, g_free) == TRUE)
  907. return;
  908. g_free(str);
  909. }
  910. static void cmd_pairable(int argc, char *argv[])
  911. {
  912. dbus_bool_t pairable;
  913. char *str;
  914. if (!parse_argument(argc, argv, NULL, NULL, &pairable, NULL))
  915. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  916. if (check_default_ctrl() == FALSE)
  917. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  918. str = g_strdup_printf("pairable %s", pairable == TRUE ? "on" : "off");
  919. if (g_dbus_proxy_set_property_basic(default_ctrl->proxy, "Pairable",
  920. DBUS_TYPE_BOOLEAN, &pairable,
  921. generic_callback, str, g_free) == TRUE)
  922. return;
  923. g_free(str);
  924. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  925. }
  926. static void cmd_discoverable(int argc, char *argv[])
  927. {
  928. dbus_bool_t discoverable;
  929. char *str;
  930. if (!parse_argument(argc, argv, NULL, NULL, &discoverable, NULL))
  931. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  932. if (check_default_ctrl() == FALSE)
  933. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  934. str = g_strdup_printf("discoverable %s",
  935. discoverable == TRUE ? "on" : "off");
  936. if (g_dbus_proxy_set_property_basic(default_ctrl->proxy, "Discoverable",
  937. DBUS_TYPE_BOOLEAN, &discoverable,
  938. generic_callback, str, g_free) == TRUE)
  939. return;
  940. g_free(str);
  941. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  942. }
  943. static void cmd_discoverable_timeout(int argc, char *argv[])
  944. {
  945. uint32_t value;
  946. char *endptr = NULL;
  947. char *str;
  948. if (argc < 2) {
  949. DBusMessageIter iter;
  950. if (!g_dbus_proxy_get_property(default_ctrl->proxy,
  951. "DiscoverableTimeout", &iter)) {
  952. bt_shell_printf("Unable to get DiscoverableTimeout\n");
  953. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  954. }
  955. dbus_message_iter_get_basic(&iter, &value);
  956. bt_shell_printf("DiscoverableTimeout: %d seconds\n", value);
  957. return;
  958. }
  959. value = strtol(argv[1], &endptr, 0);
  960. if (!endptr || *endptr != '\0' || value > UINT32_MAX) {
  961. bt_shell_printf("Invalid argument\n");
  962. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  963. }
  964. str = g_strdup_printf("discoverable-timeout %d", value);
  965. if (g_dbus_proxy_set_property_basic(default_ctrl->proxy,
  966. "DiscoverableTimeout",
  967. DBUS_TYPE_UINT32, &value,
  968. generic_callback, str, g_free))
  969. return;
  970. g_free(str);
  971. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  972. }
  973. static void cmd_agent(int argc, char *argv[])
  974. {
  975. dbus_bool_t enable;
  976. const char *capability;
  977. if (!parse_argument(argc, argv, agent_arguments, "capability",
  978. &enable, &capability))
  979. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  980. if (enable == TRUE) {
  981. g_free(auto_register_agent);
  982. auto_register_agent = g_strdup(capability);
  983. if (agent_manager)
  984. agent_register(dbus_conn, agent_manager,
  985. auto_register_agent);
  986. else
  987. bt_shell_printf("Agent registration enabled\n");
  988. } else {
  989. g_free(auto_register_agent);
  990. auto_register_agent = NULL;
  991. if (agent_manager)
  992. agent_unregister(dbus_conn, agent_manager);
  993. else
  994. bt_shell_printf("Agent registration disabled\n");
  995. }
  996. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  997. }
  998. static void cmd_default_agent(int argc, char *argv[])
  999. {
  1000. agent_default(dbus_conn, agent_manager);
  1001. }
  1002. #define DISTANCE_VAL_INVALID 0x7FFF
  1003. static struct set_discovery_filter_args {
  1004. char *transport;
  1005. char *pattern;
  1006. dbus_uint16_t rssi;
  1007. dbus_int16_t pathloss;
  1008. char **uuids;
  1009. size_t uuids_len;
  1010. dbus_bool_t duplicate;
  1011. dbus_bool_t discoverable;
  1012. bool set;
  1013. bool active;
  1014. } filter = {
  1015. .rssi = DISTANCE_VAL_INVALID,
  1016. .pathloss = DISTANCE_VAL_INVALID,
  1017. .set = true,
  1018. };
  1019. static void start_discovery_reply(DBusMessage *message, void *user_data)
  1020. {
  1021. dbus_bool_t enable = GPOINTER_TO_UINT(user_data);
  1022. DBusError error;
  1023. dbus_error_init(&error);
  1024. if (dbus_set_error_from_message(&error, message) == TRUE) {
  1025. bt_shell_printf("Failed to %s discovery: %s\n",
  1026. enable == TRUE ? "start" : "stop", error.name);
  1027. dbus_error_free(&error);
  1028. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1029. }
  1030. bt_shell_printf("Discovery %s\n", enable ? "started" : "stopped");
  1031. filter.active = enable;
  1032. /* Leave the discovery running even on noninteractive mode */
  1033. }
  1034. static void clear_discovery_filter(DBusMessageIter *iter, void *user_data)
  1035. {
  1036. DBusMessageIter dict;
  1037. dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
  1038. DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
  1039. DBUS_TYPE_STRING_AS_STRING
  1040. DBUS_TYPE_VARIANT_AS_STRING
  1041. DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
  1042. dbus_message_iter_close_container(iter, &dict);
  1043. }
  1044. static void set_discovery_filter_setup(DBusMessageIter *iter, void *user_data)
  1045. {
  1046. struct set_discovery_filter_args *args = user_data;
  1047. DBusMessageIter dict;
  1048. dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
  1049. DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
  1050. DBUS_TYPE_STRING_AS_STRING
  1051. DBUS_TYPE_VARIANT_AS_STRING
  1052. DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
  1053. g_dbus_dict_append_array(&dict, "UUIDs", DBUS_TYPE_STRING,
  1054. &args->uuids,
  1055. args->uuids_len);
  1056. if (args->pathloss != DISTANCE_VAL_INVALID)
  1057. g_dbus_dict_append_entry(&dict, "Pathloss", DBUS_TYPE_UINT16,
  1058. &args->pathloss);
  1059. if (args->rssi != DISTANCE_VAL_INVALID)
  1060. g_dbus_dict_append_entry(&dict, "RSSI", DBUS_TYPE_INT16,
  1061. &args->rssi);
  1062. if (args->transport != NULL)
  1063. g_dbus_dict_append_entry(&dict, "Transport", DBUS_TYPE_STRING,
  1064. &args->transport);
  1065. if (args->duplicate)
  1066. g_dbus_dict_append_entry(&dict, "DuplicateData",
  1067. DBUS_TYPE_BOOLEAN,
  1068. &args->duplicate);
  1069. if (args->discoverable)
  1070. g_dbus_dict_append_entry(&dict, "Discoverable",
  1071. DBUS_TYPE_BOOLEAN,
  1072. &args->discoverable);
  1073. if (args->pattern != NULL)
  1074. g_dbus_dict_append_entry(&dict, "Pattern", DBUS_TYPE_STRING,
  1075. &args->pattern);
  1076. dbus_message_iter_close_container(iter, &dict);
  1077. }
  1078. static void set_discovery_filter_reply(DBusMessage *message, void *user_data)
  1079. {
  1080. DBusError error;
  1081. dbus_error_init(&error);
  1082. if (dbus_set_error_from_message(&error, message) == TRUE) {
  1083. bt_shell_printf("SetDiscoveryFilter failed: %s\n", error.name);
  1084. dbus_error_free(&error);
  1085. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1086. }
  1087. filter.set = true;
  1088. bt_shell_printf("SetDiscoveryFilter success\n");
  1089. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  1090. }
  1091. static void set_discovery_filter(bool cleared)
  1092. {
  1093. GDBusSetupFunction func;
  1094. if (check_default_ctrl() == FALSE || filter.set)
  1095. return;
  1096. func = cleared ? clear_discovery_filter : set_discovery_filter_setup;
  1097. if (g_dbus_proxy_method_call(default_ctrl->proxy, "SetDiscoveryFilter",
  1098. func, set_discovery_filter_reply,
  1099. &filter, NULL) == FALSE) {
  1100. bt_shell_printf("Failed to set discovery filter\n");
  1101. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1102. }
  1103. filter.set = true;
  1104. }
  1105. static void cmd_scan(int argc, char *argv[])
  1106. {
  1107. dbus_bool_t enable;
  1108. const char *method;
  1109. if (!parse_argument(argc, argv, NULL, NULL, &enable, NULL))
  1110. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1111. if (check_default_ctrl() == FALSE)
  1112. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1113. if (enable == TRUE) {
  1114. set_discovery_filter(false);
  1115. method = "StartDiscovery";
  1116. } else
  1117. method = "StopDiscovery";
  1118. if (g_dbus_proxy_method_call(default_ctrl->proxy, method,
  1119. NULL, start_discovery_reply,
  1120. GUINT_TO_POINTER(enable), NULL) == FALSE) {
  1121. bt_shell_printf("Failed to %s discovery\n",
  1122. enable == TRUE ? "start" : "stop");
  1123. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1124. }
  1125. }
  1126. static void cmd_scan_filter_uuids(int argc, char *argv[])
  1127. {
  1128. if (argc < 2 || !strlen(argv[1])) {
  1129. char **uuid;
  1130. for (uuid = filter.uuids; uuid && *uuid; uuid++)
  1131. print_uuid("UUID", *uuid);
  1132. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  1133. }
  1134. g_strfreev(filter.uuids);
  1135. filter.uuids = NULL;
  1136. filter.uuids_len = 0;
  1137. if (!strcmp(argv[1], "all"))
  1138. goto commit;
  1139. filter.uuids = g_strdupv(&argv[1]);
  1140. if (!filter.uuids) {
  1141. bt_shell_printf("Failed to parse input\n");
  1142. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1143. }
  1144. filter.uuids_len = g_strv_length(filter.uuids);
  1145. commit:
  1146. filter.set = false;
  1147. if (filter.active)
  1148. set_discovery_filter(false);
  1149. }
  1150. static void cmd_scan_filter_rssi(int argc, char *argv[])
  1151. {
  1152. if (argc < 2 || !strlen(argv[1])) {
  1153. if (filter.rssi != DISTANCE_VAL_INVALID)
  1154. bt_shell_printf("RSSI: %d\n", filter.rssi);
  1155. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  1156. }
  1157. filter.pathloss = DISTANCE_VAL_INVALID;
  1158. filter.rssi = atoi(argv[1]);
  1159. filter.set = false;
  1160. if (filter.active)
  1161. set_discovery_filter(false);
  1162. }
  1163. static void cmd_scan_filter_pathloss(int argc, char *argv[])
  1164. {
  1165. if (argc < 2 || !strlen(argv[1])) {
  1166. if (filter.pathloss != DISTANCE_VAL_INVALID)
  1167. bt_shell_printf("Pathloss: %d\n",
  1168. filter.pathloss);
  1169. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  1170. }
  1171. filter.rssi = DISTANCE_VAL_INVALID;
  1172. filter.pathloss = atoi(argv[1]);
  1173. filter.set = false;
  1174. if (filter.active)
  1175. set_discovery_filter(false);
  1176. }
  1177. static void cmd_scan_filter_transport(int argc, char *argv[])
  1178. {
  1179. if (argc < 2 || !strlen(argv[1])) {
  1180. if (filter.transport)
  1181. bt_shell_printf("Transport: %s\n",
  1182. filter.transport);
  1183. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  1184. }
  1185. g_free(filter.transport);
  1186. filter.transport = g_strdup(argv[1]);
  1187. filter.set = false;
  1188. if (filter.active)
  1189. set_discovery_filter(false);
  1190. }
  1191. static void cmd_scan_filter_duplicate_data(int argc, char *argv[])
  1192. {
  1193. if (argc < 2 || !strlen(argv[1])) {
  1194. bt_shell_printf("DuplicateData: %s\n",
  1195. filter.duplicate ? "on" : "off");
  1196. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  1197. }
  1198. if (!strcmp(argv[1], "on"))
  1199. filter.duplicate = true;
  1200. else if (!strcmp(argv[1], "off"))
  1201. filter.duplicate = false;
  1202. else {
  1203. bt_shell_printf("Invalid option: %s\n", argv[1]);
  1204. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1205. }
  1206. filter.set = false;
  1207. if (filter.active)
  1208. set_discovery_filter(false);
  1209. }
  1210. static void cmd_scan_filter_discoverable(int argc, char *argv[])
  1211. {
  1212. if (argc < 2 || !strlen(argv[1])) {
  1213. bt_shell_printf("Discoverable: %s\n",
  1214. filter.discoverable ? "on" : "off");
  1215. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  1216. }
  1217. if (!strcmp(argv[1], "on"))
  1218. filter.discoverable = true;
  1219. else if (!strcmp(argv[1], "off"))
  1220. filter.discoverable = false;
  1221. else {
  1222. bt_shell_printf("Invalid option: %s\n", argv[1]);
  1223. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1224. }
  1225. filter.set = false;
  1226. if (filter.active)
  1227. set_discovery_filter(false);
  1228. }
  1229. static void cmd_scan_filter_pattern(int argc, char *argv[])
  1230. {
  1231. if (argc < 2 || !strlen(argv[1])) {
  1232. bt_shell_printf("Pattern: %s\n", filter.pattern);
  1233. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  1234. }
  1235. free(filter.pattern);
  1236. filter.pattern = strdup(argv[1]);
  1237. filter.set = false;
  1238. if (filter.active)
  1239. set_discovery_filter(false);
  1240. }
  1241. static void filter_clear_uuids(void)
  1242. {
  1243. g_strfreev(filter.uuids);
  1244. filter.uuids = NULL;
  1245. filter.uuids_len = 0;
  1246. }
  1247. static void filter_clear_rssi(void)
  1248. {
  1249. filter.rssi = DISTANCE_VAL_INVALID;
  1250. }
  1251. static void filter_clear_pathloss(void)
  1252. {
  1253. filter.pathloss = DISTANCE_VAL_INVALID;
  1254. }
  1255. static void filter_clear_transport(void)
  1256. {
  1257. g_free(filter.transport);
  1258. filter.transport = NULL;
  1259. }
  1260. static void filter_clear_duplicate(void)
  1261. {
  1262. filter.duplicate = false;
  1263. }
  1264. static void filter_clear_discoverable(void)
  1265. {
  1266. filter.discoverable = false;
  1267. }
  1268. static void filter_clear_pattern(void)
  1269. {
  1270. free(filter.pattern);
  1271. filter.pattern = NULL;
  1272. }
  1273. struct clear_entry {
  1274. const char *name;
  1275. void (*clear) (void);
  1276. };
  1277. static const struct clear_entry filter_clear[] = {
  1278. { "uuids", filter_clear_uuids },
  1279. { "rssi", filter_clear_rssi },
  1280. { "pathloss", filter_clear_pathloss },
  1281. { "transport", filter_clear_transport },
  1282. { "duplicate-data", filter_clear_duplicate },
  1283. { "discoverable", filter_clear_discoverable },
  1284. { "pattern", filter_clear_pattern },
  1285. {}
  1286. };
  1287. static char *filter_clear_generator(const char *text, int state)
  1288. {
  1289. static int index, len;
  1290. const char *arg;
  1291. if (!state) {
  1292. index = 0;
  1293. len = strlen(text);
  1294. }
  1295. while ((arg = filter_clear[index].name)) {
  1296. index++;
  1297. if (!strncmp(arg, text, len))
  1298. return strdup(arg);
  1299. }
  1300. return NULL;
  1301. }
  1302. static gboolean data_clear(const struct clear_entry *entry_table,
  1303. const char *name)
  1304. {
  1305. const struct clear_entry *entry;
  1306. bool all = false;
  1307. if (!name || !strlen(name) || !strcmp("all", name))
  1308. all = true;
  1309. for (entry = entry_table; entry && entry->name; entry++) {
  1310. if (all || !strcmp(entry->name, name)) {
  1311. entry->clear();
  1312. if (!all)
  1313. goto done;
  1314. }
  1315. }
  1316. if (!all) {
  1317. bt_shell_printf("Invalid argument %s\n", name);
  1318. return FALSE;
  1319. }
  1320. done:
  1321. return TRUE;
  1322. }
  1323. static void cmd_scan_filter_clear(int argc, char *argv[])
  1324. {
  1325. bool all = false;
  1326. if (argc < 2 || !strlen(argv[1]))
  1327. all = true;
  1328. if (!data_clear(filter_clear, all ? "all" : argv[1]))
  1329. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1330. filter.set = false;
  1331. if (check_default_ctrl() == FALSE)
  1332. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1333. set_discovery_filter(all);
  1334. }
  1335. static struct GDBusProxy *find_device(int argc, char *argv[])
  1336. {
  1337. GDBusProxy *proxy;
  1338. if (argc < 2 || !strlen(argv[1])) {
  1339. if (default_dev)
  1340. return default_dev;
  1341. bt_shell_printf("Missing device address argument\n");
  1342. return NULL;
  1343. }
  1344. if (check_default_ctrl() == FALSE)
  1345. return NULL;
  1346. proxy = find_proxy_by_address(default_ctrl->devices, argv[1]);
  1347. if (!proxy) {
  1348. bt_shell_printf("Device %s not available\n", argv[1]);
  1349. return NULL;
  1350. }
  1351. return proxy;
  1352. }
  1353. static void cmd_info(int argc, char *argv[])
  1354. {
  1355. GDBusProxy *proxy;
  1356. GDBusProxy *admin_proxy;
  1357. GDBusProxy *battery_proxy;
  1358. DBusMessageIter iter;
  1359. const char *address;
  1360. proxy = find_device(argc, argv);
  1361. if (!proxy)
  1362. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1363. if (g_dbus_proxy_get_property(proxy, "Address", &iter) == FALSE)
  1364. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1365. dbus_message_iter_get_basic(&iter, &address);
  1366. if (g_dbus_proxy_get_property(proxy, "AddressType", &iter) == TRUE) {
  1367. const char *type;
  1368. dbus_message_iter_get_basic(&iter, &type);
  1369. bt_shell_printf("Device %s (%s)\n", address, type);
  1370. } else {
  1371. bt_shell_printf("Device %s\n", address);
  1372. }
  1373. print_property(proxy, "Name");
  1374. print_property(proxy, "Alias");
  1375. print_property(proxy, "Class");
  1376. print_property(proxy, "Appearance");
  1377. print_property(proxy, "Icon");
  1378. print_property(proxy, "Paired");
  1379. print_property(proxy, "Trusted");
  1380. print_property(proxy, "Blocked");
  1381. print_property(proxy, "Connected");
  1382. print_property(proxy, "WakeAllowed");
  1383. print_property(proxy, "LegacyPairing");
  1384. print_uuids(proxy);
  1385. print_property(proxy, "Modalias");
  1386. print_property(proxy, "ManufacturerData");
  1387. print_property(proxy, "ServiceData");
  1388. print_property(proxy, "RSSI");
  1389. print_property(proxy, "TxPower");
  1390. print_property(proxy, "AdvertisingFlags");
  1391. print_property(proxy, "AdvertisingData");
  1392. battery_proxy = find_proxies_by_path(battery_proxies,
  1393. g_dbus_proxy_get_path(proxy));
  1394. admin_proxy = find_proxies_by_path(admin_devices_proxies,
  1395. g_dbus_proxy_get_path(proxy));
  1396. print_property_with_label(battery_proxy, "Percentage",
  1397. "Battery Percentage");
  1398. print_property_with_label(admin_proxy, "AffectedByPolicy",
  1399. "Affected by Policy");
  1400. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  1401. }
  1402. static void pair_reply(DBusMessage *message, void *user_data)
  1403. {
  1404. DBusError error;
  1405. dbus_error_init(&error);
  1406. if (dbus_set_error_from_message(&error, message) == TRUE) {
  1407. bt_shell_printf("Failed to pair: %s\n", error.name);
  1408. dbus_error_free(&error);
  1409. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1410. }
  1411. bt_shell_printf("Pairing successful\n");
  1412. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  1413. }
  1414. static const char *proxy_address(GDBusProxy *proxy)
  1415. {
  1416. DBusMessageIter iter;
  1417. const char *addr;
  1418. if (!g_dbus_proxy_get_property(proxy, "Address", &iter))
  1419. return NULL;
  1420. dbus_message_iter_get_basic(&iter, &addr);
  1421. return addr;
  1422. }
  1423. static void cmd_pair(int argc, char *argv[])
  1424. {
  1425. GDBusProxy *proxy;
  1426. proxy = find_device(argc, argv);
  1427. if (!proxy)
  1428. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1429. if (g_dbus_proxy_method_call(proxy, "Pair", NULL, pair_reply,
  1430. NULL, NULL) == FALSE) {
  1431. bt_shell_printf("Failed to pair\n");
  1432. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1433. }
  1434. bt_shell_printf("Attempting to pair with %s\n", proxy_address(proxy));
  1435. }
  1436. static void cancel_pairing_reply(DBusMessage *message, void *user_data)
  1437. {
  1438. DBusError error;
  1439. dbus_error_init(&error);
  1440. if (dbus_set_error_from_message(&error, message) == TRUE) {
  1441. bt_shell_printf("Failed to cancel pairing: %s\n", error.name);
  1442. dbus_error_free(&error);
  1443. return;
  1444. }
  1445. bt_shell_printf("Cancel pairing successful\n");
  1446. }
  1447. static void cmd_cancel_pairing(int argc, char *argv[])
  1448. {
  1449. GDBusProxy *proxy;
  1450. proxy = find_device(argc, argv);
  1451. if (!proxy)
  1452. return;
  1453. if (g_dbus_proxy_method_call(proxy, "CancelPairing", NULL,
  1454. cancel_pairing_reply, NULL, NULL) == FALSE) {
  1455. bt_shell_printf("Failed to cancel pairing\n");
  1456. return;
  1457. }
  1458. bt_shell_printf("Attempting to cancel pairing with %s\n",
  1459. proxy_address(proxy));
  1460. }
  1461. static void cmd_trust(int argc, char *argv[])
  1462. {
  1463. GDBusProxy *proxy;
  1464. dbus_bool_t trusted;
  1465. char *str;
  1466. proxy = find_device(argc, argv);
  1467. if (!proxy)
  1468. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1469. trusted = TRUE;
  1470. str = g_strdup_printf("%s trust", proxy_address(proxy));
  1471. if (g_dbus_proxy_set_property_basic(proxy, "Trusted",
  1472. DBUS_TYPE_BOOLEAN, &trusted,
  1473. generic_callback, str, g_free) == TRUE)
  1474. return;
  1475. g_free(str);
  1476. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1477. }
  1478. static void cmd_untrust(int argc, char *argv[])
  1479. {
  1480. GDBusProxy *proxy;
  1481. dbus_bool_t trusted;
  1482. char *str;
  1483. proxy = find_device(argc, argv);
  1484. if (!proxy)
  1485. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1486. trusted = FALSE;
  1487. str = g_strdup_printf("%s untrust", proxy_address(proxy));
  1488. if (g_dbus_proxy_set_property_basic(proxy, "Trusted",
  1489. DBUS_TYPE_BOOLEAN, &trusted,
  1490. generic_callback, str, g_free) == TRUE)
  1491. return;
  1492. g_free(str);
  1493. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1494. }
  1495. static void cmd_block(int argc, char *argv[])
  1496. {
  1497. GDBusProxy *proxy;
  1498. dbus_bool_t blocked;
  1499. char *str;
  1500. proxy = find_device(argc, argv);
  1501. if (!proxy)
  1502. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1503. blocked = TRUE;
  1504. str = g_strdup_printf("%s block", proxy_address(proxy));
  1505. if (g_dbus_proxy_set_property_basic(proxy, "Blocked",
  1506. DBUS_TYPE_BOOLEAN, &blocked,
  1507. generic_callback, str, g_free) == TRUE)
  1508. return;
  1509. g_free(str);
  1510. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1511. }
  1512. static void cmd_unblock(int argc, char *argv[])
  1513. {
  1514. GDBusProxy *proxy;
  1515. dbus_bool_t blocked;
  1516. char *str;
  1517. proxy = find_device(argc, argv);
  1518. if (!proxy)
  1519. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1520. blocked = FALSE;
  1521. str = g_strdup_printf("%s unblock", proxy_address(proxy));
  1522. if (g_dbus_proxy_set_property_basic(proxy, "Blocked",
  1523. DBUS_TYPE_BOOLEAN, &blocked,
  1524. generic_callback, str, g_free) == TRUE)
  1525. return;
  1526. g_free(str);
  1527. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1528. }
  1529. static void remove_device_reply(DBusMessage *message, void *user_data)
  1530. {
  1531. DBusError error;
  1532. dbus_error_init(&error);
  1533. if (dbus_set_error_from_message(&error, message) == TRUE) {
  1534. bt_shell_printf("Failed to remove device: %s\n", error.name);
  1535. dbus_error_free(&error);
  1536. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1537. }
  1538. bt_shell_printf("Device has been removed\n");
  1539. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  1540. }
  1541. static void remove_device_setup(DBusMessageIter *iter, void *user_data)
  1542. {
  1543. const char *path = user_data;
  1544. dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
  1545. }
  1546. static void remove_device(GDBusProxy *proxy)
  1547. {
  1548. char *path;
  1549. if (!default_ctrl)
  1550. return;
  1551. path = g_strdup(g_dbus_proxy_get_path(proxy));
  1552. if (g_dbus_proxy_method_call(default_ctrl->proxy, "RemoveDevice",
  1553. remove_device_setup,
  1554. remove_device_reply,
  1555. path, g_free) == FALSE) {
  1556. bt_shell_printf("Failed to remove device\n");
  1557. g_free(path);
  1558. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1559. }
  1560. }
  1561. static void cmd_remove(int argc, char *argv[])
  1562. {
  1563. GDBusProxy *proxy;
  1564. if (check_default_ctrl() == FALSE)
  1565. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1566. if (strcmp(argv[1], "*") == 0) {
  1567. GList *list;
  1568. for (list = default_ctrl->devices; list;
  1569. list = g_list_next(list)) {
  1570. GDBusProxy *proxy = list->data;
  1571. remove_device(proxy);
  1572. }
  1573. return;
  1574. }
  1575. proxy = find_proxy_by_address(default_ctrl->devices, argv[1]);
  1576. if (!proxy) {
  1577. bt_shell_printf("Device %s not available\n", argv[1]);
  1578. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1579. }
  1580. remove_device(proxy);
  1581. }
  1582. static void connect_reply(DBusMessage *message, void *user_data)
  1583. {
  1584. GDBusProxy *proxy = user_data;
  1585. DBusError error;
  1586. dbus_error_init(&error);
  1587. if (dbus_set_error_from_message(&error, message) == TRUE) {
  1588. bt_shell_printf("Failed to connect: %s %s\n", error.name,
  1589. error.message);
  1590. dbus_error_free(&error);
  1591. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1592. }
  1593. bt_shell_printf("Connection successful\n");
  1594. set_default_device(proxy, NULL);
  1595. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  1596. }
  1597. static void cmd_connect(int argc, char *argv[])
  1598. {
  1599. GDBusProxy *proxy;
  1600. if (check_default_ctrl() == FALSE)
  1601. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1602. proxy = find_proxy_by_address(default_ctrl->devices, argv[1]);
  1603. if (!proxy) {
  1604. bt_shell_printf("Device %s not available\n", argv[1]);
  1605. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1606. }
  1607. if (g_dbus_proxy_method_call(proxy, "Connect", NULL, connect_reply,
  1608. proxy, NULL) == FALSE) {
  1609. bt_shell_printf("Failed to connect\n");
  1610. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1611. }
  1612. bt_shell_printf("Attempting to connect to %s\n", argv[1]);
  1613. }
  1614. static void disconn_reply(DBusMessage *message, void *user_data)
  1615. {
  1616. GDBusProxy *proxy = user_data;
  1617. DBusError error;
  1618. dbus_error_init(&error);
  1619. if (dbus_set_error_from_message(&error, message) == TRUE) {
  1620. bt_shell_printf("Failed to disconnect: %s\n", error.name);
  1621. dbus_error_free(&error);
  1622. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1623. }
  1624. bt_shell_printf("Successful disconnected\n");
  1625. if (proxy == default_dev)
  1626. set_default_device(NULL, NULL);
  1627. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  1628. }
  1629. static void cmd_disconn(int argc, char *argv[])
  1630. {
  1631. GDBusProxy *proxy;
  1632. proxy = find_device(argc, argv);
  1633. if (!proxy)
  1634. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1635. if (g_dbus_proxy_method_call(proxy, "Disconnect", NULL, disconn_reply,
  1636. proxy, NULL) == FALSE) {
  1637. bt_shell_printf("Failed to disconnect\n");
  1638. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1639. }
  1640. bt_shell_printf("Attempting to disconnect from %s\n",
  1641. proxy_address(proxy));
  1642. }
  1643. static void cmd_list_attributes(int argc, char *argv[])
  1644. {
  1645. GDBusProxy *proxy;
  1646. const char *path;
  1647. if (argc > 1 && !strcmp(argv[1], "local")) {
  1648. path = argv[1];
  1649. goto done;
  1650. }
  1651. proxy = find_device(argc, argv);
  1652. if (!proxy)
  1653. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1654. path = g_dbus_proxy_get_path(proxy);
  1655. done:
  1656. gatt_list_attributes(path);
  1657. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  1658. }
  1659. static void cmd_set_alias(int argc, char *argv[])
  1660. {
  1661. char *name;
  1662. if (!default_dev) {
  1663. bt_shell_printf("No device connected\n");
  1664. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1665. }
  1666. name = g_strdup(argv[1]);
  1667. if (g_dbus_proxy_set_property_basic(default_dev, "Alias",
  1668. DBUS_TYPE_STRING, &name,
  1669. generic_callback, name, g_free) == TRUE)
  1670. return;
  1671. g_free(name);
  1672. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1673. }
  1674. static void cmd_select_attribute(int argc, char *argv[])
  1675. {
  1676. GDBusProxy *proxy;
  1677. if (!default_dev) {
  1678. bt_shell_printf("No device connected\n");
  1679. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1680. }
  1681. proxy = gatt_select_attribute(default_attr, argv[1]);
  1682. if (proxy) {
  1683. set_default_attribute(proxy);
  1684. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  1685. }
  1686. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1687. }
  1688. static struct GDBusProxy *find_attribute(int argc, char *argv[])
  1689. {
  1690. GDBusProxy *proxy;
  1691. if (argc < 2 || !strlen(argv[1])) {
  1692. if (default_attr)
  1693. return default_attr;
  1694. bt_shell_printf("Missing attribute argument\n");
  1695. return NULL;
  1696. }
  1697. proxy = gatt_select_attribute(default_attr, argv[1]);
  1698. if (!proxy) {
  1699. bt_shell_printf("Attribute %s not available\n", argv[1]);
  1700. return NULL;
  1701. }
  1702. return proxy;
  1703. }
  1704. static void cmd_attribute_info(int argc, char *argv[])
  1705. {
  1706. GDBusProxy *proxy;
  1707. DBusMessageIter iter;
  1708. const char *iface, *uuid, *text;
  1709. proxy = find_attribute(argc, argv);
  1710. if (!proxy)
  1711. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1712. if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE)
  1713. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1714. dbus_message_iter_get_basic(&iter, &uuid);
  1715. text = bt_uuidstr_to_str(uuid);
  1716. if (!text)
  1717. text = g_dbus_proxy_get_path(proxy);
  1718. iface = g_dbus_proxy_get_interface(proxy);
  1719. if (!strcmp(iface, "org.bluez.GattService1")) {
  1720. bt_shell_printf("Service - %s\n", text);
  1721. print_property(proxy, "UUID");
  1722. print_property(proxy, "Primary");
  1723. print_property(proxy, "Characteristics");
  1724. print_property(proxy, "Includes");
  1725. } else if (!strcmp(iface, "org.bluez.GattCharacteristic1")) {
  1726. bt_shell_printf("Characteristic - %s\n", text);
  1727. print_property(proxy, "UUID");
  1728. print_property(proxy, "Service");
  1729. print_property(proxy, "Value");
  1730. print_property(proxy, "Notifying");
  1731. print_property(proxy, "Flags");
  1732. print_property(proxy, "MTU");
  1733. print_property(proxy, "Descriptors");
  1734. } else if (!strcmp(iface, "org.bluez.GattDescriptor1")) {
  1735. bt_shell_printf("Descriptor - %s\n", text);
  1736. print_property(proxy, "UUID");
  1737. print_property(proxy, "Characteristic");
  1738. print_property(proxy, "Value");
  1739. }
  1740. return bt_shell_noninteractive_quit(EXIT_SUCCESS);
  1741. }
  1742. static void cmd_read(int argc, char *argv[])
  1743. {
  1744. if (!default_attr) {
  1745. bt_shell_printf("No attribute selected\n");
  1746. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1747. }
  1748. gatt_read_attribute(default_attr, argc, argv);
  1749. }
  1750. static void cmd_write(int argc, char *argv[])
  1751. {
  1752. if (!default_attr) {
  1753. bt_shell_printf("No attribute selected\n");
  1754. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1755. }
  1756. gatt_write_attribute(default_attr, argc, argv);
  1757. }
  1758. static void cmd_acquire_write(int argc, char *argv[])
  1759. {
  1760. if (!default_attr) {
  1761. bt_shell_printf("No attribute selected\n");
  1762. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1763. }
  1764. gatt_acquire_write(default_attr, argv[1]);
  1765. }
  1766. static void cmd_release_write(int argc, char *argv[])
  1767. {
  1768. if (!default_attr) {
  1769. bt_shell_printf("No attribute selected\n");
  1770. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1771. }
  1772. gatt_release_write(default_attr, argv[1]);
  1773. }
  1774. static void cmd_acquire_notify(int argc, char *argv[])
  1775. {
  1776. if (!default_attr) {
  1777. bt_shell_printf("No attribute selected\n");
  1778. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1779. }
  1780. gatt_acquire_notify(default_attr, argv[1]);
  1781. }
  1782. static void cmd_release_notify(int argc, char *argv[])
  1783. {
  1784. if (!default_attr) {
  1785. bt_shell_printf("No attribute selected\n");
  1786. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1787. }
  1788. gatt_release_notify(default_attr, argv[1]);
  1789. }
  1790. static void cmd_notify(int argc, char *argv[])
  1791. {
  1792. dbus_bool_t enable;
  1793. if (!parse_argument(argc, argv, NULL, NULL, &enable, NULL))
  1794. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1795. if (!default_attr) {
  1796. bt_shell_printf("No attribute selected\n");
  1797. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1798. }
  1799. gatt_notify_attribute(default_attr, enable ? true : false);
  1800. }
  1801. static void cmd_clone(int argc, char *argv[])
  1802. {
  1803. GDBusProxy *proxy;
  1804. proxy = default_attr ? default_attr : default_dev;
  1805. if (!proxy) {
  1806. bt_shell_printf("Not connected\n");
  1807. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1808. }
  1809. gatt_clone_attribute(proxy, argc, argv);
  1810. }
  1811. static void cmd_register_app(int argc, char *argv[])
  1812. {
  1813. if (check_default_ctrl() == FALSE)
  1814. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1815. gatt_register_app(dbus_conn, default_ctrl->proxy, argc, argv);
  1816. }
  1817. static void cmd_unregister_app(int argc, char *argv[])
  1818. {
  1819. if (check_default_ctrl() == FALSE)
  1820. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1821. gatt_unregister_app(dbus_conn, default_ctrl->proxy);
  1822. }
  1823. static void cmd_register_service(int argc, char *argv[])
  1824. {
  1825. if (check_default_ctrl() == FALSE)
  1826. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1827. gatt_register_service(dbus_conn, default_ctrl->proxy, argc, argv);
  1828. }
  1829. static void cmd_register_includes(int argc, char *argv[])
  1830. {
  1831. if (check_default_ctrl() == FALSE)
  1832. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1833. gatt_register_include(dbus_conn, default_ctrl->proxy, argc, argv);
  1834. }
  1835. static void cmd_unregister_includes(int argc, char *argv[])
  1836. {
  1837. if (check_default_ctrl() == FALSE)
  1838. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1839. gatt_unregister_include(dbus_conn, default_ctrl->proxy, argc, argv);
  1840. }
  1841. static void cmd_unregister_service(int argc, char *argv[])
  1842. {
  1843. if (check_default_ctrl() == FALSE)
  1844. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1845. gatt_unregister_service(dbus_conn, default_ctrl->proxy, argc, argv);
  1846. }
  1847. static void cmd_register_characteristic(int argc, char *argv[])
  1848. {
  1849. if (check_default_ctrl() == FALSE)
  1850. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1851. gatt_register_chrc(dbus_conn, default_ctrl->proxy, argc, argv);
  1852. }
  1853. static void cmd_unregister_characteristic(int argc, char *argv[])
  1854. {
  1855. if (check_default_ctrl() == FALSE)
  1856. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1857. gatt_unregister_chrc(dbus_conn, default_ctrl->proxy, argc, argv);
  1858. }
  1859. static void cmd_register_descriptor(int argc, char *argv[])
  1860. {
  1861. if (check_default_ctrl() == FALSE)
  1862. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1863. gatt_register_desc(dbus_conn, default_ctrl->proxy, argc, argv);
  1864. }
  1865. static void cmd_unregister_descriptor(int argc, char *argv[])
  1866. {
  1867. if (check_default_ctrl() == FALSE)
  1868. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1869. gatt_unregister_desc(dbus_conn, default_ctrl->proxy, argc, argv);
  1870. }
  1871. static char *generic_generator(const char *text, int state,
  1872. GList *source, const char *property)
  1873. {
  1874. static int index, len;
  1875. GList *list;
  1876. if (!state) {
  1877. index = 0;
  1878. len = strlen(text);
  1879. }
  1880. for (list = g_list_nth(source, index); list;
  1881. list = g_list_next(list)) {
  1882. GDBusProxy *proxy = list->data;
  1883. DBusMessageIter iter;
  1884. const char *str;
  1885. index++;
  1886. if (g_dbus_proxy_get_property(proxy, property, &iter) == FALSE)
  1887. continue;
  1888. dbus_message_iter_get_basic(&iter, &str);
  1889. if (!strncasecmp(str, text, len))
  1890. return strdup(str);
  1891. }
  1892. return NULL;
  1893. }
  1894. static char *ctrl_generator(const char *text, int state)
  1895. {
  1896. static int index = 0;
  1897. static int len = 0;
  1898. GList *list;
  1899. if (!state) {
  1900. index = 0;
  1901. len = strlen(text);
  1902. }
  1903. for (list = g_list_nth(ctrl_list, index); list;
  1904. list = g_list_next(list)) {
  1905. struct adapter *adapter = list->data;
  1906. DBusMessageIter iter;
  1907. const char *str;
  1908. index++;
  1909. if (g_dbus_proxy_get_property(adapter->proxy,
  1910. "Address", &iter) == FALSE)
  1911. continue;
  1912. dbus_message_iter_get_basic(&iter, &str);
  1913. if (!strncasecmp(str, text, len))
  1914. return strdup(str);
  1915. }
  1916. return NULL;
  1917. }
  1918. static char *dev_generator(const char *text, int state)
  1919. {
  1920. return generic_generator(text, state,
  1921. default_ctrl ? default_ctrl->devices : NULL, "Address");
  1922. }
  1923. static char *attribute_generator(const char *text, int state)
  1924. {
  1925. return gatt_attribute_generator(text, state);
  1926. }
  1927. static char *argument_generator(const char *text, int state,
  1928. const char *args_list[])
  1929. {
  1930. static int index, len;
  1931. const char *arg;
  1932. if (!state) {
  1933. index = 0;
  1934. len = strlen(text);
  1935. }
  1936. while ((arg = args_list[index])) {
  1937. index++;
  1938. if (!strncmp(arg, text, len))
  1939. return strdup(arg);
  1940. }
  1941. return NULL;
  1942. }
  1943. static char *capability_generator(const char *text, int state)
  1944. {
  1945. return argument_generator(text, state, agent_arguments);
  1946. }
  1947. static void cmd_advertise(int argc, char *argv[])
  1948. {
  1949. dbus_bool_t enable;
  1950. const char *type;
  1951. if (!parse_argument(argc, argv, ad_arguments, "type",
  1952. &enable, &type))
  1953. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1954. if (!default_ctrl || !default_ctrl->ad_proxy) {
  1955. bt_shell_printf("LEAdvertisingManager not found\n");
  1956. bt_shell_noninteractive_quit(EXIT_FAILURE);
  1957. }
  1958. if (enable == TRUE)
  1959. ad_register(dbus_conn, default_ctrl->ad_proxy, type);
  1960. else
  1961. ad_unregister(dbus_conn, default_ctrl->ad_proxy);
  1962. }
  1963. static char *ad_generator(const char *text, int state)
  1964. {
  1965. return argument_generator(text, state, ad_arguments);
  1966. }
  1967. static void cmd_advertise_uuids(int argc, char *argv[])
  1968. {
  1969. ad_advertise_uuids(dbus_conn, argc, argv);
  1970. }
  1971. static void cmd_advertise_service(int argc, char *argv[])
  1972. {
  1973. ad_advertise_service(dbus_conn, argc, argv);
  1974. }
  1975. static void cmd_advertise_manufacturer(int argc, char *argv[])
  1976. {
  1977. ad_advertise_manufacturer(dbus_conn, argc, argv);
  1978. }
  1979. static void cmd_advertise_data(int argc, char *argv[])
  1980. {
  1981. ad_advertise_data(dbus_conn, argc, argv);
  1982. }
  1983. static void cmd_advertise_discoverable(int argc, char *argv[])
  1984. {
  1985. dbus_bool_t discoverable;
  1986. if (argc < 2) {
  1987. ad_advertise_discoverable(dbus_conn, NULL);
  1988. return;
  1989. }
  1990. if (!parse_argument(argc, argv, NULL, NULL, &discoverable, NULL))
  1991. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  1992. ad_advertise_discoverable(dbus_conn, &discoverable);
  1993. }
  1994. static void cmd_advertise_discoverable_timeout(int argc, char *argv[])
  1995. {
  1996. long int value;
  1997. char *endptr = NULL;
  1998. if (argc < 2) {
  1999. ad_advertise_discoverable_timeout(dbus_conn, NULL);
  2000. return;
  2001. }
  2002. value = strtol(argv[1], &endptr, 0);
  2003. if (!endptr || *endptr != '\0' || value > UINT16_MAX) {
  2004. bt_shell_printf("Invalid argument\n");
  2005. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  2006. }
  2007. ad_advertise_discoverable_timeout(dbus_conn, &value);
  2008. }
  2009. static void cmd_advertise_tx_power(int argc, char *argv[])
  2010. {
  2011. dbus_bool_t powered;
  2012. if (argc < 2) {
  2013. ad_advertise_tx_power(dbus_conn, NULL);
  2014. return;
  2015. }
  2016. if (!parse_argument(argc, argv, NULL, NULL, &powered, NULL))
  2017. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  2018. ad_advertise_tx_power(dbus_conn, &powered);
  2019. }
  2020. static void cmd_advertise_name(int argc, char *argv[])
  2021. {
  2022. if (argc < 2) {
  2023. ad_advertise_local_name(dbus_conn, NULL);
  2024. return;
  2025. }
  2026. if (strcmp(argv[1], "on") == 0 || strcmp(argv[1], "yes") == 0) {
  2027. ad_advertise_name(dbus_conn, true);
  2028. return;
  2029. }
  2030. if (strcmp(argv[1], "off") == 0 || strcmp(argv[1], "no") == 0) {
  2031. ad_advertise_name(dbus_conn, false);
  2032. return;
  2033. }
  2034. ad_advertise_local_name(dbus_conn, argv[1]);
  2035. }
  2036. static void cmd_advertise_appearance(int argc, char *argv[])
  2037. {
  2038. long int value;
  2039. char *endptr = NULL;
  2040. if (argc < 2) {
  2041. ad_advertise_local_appearance(dbus_conn, NULL);
  2042. return;
  2043. }
  2044. if (strcmp(argv[1], "on") == 0 || strcmp(argv[1], "yes") == 0) {
  2045. ad_advertise_appearance(dbus_conn, true);
  2046. return;
  2047. }
  2048. if (strcmp(argv[1], "off") == 0 || strcmp(argv[1], "no") == 0) {
  2049. ad_advertise_appearance(dbus_conn, false);
  2050. return;
  2051. }
  2052. value = strtol(argv[1], &endptr, 0);
  2053. if (!endptr || *endptr != '\0' || value > UINT16_MAX) {
  2054. bt_shell_printf("Invalid argument\n");
  2055. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  2056. }
  2057. ad_advertise_local_appearance(dbus_conn, &value);
  2058. }
  2059. static void cmd_advertise_duration(int argc, char *argv[])
  2060. {
  2061. long int value;
  2062. char *endptr = NULL;
  2063. if (argc < 2) {
  2064. ad_advertise_duration(dbus_conn, NULL);
  2065. return;
  2066. }
  2067. value = strtol(argv[1], &endptr, 0);
  2068. if (!endptr || *endptr != '\0' || value > UINT16_MAX) {
  2069. bt_shell_printf("Invalid argument\n");
  2070. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  2071. }
  2072. ad_advertise_duration(dbus_conn, &value);
  2073. }
  2074. static void cmd_advertise_timeout(int argc, char *argv[])
  2075. {
  2076. long int value;
  2077. char *endptr = NULL;
  2078. if (argc < 2) {
  2079. ad_advertise_timeout(dbus_conn, NULL);
  2080. return;
  2081. }
  2082. value = strtol(argv[1], &endptr, 0);
  2083. if (!endptr || *endptr != '\0' || value > UINT16_MAX) {
  2084. bt_shell_printf("Invalid argument\n");
  2085. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  2086. }
  2087. ad_advertise_timeout(dbus_conn, &value);
  2088. }
  2089. static void cmd_advertise_secondary(int argc, char *argv[])
  2090. {
  2091. if (argc < 2) {
  2092. ad_advertise_secondary(dbus_conn, NULL);
  2093. return;
  2094. }
  2095. ad_advertise_secondary(dbus_conn, argv[1]);
  2096. }
  2097. static void cmd_advertise_interval(int argc, char *argv[])
  2098. {
  2099. uint32_t min, max;
  2100. char *endptr = NULL;
  2101. if (argc < 2) {
  2102. ad_advertise_interval(dbus_conn, NULL, NULL);
  2103. return;
  2104. }
  2105. min = strtol(argv[1], &endptr, 0);
  2106. if (!endptr || *endptr != '\0' || min < 20 || min > 10485) {
  2107. bt_shell_printf("Invalid argument\n");
  2108. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  2109. }
  2110. max = min;
  2111. if (argc > 2) {
  2112. max = strtol(argv[1], &endptr, 0);
  2113. if (!endptr || *endptr != '\0' || max < 20 || max > 10485) {
  2114. bt_shell_printf("Invalid argument\n");
  2115. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  2116. }
  2117. }
  2118. if (min > max) {
  2119. bt_shell_printf("Invalid argument: %u > %u\n", min, max);
  2120. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  2121. }
  2122. ad_advertise_interval(dbus_conn, &min, &max);
  2123. }
  2124. static void ad_clear_uuids(void)
  2125. {
  2126. ad_disable_uuids(dbus_conn);
  2127. }
  2128. static void ad_clear_service(void)
  2129. {
  2130. ad_disable_service(dbus_conn);
  2131. }
  2132. static void ad_clear_manufacturer(void)
  2133. {
  2134. ad_disable_manufacturer(dbus_conn);
  2135. }
  2136. static void ad_clear_data(void)
  2137. {
  2138. ad_disable_data(dbus_conn);
  2139. }
  2140. static void ad_clear_tx_power(void)
  2141. {
  2142. dbus_bool_t powered = false;
  2143. ad_advertise_tx_power(dbus_conn, &powered);
  2144. }
  2145. static void ad_clear_name(void)
  2146. {
  2147. ad_advertise_name(dbus_conn, false);
  2148. }
  2149. static void ad_clear_appearance(void)
  2150. {
  2151. ad_advertise_appearance(dbus_conn, false);
  2152. }
  2153. static void ad_clear_duration(void)
  2154. {
  2155. long int value = 0;
  2156. ad_advertise_duration(dbus_conn, &value);
  2157. }
  2158. static void ad_clear_timeout(void)
  2159. {
  2160. long int value = 0;
  2161. ad_advertise_timeout(dbus_conn, &value);
  2162. }
  2163. static void ad_clear_secondary(void)
  2164. {
  2165. const char *value = "";
  2166. ad_advertise_secondary(dbus_conn, value);
  2167. }
  2168. static void ad_clear_interval(void)
  2169. {
  2170. uint32_t min = 0;
  2171. uint32_t max = 0;
  2172. ad_advertise_interval(dbus_conn, &min, &max);
  2173. }
  2174. static const struct clear_entry ad_clear[] = {
  2175. { "uuids", ad_clear_uuids },
  2176. { "service", ad_clear_service },
  2177. { "manufacturer", ad_clear_manufacturer },
  2178. { "data", ad_clear_data },
  2179. { "tx-power", ad_clear_tx_power },
  2180. { "name", ad_clear_name },
  2181. { "appearance", ad_clear_appearance },
  2182. { "duration", ad_clear_duration },
  2183. { "timeout", ad_clear_timeout },
  2184. { "secondary", ad_clear_secondary },
  2185. { "interval", ad_clear_interval },
  2186. {}
  2187. };
  2188. static void cmd_ad_clear(int argc, char *argv[])
  2189. {
  2190. bool all = false;
  2191. if (argc < 2 || !strlen(argv[1]))
  2192. all = true;
  2193. if(!data_clear(ad_clear, all ? "all" : argv[1]))
  2194. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  2195. }
  2196. static void print_add_or_pattern_usage(void)
  2197. {
  2198. bt_shell_printf("pattern format:\n"
  2199. "\t<start_position> <ad_data_type> <content_of_pattern>\n");
  2200. bt_shell_printf("e.g.\n"
  2201. "\tadd-or-pattern 1 2 01ab55 3 4 23cd66\n");
  2202. }
  2203. static void cmd_adv_monitor_print_usage(int argc, char *argv[])
  2204. {
  2205. if (strcmp(argv[1], "add-or-pattern") == 0)
  2206. print_add_or_pattern_usage();
  2207. else
  2208. bt_shell_printf("Invalid argument %s", argv[1]);
  2209. }
  2210. static void cmd_adv_monitor_set_rssi_threshold(int argc, char *argv[])
  2211. {
  2212. int low_threshold, high_threshold;
  2213. low_threshold = atoi(argv[1]);
  2214. high_threshold = atoi(argv[2]);
  2215. adv_monitor_set_rssi_threshold(low_threshold, high_threshold);
  2216. }
  2217. static void cmd_adv_monitor_set_rssi_timeout(int argc, char *argv[])
  2218. {
  2219. int low_timeout, high_timeout;
  2220. low_timeout = atoi(argv[1]);
  2221. high_timeout = atoi(argv[2]);
  2222. adv_monitor_set_rssi_timeout(low_timeout, high_timeout);
  2223. }
  2224. static void cmd_adv_monitor_set_rssi_sampling_period(int argc, char *argv[])
  2225. {
  2226. int sampling = atoi(argv[1]);
  2227. adv_monitor_set_rssi_sampling_period(sampling);
  2228. }
  2229. static void cmd_adv_monitor_add_or_monitor(int argc, char *argv[])
  2230. {
  2231. adv_monitor_add_monitor(dbus_conn, "or_patterns", argc, argv);
  2232. }
  2233. static void cmd_adv_monitor_print_monitor(int argc, char *argv[])
  2234. {
  2235. int monitor_idx;
  2236. if (strcmp(argv[1], "all") == 0)
  2237. monitor_idx = -1;
  2238. else
  2239. monitor_idx = atoi(argv[1]);
  2240. adv_monitor_print_monitor(dbus_conn, monitor_idx);
  2241. }
  2242. static void cmd_adv_monitor_remove_monitor(int argc, char *argv[])
  2243. {
  2244. int monitor_idx;
  2245. if (strcmp(argv[1], "all") == 0)
  2246. monitor_idx = -1;
  2247. else
  2248. monitor_idx = atoi(argv[1]);
  2249. adv_monitor_remove_monitor(dbus_conn, monitor_idx);
  2250. }
  2251. static void cmd_adv_monitor_get_supported_info(int argc, char *argv[])
  2252. {
  2253. adv_monitor_get_supported_info();
  2254. }
  2255. static void cmd_admin_allow(int argc, char *argv[])
  2256. {
  2257. if (check_default_ctrl() == FALSE)
  2258. return bt_shell_noninteractive_quit(EXIT_FAILURE);
  2259. if (argc <= 1) {
  2260. admin_policy_read_service_allowlist(dbus_conn);
  2261. return;
  2262. }
  2263. if (strcmp(argv[1], "clear") == 0)
  2264. argc--;
  2265. admin_policy_set_service_allowlist(dbus_conn, argc - 1, argv + 1);
  2266. }
  2267. static const struct bt_shell_menu advertise_menu = {
  2268. .name = "advertise",
  2269. .desc = "Advertise Options Submenu",
  2270. .entries = {
  2271. { "uuids", "[uuid1 uuid2 ...]", cmd_advertise_uuids,
  2272. "Set/Get advertise uuids" },
  2273. { "service", "[uuid] [data=xx xx ...]", cmd_advertise_service,
  2274. "Set/Get advertise service data" },
  2275. { "manufacturer", "[id] [data=xx xx ...]",
  2276. cmd_advertise_manufacturer,
  2277. "Set/Get advertise manufacturer data" },
  2278. { "data", "[type] [data=xx xx ...]", cmd_advertise_data,
  2279. "Set/Get advertise data" },
  2280. { "discoverable", "[on/off]", cmd_advertise_discoverable,
  2281. "Set/Get advertise discoverable" },
  2282. { "discoverable-timeout", "[seconds]",
  2283. cmd_advertise_discoverable_timeout,
  2284. "Set/Get advertise discoverable timeout" },
  2285. { "tx-power", "[on/off]", cmd_advertise_tx_power,
  2286. "Show/Enable/Disable TX power to be advertised",
  2287. NULL },
  2288. { "name", "[on/off/name]", cmd_advertise_name,
  2289. "Configure local name to be advertised" },
  2290. { "appearance", "[on/off/value]", cmd_advertise_appearance,
  2291. "Configure custom appearance to be advertised" },
  2292. { "duration", "[seconds]", cmd_advertise_duration,
  2293. "Set/Get advertise duration" },
  2294. { "timeout", "[seconds]", cmd_advertise_timeout,
  2295. "Set/Get advertise timeout" },
  2296. { "secondary", "[1M/2M/Coded]", cmd_advertise_secondary,
  2297. "Set/Get advertise secondary channel" },
  2298. { "interval", "[min] [max] ", cmd_advertise_interval,
  2299. "Set/Get advertise interval range" },
  2300. { "clear", "[uuids/service/manufacturer/config-name...]", cmd_ad_clear,
  2301. "Clear advertise config" },
  2302. { } },
  2303. };
  2304. static const struct bt_shell_menu advertise_monitor_menu = {
  2305. .name = "monitor",
  2306. .desc = "Advertisement Monitor Options Submenu",
  2307. .entries = {
  2308. { "set-rssi-threshold", "<low_threshold> <high_threshold>",
  2309. cmd_adv_monitor_set_rssi_threshold,
  2310. "Set RSSI threshold parameter" },
  2311. { "set-rssi-timeout", "<low_timeout> <high_timeout>",
  2312. cmd_adv_monitor_set_rssi_timeout,
  2313. "Set RSSI timeout parameter" },
  2314. { "set-rssi-sampling-period", "<sampling_period>",
  2315. cmd_adv_monitor_set_rssi_sampling_period,
  2316. "Set RSSI sampling period parameter" },
  2317. { "add-or-pattern", "[patterns=pattern1 pattern2 ...]",
  2318. cmd_adv_monitor_add_or_monitor,
  2319. "Register 'or pattern' type monitor with the "
  2320. "specified RSSI parameters" },
  2321. { "get-pattern", "<monitor-id/all>",
  2322. cmd_adv_monitor_print_monitor,
  2323. "Get advertisement monitor" },
  2324. { "remove-pattern", "<monitor-id/all>",
  2325. cmd_adv_monitor_remove_monitor,
  2326. "Remove advertisement monitor" },
  2327. { "get-supported-info", NULL,
  2328. cmd_adv_monitor_get_supported_info,
  2329. "Get advertisement manager supported "
  2330. "features and supported monitor types" },
  2331. { "print-usage", "<add-or-pattern>",
  2332. cmd_adv_monitor_print_usage,
  2333. "Print the command usage"},
  2334. { } },
  2335. };
  2336. static const struct bt_shell_menu scan_menu = {
  2337. .name = "scan",
  2338. .desc = "Scan Options Submenu",
  2339. .entries = {
  2340. { "uuids", "[all/uuid1 uuid2 ...]", cmd_scan_filter_uuids,
  2341. "Set/Get UUIDs filter" },
  2342. { "rssi", "[rssi]", cmd_scan_filter_rssi,
  2343. "Set/Get RSSI filter, and clears pathloss" },
  2344. { "pathloss", "[pathloss]", cmd_scan_filter_pathloss,
  2345. "Set/Get Pathloss filter, and clears RSSI" },
  2346. { "transport", "[transport]", cmd_scan_filter_transport,
  2347. "Set/Get transport filter" },
  2348. { "duplicate-data", "[on/off]", cmd_scan_filter_duplicate_data,
  2349. "Set/Get duplicate data filter",
  2350. NULL },
  2351. { "discoverable", "[on/off]", cmd_scan_filter_discoverable,
  2352. "Set/Get discoverable filter",
  2353. NULL },
  2354. { "pattern", "[value]", cmd_scan_filter_pattern,
  2355. "Set/Get pattern filter",
  2356. NULL },
  2357. { "clear",
  2358. "[uuids/rssi/pathloss/transport/duplicate-data/discoverable/pattern]",
  2359. cmd_scan_filter_clear,
  2360. "Clears discovery filter.",
  2361. filter_clear_generator },
  2362. { } },
  2363. };
  2364. static const struct bt_shell_menu gatt_menu = {
  2365. .name = "gatt",
  2366. .desc = "Generic Attribute Submenu",
  2367. .entries = {
  2368. { "list-attributes", "[dev/local]", cmd_list_attributes,
  2369. "List attributes", dev_generator },
  2370. { "select-attribute", "<attribute/UUID>", cmd_select_attribute,
  2371. "Select attribute", attribute_generator },
  2372. { "attribute-info", "[attribute/UUID]", cmd_attribute_info,
  2373. "Select attribute", attribute_generator },
  2374. { "read", "[offset]", cmd_read, "Read attribute value" },
  2375. { "write", "<data=xx xx ...> [offset] [type]", cmd_write,
  2376. "Write attribute value" },
  2377. { "acquire-write", NULL, cmd_acquire_write,
  2378. "Acquire Write file descriptor" },
  2379. { "release-write", NULL, cmd_release_write,
  2380. "Release Write file descriptor" },
  2381. { "acquire-notify", NULL, cmd_acquire_notify,
  2382. "Acquire Notify file descriptor" },
  2383. { "release-notify", NULL, cmd_release_notify,
  2384. "Release Notify file descriptor" },
  2385. { "notify", "<on/off>", cmd_notify, "Notify attribute value",
  2386. NULL },
  2387. { "clone", "[dev/attribute/UUID]", cmd_clone,
  2388. "Clone a device or attribute" },
  2389. { "register-application", "[UUID ...]", cmd_register_app,
  2390. "Register profile to connect" },
  2391. { "unregister-application", NULL, cmd_unregister_app,
  2392. "Unregister profile" },
  2393. { "register-service", "<UUID> [handle]", cmd_register_service,
  2394. "Register application service." },
  2395. { "unregister-service", "<UUID/object>", cmd_unregister_service,
  2396. "Unregister application service" },
  2397. { "register-includes", "<UUID> [handle]", cmd_register_includes,
  2398. "Register as Included service in." },
  2399. { "unregister-includes", "<Service-UUID><Inc-UUID>",
  2400. cmd_unregister_includes,
  2401. "Unregister Included service." },
  2402. { "register-characteristic",
  2403. "<UUID> <Flags=read,write,notify...> [handle]",
  2404. cmd_register_characteristic,
  2405. "Register application characteristic" },
  2406. { "unregister-characteristic", "<UUID/object>",
  2407. cmd_unregister_characteristic,
  2408. "Unregister application characteristic" },
  2409. { "register-descriptor", "<UUID> <Flags=read,write...> [handle]",
  2410. cmd_register_descriptor,
  2411. "Register application descriptor" },
  2412. { "unregister-descriptor", "<UUID/object>",
  2413. cmd_unregister_descriptor,
  2414. "Unregister application descriptor" },
  2415. { } },
  2416. };
  2417. static const struct bt_shell_menu admin_menu = {
  2418. .name = "admin",
  2419. .desc = "Admin Policy Submenu",
  2420. .entries = {
  2421. { "allow", "[clear/uuid1 uuid2 ...]", cmd_admin_allow,
  2422. "Allow service UUIDs and block rest of them"},
  2423. {} },
  2424. };
  2425. static const struct bt_shell_menu main_menu = {
  2426. .name = "main",
  2427. .entries = {
  2428. { "list", NULL, cmd_list, "List available controllers" },
  2429. { "show", "[ctrl]", cmd_show, "Controller information",
  2430. ctrl_generator },
  2431. { "select", "<ctrl>", cmd_select, "Select default controller",
  2432. ctrl_generator },
  2433. { "devices", NULL, cmd_devices, "List available devices" },
  2434. { "paired-devices", NULL, cmd_paired_devices,
  2435. "List paired devices"},
  2436. { "system-alias", "<name>", cmd_system_alias,
  2437. "Set controller alias" },
  2438. { "reset-alias", NULL, cmd_reset_alias,
  2439. "Reset controller alias" },
  2440. { "power", "<on/off>", cmd_power, "Set controller power",
  2441. NULL },
  2442. { "pairable", "<on/off>", cmd_pairable,
  2443. "Set controller pairable mode",
  2444. NULL },
  2445. { "discoverable", "<on/off>", cmd_discoverable,
  2446. "Set controller discoverable mode",
  2447. NULL },
  2448. { "discoverable-timeout", "[value]", cmd_discoverable_timeout,
  2449. "Set discoverable timeout", NULL },
  2450. { "agent", "<on/off/capability>", cmd_agent,
  2451. "Enable/disable agent with given capability",
  2452. capability_generator},
  2453. { "default-agent",NULL, cmd_default_agent,
  2454. "Set agent as the default one" },
  2455. { "advertise", "<on/off/type>", cmd_advertise,
  2456. "Enable/disable advertising with given type",
  2457. ad_generator},
  2458. { "set-alias", "<alias>", cmd_set_alias, "Set device alias" },
  2459. { "scan", "<on/off>", cmd_scan, "Scan for devices", NULL },
  2460. { "info", "[dev]", cmd_info, "Device information",
  2461. dev_generator },
  2462. { "pair", "[dev]", cmd_pair, "Pair with device",
  2463. dev_generator },
  2464. { "cancel-pairing", "[dev]", cmd_cancel_pairing,
  2465. "Cancel pairing with device", dev_generator },
  2466. { "trust", "[dev]", cmd_trust, "Trust device",
  2467. dev_generator },
  2468. { "untrust", "[dev]", cmd_untrust, "Untrust device",
  2469. dev_generator },
  2470. { "block", "[dev]", cmd_block, "Block device",
  2471. dev_generator },
  2472. { "unblock", "[dev]", cmd_unblock, "Unblock device",
  2473. dev_generator },
  2474. { "remove", "<dev>", cmd_remove, "Remove device",
  2475. dev_generator },
  2476. { "connect", "<dev>", cmd_connect, "Connect device",
  2477. dev_generator },
  2478. { "disconnect", "[dev]", cmd_disconn, "Disconnect device",
  2479. dev_generator },
  2480. { } },
  2481. };
  2482. static const struct option options[] = {
  2483. { "agent", required_argument, 0, 'a' },
  2484. { 0, 0, 0, 0 }
  2485. };
  2486. static const char *agent_option;
  2487. static const char **optargs[] = {
  2488. &agent_option
  2489. };
  2490. static const char *help[] = {
  2491. "Register agent handler: <capability>"
  2492. };
  2493. static const struct bt_shell_opt opt = {
  2494. .options = options,
  2495. .optno = sizeof(options) / sizeof(struct option),
  2496. .optstr = "a:",
  2497. .optarg = optargs,
  2498. .help = help,
  2499. };
  2500. static void client_ready(GDBusClient *client, void *user_data)
  2501. {
  2502. setup_standard_input();
  2503. }
  2504. int main(int argc, char *argv[])
  2505. {
  2506. GDBusClient *client;
  2507. int status;
  2508. bt_shell_init(argc, argv, &opt);
  2509. bt_shell_set_menu(&main_menu);
  2510. bt_shell_add_submenu(&advertise_menu);
  2511. bt_shell_add_submenu(&advertise_monitor_menu);
  2512. bt_shell_add_submenu(&scan_menu);
  2513. bt_shell_add_submenu(&gatt_menu);
  2514. bt_shell_add_submenu(&admin_menu);
  2515. bt_shell_set_prompt(PROMPT_OFF);
  2516. if (agent_option)
  2517. auto_register_agent = g_strdup(agent_option);
  2518. else
  2519. auto_register_agent = g_strdup("");
  2520. dbus_conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
  2521. g_dbus_attach_object_manager(dbus_conn);
  2522. bt_shell_set_env("DBUS_CONNECTION", dbus_conn);
  2523. client = g_dbus_client_new(dbus_conn, "org.bluez", "/org/bluez");
  2524. g_dbus_client_set_connect_watch(client, connect_handler, NULL);
  2525. g_dbus_client_set_disconnect_watch(client, disconnect_handler, NULL);
  2526. g_dbus_client_set_signal_watch(client, message_handler, NULL);
  2527. g_dbus_client_set_proxy_handlers(client, proxy_added, proxy_removed,
  2528. property_changed, NULL);
  2529. g_dbus_client_set_ready_watch(client, client_ready, NULL);
  2530. status = bt_shell_run();
  2531. g_dbus_client_unref(client);
  2532. dbus_connection_unref(dbus_conn);
  2533. g_list_free_full(ctrl_list, proxy_leak);
  2534. g_free(auto_register_agent);
  2535. return status;
  2536. }