diff options
Diffstat (limited to 'common')
-rw-r--r-- | common/Makefile.am | 16 | ||||
-rw-r--r-- | common/error.c | 126 | ||||
-rw-r--r-- | common/error.h | 49 | ||||
-rw-r--r-- | common/glib-helper.c | 1283 | ||||
-rw-r--r-- | common/glib-helper.h | 96 | ||||
-rw-r--r-- | common/logging.c | 106 | ||||
-rw-r--r-- | common/logging.h | 38 | ||||
-rw-r--r-- | common/oui.c | 109 | ||||
-rw-r--r-- | common/oui.h | 25 | ||||
-rw-r--r-- | common/ppoll.h | 16 | ||||
-rw-r--r-- | common/sdp-glib.c | 239 | ||||
-rw-r--r-- | common/sdp-xml.c | 793 | ||||
-rw-r--r-- | common/sdp-xml.h | 61 | ||||
-rw-r--r-- | common/test_textfile.c | 187 | ||||
-rw-r--r-- | common/textfile.c | 463 | ||||
-rw-r--r-- | common/textfile.h | 42 | ||||
-rw-r--r-- | common/uinput.h | 586 |
17 files changed, 4235 insertions, 0 deletions
diff --git a/common/Makefile.am b/common/Makefile.am new file mode 100644 index 00000000..6ae5ed7a --- /dev/null +++ b/common/Makefile.am @@ -0,0 +1,16 @@ + +noinst_LIBRARIES = libhelper.a + +libhelper_a_SOURCES = oui.h oui.c textfile.h textfile.c \ + logging.h logging.c error.h error.c \ + glib-helper.h glib-helper.c sdp-xml.h sdp-xml.c sdp-glib.c + +noinst_PROGRAMS = test_textfile + +test_textfile_LDADD = libhelper.a + +AM_CFLAGS = @DBUS_CFLAGS@ @GLIB_CFLAGS@ @GDBUS_CFLAGS@ + +EXTRA_DIST = ppoll.h uinput.h + +MAINTAINERCLEANFILES = Makefile.in diff --git a/common/error.c b/common/error.c new file mode 100644 index 00000000..bc813d74 --- /dev/null +++ b/common/error.c @@ -0,0 +1,126 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2006-2007 Nokia Corporation + * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org> + * Copyright (C) 2007-2008 Fabien Chevalier <fabchevalier@free.fr> + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <string.h> +#include <errno.h> + +#include <gdbus.h> + +#include "error.h" + +DBusMessage *create_errno_message(DBusMessage *msg, int err) +{ + return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed", + strerror(err)); +} + +/** + org.bluez.Error.ConnectionAttemptFailed: + + An unexpected error (other than DeviceUnreachable) error has occured while + attempting a connection to a device +*/ +DBusHandlerResult error_connection_attempt_failed(DBusConnection *conn, DBusMessage *msg, int err) +{ + return error_common_reply(conn, msg, + ERROR_INTERFACE ".ConnectionAttemptFailed", + err > 0 ? strerror(err) : "Connection attempt failed"); +} + +/** + org.bluez.Error.NotSupported: + + The remote device does not support the expected feature. + Examples of use: trying to connect to audio device while audio is not + declared in device sdp record. +*/ +DBusHandlerResult error_not_supported(DBusConnection *conn, DBusMessage *msg) +{ + return error_common_reply(conn, msg, ERROR_INTERFACE ".NotSupported", + "Not supported"); +} + +/** + org.bluez.Error.Canceled: + + The operation was canceled. + Examples of use : autorization process canceled, connection canceled +*/ +DBusHandlerResult error_canceled(DBusConnection *conn, DBusMessage *msg, + const char *str) +{ + return error_common_reply(conn, msg, ERROR_INTERFACE ".Canceled", str); +} + +/** + org.bluez.Error.Failed: + + This is a the most generic error. + desc filed is MANDATORY +*/ +DBusHandlerResult error_failed(DBusConnection *conn, DBusMessage *msg, + const char * desc) +{ + return error_common_reply(conn, msg, ERROR_INTERFACE ".Failed", desc); +} + +/** + org.bluez.Error.Failed: + + This is a the most generic error, instantiated form a UNIX errno number. +*/ +DBusHandlerResult error_failed_errno(DBusConnection *conn, DBusMessage *msg, + int err) +{ + const char *desc = strerror(err); + + return error_failed(conn, msg, desc); +} + +/* Helper function - internal use only */ +DBusHandlerResult error_common_reply(DBusConnection *conn, DBusMessage *msg, + const char *name, const char *descr) +{ + DBusMessage *derr; + dbus_bool_t ret; + + if (!conn || !msg) + return DBUS_HANDLER_RESULT_HANDLED; + + derr = dbus_message_new_error(msg, name, descr); + if (!derr) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + ret = dbus_connection_send(conn, derr, NULL); + + dbus_message_unref(derr); + + return DBUS_HANDLER_RESULT_HANDLED; +} diff --git a/common/error.h b/common/error.h new file mode 100644 index 00000000..68ce105a --- /dev/null +++ b/common/error.h @@ -0,0 +1,49 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2006-2007 Nokia Corporation + * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org> + * Copyright (C) 2007-2008 Fabien Chevalier <fabchevalier@free.fr> + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include <dbus/dbus.h> + +#define ERROR_INTERFACE "org.bluez.Error" + +DBusMessage *create_errno_message(DBusMessage *msg, int err); + +DBusHandlerResult error_connection_attempt_failed(DBusConnection *conn, + DBusMessage *msg, + int err); + +DBusHandlerResult error_not_supported(DBusConnection *conn, + DBusMessage *msg); + +DBusHandlerResult error_canceled(DBusConnection *conn, DBusMessage *msg, + const char *str); + +DBusHandlerResult error_failed(DBusConnection *conn, DBusMessage *msg, + const char *desc); + +DBusHandlerResult error_failed_errno(DBusConnection *conn, DBusMessage *msg, + int err); + +DBusHandlerResult error_common_reply(DBusConnection *conn, DBusMessage *msg, + const char *name, const char *descr); diff --git a/common/glib-helper.c b/common/glib-helper.c new file mode 100644 index 00000000..f0435aca --- /dev/null +++ b/common/glib-helper.c @@ -0,0 +1,1283 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org> + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <sys/socket.h> + +#include <bluetooth/bluetooth.h> +#include <bluetooth/rfcomm.h> +#include <bluetooth/l2cap.h> +#include <bluetooth/sco.h> +#include <bluetooth/sdp.h> +#include <bluetooth/sdp_lib.h> + +#include <glib.h> + +#include "glib-helper.h" + +typedef int (*resolver_t) (int fd, char *src, char *dst); +typedef BtIOError (*connect_t) (BtIO *io, BtIOFunc func); +typedef BtIOError (*listen_t) (BtIO *io, BtIOFunc func); + +int set_nonblocking(int fd) +{ + long arg; + + arg = fcntl(fd, F_GETFL); + if (arg < 0) + return -errno; + + /* Return if already nonblocking */ + if (arg & O_NONBLOCK) + return 0; + + arg |= O_NONBLOCK; + if (fcntl(fd, F_SETFL, arg) < 0) + return -errno; + + return 0; +} + +struct io_context { + int fd; + GIOChannel *io; + BtIOFunc func; + bt_io_callback_t cb; + resolver_t resolver; + gpointer user_data; +}; + +struct bt_io { + char src[18]; + char dst[18]; + guint32 flags; + guint8 channel; + guint16 psm; + guint16 mtu; + BtIOTransport type; + connect_t connect; + listen_t listen; + struct io_context *io_ctxt; + int refcount; +}; + +struct search_context { + bdaddr_t src; + bdaddr_t dst; + sdp_session_t *session; + bt_callback_t cb; + bt_destroy_t destroy; + gpointer user_data; + uuid_t uuid; +}; + +static GSList *context_list = NULL; + +static void search_context_cleanup(struct search_context *ctxt) +{ + context_list = g_slist_remove(context_list, ctxt); + + if (ctxt->destroy) + ctxt->destroy(ctxt->user_data); + + g_free(ctxt); +} + +static void search_completed_cb(uint8_t type, uint16_t status, + uint8_t *rsp, size_t size, void *user_data) +{ + struct search_context *ctxt = user_data; + sdp_list_t *recs = NULL; + int scanned, seqlen = 0, bytesleft = size; + uint8_t dataType; + int err = 0; + + if (status || type != SDP_SVC_SEARCH_ATTR_RSP) { + err = -EPROTO; + goto done; + } + + scanned = sdp_extract_seqtype_safe(rsp, bytesleft, &dataType, &seqlen); + if (!scanned || !seqlen) + goto done; + + rsp += scanned; + bytesleft -= scanned; + do { + sdp_record_t *rec; + int recsize; + + recsize = 0; + rec = sdp_extract_pdu_safe(rsp, bytesleft, &recsize); + if (!rec) + break; + + if (!recsize) { + sdp_record_free(rec); + break; + } + + scanned += recsize; + rsp += recsize; + bytesleft -= recsize; + + recs = sdp_list_append(recs, rec); + } while (scanned < size && bytesleft > 0); + +done: + sdp_close(ctxt->session); + + if (ctxt->cb) + ctxt->cb(recs, err, ctxt->user_data); + + search_context_cleanup(ctxt); +} + +static gboolean search_process_cb(GIOChannel *chan, + GIOCondition cond, void *user_data) +{ + struct search_context *ctxt = user_data; + int err = 0; + + if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) { + err = EIO; + goto failed; + } + + if (sdp_process(ctxt->session) < 0) + goto failed; + + return TRUE; + +failed: + if (err) { + sdp_close(ctxt->session); + + if (ctxt->cb) + ctxt->cb(NULL, err, ctxt->user_data); + + search_context_cleanup(ctxt); + } + + return FALSE; +} + +static gboolean connect_watch(GIOChannel *chan, GIOCondition cond, gpointer user_data) +{ + struct search_context *ctxt = user_data; + sdp_list_t *search, *attrids; + uint32_t range = 0x0000ffff; + socklen_t len; + int sk, err = 0; + + sk = g_io_channel_unix_get_fd(chan); + + len = sizeof(err); + if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &err, &len) < 0) { + err = errno; + goto failed; + } + + if (err != 0) + goto failed; + + if (sdp_set_notify(ctxt->session, search_completed_cb, ctxt) < 0) { + err = EIO; + goto failed; + } + + search = sdp_list_append(NULL, &ctxt->uuid); + attrids = sdp_list_append(NULL, &range); + if (sdp_service_search_attr_async(ctxt->session, + search, SDP_ATTR_REQ_RANGE, attrids) < 0) { + sdp_list_free(attrids, NULL); + sdp_list_free(search, NULL); + err = EIO; + goto failed; + } + + sdp_list_free(attrids, NULL); + sdp_list_free(search, NULL); + + /* Set callback responsible for update the internal SDP transaction */ + g_io_add_watch(chan, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + search_process_cb, ctxt); + return FALSE; + +failed: + sdp_close(ctxt->session); + + if (ctxt->cb) + ctxt->cb(NULL, -err, ctxt->user_data); + + search_context_cleanup(ctxt); + + return FALSE; +} + +static int create_search_context(struct search_context **ctxt, + const bdaddr_t *src, const bdaddr_t *dst, + uuid_t *uuid) +{ + sdp_session_t *s; + GIOChannel *chan; + + if (!ctxt) + return -EINVAL; + + s = sdp_connect(src, dst, SDP_NON_BLOCKING); + if (!s) + return -errno; + + *ctxt = g_try_malloc0(sizeof(struct search_context)); + if (!*ctxt) { + sdp_close(s); + return -ENOMEM; + } + + bacpy(&(*ctxt)->src, src); + bacpy(&(*ctxt)->dst, dst); + (*ctxt)->session = s; + (*ctxt)->uuid = *uuid; + + chan = g_io_channel_unix_new(sdp_get_socket(s)); + g_io_add_watch(chan, G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + connect_watch, *ctxt); + g_io_channel_unref(chan); + + return 0; +} + +int bt_search_service(const bdaddr_t *src, const bdaddr_t *dst, + uuid_t *uuid, bt_callback_t cb, void *user_data, + bt_destroy_t destroy) +{ + struct search_context *ctxt = NULL; + int err; + + if (!cb) + return -EINVAL; + + err = create_search_context(&ctxt, src, dst, uuid); + if (err < 0) + return err; + + ctxt->cb = cb; + ctxt->destroy = destroy; + ctxt->user_data = user_data; + + context_list = g_slist_append(context_list, ctxt); + + return 0; +} + +int bt_discover_services(const bdaddr_t *src, const bdaddr_t *dst, + bt_callback_t cb, void *user_data, bt_destroy_t destroy) +{ + uuid_t uuid; + + sdp_uuid16_create(&uuid, PUBLIC_BROWSE_GROUP); + + return bt_search_service(src, dst, &uuid, cb, user_data, destroy); +} + +static int find_by_bdaddr(const void *data, const void *user_data) +{ + const struct search_context *ctxt = data, *search = user_data; + + return (bacmp(&ctxt->dst, &search->dst) && + bacmp(&ctxt->src, &search->src)); +} + +int bt_cancel_discovery(const bdaddr_t *src, const bdaddr_t *dst) +{ + struct search_context search, *ctxt; + GSList *match; + + memset(&search, 0, sizeof(search)); + bacpy(&search.src, src); + bacpy(&search.dst, dst); + + /* Ongoing SDP Discovery */ + match = g_slist_find_custom(context_list, &search, find_by_bdaddr); + if (!match) + return -ENODATA; + + ctxt = match->data; + if (!ctxt->session) + return -ENOTCONN; + + close(ctxt->session->sock); + return 0; +} + +char *bt_uuid2string(uuid_t *uuid) +{ + gchar *str; + uuid_t uuid128; + unsigned int data0; + unsigned short data1; + unsigned short data2; + unsigned short data3; + unsigned int data4; + unsigned short data5; + + if (!uuid) + return NULL; + + switch (uuid->type) { + case SDP_UUID16: + sdp_uuid16_to_uuid128(&uuid128, uuid); + break; + case SDP_UUID32: + sdp_uuid32_to_uuid128(&uuid128, uuid); + break; + case SDP_UUID128: + memcpy(&uuid128, uuid, sizeof(uuid_t)); + break; + default: + /* Type of UUID unknown */ + return NULL; + } + + memcpy(&data0, &uuid128.value.uuid128.data[0], 4); + memcpy(&data1, &uuid128.value.uuid128.data[4], 2); + memcpy(&data2, &uuid128.value.uuid128.data[6], 2); + memcpy(&data3, &uuid128.value.uuid128.data[8], 2); + memcpy(&data4, &uuid128.value.uuid128.data[10], 4); + memcpy(&data5, &uuid128.value.uuid128.data[14], 2); + + str = g_try_malloc0(MAX_LEN_UUID_STR); + if (!str) + return NULL; + + sprintf(str, "%.8x-%.4x-%.4x-%.4x-%.8x%.4x", + g_ntohl(data0), g_ntohs(data1), + g_ntohs(data2), g_ntohs(data3), + g_ntohl(data4), g_ntohs(data5)); + + return str; +} + +static struct { + const char *name; + uint16_t class; +} bt_services[] = { + { "vcp", VIDEO_CONF_SVCLASS_ID }, + { "pbap", PBAP_SVCLASS_ID }, + { "sap", SAP_SVCLASS_ID }, + { "ftp", OBEX_FILETRANS_SVCLASS_ID }, + { "bpp", BASIC_PRINTING_SVCLASS_ID }, + { "bip", IMAGING_SVCLASS_ID }, + { "synch", IRMC_SYNC_SVCLASS_ID }, + { "dun", DIALUP_NET_SVCLASS_ID }, + { "opp", OBEX_OBJPUSH_SVCLASS_ID }, + { "fax", FAX_SVCLASS_ID }, + { "spp", SERIAL_PORT_SVCLASS_ID }, + { "hsp", HEADSET_SVCLASS_ID }, + { "hfp", HANDSFREE_SVCLASS_ID }, + { } +}; + +uint16_t bt_string2class(const char *pattern) +{ + int i; + + for (i = 0; bt_services[i].name; i++) { + if (strcasecmp(bt_services[i].name, pattern) == 0) + return bt_services[i].class; + } + + return 0; +} + +int bt_string2uuid(uuid_t *uuid, const char *string) +{ + uint32_t data0, data4; + uint16_t data1, data2, data3, data5; + + if (strlen(string) == 36 && + string[8] == '-' && + string[13] == '-' && + string[18] == '-' && + string[23] == '-' && + sscanf(string, "%08x-%04hx-%04hx-%04hx-%08x%04hx", + &data0, &data1, &data2, &data3, &data4, &data5) == 6) { + uint8_t val[16]; + + data0 = g_htonl(data0); + data1 = g_htons(data1); + data2 = g_htons(data2); + data3 = g_htons(data3); + data4 = g_htonl(data4); + data5 = g_htons(data5); + + memcpy(&val[0], &data0, 4); + memcpy(&val[4], &data1, 2); + memcpy(&val[6], &data2, 2); + memcpy(&val[8], &data3, 2); + memcpy(&val[10], &data4, 4); + memcpy(&val[14], &data5, 2); + + sdp_uuid128_create(uuid, val); + + return 0; + } else { + uint16_t class = bt_string2class(string); + if (class) { + sdp_uuid16_create(uuid, class); + return 0; + } + } + + return -1; +} + +gchar *bt_list2string(GSList *list) +{ + GSList *l; + gchar *str, *tmp; + + if (!list) + return NULL; + + str = g_strdup((const gchar *) list->data); + + for (l = list->next; l; l = l->next) { + tmp = g_strconcat(str, " " , (const gchar *) l->data, NULL); + g_free(str); + str = tmp; + } + + return str; +} + +GSList *bt_string2list(const gchar *str) +{ + GSList *l = NULL; + gchar **uuids; + int i = 0; + + if (!str) + return NULL; + + /* FIXME: eglib doesn't support g_strsplit */ + uuids = g_strsplit(str, " ", 0); + if (!uuids) + return NULL; + + while (uuids[i]) { + l = g_slist_append(l, uuids[i]); + i++; + } + + g_free(uuids); + + return l; +} + +static inline int resolve_names(int fd, struct sockaddr *host, + struct sockaddr *peer, socklen_t len) +{ + int err; + socklen_t namelen; + + namelen = len; + memset(host, 0, len); + err = getsockname(fd, host, &namelen); + if (err < 0) + return err; + + namelen = len; + memset(peer, 0, len); + err = getpeername(fd, peer, &namelen); + if (err < 0) + return err; + + return 0; +} + +static int rfcomm_resolver(int fd, char *src, char *dst) +{ + struct sockaddr_rc host, peer; + socklen_t len; + int err; + + len = sizeof(host); + err = resolve_names(fd, (struct sockaddr *) &host, + (struct sockaddr *) &peer, len); + if (err < 0) + return err; + + ba2str(&host.rc_bdaddr, src); + ba2str(&peer.rc_bdaddr, dst); + + return 0; +} + +static int l2cap_resolver(int fd, char *src, char *dst) +{ + struct sockaddr_l2 host, peer; + socklen_t len; + int err; + + len = sizeof(host); + err = resolve_names(fd, (struct sockaddr *) &host, + (struct sockaddr *) &peer, len); + if (err < 0) + return err; + + ba2str(&host.l2_bdaddr, src); + ba2str(&peer.l2_bdaddr, dst); + + return 0; +} + +static int sco_resolver(int fd, char *src, char *dst) +{ + struct sockaddr_sco host, peer; + socklen_t len; + int err; + + len = sizeof(host); + err = resolve_names(fd, (struct sockaddr *) &host, + (struct sockaddr *) &peer, len); + if (err < 0) + return err; + + ba2str(&host.sco_bdaddr, src); + ba2str(&peer.sco_bdaddr, dst); + + return 0; +} + +static gboolean listen_cb(GIOChannel *chan, GIOCondition cond, + gpointer user_data) +{ + BtIO *io = user_data; + struct io_context *io_ctxt = io->io_ctxt; + int fd, err = 0; + GIOChannel *gio; + struct sockaddr addr; + socklen_t len; + bdaddr_t src, dst; + + if (cond & G_IO_NVAL) + return FALSE; + + if (cond & (G_IO_HUP | G_IO_ERR)) { + g_io_channel_close(chan); + g_io_channel_unref(chan); + g_free(io_ctxt); + return FALSE; + } + + len = sizeof(addr); + memset(&addr, 0, len); + fd = accept(io_ctxt->fd, &addr, &len); + if (fd < 0) + goto drop; + + if (io_ctxt->resolver) { + err = io_ctxt->resolver(fd, io->src, io->dst); + if (err < 0) { + close(fd); + goto drop; + } + } + + gio = g_io_channel_unix_new(fd); + if (!gio) + err = -ENOMEM; + + if (io_ctxt->func) + io_ctxt->func(io, err, gio, io_ctxt->user_data); + if (io_ctxt->cb) { + str2ba(io->src, &src); + str2ba(io->dst, &dst); + io_ctxt->cb(gio, err, &src, &dst, io_ctxt->user_data); + } + + return TRUE; + +drop: + if (io_ctxt->func) + io_ctxt->func(io, -errno, NULL, io_ctxt->user_data); + if (io_ctxt->cb) + io_ctxt->cb(NULL, err, NULL, NULL, io_ctxt->user_data); + + return TRUE; +} + +static int transport_listen(BtIO *io) +{ + struct io_context *io_ctxt = io->io_ctxt; + int err; + + err = listen(io_ctxt->fd, 5); + if (err < 0) + return -errno; + + io_ctxt->io = g_io_channel_unix_new(io_ctxt->fd); + if (!io_ctxt->io) + return -ENOMEM; + + g_io_add_watch(io_ctxt->io, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, + (GIOFunc) listen_cb, io); + + return 0; +} + +static gboolean connect_cb(GIOChannel *gio, GIOCondition cond, + gpointer user_data) +{ + BtIO *io = user_data; + struct io_context *io_ctxt = io->io_ctxt; + int err = 0, ret; + socklen_t len; + bdaddr_t src, dst; + + if (cond & G_IO_NVAL) + return FALSE; + + len = sizeof(ret); + if (getsockopt(io_ctxt->fd, SOL_SOCKET, SO_ERROR, &ret, &len) < 0) { + err = -errno; + goto done; + } + + if (ret != 0) { + err = -ret; + goto done; + } + + if (io_ctxt->resolver) { + err = io_ctxt->resolver(io_ctxt->fd, io->src, io->dst); + if (err < 0) + goto done; + } + + io_ctxt->io = NULL; + +done: + if (io_ctxt->func) + io_ctxt->func(io, err, gio, io_ctxt->user_data); + if (io_ctxt->cb) { + str2ba(io->src, &src); + str2ba(io->dst, &dst); + io_ctxt->cb(gio, err, &src, &dst, io_ctxt->user_data); + } + if (io_ctxt->io) { + g_io_channel_close(io_ctxt->io); + g_io_channel_unref(io_ctxt->io); + } + g_free(io_ctxt); + + return FALSE; +} + +static int transport_connect(BtIO *io, struct sockaddr *addr, + socklen_t addrlen) +{ + struct io_context *io_ctxt = io->io_ctxt; + int err; + + io_ctxt->io = g_io_channel_unix_new(io_ctxt->fd); + if (!io_ctxt->io) + return -ENOMEM; + + err = g_io_channel_set_flags(io_ctxt->io, G_IO_FLAG_NONBLOCK, NULL); + if (err != G_IO_STATUS_NORMAL) + return -EPERM; + + err = connect(io_ctxt->fd, addr, addrlen); + if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS)) + return -errno; + + g_io_add_watch(io_ctxt->io, G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL, + (GIOFunc) connect_cb, io); + + return 0; +} + +static BtIOError sco_connect(BtIO *io, BtIOFunc func) +{ + struct io_context *io_ctxt = io->io_ctxt; + struct sockaddr_sco addr; + int sk, err; + + io_ctxt->func = func; + + sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO); + if (sk < 0) + return -errno; + + memset(&addr, 0, sizeof(addr)); + addr.sco_family = AF_BLUETOOTH; + str2ba(io->src, &addr.sco_bdaddr); + + err = bind(sk, (struct sockaddr *) &addr, sizeof(addr)); + if (err < 0) { + close(sk); + return BT_IO_FAILED; + } + + io_ctxt->fd = sk; + + memset(&addr, 0, sizeof(addr)); + addr.sco_family = AF_BLUETOOTH; + str2ba(io->dst, &addr.sco_bdaddr); + + err = transport_connect(io, (struct sockaddr *) &addr, + sizeof(addr)); + if (err < 0) { + close(sk); + return BT_IO_FAILED; + } + + return BT_IO_SUCCESS; +} + +static int l2cap_bind(struct io_context *io_ctxt, const char *address, + uint16_t psm, uint16_t mtu, uint32_t flags, + struct sockaddr_l2 *addr) +{ + int err; + struct l2cap_options l2o; + struct sockaddr_l2 l2a; + + io_ctxt->fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); + if (io_ctxt->fd < 0) + return -errno; + + if (mtu) { + socklen_t olen = sizeof(l2o); + memset(&l2o, 0, olen); + getsockopt(io_ctxt->fd, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &olen); + l2o.imtu = l2o.omtu = mtu; + setsockopt(io_ctxt->fd, SOL_L2CAP, L2CAP_OPTIONS, &l2o, sizeof(l2o)); + } + + if (flags) { + int opt = flags; + err = setsockopt(io_ctxt->fd, SOL_L2CAP, L2CAP_LM, &opt, + sizeof(opt)); + if (err < 0) { + close(io_ctxt->fd); + return -errno; + } + } + + memset(addr, 0, sizeof(*addr)); + addr->l2_family = AF_BLUETOOTH; + str2ba(address, &l2a.l2_bdaddr); + addr->l2_psm = htobs(psm); + + err = bind(io_ctxt->fd, (struct sockaddr *) addr, sizeof(*addr)); + if (err < 0) { + close(io_ctxt->fd); + return -errno; + } + + return 0; +} + +static BtIOError l2cap_listen(BtIO *io, BtIOFunc func) +{ + struct io_context *io_ctxt = io->io_ctxt; + struct sockaddr_l2 addr; + BtIOError err; + + io_ctxt->func = func; + + err = l2cap_bind(io_ctxt, io->src, io->psm, io->mtu, io->flags, &addr); + if (err < 0) + return err; + + err = transport_listen(io); + if (err < 0) { + close(io_ctxt->fd); + return err; + } + + return BT_IO_SUCCESS; +} + +static BtIOError l2cap_connect(BtIO *io, BtIOFunc func) +{ + struct io_context *io_ctxt = io->io_ctxt; + struct sockaddr_l2 l2a; + BtIOError err; + + io_ctxt->func = func; + + err = l2cap_bind(io_ctxt, io->src, 0, io->mtu, 0, &l2a); + if (err < 0) + return err; + + memset(&l2a, 0, sizeof(l2a)); + l2a.l2_family = AF_BLUETOOTH; + str2ba(io->dst, &l2a.l2_bdaddr); + l2a.l2_psm = htobs(io->psm); + + err = transport_connect(io, (struct sockaddr *) &l2a, + sizeof(l2a)); + if (err < 0) { + close(io_ctxt->fd); + return err; + } + + return BT_IO_SUCCESS; +} + +static BtIOError rfcomm_bind(struct io_context *io_ctxt, const char *address, + uint8_t channel, uint32_t flags, + struct sockaddr_rc *addr) +{ + BtIOError err; + + io_ctxt->fd = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); + if (io_ctxt->fd < 0) + return BT_IO_FAILED; + + if (flags) { + int opt = flags; + err = setsockopt(io_ctxt->fd, SOL_RFCOMM, RFCOMM_LM, &opt, + sizeof(opt)); + if (err < 0) { + close(io_ctxt->fd); + return BT_IO_FAILED; + } + } + + memset(addr, 0, sizeof(*addr)); + addr->rc_family = AF_BLUETOOTH; + str2ba(address, &addr->rc_bdaddr); + addr->rc_channel = channel; + + err = bind(io_ctxt->fd, (struct sockaddr *) addr, sizeof(*addr)); + if (err < 0) { + close(io_ctxt->fd); + return BT_IO_FAILED; + } + + return BT_IO_SUCCESS; +} + +static BtIOError rfcomm_listen(BtIO *io, BtIOFunc func) +{ + struct io_context *io_ctxt = io->io_ctxt; + struct sockaddr_rc addr; + socklen_t sa_len; + BtIOError err; + + io_ctxt->func = func; + + err = rfcomm_bind(io_ctxt, io->src, io->channel, io->flags, &addr); + if (err < 0) + return err; + + err = transport_listen(io); + if (err < 0) { + close(io_ctxt->fd); + return err; + } + + sa_len = sizeof(struct sockaddr_rc); + memset(&addr, 0, sizeof(addr)); + if (getsockname(io_ctxt->fd, (struct sockaddr *) &addr, &sa_len) < 0) { + err = -errno; + close(io_ctxt->fd); + return err; + } + + io->channel = addr.rc_channel; + + return BT_IO_SUCCESS; +} + +static BtIOError rfcomm_connect(BtIO *io, BtIOFunc func) +{ + struct io_context *io_ctxt = io->io_ctxt; + struct sockaddr_rc addr; + BtIOError err; + + io_ctxt->func = func; + + err = rfcomm_bind(io_ctxt, io->src, 0, 0, &addr); + if (err < 0) + return err; + + memset(&addr, 0, sizeof(addr)); + addr.rc_family = AF_BLUETOOTH; + str2ba(io->dst, &addr.rc_bdaddr); + addr.rc_channel = io->channel; + + err = transport_connect(io, (struct sockaddr *) &addr, + sizeof(addr)); + if (err < 0) { + close(io_ctxt->fd); + return err; + } + + return BT_IO_SUCCESS; +} + +static int create_io_context(struct io_context **io_ctxt, BtIOFunc func, + gpointer cb, gpointer resolver, gpointer user_data) +{ + *io_ctxt = g_try_malloc0(sizeof(struct search_context)); + if (!*io_ctxt) + return -ENOMEM; + + (*io_ctxt)->cb = cb; + (*io_ctxt)->func = func; + (*io_ctxt)->resolver = resolver; + (*io_ctxt)->user_data = user_data; + + return 0; +} + +static void io_context_cleanup(struct io_context *io_ctxt) +{ + if (io_ctxt->io) { + g_io_channel_close(io_ctxt->io); + g_io_channel_unref(io_ctxt->io); + } + g_free(io_ctxt); +} + +GIOChannel *rfcomm_listen_internal(const bdaddr_t *src, uint8_t *channel, + uint32_t flags, bt_io_callback_t cb, void *user_data) +{ + BtIO *io; + BtIOError err; + + io = bt_io_create(BT_IO_RFCOMM, user_data, NULL); + if (!io) + return NULL; + + ba2str(src, io->src); + io->channel = *channel; + io->flags = flags; + io->io_ctxt->cb = cb; + err = bt_io_listen(io, NULL, NULL); + if (err != BT_IO_SUCCESS) { + bt_io_unref(io); + return NULL; + } + + *channel = io->channel; + + return io->io_ctxt->io; +} + +GIOChannel *bt_rfcomm_listen_allocate(const bdaddr_t *src, uint8_t *channel, + uint32_t flags, bt_io_callback_t cb, void *user_data) +{ + if (!channel) + return NULL; + + *channel = 0; + + return rfcomm_listen_internal(src, channel, flags, cb, user_data); +} + +GIOChannel *bt_rfcomm_listen(const bdaddr_t *src, uint8_t channel, + uint32_t flags, bt_io_callback_t cb, void *user_data) +{ + if (channel < 1 || channel > 30) + return NULL; + + return rfcomm_listen_internal(src, &channel, flags, cb, user_data); + +} + +int bt_rfcomm_connect(const bdaddr_t *src, const bdaddr_t *dst, + uint8_t channel, bt_io_callback_t cb, void *user_data) +{ + BtIO *io; + BtIOError err; + + io = bt_io_create(BT_IO_RFCOMM, user_data, NULL); + if (!io) + return -1; + + ba2str(src, io->src); + ba2str(dst, io->dst); + io->channel = channel; + io->io_ctxt->cb = cb; + err = bt_io_connect(io, NULL, NULL); + if (err != BT_IO_SUCCESS) { + bt_io_unref(io); + return -1; + } + + return 0; +} + +GIOChannel *bt_l2cap_listen(const bdaddr_t *src, uint16_t psm, uint16_t mtu, + uint32_t flags, bt_io_callback_t cb, void *user_data) +{ + BtIO *io; + BtIOError err; + + io = bt_io_create(BT_IO_L2CAP, user_data, NULL); + if (!io) + return NULL; + + ba2str(src, io->src); + io->psm = psm; + io->mtu = mtu; + io->flags = flags; + io->io_ctxt->cb = cb; + err = bt_io_listen(io, NULL, NULL); + if (err != BT_IO_SUCCESS) { + bt_io_unref(io); + return NULL; + } + + return io->io_ctxt->io; +} + +int bt_l2cap_connect(const bdaddr_t *src, const bdaddr_t *dst, + uint16_t psm, uint16_t mtu, bt_io_callback_t cb, + void *user_data) +{ + BtIO *io; + BtIOError err; + + io = bt_io_create(BT_IO_L2CAP, user_data, NULL); + if (!io) + return -1; + + ba2str(src, io->src); + ba2str(dst, io->dst); + io->psm = psm; + io->mtu = mtu; + io->io_ctxt->cb = cb; + err = bt_io_connect(io, NULL, NULL); + if (err != BT_IO_SUCCESS) { + bt_io_unref(io); + return -1; + } + + return 0; +} + +int bt_sco_connect(const bdaddr_t *src, const bdaddr_t *dst, + bt_io_callback_t cb, void *user_data) +{ + BtIO *io; + BtIOError err; + + io = bt_io_create(BT_IO_SCO, user_data, NULL); + if (!io) + return -1; + + ba2str(src, io->src); + ba2str(dst, io->dst); + io->io_ctxt->cb = cb; + err = bt_io_connect(io, NULL, NULL); + if (err != BT_IO_SUCCESS) { + bt_io_unref(io); + return -1; + } + + return 0; +} + +/* Experiemental bt_io API */ + +BtIO *bt_io_create(BtIOTransport type, gpointer user_data, GDestroyNotify notify) +{ + BtIO *io; + int err; + + io = g_new0(BtIO, 1); + if (!io) + return NULL; + + io->refcount = 1; + + switch (type) { + case BT_IO_L2CAP: + err = create_io_context(&io->io_ctxt, NULL, NULL, + l2cap_resolver, user_data); + io->connect = l2cap_connect; + io->listen = l2cap_listen; + break; + case BT_IO_RFCOMM: + err = create_io_context(&io->io_ctxt, NULL, NULL, + rfcomm_resolver, user_data); + io->connect = rfcomm_connect; + io->listen = rfcomm_listen; + break; + case BT_IO_SCO: + err = create_io_context(&io->io_ctxt, NULL, NULL, + sco_resolver, user_data); + io->connect = sco_connect; + break; + default: + return NULL; + } + + if (err < 0) { + bt_io_unref(io); + return NULL; + } + + return io; +} + +BtIO *bt_io_ref(BtIO *io) +{ + io->refcount++; + + return io; +} + +void bt_io_unref(BtIO *io) +{ + io->refcount--; + + if (io->refcount) + return; + + io_context_cleanup(io->io_ctxt); + g_free(io); +} + +gboolean bt_io_set_source(BtIO *io, const char *address) +{ + if (strlen(address) != sizeof(io->src)) + return FALSE; + + memcpy(io->src, address, sizeof(io->src)); + + return TRUE; +} + +const char *bt_io_get_source(BtIO *io) +{ + return io->src; +} + +gboolean bt_io_set_destination(BtIO *io, const char *address) +{ + if (strlen(address) != sizeof(io->dst)) + return FALSE; + + memcpy(io->dst, address, sizeof(io->dst)); + + return TRUE; +} + +const char *bt_io_get_destination(BtIO *io) +{ + return io->dst; +} + +gboolean bt_io_set_flags(BtIO *io, guint32 flags) +{ + io->flags = flags; + + return TRUE; +} + +guint32 bt_io_get_flags(BtIO *io) +{ + return io->flags; +} + +gboolean bt_io_set_channel(BtIO *io, guint8 channel) +{ + if (io->type != BT_IO_RFCOMM) + return FALSE; + + io->channel = channel; + + return TRUE; +} + +guint8 bt_io_get_channel(BtIO *io) +{ + return io->channel; +} + +gboolean bt_io_set_psm(BtIO *io, guint16 psm) +{ + if (io->type != BT_IO_L2CAP) + return FALSE; + + io->psm = psm; + + return TRUE; +} + +guint16 bt_io_get_psm(BtIO *io) +{ + return io->psm; +} + +gboolean bt_io_set_mtu(BtIO *io, guint16 mtu) +{ + io->mtu = mtu; + + return TRUE; +} + +guint16 bt_io_get_mtu(BtIO *io) +{ + return io->mtu; +} + +BtIOError bt_io_connect(BtIO *io, const char *uuid, BtIOFunc func) +{ + if (!io->connect) + return BT_IO_FAILED; + + return io->connect(io, func); +} + +BtIOError bt_io_listen(BtIO *io, const char *uuid, BtIOFunc func) +{ + if (!io->listen) + return BT_IO_FAILED; + + return io->listen(io, func); +} + +BtIOError bt_io_shutdown(BtIO *io) +{ + io_context_cleanup(io->io_ctxt); + + return BT_IO_SUCCESS; +} diff --git a/common/glib-helper.h b/common/glib-helper.h new file mode 100644 index 00000000..6b188f7f --- /dev/null +++ b/common/glib-helper.h @@ -0,0 +1,96 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org> + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +int set_nonblocking(int fd); + +typedef void (*bt_io_callback_t) (GIOChannel *io, int err, const bdaddr_t *src, + const bdaddr_t *dst, gpointer user_data); +typedef void (*bt_callback_t) (sdp_list_t *recs, int err, gpointer user_data); +typedef void (*bt_destroy_t) (gpointer user_data); + +int bt_discover_services(const bdaddr_t *src, const bdaddr_t *dst, + bt_callback_t cb, void *user_data, bt_destroy_t destroy); + +int bt_search_service(const bdaddr_t *src, const bdaddr_t *dst, + uuid_t *uuid, bt_callback_t cb, void *user_data, + bt_destroy_t destroy); +int bt_cancel_discovery(const bdaddr_t *src, const bdaddr_t *dst); + +gchar *bt_uuid2string(uuid_t *uuid); +uint16_t bt_string2class(const char *string); +int bt_string2uuid(uuid_t *uuid, const char *string); +gchar *bt_list2string(GSList *list); +GSList *bt_string2list(const gchar *str); + +GIOChannel *bt_rfcomm_listen(const bdaddr_t *src, uint8_t channel, + uint32_t flags, bt_io_callback_t cb, void *user_data); +GIOChannel *bt_rfcomm_listen_allocate(const bdaddr_t *src, uint8_t *channel, + uint32_t flags, bt_io_callback_t cb, void *user_data); +int bt_rfcomm_connect(const bdaddr_t *src, const bdaddr_t *dst, + uint8_t channel, bt_io_callback_t cb, void *user_data); + +GIOChannel *bt_l2cap_listen(const bdaddr_t *src, uint16_t psm, uint16_t mtu, + uint32_t flags, bt_io_callback_t cb, void *user_data); +int bt_l2cap_connect(const bdaddr_t *src, const bdaddr_t *dst, + uint16_t psm, uint16_t mtu, bt_io_callback_t cb, + void *user_data); +int bt_sco_connect(const bdaddr_t *src, const bdaddr_t *dst, + bt_io_callback_t cb, void *user_data); + +/* Experiemental bt_io API */ + +typedef struct bt_io BtIO; + +typedef enum { + BT_IO_AUTO, + BT_IO_L2CAP, + BT_IO_RFCOMM, + BT_IO_SCO, +} BtIOTransport; + +typedef enum { + BT_IO_SUCCESS, + BT_IO_FAILED, +} BtIOError; + +typedef void (*BtIOFunc) (BtIO *io, BtIOError err, GIOChannel *chan, + gpointer user_data); + +BtIO *bt_io_create(BtIOTransport type, gpointer user_data, GDestroyNotify notify); +BtIO *bt_io_ref(BtIO *io); +void bt_io_unref(BtIO *io); +gboolean bt_io_set_source(BtIO *io, const char *address); +const char *bt_io_get_source(BtIO *io); +gboolean bt_io_set_destination(BtIO *io, const char *address); +const char *bt_io_get_destination(BtIO *io); +gboolean bt_io_set_flags(BtIO *io, guint32 flags); +guint32 bt_io_get_flags(BtIO *io); +gboolean bt_io_set_channel(BtIO *io, guint8 channel); +guint8 bt_io_get_channel(BtIO *io); +gboolean bt_io_set_psm(BtIO *io, guint16 psm); +guint16 bt_io_get_psm(BtIO *io); +gboolean bt_io_set_mtu(BtIO *io, guint16 mtu); +guint16 bt_io_get_mtu(BtIO *io); +BtIOError bt_io_connect(BtIO *io, const char *uuid, BtIOFunc func); +BtIOError bt_io_listen(BtIO *io, const char *uuid, BtIOFunc func); +BtIOError bt_io_shutdown(BtIO *io); diff --git a/common/logging.c b/common/logging.c new file mode 100644 index 00000000..28fc9af5 --- /dev/null +++ b/common/logging.c @@ -0,0 +1,106 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org> + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdarg.h> +#include <syslog.h> + +static volatile int debug_enabled = 0; + +static inline void vinfo(const char *format, va_list ap) +{ + vsyslog(LOG_INFO, format, ap); +} + +void info(const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + + vinfo(format, ap); + + va_end(ap); +} + +void error(const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + + vsyslog(LOG_ERR, format, ap); + + va_end(ap); +} + +void debug(const char *format, ...) +{ + va_list ap; + + if (!debug_enabled) + return; + + va_start(ap, format); + + vsyslog(LOG_DEBUG, format, ap); + + va_end(ap); +} + +void toggle_debug(void) +{ + debug_enabled = (debug_enabled + 1) % 2; +} + +void enable_debug(void) +{ + debug_enabled = 1; +} + +void disable_debug(void) +{ + debug_enabled = 0; +} + +void start_logging(const char *ident, const char *message, ...) +{ + va_list ap; + + openlog(ident, LOG_PID | LOG_NDELAY | LOG_PERROR, LOG_DAEMON); + + va_start(ap, message); + + vinfo(message, ap); + + va_end(ap); +} + +void stop_logging(void) +{ + closelog(); +} diff --git a/common/logging.h b/common/logging.h new file mode 100644 index 00000000..822eb15a --- /dev/null +++ b/common/logging.h @@ -0,0 +1,38 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org> + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __LOGGING_H +#define __LOGGING_H + +void info(const char *format, ...); +void error(const char *format, ...); +void debug(const char *format, ...); +void toggle_debug(void); +void enable_debug(void); +void disable_debug(void); +void start_logging(const char *ident, const char *message, ...); +void stop_logging(void); + +#define DBG(fmt, arg...) debug("%s: " fmt "\n" , __FUNCTION__ , ## arg) + +#endif /* __LOGGING_H */ diff --git a/common/oui.c b/common/oui.c new file mode 100644 index 00000000..0915afdf --- /dev/null +++ b/common/oui.c @@ -0,0 +1,109 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org> + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/mman.h> + +#include "oui.h" + +/* http://standards.ieee.org/regauth/oui/oui.txt */ + +#define OUIFILE "/var/lib/misc/oui.txt" + +char *ouitocomp(const char *oui) +{ + struct stat st; + char *str, *map, *off, *end; + int fd; + + fd = open("oui.txt", O_RDONLY); + if (fd < 0) { + fd = open(OUIFILE, O_RDONLY); + if (fd < 0) { + fd = open("/usr/share/misc/oui.txt", O_RDONLY); + if (fd < 0) + return NULL; + } + } + + if (fstat(fd, &st) < 0) { + close(fd); + return NULL; + } + + str = malloc(128); + if (!str) { + close(fd); + return NULL; + } + + memset(str, 0, 128); + + map = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0); + if (!map || map == MAP_FAILED) { + free(str); + close(fd); + return NULL; + } + + off = strstr(map, oui); + if (off) { + off += 18; + end = strpbrk(off, "\r\n"); + strncpy(str, off, end - off); + } else { + free(str); + str = NULL; + } + + munmap(map, st.st_size); + + close(fd); + + return str; +} + +int oui2comp(const char *oui, char *comp, size_t size) +{ + char *tmp; + + tmp = ouitocomp(oui); + if (!tmp) + return -1; + + snprintf(comp, size, "%s", tmp); + + free(tmp); + + return 0; +} diff --git a/common/oui.h b/common/oui.h new file mode 100644 index 00000000..991e145c --- /dev/null +++ b/common/oui.h @@ -0,0 +1,25 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org> + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +char *ouitocomp(const char *oui); +int oui2comp(const char *oui, char *comp, size_t size); diff --git a/common/ppoll.h b/common/ppoll.h new file mode 100644 index 00000000..7d09d44d --- /dev/null +++ b/common/ppoll.h @@ -0,0 +1,16 @@ +#ifdef ppoll +#undef ppoll +#endif + +#define ppoll compat_ppoll + +static inline int compat_ppoll(struct pollfd *fds, nfds_t nfds, + const struct timespec *timeout, const sigset_t *sigmask) +{ + if (timeout == NULL) + return poll(fds, nfds, -1); + else if (timeout->tv_sec == 0) + return poll(fds, nfds, 500); + else + return poll(fds, nfds, timeout->tv_sec * 1000); +} diff --git a/common/sdp-glib.c b/common/sdp-glib.c new file mode 100644 index 00000000..bd27d822 --- /dev/null +++ b/common/sdp-glib.c @@ -0,0 +1,239 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org> + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdlib.h> +#include <limits.h> + +#include <bluetooth/sdp.h> +#include <bluetooth/sdp_lib.h> + +#include <glib.h> + +#include "logging.h" +#include "sdp-xml.h" + +static int compute_seq_size(sdp_data_t *data) +{ + int unit_size = data->unitSize; + sdp_data_t *seq = data->val.dataseq; + + for (; seq; seq = seq->next) + unit_size += seq->unitSize; + + return unit_size; +} + +struct context_data { + sdp_record_t *record; + sdp_data_t attr_data; + struct sdp_xml_data *stack_head; + uint16_t attr_id; +}; + +static void element_start(GMarkupParseContext *context, + const gchar *element_name, const gchar **attribute_names, + const gchar **attribute_values, gpointer user_data, GError **err) +{ + struct context_data *ctx_data = user_data; + + if (!strcmp(element_name, "record")) + return; + + if (!strcmp(element_name, "attribute")) { + int i; + for (i = 0; attribute_names[i]; i++) { + if (!strcmp(attribute_names[i], "id")) { + ctx_data->attr_id = strtol(attribute_values[i], 0, 0); + break; + } + } + debug("New attribute 0x%04x", ctx_data->attr_id); + return; + } + + if (ctx_data->stack_head) { + struct sdp_xml_data *newelem = sdp_xml_data_alloc(); + newelem->next = ctx_data->stack_head; + ctx_data->stack_head = newelem; + } else { + ctx_data->stack_head = sdp_xml_data_alloc(); + ctx_data->stack_head->next = NULL; + } + + if (!strcmp(element_name, "sequence")) + ctx_data->stack_head->data = sdp_data_alloc(SDP_SEQ8, NULL); + else if (!strcmp(element_name, "alternate")) + ctx_data->stack_head->data = sdp_data_alloc(SDP_ALT8, NULL); + else { + int i; + /* Parse value, name, encoding */ + for (i = 0; attribute_names[i]; i++) { + if (!strcmp(attribute_names[i], "value")) { + int curlen = strlen(ctx_data->stack_head->text); + int attrlen = strlen(attribute_values[i]); + + /* Ensure we're big enough */ + while ((curlen + 1 + attrlen) > ctx_data->stack_head->size) { + sdp_xml_data_expand(ctx_data->stack_head); + } + + memcpy(ctx_data->stack_head->text + curlen, + attribute_values[i], attrlen); + ctx_data->stack_head->text[curlen + attrlen] = '\0'; + } + + if (!strcmp(attribute_names[i], "encoding")) { + if (!strcmp(attribute_values[i], "hex")) + ctx_data->stack_head->type = 1; + } + + if (!strcmp(attribute_names[i], "name")) { + ctx_data->stack_head->name = strdup(attribute_values[i]); + } + } + + ctx_data->stack_head->data = sdp_xml_parse_datatype(element_name, + ctx_data->stack_head, ctx_data->record); + + if (ctx_data->stack_head->data == NULL) + error("Can't parse element %s", element_name); + } +} + +static void element_end(GMarkupParseContext *context, + const gchar *element_name, gpointer user_data, GError **err) +{ + struct context_data *ctx_data = user_data; + struct sdp_xml_data *elem; + + if (!strcmp(element_name, "record")) + return; + + if (!strcmp(element_name, "attribute")) { + if (ctx_data->stack_head && ctx_data->stack_head->data) { + int ret = sdp_attr_add(ctx_data->record, ctx_data->attr_id, + ctx_data->stack_head->data); + if (ret == -1) + debug("Trouble adding attribute\n"); + + ctx_data->stack_head->data = NULL; + sdp_xml_data_free(ctx_data->stack_head); + ctx_data->stack_head = NULL; + } else { + debug("No data for attribute 0x%04x\n", ctx_data->attr_id); + } + return; + } + + if (!strcmp(element_name, "sequence")) { + ctx_data->stack_head->data->unitSize = compute_seq_size(ctx_data->stack_head->data); + + if (ctx_data->stack_head->data->unitSize > USHRT_MAX) { + ctx_data->stack_head->data->unitSize += sizeof(uint32_t); + ctx_data->stack_head->data->dtd = SDP_SEQ32; + } else if (ctx_data->stack_head->data->unitSize > UCHAR_MAX) { + ctx_data->stack_head->data->unitSize += sizeof(uint16_t); + ctx_data->stack_head->data->dtd = SDP_SEQ16; + } else { + ctx_data->stack_head->data->unitSize += sizeof(uint8_t); + } + } else if (!strcmp(element_name, "alternate")) { + ctx_data->stack_head->data->unitSize = compute_seq_size(ctx_data->stack_head->data); + + if (ctx_data->stack_head->data->unitSize > USHRT_MAX) { + ctx_data->stack_head->data->unitSize += sizeof(uint32_t); + ctx_data->stack_head->data->dtd = SDP_ALT32; + } else if (ctx_data->stack_head->data->unitSize > UCHAR_MAX) { + ctx_data->stack_head->data->unitSize += sizeof(uint16_t); + ctx_data->stack_head->data->dtd = SDP_ALT16; + } else { + ctx_data->stack_head->data->unitSize += sizeof(uint8_t); + } + } + + if (ctx_data->stack_head->next && ctx_data->stack_head->data && + ctx_data->stack_head->next->data) { + switch (ctx_data->stack_head->next->data->dtd) { + case SDP_SEQ8: + case SDP_SEQ16: + case SDP_SEQ32: + case SDP_ALT8: + case SDP_ALT16: + case SDP_ALT32: + ctx_data->stack_head->next->data->val.dataseq = + sdp_seq_append(ctx_data->stack_head->next->data->val.dataseq, + ctx_data->stack_head->data); + ctx_data->stack_head->data = NULL; + break; + } + + elem = ctx_data->stack_head; + ctx_data->stack_head = ctx_data->stack_head->next; + + sdp_xml_data_free(elem); + } +} + +static GMarkupParser parser = { + element_start, element_end, NULL, NULL, NULL +}; + +sdp_record_t *sdp_xml_parse_record(const char *data, int size) +{ + GMarkupParseContext *ctx; + struct context_data *ctx_data; + sdp_record_t *record; + + ctx_data = malloc(sizeof(*ctx_data)); + if (!ctx_data) + return NULL; + + record = sdp_record_alloc(); + if (!record) { + free(ctx_data); + return NULL; + } + + memset(ctx_data, 0, sizeof(*ctx_data)); + ctx_data->record = record; + + ctx = g_markup_parse_context_new(&parser, 0, ctx_data, NULL); + + if (g_markup_parse_context_parse(ctx, data, size, NULL) == FALSE) { + error("XML parsing error"); + g_markup_parse_context_free(ctx); + sdp_record_free(record); + free(ctx_data); + return NULL; + } + + g_markup_parse_context_free(ctx); + + free(ctx_data); + + return record; +} diff --git a/common/sdp-xml.c b/common/sdp-xml.c new file mode 100644 index 00000000..1e1e07c3 --- /dev/null +++ b/common/sdp-xml.c @@ -0,0 +1,793 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2005-2008 Marcel Holtmann <marcel@holtmann.org> + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <errno.h> +#include <ctype.h> +#include <string.h> +#include <limits.h> +#include <stdlib.h> + +#include <bluetooth/sdp.h> +#include <bluetooth/sdp_lib.h> + +#include "logging.h" +#include "sdp-xml.h" + +#define STRBUFSIZE 1024 +#define MAXINDENT 64 + +static void convert_raw_data_to_xml(sdp_data_t *value, int indent_level, + void *data, void (*appender)(void *, const char *)) +{ + int i, hex; + char buf[STRBUFSIZE]; + char indent[MAXINDENT]; + char next_indent[MAXINDENT]; + + if (!value) + return; + + if (indent_level >= MAXINDENT) + indent_level = MAXINDENT - 2; + + for (i = 0; i < indent_level; i++) { + indent[i] = '\t'; + next_indent[i] = '\t'; + } + + indent[i] = '\0'; + next_indent[i] = '\t'; + next_indent[i + 1] = '\0'; + + buf[STRBUFSIZE - 1] = '\0'; + + switch (value->dtd) { + case SDP_DATA_NIL: + appender(data, indent); + appender(data, "<nil/>\n"); + break; + + case SDP_BOOL: + appender(data, indent); + appender(data, "<boolean value=\""); + appender(data, value->val.uint8 ? "true" : "false"); + appender(data, "\" />\n"); + break; + + case SDP_UINT8: + appender(data, indent); + appender(data, "<uint8 value=\""); + snprintf(buf, STRBUFSIZE - 1, "0x%02x", value->val.uint8); + appender(data, buf); + appender(data, "\" />\n"); + break; + + case SDP_UINT16: + appender(data, indent); + appender(data, "<uint16 value=\""); + snprintf(buf, STRBUFSIZE - 1, "0x%04x", value->val.uint16); + appender(data, buf); + appender(data, "\" />\n"); + break; + + case SDP_UINT32: + appender(data, indent); + appender(data, "<uint32 value=\""); + snprintf(buf, STRBUFSIZE - 1, "0x%08x", value->val.uint32); + appender(data, buf); + appender(data, "\" />\n"); + break; + + case SDP_UINT64: + appender(data, indent); + appender(data, "<uint64 value=\""); + snprintf(buf, STRBUFSIZE - 1, "0x%016jx", value->val.uint64); + appender(data, buf); + appender(data, "\" />\n"); + break; + + case SDP_UINT128: + appender(data, indent); + appender(data, "<uint128 value=\""); + + for (i = 0; i < 16; i++) { + sprintf(&buf[i * 2], "%02x", + (unsigned char) value->val.uint128.data[i]); + } + + appender(data, buf); + appender(data, "\" />\n"); + break; + + case SDP_INT8: + appender(data, indent); + appender(data, "<int8 value=\""); + snprintf(buf, STRBUFSIZE - 1, "%d", value->val.int8); + appender(data, buf); + appender(data, "\" />\n"); + break; + + case SDP_INT16: + appender(data, indent); + appender(data, "<int16 value=\""); + snprintf(buf, STRBUFSIZE - 1, "%d", value->val.int16); + appender(data, buf); + appender(data, "\" />\n"); + break; + + case SDP_INT32: + appender(data, indent); + appender(data, "<int32 value=\""); + snprintf(buf, STRBUFSIZE - 1, "%d", value->val.int32); + appender(data, buf); + appender(data, "\" />\n"); + break; + + case SDP_INT64: + appender(data, indent); + appender(data, "<int64 value=\""); + snprintf(buf, STRBUFSIZE - 1, "%jd", value->val.int64); + appender(data, buf); + appender(data, "\" />\n"); + break; + + case SDP_INT128: + appender(data, indent); + appender(data, "<int128 value=\""); + + for (i = 0; i < 16; i++) { + sprintf(&buf[i * 2], "%02x", + (unsigned char) value->val.int128.data[i]); + } + appender(data, buf); + + appender(data, "\" />\n"); + break; + + case SDP_UUID16: + appender(data, indent); + appender(data, "<uuid value=\""); + snprintf(buf, STRBUFSIZE - 1, "0x%04x", value->val.uuid.value.uuid16); + appender(data, buf); + appender(data, "\" />\n"); + break; + + case SDP_UUID32: + appender(data, indent); + appender(data, "<uuid value=\""); + snprintf(buf, STRBUFSIZE - 1, "0x%08x", value->val.uuid.value.uuid32); + appender(data, buf); + appender(data, "\" />\n"); + break; + + case SDP_UUID128: + appender(data, indent); + appender(data, "<uuid value=\""); + + snprintf(buf, STRBUFSIZE - 1, + "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", + (unsigned char) value->val.uuid.value. + uuid128.data[0], + (unsigned char) value->val.uuid.value. + uuid128.data[1], + (unsigned char) value->val.uuid.value. + uuid128.data[2], + (unsigned char) value->val.uuid.value. + uuid128.data[3], + (unsigned char) value->val.uuid.value. + uuid128.data[4], + (unsigned char) value->val.uuid.value. + uuid128.data[5], + (unsigned char) value->val.uuid.value. + uuid128.data[6], + (unsigned char) value->val.uuid.value. + uuid128.data[7], + (unsigned char) value->val.uuid.value. + uuid128.data[8], + (unsigned char) value->val.uuid.value. + uuid128.data[9], + (unsigned char) value->val.uuid.value. + uuid128.data[10], + (unsigned char) value->val.uuid.value. + uuid128.data[11], + (unsigned char) value->val.uuid.value. + uuid128.data[12], + (unsigned char) value->val.uuid.value. + uuid128.data[13], + (unsigned char) value->val.uuid.value. + uuid128.data[14], + (unsigned char) value->val.uuid.value. + uuid128.data[15]); + + appender(data, buf); + appender(data, "\" />\n"); + break; + + case SDP_TEXT_STR8: + case SDP_TEXT_STR16: + case SDP_TEXT_STR32: + { + hex = 0; + + int num_chars_to_escape = 0; + + for (i = 0; i < value->unitSize; i++) { + if (i == (value->unitSize - 1) + && value->val.str[i] == '\0') + break; + if (!isprint(value->val.str[i])) { + hex = 1; + break; + } + + /* XML is evil, must do this... */ + if ((value->val.str[i] == '<') || + (value->val.str[i] == '>') || + (value->val.str[i] == '"') || + (value->val.str[i] == '&')) + num_chars_to_escape++; + } + + appender(data, indent); + + appender(data, "<text "); + + char *strBuf = 0; + + if (hex) { + appender(data, "encoding=\"hex\" "); + strBuf = (char *) malloc(sizeof(char) + * ((value->unitSize-1) * 2 + 1)); + + /* Unit Size seems to include the size for dtd + It is thus off by 1 + This is safe for Normal strings, but not + hex encoded data */ + for (i = 0; i < (value->unitSize-1); i++) + sprintf(&strBuf[i*sizeof(char)*2], + "%02x", + (unsigned char) value->val.str[i]); + + strBuf[(value->unitSize-1) * 2] = '\0'; + } + else { + int j; + /* escape the XML disallowed chars */ + strBuf = (char *) + malloc(sizeof(char) * + (value->unitSize + 1 + num_chars_to_escape * 4)); + for (i = 0, j = 0; i < value->unitSize; i++) { + if (value->val.str[i] == '&') { + strBuf[j++] = '&'; + strBuf[j++] = 'a'; + strBuf[j++] = 'm'; + strBuf[j++] = 'p'; + } + else if (value->val.str[i] == '<') { + strBuf[j++] = '&'; + strBuf[j++] = 'l'; + strBuf[j++] = 't'; + } + else if (value->val.str[i] == '>') { + strBuf[j++] = '&'; + strBuf[j++] = 'g'; + strBuf[j++] = 't'; + } + else if (value->val.str[i] == '"') { + strBuf[j++] = '&'; + strBuf[j++] = 'q'; + strBuf[j++] = 'u'; + strBuf[j++] = 'o'; + strBuf[j++] = 't'; + } + else { + strBuf[j++] = value->val.str[i]; + } + } + + strBuf[j] = '\0'; + } + + appender(data, "value=\""); + appender(data, strBuf); + appender(data, "\" />\n"); + free(strBuf); + break; + } + + case SDP_URL_STR8: + case SDP_URL_STR16: + case SDP_URL_STR32: + appender(data, indent); + appender(data, "<url value=\""); + appender(data, value->val.str); + appender(data, "\" />\n"); + break; + + case SDP_SEQ8: + case SDP_SEQ16: + case SDP_SEQ32: + appender(data, indent); + appender(data, "<sequence>\n"); + + convert_raw_data_to_xml(value->val.dataseq, + indent_level + 1, data, appender); + + appender(data, indent); + appender(data, "</sequence>\n"); + + break; + + case SDP_ALT8: + case SDP_ALT16: + case SDP_ALT32: + appender(data, indent); + + appender(data, "<alternate>\n"); + + convert_raw_data_to_xml(value->val.dataseq, + indent_level + 1, data, appender); + appender(data, indent); + + appender(data, "</alternate>\n"); + + break; + } + + convert_raw_data_to_xml(value->next, indent_level, data, appender); +} + +struct conversion_data { + void *data; + void (*appender)(void *data, const char *); +}; + +static void convert_raw_attr_to_xml_func(void *val, void *data) +{ + struct conversion_data *cd = (struct conversion_data *) data; + sdp_data_t *value = (sdp_data_t *) val; + char buf[STRBUFSIZE]; + + buf[STRBUFSIZE - 1] = '\0'; + snprintf(buf, STRBUFSIZE - 1, "\t<attribute id=\"0x%04x\">\n", + value->attrId); + cd->appender(cd->data, buf); + + if (data) + convert_raw_data_to_xml(value, 2, cd->data, cd->appender); + else + cd->appender(cd->data, "\t\tNULL\n"); + + cd->appender(cd->data, "\t</attribute>\n"); +} + +/* + * Will convert the sdp record to XML. The appender and data can be used + * to control where to output the record (e.g. file or a data buffer). The + * appender will be called repeatedly with data and the character buffer + * (containing parts of the generated XML) to append. + */ +void convert_sdp_record_to_xml(sdp_record_t *rec, + void *data, void (*appender)(void *, const char *)) +{ + struct conversion_data cd; + + cd.data = data; + cd.appender = appender; + + if (rec && rec->attrlist) { + appender(data, "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n\n"); + appender(data, "<record>\n"); + sdp_list_foreach(rec->attrlist, + convert_raw_attr_to_xml_func, &cd); + appender(data, "</record>\n"); + } +} + +static sdp_data_t *sdp_xml_parse_uuid128(const char *data) +{ + uint128_t val; + int i; + int j; + + char buf[3]; + + memset(&val, 0, sizeof(val)); + + buf[2] = '\0'; + + for (j = 0, i = 0; i < strlen(data);) { + if (data[i] == '-') { + i++; + continue; + } + + buf[0] = data[i]; + buf[1] = data[i + 1]; + + val.data[j++] = strtoul(buf, 0, 16); + i += 2; + } + + return sdp_data_alloc(SDP_UUID128, &val); +} + +sdp_data_t *sdp_xml_parse_uuid(const char *data, sdp_record_t *record) +{ + sdp_data_t *ret; + char *endptr; + uint32_t val; + uint16_t val2; + int len; + + len = strlen(data); + + if (len == 36) { + ret = sdp_xml_parse_uuid128(data); + goto result; + } + + val = strtoll(data, &endptr, 16); + + /* Couldn't parse */ + if (*endptr != '\0') + return NULL; + + if (val > USHRT_MAX) { + ret = sdp_data_alloc(SDP_UUID32, &val); + goto result; + } + + val2 = val; + + ret = sdp_data_alloc(SDP_UUID16, &val2); + +result: + if (record && ret) + sdp_pattern_add_uuid(record, &ret->val.uuid); + + return ret; +} + +sdp_data_t *sdp_xml_parse_int(const char * data, uint8_t dtd) +{ + char *endptr; + sdp_data_t *ret = NULL; + + switch (dtd) { + case SDP_BOOL: + { + uint8_t val = 0; + + if (!strcmp("true", data)) { + val = 1; + } + + else if (!strcmp("false", data)) { + val = 0; + } + else { + return NULL; + } + + ret = sdp_data_alloc(dtd, &val); + break; + } + + case SDP_INT8: + { + int8_t val = strtoul(data, &endptr, 0); + + /* Failed to parse */ + if ((endptr != data) && (*endptr != '\0')) + return NULL; + + ret = sdp_data_alloc(dtd, &val); + break; + } + + case SDP_UINT8: + { + uint8_t val = strtoul(data, &endptr, 0); + + /* Failed to parse */ + if ((endptr != data) && (*endptr != '\0')) + return NULL; + + ret = sdp_data_alloc(dtd, &val); + break; + } + + case SDP_INT16: + { + int16_t val = strtoul(data, &endptr, 0); + + /* Failed to parse */ + if ((endptr != data) && (*endptr != '\0')) + return NULL; + + ret = sdp_data_alloc(dtd, &val); + break; + } + + case SDP_UINT16: + { + uint16_t val = strtoul(data, &endptr, 0); + + /* Failed to parse */ + if ((endptr != data) && (*endptr != '\0')) + return NULL; + + ret = sdp_data_alloc(dtd, &val); + break; + } + + case SDP_INT32: + { + int32_t val = strtoul(data, &endptr, 0); + + /* Failed to parse */ + if ((endptr != data) && (*endptr != '\0')) + return NULL; + + ret = sdp_data_alloc(dtd, &val); + break; + } + + case SDP_UINT32: + { + uint32_t val = strtoul(data, &endptr, 0); + + /* Failed to parse */ + if ((endptr != data) && (*endptr != '\0')) + return NULL; + + ret = sdp_data_alloc(dtd, &val); + break; + } + + case SDP_INT64: + { + int64_t val = strtoull(data, &endptr, 0); + + /* Failed to parse */ + if ((endptr != data) && (*endptr != '\0')) + return NULL; + + ret = sdp_data_alloc(dtd, &val); + break; + } + + case SDP_UINT64: + { + uint64_t val = strtoull(data, &endptr, 0); + + /* Failed to parse */ + if ((endptr != data) && (*endptr != '\0')) + return NULL; + + ret = sdp_data_alloc(dtd, &val); + break; + } + + case SDP_INT128: + case SDP_UINT128: + { + uint128_t val; + int i = 0; + char buf[3]; + + buf[2] = '\0'; + + for (; i < 32; i += 2) { + buf[0] = data[i]; + buf[1] = data[i + 1]; + + val.data[i >> 1] = strtoul(buf, 0, 16); + } + + ret = sdp_data_alloc(dtd, &val); + break; + } + + }; + + return ret; +} + +static char *sdp_xml_parse_string_decode(const char *data, char encoding, uint32_t *length) +{ + int len = strlen(data); + char *text; + + if (encoding == SDP_XML_ENCODING_NORMAL) { + text = strdup(data); + *length = len; + } else { + char buf[3], *decoded; + int i; + + decoded = malloc((len >> 1) + 1); + + /* Ensure the string is a power of 2 */ + len = (len >> 1) << 1; + + buf[2] = '\0'; + + for (i = 0; i < len; i += 2) { + buf[0] = data[i]; + buf[1] = data[i + 1]; + + decoded[i >> 1] = strtoul(buf, 0, 16); + } + + decoded[len >> 1] = '\0'; + text = decoded; + *length = len >> 1; + } + + return text; +} + +sdp_data_t *sdp_xml_parse_url(const char *data) +{ + uint8_t dtd = SDP_URL_STR8; + char *url; + uint32_t length; + sdp_data_t *ret; + + url = sdp_xml_parse_string_decode(data, + SDP_XML_ENCODING_NORMAL, &length); + + if (length > UCHAR_MAX) + dtd = SDP_URL_STR16; + + ret = sdp_data_alloc_with_length(dtd, url, length); + + debug("URL size %d length %d: -->%s<--", ret->unitSize, length, url); + + free(url); + + return ret; +} + +sdp_data_t *sdp_xml_parse_text(const char *data, char encoding) +{ + uint8_t dtd = SDP_TEXT_STR8; + char *text; + uint32_t length; + sdp_data_t *ret; + + text = sdp_xml_parse_string_decode(data, encoding, &length); + + if (length > UCHAR_MAX) + dtd = SDP_TEXT_STR16; + + ret = sdp_data_alloc_with_length(dtd, text, length); + + debug("Text size %d length %d: -->%s<--", ret->unitSize, length, text); + + free(text); + + return ret; +} + +sdp_data_t *sdp_xml_parse_nil(const char *data) +{ + return sdp_data_alloc(SDP_DATA_NIL, 0); +} + +#define DEFAULT_XML_DATA_SIZE 1024 + +struct sdp_xml_data *sdp_xml_data_alloc() +{ + struct sdp_xml_data *elem; + + elem = malloc(sizeof(struct sdp_xml_data)); + if (!elem) + return NULL; + + memset(elem, 0, sizeof(struct sdp_xml_data)); + + /* Null terminate the text */ + elem->size = DEFAULT_XML_DATA_SIZE; + elem->text = malloc(DEFAULT_XML_DATA_SIZE); + elem->text[0] = '\0'; + + return elem; +} + +void sdp_xml_data_free(struct sdp_xml_data *elem) +{ + if (elem->data) + sdp_data_free(elem->data); + + if (elem->name) + free(elem->name); + + if (elem->text) + + free(elem->text); + free(elem); +} + +struct sdp_xml_data *sdp_xml_data_expand(struct sdp_xml_data *elem) +{ + char *newbuf; + + newbuf = malloc(elem->size * 2); + if (!newbuf) + return NULL; + + memcpy(newbuf, elem->text, elem->size); + elem->size *= 2; + free(elem->text); + + elem->text = newbuf; + + return elem; +} + +sdp_data_t *sdp_xml_parse_datatype(const char *el, struct sdp_xml_data *elem, + sdp_record_t *record) +{ + const char *data = elem->text; + + if (!strcmp(el, "boolean")) + return sdp_xml_parse_int(data, SDP_BOOL); + else if (!strcmp(el, "uint8")) + return sdp_xml_parse_int(data, SDP_UINT8); + else if (!strcmp(el, "uint16")) + return sdp_xml_parse_int(data, SDP_UINT16); + else if (!strcmp(el, "uint32")) + return sdp_xml_parse_int(data, SDP_UINT32); + else if (!strcmp(el, "uint64")) + return sdp_xml_parse_int(data, SDP_UINT64); + else if (!strcmp(el, "uint128")) + return sdp_xml_parse_int(data, SDP_UINT128); + else if (!strcmp(el, "int8")) + return sdp_xml_parse_int(data, SDP_INT8); + else if (!strcmp(el, "int16")) + return sdp_xml_parse_int(data, SDP_INT16); + else if (!strcmp(el, "int32")) + return sdp_xml_parse_int(data, SDP_INT32); + else if (!strcmp(el, "int64")) + return sdp_xml_parse_int(data, SDP_INT64); + else if (!strcmp(el, "int128")) + return sdp_xml_parse_int(data, SDP_INT128); + else if (!strcmp(el, "uuid")) + return sdp_xml_parse_uuid(data, record); + else if (!strcmp(el, "url")) + return sdp_xml_parse_url(data); + else if (!strcmp(el, "text")) + return sdp_xml_parse_text(data, elem->type); + else if (!strcmp(el, "nil")) + return sdp_xml_parse_nil(data); + + return NULL; +} diff --git a/common/sdp-xml.h b/common/sdp-xml.h new file mode 100644 index 00000000..adb173e2 --- /dev/null +++ b/common/sdp-xml.h @@ -0,0 +1,61 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2005-2008 Marcel Holtmann <marcel@holtmann.org> + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#ifndef __SDP_XML_H +#define __SDP_XML_H + +#include <bluetooth/sdp.h> + +#define SDP_XML_ENCODING_NORMAL 0 +#define SDP_XML_ENCODING_HEX 1 + +void convert_sdp_record_to_xml(sdp_record_t *rec, + void *user_data, void (*append_func) (void *, const char *)); + +sdp_data_t *sdp_xml_parse_nil(const char *data); +sdp_data_t *sdp_xml_parse_text(const char *data, char encoding); +sdp_data_t *sdp_xml_parse_url(const char *data); +sdp_data_t *sdp_xml_parse_int(const char *data, uint8_t dtd); +sdp_data_t *sdp_xml_parse_uuid(const char *data, sdp_record_t *record); + +struct sdp_xml_data { + char *text; /* Pointer to the current buffer */ + int size; /* Size of the current buffer */ + sdp_data_t *data; /* The current item being built */ + struct sdp_xml_data *next; /* Next item on the stack */ + char type; /* 0 = Text or Hexadecimal */ + char *name; /* Name, optional in the dtd */ + /* TODO: What is it used for? */ +}; + +struct sdp_xml_data *sdp_xml_data_alloc(); +void sdp_xml_data_free(struct sdp_xml_data *elem); +struct sdp_xml_data *sdp_xml_data_expand(struct sdp_xml_data *elem); + +sdp_data_t *sdp_xml_parse_datatype(const char *el, struct sdp_xml_data *elem, + sdp_record_t *record); + +sdp_record_t *sdp_xml_parse_record(const char *data, int size); + +#endif /* __SDP_XML_H */ diff --git a/common/test_textfile.c b/common/test_textfile.c new file mode 100644 index 00000000..1bb024eb --- /dev/null +++ b/common/test_textfile.c @@ -0,0 +1,187 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org> + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> + +#include "textfile.h" + +static void print_entry(char *key, char *value, void *data) +{ + printf("%s %s\n", key, value); +} + +int main(int argc, char *argv[]) +{ + char filename[] = "/tmp/textfile"; + char key[18], value[512], *str; + int i, j, fd, err, size, max = 10; + + size = getpagesize(); + printf("System uses a page size of %d bytes\n\n", size); + + fd = creat(filename, 0644); + err = ftruncate(fd, 0); + + memset(value, 0, sizeof(value)); + for (i = 0; i < (size / sizeof(value)); i++) + err = write(fd, value, sizeof(value)); + + close(fd); + + sprintf(key, "11:11:11:11:11:11"); + str = textfile_get(filename, key); + + err = truncate(filename, 0); + + + sprintf(key, "00:00:00:00:00:00"); + if (textfile_del(filename, key) < 0) + fprintf(stderr, "%s (%d)\n", strerror(errno), errno); + + memset(value, 0, sizeof(value)); + if (textfile_put(filename, key, value) < 0) + fprintf(stderr, "%s (%d)\n", strerror(errno), errno); + + str = textfile_get(filename, key); + if (!str) + fprintf(stderr, "No value for %s\n", key); + else + free(str); + + snprintf(value, sizeof(value), "Test"); + if (textfile_put(filename, key, value) < 0) + fprintf(stderr, "%s (%d)\n", strerror(errno), errno); + + if (textfile_put(filename, key, value) < 0) + fprintf(stderr, "%s (%d)\n", strerror(errno), errno); + + if (textfile_put(filename, key, value) < 0) + fprintf(stderr, "%s (%d)\n", strerror(errno), errno); + + if (textfile_del(filename, key) < 0) + fprintf(stderr, "%s (%d)\n", strerror(errno), errno); + + str = textfile_get(filename, key); + if (str) { + fprintf(stderr, "Found value for %s\n", key); + free(str); + } + + for (i = 1; i < max + 1; i++) { + sprintf(key, "00:00:00:00:00:%02X", i); + + memset(value, 0, sizeof(value)); + for (j = 0; j < i; j++) + value[j] = 'x'; + + printf("%s %s\n", key, value); + + if (textfile_put(filename, key, value) < 0) { + fprintf(stderr, "%s (%d)\n", strerror(errno), errno); + break; + } + + str = textfile_get(filename, key); + if (!str) + fprintf(stderr, "No value for %s\n", key); + else + free(str); + } + + + sprintf(key, "00:00:00:00:00:%02X", max); + + memset(value, 0, sizeof(value)); + for (j = 0; j < max; j++) + value[j] = 'y'; + + if (textfile_put(filename, key, value) < 0) + fprintf(stderr, "%s (%d)\n", strerror(errno), errno); + + sprintf(key, "00:00:00:00:00:%02X", 1); + + memset(value, 0, sizeof(value)); + for (j = 0; j < max; j++) + value[j] = 'z'; + + if (textfile_put(filename, key, value) < 0) + fprintf(stderr, "%s (%d)\n", strerror(errno), errno); + + printf("\n"); + + for (i = 1; i < max + 1; i++) { + sprintf(key, "00:00:00:00:00:%02X", i); + + str = textfile_get(filename, key); + if (str) { + printf("%s %s\n", key, str); + free(str); + } + } + + + sprintf(key, "00:00:00:00:00:%02X", 2); + + if (textfile_del(filename, key) < 0) + fprintf(stderr, "%s (%d)\n", strerror(errno), errno); + + sprintf(key, "00:00:00:00:00:%02X", max - 3); + + if (textfile_del(filename, key) < 0) + fprintf(stderr, "%s (%d)\n", strerror(errno), errno); + + printf("\n"); + + textfile_foreach(filename, print_entry, NULL); + + + sprintf(key, "00:00:00:00:00:%02X", 1); + + if (textfile_del(filename, key) < 0) + fprintf(stderr, "%s (%d)\n", strerror(errno), errno); + + sprintf(key, "00:00:00:00:00:%02X", max); + + if (textfile_del(filename, key) < 0) + fprintf(stderr, "%s (%d)\n", strerror(errno), errno); + + sprintf(key, "00:00:00:00:00:%02X", max + 1); + + if (textfile_del(filename, key) < 0) + fprintf(stderr, "%s (%d)\n", strerror(errno), errno); + + printf("\n"); + + textfile_foreach(filename, print_entry, NULL); + + return 0; +} diff --git a/common/textfile.c b/common/textfile.c new file mode 100644 index 00000000..eef0f96a --- /dev/null +++ b/common/textfile.c @@ -0,0 +1,463 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org> + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#define _GNU_SOURCE +#include <stdio.h> +#include <errno.h> +#include <ctype.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <sys/param.h> + +#include "textfile.h" + +int create_dirs(const char *filename, const mode_t mode) +{ + struct stat st; + char dir[PATH_MAX + 1], *prev, *next; + int err; + + err = stat(filename, &st); + if (!err && S_ISREG(st.st_mode)) + return 0; + + memset(dir, 0, PATH_MAX + 1); + strcat(dir, "/"); + + prev = strchr(filename, '/'); + + while (prev) { + next = strchr(prev + 1, '/'); + if (!next) + break; + + if (next - prev == 1) { + prev = next; + continue; + } + + strncat(dir, prev + 1, next - prev); + mkdir(dir, mode); + + prev = next; + } + + return 0; +} + +int create_file(const char *filename, const mode_t mode) +{ + int fd; + + umask(S_IWGRP | S_IWOTH); + create_dirs(filename, S_IRUSR | S_IWUSR | S_IXUSR | + S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); + + fd = open(filename, O_RDWR | O_CREAT, mode); + if (fd < 0) + return fd; + + close(fd); + + return 0; +} + +int create_name(char *buf, size_t size, const char *path, const char *address, const char *name) +{ + return snprintf(buf, size, "%s/%s/%s", path, address, name); +} + +static inline char *find_key(char *map, size_t size, const char *key, size_t len, int icase) +{ + char *ptr = map; + size_t ptrlen = size; + + while (ptrlen > len + 1) { + int cmp = (icase) ? strncasecmp(ptr, key, len) : strncmp(ptr, key, len); + if (cmp == 0) { + if (ptr == map) + return ptr; + + if ((*(ptr - 1) == '\r' || *(ptr - 1) == '\n') && + *(ptr + len) == ' ') + return ptr; + } + + if (icase) { + char *p1 = memchr(ptr + 1, tolower(*key), ptrlen - 1); + char *p2 = memchr(ptr + 1, toupper(*key), ptrlen - 1); + + if (!p1) + ptr = p2; + else if (!p2) + ptr = p1; + else + ptr = (p1 < p2) ? p1 : p2; + } else + ptr = memchr(ptr + 1, *key, ptrlen - 1); + + if (!ptr) + return NULL; + + ptrlen = size - (ptr - map); + } + + return NULL; +} + +static inline int write_key_value(int fd, const char *key, const char *value) +{ + char *str; + size_t size; + int err = 0; + + size = strlen(key) + strlen(value) + 2; + + str = malloc(size + 1); + if (!str) + return ENOMEM; + + sprintf(str, "%s %s\n", key, value); + + if (write(fd, str, size) < 0) + err = errno; + + free(str); + + return err; +} + +static int write_key(const char *pathname, const char *key, const char *value, int icase) +{ + struct stat st; + char *map, *off, *end, *str; + off_t size, pos; size_t base, len; + int fd, err = 0; + + fd = open(pathname, O_RDWR); + if (fd < 0) + return -errno; + + if (flock(fd, LOCK_EX) < 0) { + err = errno; + goto close; + } + + if (fstat(fd, &st) < 0) { + err = errno; + goto unlock; + } + + size = st.st_size; + + if (!size) { + if (value) { + pos = lseek(fd, size, SEEK_SET); + err = write_key_value(fd, key, value); + } + goto unlock; + } + + map = mmap(NULL, size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_LOCKED, fd, 0); + if (!map || map == MAP_FAILED) { + err = errno; + goto unlock; + } + + len = strlen(key); + off = find_key(map, size, key, len, icase); + if (!off) { + if (value) { + munmap(map, size); + pos = lseek(fd, size, SEEK_SET); + err = write_key_value(fd, key, value); + } + goto unlock; + } + + base = off - map; + + end = strpbrk(off, "\r\n"); + if (!end) { + err = EILSEQ; + goto unmap; + } + + if (value && (strlen(value) == end - off - len - 1) && + !strncmp(off + len + 1, value, end - off - len - 1)) + goto unmap; + + len = strspn(end, "\r\n"); + end += len; + + len = size - (end - map); + if (!len) { + munmap(map, size); + if (ftruncate(fd, base) < 0) { + err = errno; + goto unlock; + } + pos = lseek(fd, base, SEEK_SET); + if (value) + err = write_key_value(fd, key, value); + + goto unlock; + } + + if (len < 0 || len > size) { + err = EILSEQ; + goto unmap; + } + + str = malloc(len); + if (!str) { + err = errno; + goto unmap; + } + + memcpy(str, end, len); + + munmap(map, size); + if (ftruncate(fd, base) < 0) { + err = errno; + free(str); + goto unlock; + } + pos = lseek(fd, base, SEEK_SET); + if (value) + err = write_key_value(fd, key, value); + + if (write(fd, str, len) < 0) + err = errno; + + free(str); + + goto unlock; + +unmap: + munmap(map, size); + +unlock: + flock(fd, LOCK_UN); + +close: + close(fd); + errno = err; + + return -err; +} + +static char *read_key(const char *pathname, const char *key, int icase) +{ + struct stat st; + char *map, *off, *end, *str = NULL; + off_t size; size_t len; + int fd, err = 0; + + fd = open(pathname, O_RDONLY); + if (fd < 0) + return NULL; + + if (flock(fd, LOCK_SH) < 0) { + err = errno; + goto close; + } + + if (fstat(fd, &st) < 0) { + err = errno; + goto unlock; + } + + size = st.st_size; + + map = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); + if (!map || map == MAP_FAILED) { + err = errno; + goto unlock; + } + + len = strlen(key); + off = find_key(map, size, key, len, icase); + if (!off) { + err = EILSEQ; + goto unmap; + } + + end = strpbrk(off, "\r\n"); + if (!end) { + err = EILSEQ; + goto unmap; + } + + str = malloc(end - off - len); + if (!str) { + err = EILSEQ; + goto unmap; + } + + memset(str, 0, end - off - len); + strncpy(str, off + len + 1, end - off - len - 1); + +unmap: + munmap(map, size); + +unlock: + flock(fd, LOCK_UN); + +close: + close(fd); + errno = err; + + return str; +} + +int textfile_put(const char *pathname, const char *key, const char *value) +{ + return write_key(pathname, key, value, 0); +} + +int textfile_caseput(const char *pathname, const char *key, const char *value) +{ + return write_key(pathname, key, value, 1); +} + +int textfile_del(const char *pathname, const char *key) +{ + return write_key(pathname, key, NULL, 0); +} + +int textfile_casedel(const char *pathname, const char *key) +{ + return write_key(pathname, key, NULL, 1); +} + +char *textfile_get(const char *pathname, const char *key) +{ + return read_key(pathname, key, 0); +} + +char *textfile_caseget(const char *pathname, const char *key) +{ + return read_key(pathname, key, 1); +} + +int textfile_foreach(const char *pathname, + void (*func)(char *key, char *value, void *data), void *data) +{ + struct stat st; + char *map, *off, *end, *key, *value; + off_t size; size_t len; + int fd, err = 0; + + fd = open(pathname, O_RDONLY); + if (fd < 0) + return -errno; + + if (flock(fd, LOCK_SH) < 0) { + err = errno; + goto close; + } + + if (fstat(fd, &st) < 0) { + err = errno; + goto unlock; + } + + size = st.st_size; + + map = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); + if (!map || map == MAP_FAILED) { + err = errno; + goto unlock; + } + + off = map; + + while (1) { + end = strpbrk(off, " "); + if (!end) { + err = EILSEQ; + break; + } + + len = end - off; + + key = malloc(len + 1); + if (!key) { + err = errno; + break; + } + + memset(key, 0, len + 1); + memcpy(key, off, len); + + off = end + 1; + + end = strpbrk(off, "\r\n"); + if (!end) { + err = EILSEQ; + free(key); + break; + } + + len = end - off; + + value = malloc(len + 1); + if (!value) { + err = errno; + free(key); + break; + } + + memset(value, 0, len + 1); + memcpy(value, off, len); + + func(key, value, data); + + free(key); + free(value); + + off = end + 1; + } + + munmap(map, size); + +unlock: + flock(fd, LOCK_UN); + +close: + close(fd); + errno = err; + + return 0; +} diff --git a/common/textfile.h b/common/textfile.h new file mode 100644 index 00000000..b2276e3a --- /dev/null +++ b/common/textfile.h @@ -0,0 +1,42 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org> + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __TEXTFILE_H +#define __TEXTFILE_H + +int create_dirs(const char *filename, const mode_t mode); +int create_file(const char *filename, const mode_t mode); +int create_name(char *buf, size_t size, const char *path, + const char *address, const char *name); + +int textfile_put(const char *pathname, const char *key, const char *value); +int textfile_caseput(const char *pathname, const char *key, const char *value); +int textfile_del(const char *pathname, const char *key); +int textfile_casedel(const char *pathname, const char *key); +char *textfile_get(const char *pathname, const char *key); +char *textfile_caseget(const char *pathname, const char *key); + +int textfile_foreach(const char *pathname, + void (*func)(char *key, char *value, void *data), void *data); + +#endif /* __TEXTFILE_H */ diff --git a/common/uinput.h b/common/uinput.h new file mode 100644 index 00000000..dca87829 --- /dev/null +++ b/common/uinput.h @@ -0,0 +1,586 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2003-2008 Marcel Holtmann <marcel@holtmann.org> + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __UINPUT_H +#define __UINPUT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdint.h> +#include <sys/time.h> +#include <sys/ioctl.h> + +/* Events */ + +#define EV_SYN 0x00 +#define EV_KEY 0x01 +#define EV_REL 0x02 +#define EV_ABS 0x03 +#define EV_MSC 0x04 +#define EV_LED 0x11 +#define EV_SND 0x12 +#define EV_REP 0x14 +#define EV_FF 0x15 +#define EV_PWR 0x16 +#define EV_FF_STATUS 0x17 +#define EV_MAX 0x1f + +/* Synchronization events */ + +#define SYN_REPORT 0 +#define SYN_CONFIG 1 + +/* Keys and buttons */ + +#define KEY_RESERVED 0 +#define KEY_ESC 1 +#define KEY_1 2 +#define KEY_2 3 +#define KEY_3 4 +#define KEY_4 5 +#define KEY_5 6 +#define KEY_6 7 +#define KEY_7 8 +#define KEY_8 9 +#define KEY_9 10 +#define KEY_0 11 +#define KEY_MINUS 12 +#define KEY_EQUAL 13 +#define KEY_BACKSPACE 14 +#define KEY_TAB 15 +#define KEY_Q 16 +#define KEY_W 17 +#define KEY_E 18 +#define KEY_R 19 +#define KEY_T 20 +#define KEY_Y 21 +#define KEY_U 22 +#define KEY_I 23 +#define KEY_O 24 +#define KEY_P 25 +#define KEY_LEFTBRACE 26 +#define KEY_RIGHTBRACE 27 +#define KEY_ENTER 28 +#define KEY_LEFTCTRL 29 +#define KEY_A 30 +#define KEY_S 31 +#define KEY_D 32 +#define KEY_F 33 +#define KEY_G 34 +#define KEY_H 35 +#define KEY_J 36 +#define KEY_K 37 +#define KEY_L 38 +#define KEY_SEMICOLON 39 +#define KEY_APOSTROPHE 40 +#define KEY_GRAVE 41 +#define KEY_LEFTSHIFT 42 +#define KEY_BACKSLASH 43 +#define KEY_Z 44 +#define KEY_X 45 +#define KEY_C 46 +#define KEY_V 47 +#define KEY_B 48 +#define KEY_N 49 +#define KEY_M 50 +#define KEY_COMMA 51 +#define KEY_DOT 52 +#define KEY_SLASH 53 +#define KEY_RIGHTSHIFT 54 +#define KEY_KPASTERISK 55 +#define KEY_LEFTALT 56 +#define KEY_SPACE 57 +#define KEY_CAPSLOCK 58 +#define KEY_F1 59 +#define KEY_F2 60 +#define KEY_F3 61 +#define KEY_F4 62 +#define KEY_F5 63 +#define KEY_F6 64 +#define KEY_F7 65 +#define KEY_F8 66 +#define KEY_F9 67 +#define KEY_F10 68 +#define KEY_NUMLOCK 69 +#define KEY_SCROLLLOCK 70 +#define KEY_KP7 71 +#define KEY_KP8 72 +#define KEY_KP9 73 +#define KEY_KPMINUS 74 +#define KEY_KP4 75 +#define KEY_KP5 76 +#define KEY_KP6 77 +#define KEY_KPPLUS 78 +#define KEY_KP1 79 +#define KEY_KP2 80 +#define KEY_KP3 81 +#define KEY_KP0 82 +#define KEY_KPDOT 83 +#define KEY_103RD 84 +#define KEY_F13 85 +#define KEY_102ND 86 +#define KEY_F11 87 +#define KEY_F12 88 +#define KEY_F14 89 +#define KEY_F15 90 +#define KEY_F16 91 +#define KEY_F17 92 +#define KEY_F18 93 +#define KEY_F19 94 +#define KEY_F20 95 +#define KEY_KPENTER 96 +#define KEY_RIGHTCTRL 97 +#define KEY_KPSLASH 98 +#define KEY_SYSRQ 99 +#define KEY_RIGHTALT 100 +#define KEY_LINEFEED 101 +#define KEY_HOME 102 +#define KEY_UP 103 +#define KEY_PAGEUP 104 +#define KEY_LEFT 105 +#define KEY_RIGHT 106 +#define KEY_END 107 +#define KEY_DOWN 108 +#define KEY_PAGEDOWN 109 +#define KEY_INSERT 110 +#define KEY_DELETE 111 +#define KEY_MACRO 112 +#define KEY_MUTE 113 +#define KEY_VOLUMEDOWN 114 +#define KEY_VOLUMEUP 115 +#define KEY_POWER 116 +#define KEY_KPEQUAL 117 +#define KEY_KPPLUSMINUS 118 +#define KEY_PAUSE 119 +#define KEY_F21 120 +#define KEY_F22 121 +#define KEY_F23 122 +#define KEY_F24 123 +#define KEY_KPCOMMA 124 +#define KEY_LEFTMETA 125 +#define KEY_RIGHTMETA 126 +#define KEY_COMPOSE 127 + +#define KEY_STOP 128 +#define KEY_AGAIN 129 +#define KEY_PROPS 130 +#define KEY_UNDO 131 +#define KEY_FRONT 132 +#define KEY_COPY 133 +#define KEY_OPEN 134 +#define KEY_PASTE 135 +#define KEY_FIND 136 +#define KEY_CUT 137 +#define KEY_HELP 138 +#define KEY_MENU 139 +#define KEY_CALC 140 +#define KEY_SETUP 141 +#define KEY_SLEEP 142 +#define KEY_WAKEUP 143 +#define KEY_FILE 144 +#define KEY_SENDFILE 145 +#define KEY_DELETEFILE 146 +#define KEY_XFER 147 +#define KEY_PROG1 148 +#define KEY_PROG2 149 +#define KEY_WWW 150 +#define KEY_MSDOS 151 +#define KEY_COFFEE 152 +#define KEY_DIRECTION 153 +#define KEY_CYCLEWINDOWS 154 +#define KEY_MAIL 155 +#define KEY_BOOKMARKS 156 +#define KEY_COMPUTER 157 +#define KEY_BACK 158 +#define KEY_FORWARD 159 +#define KEY_CLOSECD 160 +#define KEY_EJECTCD 161 +#define KEY_EJECTCLOSECD 162 +#define KEY_NEXTSONG 163 +#define KEY_PLAYPAUSE 164 +#define KEY_PREVIOUSSONG 165 +#define KEY_STOPCD 166 +#define KEY_RECORD 167 +#define KEY_REWIND 168 +#define KEY_PHONE 169 +#define KEY_ISO 170 +#define KEY_CONFIG 171 +#define KEY_HOMEPAGE 172 +#define KEY_REFRESH 173 +#define KEY_EXIT 174 +#define KEY_MOVE 175 +#define KEY_EDIT 176 +#define KEY_SCROLLUP 177 +#define KEY_SCROLLDOWN 178 +#define KEY_KPLEFTPAREN 179 +#define KEY_KPRIGHTPAREN 180 + +#define KEY_INTL1 181 +#define KEY_INTL2 182 +#define KEY_INTL3 183 +#define KEY_INTL4 184 +#define KEY_INTL5 185 +#define KEY_INTL6 186 +#define KEY_INTL7 187 +#define KEY_INTL8 188 +#define KEY_INTL9 189 +#define KEY_LANG1 190 +#define KEY_LANG2 191 +#define KEY_LANG3 192 +#define KEY_LANG4 193 +#define KEY_LANG5 194 +#define KEY_LANG6 195 +#define KEY_LANG7 196 +#define KEY_LANG8 197 +#define KEY_LANG9 198 + +#define KEY_PLAYCD 200 +#define KEY_PAUSECD 201 +#define KEY_PROG3 202 +#define KEY_PROG4 203 +#define KEY_SUSPEND 205 +#define KEY_CLOSE 206 +#define KEY_PLAY 207 + +#define KEY_UNKNOWN 220 + +#define KEY_BRIGHTNESSDOWN 224 +#define KEY_BRIGHTNESSUP 225 + +#define BTN_MISC 0x100 +#define BTN_0 0x100 +#define BTN_1 0x101 +#define BTN_2 0x102 +#define BTN_3 0x103 +#define BTN_4 0x104 +#define BTN_5 0x105 +#define BTN_6 0x106 +#define BTN_7 0x107 +#define BTN_8 0x108 +#define BTN_9 0x109 + +#define BTN_MOUSE 0x110 +#define BTN_LEFT 0x110 +#define BTN_RIGHT 0x111 +#define BTN_MIDDLE 0x112 +#define BTN_SIDE 0x113 +#define BTN_EXTRA 0x114 +#define BTN_FORWARD 0x115 +#define BTN_BACK 0x116 +#define BTN_TASK 0x117 + +#define BTN_JOYSTICK 0x120 +#define BTN_TRIGGER 0x120 +#define BTN_THUMB 0x121 +#define BTN_THUMB2 0x122 +#define BTN_TOP 0x123 +#define BTN_TOP2 0x124 +#define BTN_PINKIE 0x125 +#define BTN_BASE 0x126 +#define BTN_BASE2 0x127 +#define BTN_BASE3 0x128 +#define BTN_BASE4 0x129 +#define BTN_BASE5 0x12a +#define BTN_BASE6 0x12b +#define BTN_DEAD 0x12f + +#define BTN_GAMEPAD 0x130 +#define BTN_A 0x130 +#define BTN_B 0x131 +#define BTN_C 0x132 +#define BTN_X 0x133 +#define BTN_Y 0x134 +#define BTN_Z 0x135 +#define BTN_TL 0x136 +#define BTN_TR 0x137 +#define BTN_TL2 0x138 +#define BTN_TR2 0x139 +#define BTN_SELECT 0x13a +#define BTN_START 0x13b +#define BTN_MODE 0x13c +#define BTN_THUMBL 0x13d +#define BTN_THUMBR 0x13e + +#define BTN_DIGI 0x140 +#define BTN_TOOL_PEN 0x140 +#define BTN_TOOL_RUBBER 0x141 +#define BTN_TOOL_BRUSH 0x142 +#define BTN_TOOL_PENCIL 0x143 +#define BTN_TOOL_AIRBRUSH 0x144 +#define BTN_TOOL_FINGER 0x145 +#define BTN_TOOL_MOUSE 0x146 +#define BTN_TOOL_LENS 0x147 +#define BTN_TOUCH 0x14a +#define BTN_STYLUS 0x14b +#define BTN_STYLUS2 0x14c +#define BTN_TOOL_DOUBLETAP 0x14d +#define BTN_TOOL_TRIPLETAP 0x14e + +#define BTN_WHEEL 0x150 +#define BTN_GEAR_DOWN 0x150 +#define BTN_GEAR_UP 0x151 + +#define KEY_OK 0x160 +#define KEY_SELECT 0x161 +#define KEY_GOTO 0x162 +#define KEY_CLEAR 0x163 +#define KEY_POWER2 0x164 +#define KEY_OPTION 0x165 +#define KEY_INFO 0x166 +#define KEY_TIME 0x167 +#define KEY_VENDOR 0x168 +#define KEY_ARCHIVE 0x169 +#define KEY_PROGRAM 0x16a +#define KEY_CHANNEL 0x16b +#define KEY_FAVORITES 0x16c +#define KEY_EPG 0x16d +#define KEY_PVR 0x16e +#define KEY_MHP 0x16f +#define KEY_LANGUAGE 0x170 +#define KEY_TITLE 0x171 +#define KEY_SUBTITLE 0x172 +#define KEY_ANGLE 0x173 +#define KEY_ZOOM 0x174 +#define KEY_MODE 0x175 +#define KEY_KEYBOARD 0x176 +#define KEY_SCREEN 0x177 +#define KEY_PC 0x178 +#define KEY_TV 0x179 +#define KEY_TV2 0x17a +#define KEY_VCR 0x17b +#define KEY_VCR2 0x17c +#define KEY_SAT 0x17d +#define KEY_SAT2 0x17e +#define KEY_CD 0x17f +#define KEY_TAPE 0x180 +#define KEY_RADIO 0x181 +#define KEY_TUNER 0x182 +#define KEY_PLAYER 0x183 +#define KEY_TEXT 0x184 +#define KEY_DVD 0x185 +#define KEY_AUX 0x186 +#define KEY_MP3 0x187 +#define KEY_AUDIO 0x188 +#define KEY_VIDEO 0x189 +#define KEY_DIRECTORY 0x18a +#define KEY_LIST 0x18b +#define KEY_MEMO 0x18c +#define KEY_CALENDAR 0x18d +#define KEY_RED 0x18e +#define KEY_GREEN 0x18f +#define KEY_YELLOW 0x190 +#define KEY_BLUE 0x191 +#define KEY_CHANNELUP 0x192 +#define KEY_CHANNELDOWN 0x193 +#define KEY_FIRST 0x194 +#define KEY_LAST 0x195 +#define KEY_AB 0x196 +#define KEY_NEXT 0x197 +#define KEY_RESTART 0x198 +#define KEY_SLOW 0x199 +#define KEY_SHUFFLE 0x19a +#define KEY_BREAK 0x19b +#define KEY_PREVIOUS 0x19c +#define KEY_DIGITS 0x19d +#define KEY_TEEN 0x19e +#define KEY_TWEN 0x19f + +#define KEY_FRAMEBACK 0x1b2 +#define KEY_FRAMEFORWARD 0x1b3 +#define KEY_CONTEXT_MENU 0x1fb + +#define KEY_MAX 0x1ff + +/* Relative axes */ + +#define REL_X 0x00 +#define REL_Y 0x01 +#define REL_Z 0x02 +#define REL_RX 0x03 +#define REL_RY 0x04 +#define REL_RZ 0x05 +#define REL_HWHEEL 0x06 +#define REL_DIAL 0x07 +#define REL_WHEEL 0x08 +#define REL_MISC 0x09 +#define REL_MAX 0x0f + +/* Absolute axes */ + +#define ABS_X 0x00 +#define ABS_Y 0x01 +#define ABS_Z 0x02 +#define ABS_RX 0x03 +#define ABS_RY 0x04 +#define ABS_RZ 0x05 +#define ABS_THROTTLE 0x06 +#define ABS_RUDDER 0x07 +#define ABS_WHEEL 0x08 +#define ABS_GAS 0x09 +#define ABS_BRAKE 0x0a +#define ABS_HAT0X 0x10 +#define ABS_HAT0Y 0x11 +#define ABS_HAT1X 0x12 +#define ABS_HAT1Y 0x13 +#define ABS_HAT2X 0x14 +#define ABS_HAT2Y 0x15 +#define ABS_HAT3X 0x16 +#define ABS_HAT3Y 0x17 +#define ABS_PRESSURE 0x18 +#define ABS_DISTANCE 0x19 +#define ABS_TILT_X 0x1a +#define ABS_TILT_Y 0x1b +#define ABS_TOOL_WIDTH 0x1c +#define ABS_VOLUME 0x20 +#define ABS_MISC 0x28 +#define ABS_MAX 0x3f + +/* Switch events */ + +#define SW_0 0x00 +#define SW_1 0x01 +#define SW_2 0x02 +#define SW_3 0x03 +#define SW_4 0x04 +#define SW_5 0x05 +#define SW_6 0x06 +#define SW_7 0x07 +#define SW_MAX 0x0f + +/* Misc events */ + +#define MSC_SERIAL 0x00 +#define MSC_PULSELED 0x01 +#define MSC_GESTURE 0x02 +#define MSC_RAW 0x03 +#define MSC_SCAN 0x04 +#define MSC_MAX 0x07 + +/* LEDs */ + +#define LED_NUML 0x00 +#define LED_CAPSL 0x01 +#define LED_SCROLLL 0x02 +#define LED_COMPOSE 0x03 +#define LED_KANA 0x04 +#define LED_SLEEP 0x05 +#define LED_SUSPEND 0x06 +#define LED_MUTE 0x07 +#define LED_MISC 0x08 +#define LED_MAIL 0x09 +#define LED_CHARGING 0x0a +#define LED_MAX 0x0f + +/* Autorepeat values */ + +#define REP_DELAY 0x00 +#define REP_PERIOD 0x01 +#define REP_MAX 0x01 + +/* Sounds */ + +#define SND_CLICK 0x00 +#define SND_BELL 0x01 +#define SND_TONE 0x02 +#define SND_MAX 0x07 + +/* Identifiers */ + +#define ID_BUS 0 +#define ID_VENDOR 1 +#define ID_PRODUCT 2 +#define ID_VERSION 3 + +#define BUS_PCI 0x01 +#define BUS_ISAPNP 0x02 +#define BUS_USB 0x03 +#define BUS_HIL 0x04 +#define BUS_BLUETOOTH 0x05 + +#define BUS_ISA 0x10 +#define BUS_I8042 0x11 +#define BUS_XTKBD 0x12 +#define BUS_RS232 0x13 +#define BUS_GAMEPORT 0x14 +#define BUS_PARPORT 0x15 +#define BUS_AMIGA 0x16 +#define BUS_ADB 0x17 +#define BUS_I2C 0x18 +#define BUS_HOST 0x19 +#define BUS_GSC 0x1A + +/* User input interface */ + +#define UINPUT_IOCTL_BASE 'U' + +#define UI_DEV_CREATE _IO(UINPUT_IOCTL_BASE, 1) +#define UI_DEV_DESTROY _IO(UINPUT_IOCTL_BASE, 2) + +#define UI_SET_EVBIT _IOW(UINPUT_IOCTL_BASE, 100, int) +#define UI_SET_KEYBIT _IOW(UINPUT_IOCTL_BASE, 101, int) +#define UI_SET_RELBIT _IOW(UINPUT_IOCTL_BASE, 102, int) +#define UI_SET_ABSBIT _IOW(UINPUT_IOCTL_BASE, 103, int) +#define UI_SET_MSCBIT _IOW(UINPUT_IOCTL_BASE, 104, int) +#define UI_SET_LEDBIT _IOW(UINPUT_IOCTL_BASE, 105, int) +#define UI_SET_SNDBIT _IOW(UINPUT_IOCTL_BASE, 106, int) +#define UI_SET_FFBIT _IOW(UINPUT_IOCTL_BASE, 107, int) +#define UI_SET_PHYS _IOW(UINPUT_IOCTL_BASE, 108, char*) +#define UI_SET_SWBIT _IOW(UINPUT_IOCTL_BASE, 109, int) + +#ifndef NBITS +#define NBITS(x) ((((x) - 1) / (sizeof(long) * 8)) + 1) +#endif + +#define UINPUT_MAX_NAME_SIZE 80 + +struct uinput_id { + uint16_t bustype; + uint16_t vendor; + uint16_t product; + uint16_t version; +}; + +struct uinput_dev { + char name[UINPUT_MAX_NAME_SIZE]; + struct uinput_id id; + int ff_effects_max; + int absmax[ABS_MAX + 1]; + int absmin[ABS_MAX + 1]; + int absfuzz[ABS_MAX + 1]; + int absflat[ABS_MAX + 1]; +}; + +struct uinput_event { + struct timeval time; + uint16_t type; + uint16_t code; + int32_t value; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* __UINPUT_H */ |