This moves IO handling to IPC code making it fully responsible for creating and veryfing IPC messages exchange. --- This should be applied on top of IPC daemon improvements. android/ipc.c | 273 ++++++++++++++++++++++++++++++++++++++++++--------------- android/ipc.h | 4 +- android/main.c | 151 +------------------------------ 3 files changed, 207 insertions(+), 221 deletions(-) diff --git a/android/ipc.c b/android/ipc.c index 56f328b..1d369a8 100644 --- a/android/ipc.c +++ b/android/ipc.c @@ -32,6 +32,10 @@ #include <signal.h> #include <stdbool.h> #include <sys/socket.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <unistd.h> +#include <glib.h> #include "hal-msg.h" #include "ipc.h" @@ -44,19 +48,202 @@ struct service_handler { static struct service_handler services[HAL_SERVICE_ID_MAX + 1]; -static int cmd_sk = -1; -static int notif_sk = -1; +static GIOChannel *cmd_io = NULL; +static GIOChannel *notif_io = NULL; -void ipc_init(int command_sk, int notification_sk) +static void ipc_handle_msg(const void *buf, ssize_t len) { - cmd_sk = command_sk; - notif_sk = notification_sk; + const struct hal_hdr *msg = buf; + const struct ipc_handler *handler; + + if (len < (ssize_t) sizeof(*msg)) { + error("IPC: message too small (%zd bytes), terminating", len); + raise(SIGTERM); + return; + } + + if (len != (ssize_t) (sizeof(*msg) + msg->len)) { + error("IPC: message malformed (%zd bytes), terminating", len); + raise(SIGTERM); + return; + } + + /* if service is valid */ + if (msg->service_id > HAL_SERVICE_ID_MAX) { + error("IPC: unknown service (0x%x), terminating", + msg->service_id); + raise(SIGTERM); + return; + } + + /* if service is registered */ + if (!services[msg->service_id].handler) { + error("IPC: unregistered service (0x%x), terminating", + msg->service_id); + raise(SIGTERM); + return; + } + + /* if opcode is valid */ + if (msg->opcode == HAL_OP_STATUS || + msg->opcode > services[msg->service_id].size) { + error("IPC: invalid opcode 0x%x for service 0x%x, terminating", + msg->opcode, msg->service_id); + raise(SIGTERM); + return; + } + + /* opcode is table offset + 1 */ + handler = &services[msg->service_id].handler[msg->opcode - 1]; + + /* if payload size is valid */ + if ((handler->var_len && handler->data_len > msg->len) || + (!handler->var_len && handler->data_len != msg->len)) { + error("IPC: size invalid opcode 0x%x service 0x%x, terminating", + msg->service_id, msg->opcode); + raise(SIGTERM); + return; + } + + handler->handler(msg->payload, msg->len); +} + +static gboolean cmd_watch_cb(GIOChannel *io, GIOCondition cond, + gpointer user_data) +{ + char buf[BLUEZ_HAL_MTU]; + ssize_t ret; + int fd; + + if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { + info("IPC: command socket closed, terminating"); + goto fail; + } + + fd = g_io_channel_unix_get_fd(io); + + ret = read(fd, buf, sizeof(buf)); + if (ret < 0) { + error("IPC: command read failed, terminating (%s)", + strerror(errno)); + goto fail; + } + + ipc_handle_msg(buf, ret); + return TRUE; + +fail: + raise(SIGTERM); + return FALSE; +} + +static gboolean notif_watch_cb(GIOChannel *io, GIOCondition cond, + gpointer user_data) +{ + info("IPC: notification socket closed, terminating"); + raise(SIGTERM); + + return FALSE; +} + +static GIOChannel *connect_hal(GIOFunc connect_cb) +{ + struct sockaddr_un addr; + GIOCondition cond; + GIOChannel *io; + int sk; + + sk = socket(PF_LOCAL, SOCK_SEQPACKET, 0); + if (sk < 0) { + error("IPC: failed to create socket: %d (%s)", errno, + strerror(errno)); + return NULL; + } + + io = g_io_channel_unix_new(sk); + + g_io_channel_set_close_on_unref(io, TRUE); + g_io_channel_set_flags(io, G_IO_FLAG_NONBLOCK, NULL); + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + + memcpy(addr.sun_path, BLUEZ_HAL_SK_PATH, sizeof(BLUEZ_HAL_SK_PATH)); + + if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + error("IPC: failed to connect HAL socket: %d (%s)", errno, + strerror(errno)); + g_io_channel_unref(io); + return NULL; + } + + cond = G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL; + + g_io_add_watch(io, cond, connect_cb, NULL); + + return io; +} + +static gboolean notif_connect_cb(GIOChannel *io, GIOCondition cond, + gpointer user_data) +{ + DBG(""); + + if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { + error("IPC: notification socket connect failed, terminating"); + raise(SIGTERM); + return FALSE; + } + + cond = G_IO_ERR | G_IO_HUP | G_IO_NVAL; + + g_io_add_watch(io, cond, notif_watch_cb, NULL); + + cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL; + + g_io_add_watch(cmd_io, cond, cmd_watch_cb, NULL); + + info("IPC: successfully connected"); + + return FALSE; +} + +static gboolean cmd_connect_cb(GIOChannel *io, GIOCondition cond, + gpointer user_data) +{ + DBG(""); + + if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { + error("IPC: command socket connect failed, terminating"); + raise(SIGTERM); + return FALSE; + } + + notif_io = connect_hal(notif_connect_cb); + if (!notif_io) + raise(SIGTERM); + + return FALSE; +} + +void ipc_init(void) +{ + cmd_io = connect_hal(cmd_connect_cb); } void ipc_cleanup(void) { - cmd_sk = -1; - notif_sk = -1; + if (cmd_io) { + g_io_channel_shutdown(cmd_io, TRUE, NULL); + g_io_channel_unref(cmd_io); + cmd_io = NULL; + } + + if (notif_io) { + g_io_channel_shutdown(notif_io, TRUE, NULL); + g_io_channel_unref(notif_io); + notif_io = NULL; + } } static void ipc_send(int sk, uint8_t service_id, uint8_t opcode, uint16_t len, @@ -107,30 +294,35 @@ static void ipc_send(int sk, uint8_t service_id, uint8_t opcode, uint16_t len, void ipc_send_rsp(uint8_t service_id, uint8_t opcode, uint8_t status) { struct hal_status s; + int sk; + + sk = g_io_channel_unix_get_fd(cmd_io); if (status == HAL_STATUS_SUCCESS) { - ipc_send(cmd_sk, service_id, opcode, 0, NULL, -1); + ipc_send(sk, service_id, opcode, 0, NULL, -1); return; } s.code = status; - ipc_send(cmd_sk, service_id, HAL_OP_STATUS, sizeof(s), &s, -1); + ipc_send(sk, service_id, HAL_OP_STATUS, sizeof(s), &s, -1); } void ipc_send_rsp_full(uint8_t service_id, uint8_t opcode, uint16_t len, void *param, int fd) { - ipc_send(cmd_sk, service_id, opcode, len, param, fd); + ipc_send(g_io_channel_unix_get_fd(cmd_io), service_id, opcode, len, + param, fd); } void ipc_send_notif(uint8_t service_id, uint8_t opcode, uint16_t len, void *param) { - if (notif_sk < 0) + if (!notif_io) return; - ipc_send(notif_sk, service_id, opcode, len, param, -1); + ipc_send(g_io_channel_unix_get_fd(notif_io), service_id, opcode, len, + param, -1); } void ipc_register(uint8_t service, const struct ipc_handler *handlers, @@ -145,60 +337,3 @@ void ipc_unregister(uint8_t service) services[service].handler = NULL; services[service].size = 0; } - -void ipc_handle_msg(const void *buf, ssize_t len) -{ - const struct hal_hdr *msg = buf; - const struct ipc_handler *handler; - - if (len < (ssize_t) sizeof(*msg)) { - error("IPC: message too small (%zd bytes), terminating", len); - raise(SIGTERM); - return; - } - - if (len != (ssize_t) (sizeof(*msg) + msg->len)) { - error("IPC: message malformed (%zd bytes), terminating", len); - raise(SIGTERM); - return; - } - - /* if service is valid */ - if (msg->service_id > HAL_SERVICE_ID_MAX) { - error("IPC: unknown service (0x%x), terminating", - msg->service_id); - raise(SIGTERM); - return; - } - - /* if service is registered */ - if (!services[msg->service_id].handler) { - error("IPC: unregistered service (0x%x), terminating", - msg->service_id); - raise(SIGTERM); - return; - } - - /* if opcode is valid */ - if (msg->opcode == HAL_OP_STATUS || - msg->opcode > services[msg->service_id].size) { - error("IPC: invalid opcode 0x%x for service 0x%x, terminating", - msg->opcode, msg->service_id); - raise(SIGTERM); - return; - } - - /* opcode is table offset + 1 */ - handler = &services[msg->service_id].handler[msg->opcode - 1]; - - /* if payload size is valid */ - if ((handler->var_len && handler->data_len > msg->len) || - (!handler->var_len && handler->data_len != msg->len)) { - error("IPC: size invalid opcode 0x%x service 0x%x, terminating", - msg->service_id, msg->opcode); - raise(SIGTERM); - return; - } - - handler->handler(msg->payload, msg->len); -} diff --git a/android/ipc.h b/android/ipc.h index 9d0c5e1..6cd102b 100644 --- a/android/ipc.h +++ b/android/ipc.h @@ -26,7 +26,7 @@ struct ipc_handler { bool var_len; size_t data_len; }; -void ipc_init(int command_sk, int notification_sk); +void ipc_init(void); void ipc_cleanup(void); void ipc_send_rsp(uint8_t service_id, uint8_t opcode, uint8_t status); @@ -37,5 +37,3 @@ void ipc_send_notif(uint8_t service_id, uint8_t opcode, uint16_t len, void ipc_register(uint8_t service, const struct ipc_handler *handlers, uint8_t size); void ipc_unregister(uint8_t service); - -void ipc_handle_msg(const void *buf, ssize_t len); diff --git a/android/main.c b/android/main.c index c0f8901..b9655c5 100644 --- a/android/main.c +++ b/android/main.c @@ -36,8 +36,6 @@ #include <unistd.h> #include <sys/signalfd.h> -#include <sys/socket.h> -#include <sys/un.h> #include <glib.h> @@ -69,9 +67,6 @@ static bdaddr_t adapter_bdaddr; static GMainLoop *event_loop; -static GIOChannel *hal_cmd_io = NULL; -static GIOChannel *hal_notif_io = NULL; - static bool services[HAL_SERVICE_ID_MAX + 1] = { false }; static void service_register(const void *buf, uint16_t len) @@ -209,127 +204,6 @@ static void stop_bluetooth(void) g_timeout_add_seconds(SHUTDOWN_GRACE_SECONDS, quit_eventloop, NULL); } -static gboolean cmd_watch_cb(GIOChannel *io, GIOCondition cond, - gpointer user_data) -{ - char buf[BLUEZ_HAL_MTU]; - ssize_t ret; - int fd; - - if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { - info("HAL command socket closed, terminating"); - goto fail; - } - - fd = g_io_channel_unix_get_fd(io); - - ret = read(fd, buf, sizeof(buf)); - if (ret < 0) { - error("HAL command read failed, terminating (%s)", - strerror(errno)); - goto fail; - } - - ipc_handle_msg(buf, ret); - return TRUE; - -fail: - stop_bluetooth(); - return FALSE; -} - -static gboolean notif_watch_cb(GIOChannel *io, GIOCondition cond, - gpointer user_data) -{ - info("HAL notification socket closed, terminating"); - stop_bluetooth(); - - return FALSE; -} - -static GIOChannel *connect_hal(GIOFunc connect_cb) -{ - struct sockaddr_un addr; - GIOCondition cond; - GIOChannel *io; - int sk; - - sk = socket(PF_LOCAL, SOCK_SEQPACKET, 0); - if (sk < 0) { - error("Failed to create socket: %d (%s)", errno, - strerror(errno)); - return NULL; - } - - io = g_io_channel_unix_new(sk); - - g_io_channel_set_close_on_unref(io, TRUE); - g_io_channel_set_flags(io, G_IO_FLAG_NONBLOCK, NULL); - - memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_UNIX; - - memcpy(addr.sun_path, BLUEZ_HAL_SK_PATH, sizeof(BLUEZ_HAL_SK_PATH)); - - if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - error("Failed to connect HAL socket: %d (%s)", errno, - strerror(errno)); - g_io_channel_unref(io); - return NULL; - } - - cond = G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL; - - g_io_add_watch(io, cond, connect_cb, NULL); - - return io; -} - -static gboolean notif_connect_cb(GIOChannel *io, GIOCondition cond, - gpointer user_data) -{ - DBG(""); - - if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { - stop_bluetooth(); - return FALSE; - } - - cond = G_IO_ERR | G_IO_HUP | G_IO_NVAL; - - g_io_add_watch(io, cond, notif_watch_cb, NULL); - - cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL; - - g_io_add_watch(hal_cmd_io, cond, cmd_watch_cb, NULL); - - ipc_init(g_io_channel_unix_get_fd(hal_cmd_io), - g_io_channel_unix_get_fd(hal_notif_io)); - - info("Successfully connected to HAL"); - - return FALSE; -} - -static gboolean cmd_connect_cb(GIOChannel *io, GIOCondition cond, - gpointer user_data) -{ - DBG(""); - - if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { - stop_bluetooth(); - return FALSE; - } - - hal_notif_io = connect_hal(notif_connect_cb); - if (!hal_notif_io) { - error("Cannot connect to HAL, terminating"); - stop_bluetooth(); - } - - return FALSE; -} - static void adapter_ready(int err, const bdaddr_t *addr) { if (err < 0) { @@ -346,11 +220,7 @@ static void adapter_ready(int err, const bdaddr_t *addr) info("Adapter initialized"); - hal_cmd_io = connect_hal(cmd_connect_cb); - if (!hal_cmd_io) { - error("Cannot connect to HAL, terminating"); - stop_bluetooth(); - } + ipc_init(); } static gboolean signal_handler(GIOChannel *channel, GIOCondition cond, @@ -433,23 +303,6 @@ static GOptionEntry options[] = { { NULL } }; -static void cleanup_hal_connection(void) -{ - if (hal_cmd_io) { - g_io_channel_shutdown(hal_cmd_io, TRUE, NULL); - g_io_channel_unref(hal_cmd_io); - hal_cmd_io = NULL; - } - - if (hal_notif_io) { - g_io_channel_shutdown(hal_notif_io, TRUE, NULL); - g_io_channel_unref(hal_notif_io); - hal_notif_io = NULL; - } - - ipc_cleanup(); -} - static void cleanup_services(void) { int i; @@ -592,7 +445,7 @@ int main(int argc, char *argv[]) cleanup_services(); - cleanup_hal_connection(); + ipc_cleanup(); stop_sdp_server(); bt_bluetooth_cleanup(); g_main_loop_unref(event_loop); -- 1.8.3.2 -- To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html