main.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. *
  4. * BlueZ - Bluetooth protocol stack for Linux
  5. *
  6. * Copyright (C) 2012 Marcel Holtmann <marcel@holtmann.org>
  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 <ctype.h>
  17. #include <unistd.h>
  18. #include <stdlib.h>
  19. #include <string.h>
  20. #include <signal.h>
  21. #include <sys/signalfd.h>
  22. #include <glib.h>
  23. #include "gdbus/gdbus.h"
  24. #define IAP_PATH "/org/bluez/iap"
  25. #define IAP_UUID "00000000-deca-fade-deca-deafdecacafe"
  26. #define IAP_RECORD \
  27. "<?xml version=\"1.0\" encoding=\"UTF-8\" ?> \
  28. <record> \
  29. <attribute id=\"0x0001\"> \
  30. <sequence> \
  31. <uuid value=\"%s\" /> \
  32. </sequence> \
  33. </attribute> \
  34. <attribute id=\"0x0002\"> \
  35. <uint32 value=\"0x00000000\" /> \
  36. </attribute> \
  37. <attribute id=\"0x0004\"> \
  38. <sequence> \
  39. <sequence> \
  40. <uuid value=\"0x0100\" /> \
  41. </sequence> \
  42. <sequence> \
  43. <uuid value=\"0x0003\" /> \
  44. <uint8 value=\"0x%02x\" /> \
  45. </sequence> \
  46. </sequence> \
  47. </attribute> \
  48. <attribute id=\"0x0005\"> \
  49. <sequence> \
  50. <uuid value=\"0x1002\" /> \
  51. </sequence> \
  52. </attribute> \
  53. <attribute id=\"0x0006\"> \
  54. <sequence> \
  55. <uint16 value=\"0x656e\" /> \
  56. <uint16 value=\"0x006a\" /> \
  57. <uint16 value=\"0x0100\" /> \
  58. <uint16 value=\"0x6672\" /> \
  59. <uint16 value=\"0x006a\" /> \
  60. <uint16 value=\"0x0110\" /> \
  61. <uint16 value=\"0x6465\" /> \
  62. <uint16 value=\"0x006a\" /> \
  63. <uint16 value=\"0x0120\" /> \
  64. <uint16 value=\"0x6a61\" /> \
  65. <uint16 value=\"0x006a\" /> \
  66. <uint16 value=\"0x0130\" /> \
  67. </sequence> \
  68. </attribute> \
  69. <attribute id=\"0x0008\"> \
  70. <uint8 value=\"0xff\" /> \
  71. </attribute> \
  72. <attribute id=\"0x0009\"> \
  73. <sequence> \
  74. <sequence> \
  75. <uuid value=\"0x1101\" /> \
  76. <uint16 value=\"0x0100\" /> \
  77. </sequence> \
  78. </sequence> \
  79. </attribute> \
  80. <attribute id=\"0x0100\"> \
  81. <text value=\"Wireless iAP\" /> \
  82. </attribute> \
  83. </record>"
  84. static GMainLoop *main_loop;
  85. static guint iap_source = 0;
  86. static gboolean iap_handler(GIOChannel *channel, GIOCondition condition,
  87. gpointer user_data)
  88. {
  89. unsigned char buf[512];
  90. ssize_t len;
  91. int fd;
  92. if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
  93. iap_source = 0;
  94. return FALSE;
  95. }
  96. fd = g_io_channel_unix_get_fd(channel);
  97. len = read(fd, buf, sizeof(buf));
  98. if (len < 0) {
  99. iap_source = 0;
  100. return FALSE;
  101. }
  102. return TRUE;
  103. }
  104. static guint create_source(int fd, GIOFunc func)
  105. {
  106. GIOChannel *channel;
  107. guint source;
  108. channel = g_io_channel_unix_new(fd);
  109. g_io_channel_set_close_on_unref(channel, TRUE);
  110. g_io_channel_set_encoding(channel, NULL, NULL);
  111. g_io_channel_set_buffered(channel, FALSE);
  112. source = g_io_add_watch(channel,
  113. G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, func, NULL);
  114. g_io_channel_unref(channel);
  115. return source;
  116. }
  117. static void remove_source(const char *path)
  118. {
  119. if (iap_source > 0) {
  120. g_source_remove(iap_source);
  121. iap_source = 0;
  122. }
  123. }
  124. static DBusMessage *release_profile(DBusConnection *conn,
  125. DBusMessage *msg, void *user_data)
  126. {
  127. g_print("Profile released\n");
  128. remove_source(IAP_PATH);
  129. return dbus_message_new_method_return(msg);
  130. }
  131. static DBusMessage *new_connection(DBusConnection *conn,
  132. DBusMessage *msg, void *user_data)
  133. {
  134. const char *path, *device;
  135. int fd;
  136. g_print("New connection\n");
  137. path = dbus_message_get_path(msg);
  138. dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device,
  139. DBUS_TYPE_UNIX_FD, &fd, DBUS_TYPE_INVALID);
  140. g_print(" from %s\n", path);
  141. g_print(" for device %s with fd %d\n", device, fd);
  142. if (iap_source == 0)
  143. iap_source = create_source(fd, iap_handler);
  144. else
  145. close(fd);
  146. return dbus_message_new_method_return(msg);
  147. }
  148. static DBusMessage *request_disconnection(DBusConnection *conn,
  149. DBusMessage *msg, void *user_data)
  150. {
  151. DBusMessageIter iter;
  152. const char *path, *device;
  153. g_print("Request disconnection\n");
  154. path = dbus_message_get_path(msg);
  155. dbus_message_iter_init(msg, &iter);
  156. dbus_message_iter_get_basic(&iter, &device);
  157. g_print(" from %s\n", path);
  158. g_print(" for device %s\n", device);
  159. remove_source(path);
  160. return dbus_message_new_method_return(msg);
  161. }
  162. static DBusMessage *cancel_request(DBusConnection *conn,
  163. DBusMessage *msg, void *user_data)
  164. {
  165. const char *path;
  166. g_print("Request canceled\n");
  167. path = dbus_message_get_path(msg);
  168. g_print(" from %s\n", path);
  169. remove_source(path);
  170. return dbus_message_new_method_return(msg);
  171. }
  172. static const GDBusMethodTable methods[] = {
  173. { GDBUS_METHOD("Release", NULL, NULL, release_profile) },
  174. { GDBUS_METHOD("NewConnection",
  175. GDBUS_ARGS({ "device", "o" },
  176. { "fd", "h"}, { "opts", "a{sv}"}),
  177. NULL, new_connection) },
  178. { GDBUS_METHOD("RequestDisconnection",
  179. GDBUS_ARGS({ "device", "o" }),
  180. NULL, request_disconnection) },
  181. { GDBUS_METHOD("Cancel", NULL, NULL, cancel_request) },
  182. { }
  183. };
  184. static void register_profile_setup(DBusMessageIter *iter, void *user_data)
  185. {
  186. const char *path = IAP_PATH;
  187. const char *uuid = IAP_UUID;
  188. DBusMessageIter dict, entry, value;
  189. dbus_uint16_t channel;
  190. char *record;
  191. const char *str;
  192. dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
  193. dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid);
  194. dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
  195. DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
  196. DBUS_TYPE_STRING_AS_STRING
  197. DBUS_TYPE_VARIANT_AS_STRING
  198. DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
  199. dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY,
  200. NULL, &entry);
  201. str = "Role";
  202. dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &str);
  203. dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
  204. DBUS_TYPE_STRING_AS_STRING, &value);
  205. str = "server";
  206. dbus_message_iter_append_basic(&value, DBUS_TYPE_STRING, &str);
  207. dbus_message_iter_close_container(&entry, &value);
  208. dbus_message_iter_close_container(&dict, &entry);
  209. dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY,
  210. NULL, &entry);
  211. str = "Channel";
  212. dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &str);
  213. dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
  214. DBUS_TYPE_UINT16_AS_STRING, &value);
  215. channel = 23;
  216. dbus_message_iter_append_basic(&value, DBUS_TYPE_UINT16, &channel);
  217. dbus_message_iter_close_container(&entry, &value);
  218. dbus_message_iter_close_container(&dict, &entry);
  219. dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY,
  220. NULL, &entry);
  221. str = "ServiceRecord";
  222. dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &str);
  223. dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
  224. DBUS_TYPE_STRING_AS_STRING, &value);
  225. record = g_strdup_printf(IAP_RECORD, IAP_UUID, channel);
  226. dbus_message_iter_append_basic(&value, DBUS_TYPE_STRING, &record);
  227. g_free(record);
  228. dbus_message_iter_close_container(&entry, &value);
  229. dbus_message_iter_close_container(&dict, &entry);
  230. dbus_message_iter_close_container(iter, &dict);
  231. }
  232. static void register_profile_reply(DBusMessage *message, void *user_data)
  233. {
  234. DBusError error;
  235. dbus_error_init(&error);
  236. if (dbus_set_error_from_message(&error, message) == TRUE) {
  237. g_print("Failed to register profile\n");
  238. return;
  239. }
  240. g_print("Profile registered\n");
  241. }
  242. static void connect_handler(DBusConnection *connection, void *user_data)
  243. {
  244. GDBusClient *client = user_data;
  245. GDBusProxy *proxy;
  246. g_print("Bluetooth connected\n");
  247. proxy = g_dbus_proxy_new(client, "/org/bluez",
  248. "org.bluez.ProfileManager1");
  249. if (!proxy)
  250. return;
  251. g_dbus_register_interface(connection, IAP_PATH,
  252. "org.bluez.Profile1",
  253. methods, NULL, NULL, NULL, NULL);
  254. g_dbus_proxy_method_call(proxy, "RegisterProfile",
  255. register_profile_setup,
  256. register_profile_reply, NULL, NULL);
  257. g_dbus_proxy_unref(proxy);
  258. }
  259. static void disconnect_handler(DBusConnection *connection, void *user_data)
  260. {
  261. g_print("Bluetooth disconnected\n");
  262. g_dbus_unregister_interface(connection, IAP_PATH,
  263. "org.bluez.Profile1");
  264. }
  265. static gboolean signal_handler(GIOChannel *channel, GIOCondition condition,
  266. gpointer user_data)
  267. {
  268. static unsigned int __terminated = 0;
  269. struct signalfd_siginfo si;
  270. ssize_t result;
  271. int fd;
  272. if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
  273. g_main_loop_quit(main_loop);
  274. return FALSE;
  275. }
  276. fd = g_io_channel_unix_get_fd(channel);
  277. result = read(fd, &si, sizeof(si));
  278. if (result != sizeof(si))
  279. return FALSE;
  280. switch (si.ssi_signo) {
  281. case SIGINT:
  282. case SIGTERM:
  283. if (__terminated == 0)
  284. g_main_loop_quit(main_loop);
  285. __terminated = 1;
  286. break;
  287. }
  288. return TRUE;
  289. }
  290. static guint setup_signalfd(void)
  291. {
  292. GIOChannel *channel;
  293. guint source;
  294. sigset_t mask;
  295. int fd;
  296. sigemptyset(&mask);
  297. sigaddset(&mask, SIGINT);
  298. sigaddset(&mask, SIGTERM);
  299. if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) {
  300. perror("Failed to set signal mask");
  301. return 0;
  302. }
  303. fd = signalfd(-1, &mask, 0);
  304. if (fd < 0) {
  305. perror("Failed to create signal descriptor");
  306. return 0;
  307. }
  308. channel = g_io_channel_unix_new(fd);
  309. g_io_channel_set_close_on_unref(channel, TRUE);
  310. g_io_channel_set_encoding(channel, NULL, NULL);
  311. g_io_channel_set_buffered(channel, FALSE);
  312. source = g_io_add_watch(channel,
  313. G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
  314. signal_handler, NULL);
  315. g_io_channel_unref(channel);
  316. return source;
  317. }
  318. static gboolean option_version = FALSE;
  319. static GOptionEntry options[] = {
  320. { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version,
  321. "Show version information and exit" },
  322. { NULL },
  323. };
  324. int main(int argc, char *argv[])
  325. {
  326. GOptionContext *context;
  327. GError *error = NULL;
  328. DBusConnection *dbus_conn;
  329. GDBusClient *client;
  330. guint signal;
  331. context = g_option_context_new(NULL);
  332. g_option_context_add_main_entries(context, options, NULL);
  333. if (g_option_context_parse(context, &argc, &argv, &error) == FALSE) {
  334. if (error != NULL) {
  335. g_printerr("%s\n", error->message);
  336. g_error_free(error);
  337. } else
  338. g_printerr("An unknown error occurred\n");
  339. exit(1);
  340. }
  341. g_option_context_free(context);
  342. if (option_version == TRUE) {
  343. g_print("%s\n", VERSION);
  344. exit(0);
  345. }
  346. main_loop = g_main_loop_new(NULL, FALSE);
  347. dbus_conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
  348. signal = setup_signalfd();
  349. client = g_dbus_client_new(dbus_conn, "org.bluez", "/org/bluez");
  350. g_dbus_client_set_connect_watch(client, connect_handler, client);
  351. g_dbus_client_set_disconnect_watch(client, disconnect_handler, NULL);
  352. g_main_loop_run(main_loop);
  353. g_dbus_client_unref(client);
  354. g_source_remove(signal);
  355. dbus_connection_unref(dbus_conn);
  356. g_main_loop_unref(main_loop);
  357. return 0;
  358. }