| /* |
| * |
| * RIL library with GLib integration |
| * |
| * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. |
| * Copyright (C) 2012-2013 Canonical Ltd. |
| * Copyright (C) 2013 Jolla Ltd. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 as |
| * published by the Free Software Foundation. |
| * |
| * 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 <stdio.h> |
| #include <unistd.h> |
| #include <string.h> |
| #include <ctype.h> |
| #include <errno.h> |
| #include <sys/socket.h> |
| #include <sys/un.h> |
| #include <netinet/in.h> |
| #include <unistd.h> |
| |
| #include <glib.h> |
| |
| #include <ofono/log.h> |
| #include "ringbuffer.h" |
| #include "gril.h" |
| #include "grilutil.h" |
| |
| #define RIL_TRACE(ril, fmt, arg...) do { \ |
| if (ril->trace == TRUE) \ |
| ofono_debug(fmt, ## arg); \ |
| } while (0) |
| |
| struct ril_request { |
| gchar *data; |
| guint data_len; |
| gint req; |
| gint id; |
| guint gid; |
| GRilResponseFunc callback; |
| gpointer user_data; |
| GDestroyNotify notify; |
| }; |
| |
| struct ril_notify_node { |
| guint id; |
| guint gid; |
| GRilNotifyFunc callback; |
| gpointer user_data; |
| gboolean destroyed; |
| }; |
| |
| typedef gboolean (*node_remove_func)(struct ril_notify_node *node, |
| gpointer user_data); |
| |
| struct ril_notify { |
| GSList *nodes; |
| }; |
| |
| struct ril_s { |
| gint ref_count; /* Ref count */ |
| gint next_cmd_id; /* Next command id */ |
| guint next_notify_id; /* Next notify id */ |
| guint next_gid; /* Next group id */ |
| GRilIO *io; /* GRil IO */ |
| GQueue *command_queue; /* Command queue */ |
| GQueue *out_queue; /* Commands sent/been sent */ |
| guint req_bytes_written; /* bytes written from req */ |
| GHashTable *notify_list; /* List of notification reg */ |
| GRilDisconnectFunc user_disconnect; /* user disconnect func */ |
| gpointer user_disconnect_data; /* user disconnect data */ |
| guint read_so_far; /* Number of bytes processed */ |
| gboolean suspended; /* Are we suspended? */ |
| gboolean debug; |
| gboolean trace; |
| gint timeout_source; |
| gboolean destroyed; /* Re-entrancy guard */ |
| gboolean in_read_handler; /* Re-entrancy guard */ |
| gboolean in_notify; |
| enum ofono_ril_vendor vendor; |
| int slot; |
| GRilMsgIdToStrFunc req_to_string; |
| GRilMsgIdToStrFunc unsol_to_string; |
| }; |
| |
| struct _GRil { |
| gint ref_count; |
| struct ril_s *parent; |
| guint group; |
| }; |
| |
| struct req_hdr { |
| /* Warning: length is stored in network order */ |
| uint32_t length; |
| uint32_t reqid; |
| uint32_t serial; |
| }; |
| |
| #define RIL_PRINT_BUF_SIZE 8096 |
| char print_buf[RIL_PRINT_BUF_SIZE] __attribute__((used)); |
| |
| static void ril_wakeup_writer(struct ril_s *ril); |
| |
| static const char *request_id_to_string(struct ril_s *ril, int req) |
| { |
| const char *str = NULL; |
| |
| if (ril->req_to_string) |
| str = ril->req_to_string(req); |
| |
| if (str == NULL) |
| str = ril_request_id_to_string(req); |
| |
| return str; |
| } |
| |
| static const char *unsol_request_to_string(struct ril_s *ril, int req) |
| { |
| const char *str = NULL; |
| |
| if (ril->unsol_to_string) |
| str = ril->unsol_to_string(req); |
| |
| if (str == NULL) |
| str = ril_unsol_request_to_string(req); |
| |
| return str; |
| } |
| |
| static void ril_notify_node_destroy(gpointer data, gpointer user_data) |
| { |
| struct ril_notify_node *node = data; |
| g_free(node); |
| } |
| |
| static void ril_notify_destroy(gpointer user_data) |
| { |
| struct ril_notify *notify = user_data; |
| |
| g_slist_foreach(notify->nodes, ril_notify_node_destroy, NULL); |
| g_slist_free(notify->nodes); |
| g_free(notify); |
| } |
| |
| static gint ril_notify_node_compare_by_id(gconstpointer a, gconstpointer b) |
| { |
| const struct ril_notify_node *node = a; |
| guint id = GPOINTER_TO_UINT(b); |
| |
| if (node->id < id) |
| return -1; |
| |
| if (node->id > id) |
| return 1; |
| |
| return 0; |
| } |
| |
| static gboolean ril_unregister_all(struct ril_s *ril, |
| gboolean mark_only, |
| node_remove_func func, |
| gpointer userdata) |
| { |
| GHashTableIter iter; |
| struct ril_notify *notify; |
| struct ril_notify_node *node; |
| gpointer key, value; |
| GSList *p; |
| GSList *c; |
| GSList *t; |
| |
| if (ril->notify_list == NULL) |
| return FALSE; |
| |
| g_hash_table_iter_init(&iter, ril->notify_list); |
| |
| while (g_hash_table_iter_next(&iter, &key, &value)) { |
| notify = value; |
| |
| p = NULL; |
| c = notify->nodes; |
| |
| while (c) { |
| node = c->data; |
| |
| if (func(node, userdata) != TRUE) { |
| p = c; |
| c = c->next; |
| continue; |
| } |
| |
| if (mark_only) { |
| node->destroyed = TRUE; |
| p = c; |
| c = c->next; |
| continue; |
| } |
| |
| if (p) |
| p->next = c->next; |
| else |
| notify->nodes = c->next; |
| |
| ril_notify_node_destroy(node, NULL); |
| |
| t = c; |
| c = c->next; |
| g_slist_free_1(t); |
| } |
| |
| if (notify->nodes == NULL) |
| g_hash_table_iter_remove(&iter); |
| } |
| |
| return TRUE; |
| } |
| |
| /* |
| * This function creates a RIL request. For a good reference on |
| * the layout of RIL requests, responses, and unsolicited requests |
| * see: |
| * |
| * https://wiki.mozilla.org/B2G/RIL |
| */ |
| static struct ril_request *ril_request_create(struct ril_s *ril, |
| guint gid, |
| const gint req, |
| const gint id, |
| struct parcel *rilp, |
| GRilResponseFunc func, |
| gpointer user_data, |
| GDestroyNotify notify, |
| gboolean wakeup) |
| { |
| struct ril_request *r; |
| struct req_hdr header; |
| guint data_len = 0; |
| |
| if (rilp != NULL) |
| data_len = rilp->size; |
| |
| r = g_try_new0(struct ril_request, 1); |
| if (r == NULL) { |
| ofono_error("%s Out of memory", __func__); |
| return NULL; |
| } |
| |
| /* Full request size: header size plus buffer length */ |
| r->data_len = data_len + sizeof(header); |
| |
| r->data = g_try_new(char, r->data_len); |
| if (r->data == NULL) { |
| ofono_error("ril_request: can't allocate new request."); |
| g_free(r); |
| return NULL; |
| } |
| |
| /* Length does not include the length field. Network order. */ |
| header.length = htonl(r->data_len - sizeof(header.length)); |
| header.reqid = req; |
| header.serial = id; |
| |
| /* copy header */ |
| memcpy(r->data, &header, sizeof(header)); |
| /* copy request data */ |
| if (data_len) |
| memcpy(r->data + sizeof(header), rilp->data, data_len); |
| |
| r->req = req; |
| r->gid = gid; |
| r->id = id; |
| r->callback = func; |
| r->user_data = user_data; |
| r->notify = notify; |
| |
| return r; |
| } |
| |
| static void ril_request_destroy(struct ril_request *req) |
| { |
| if (req->notify) |
| req->notify(req->user_data); |
| |
| g_free(req->data); |
| g_free(req); |
| } |
| |
| static void ril_cleanup(struct ril_s *p) |
| { |
| /* Cleanup pending commands */ |
| |
| if (p->command_queue) { |
| g_queue_free(p->command_queue); |
| p->command_queue = NULL; |
| } |
| |
| if (p->out_queue) { |
| g_queue_free(p->out_queue); |
| p->out_queue = NULL; |
| } |
| |
| /* Cleanup registered notifications */ |
| if (p->notify_list) { |
| g_hash_table_destroy(p->notify_list); |
| p->notify_list = NULL; |
| } |
| |
| if (p->timeout_source) { |
| g_source_remove(p->timeout_source); |
| p->timeout_source = 0; |
| } |
| } |
| |
| void g_ril_set_disconnect_function(GRil *ril, GRilDisconnectFunc disconnect, |
| gpointer user_data) |
| { |
| ril->parent->user_disconnect = disconnect; |
| ril->parent->user_disconnect_data = user_data; |
| } |
| |
| static void io_disconnect(gpointer user_data) |
| { |
| struct ril_s *ril = user_data; |
| |
| ofono_error("%s: disconnected from rild", __func__); |
| |
| ril_cleanup(ril); |
| g_ril_io_unref(ril->io); |
| ril->io = NULL; |
| |
| if (ril->user_disconnect) |
| ril->user_disconnect(ril->user_disconnect_data); |
| } |
| |
| static void handle_response(struct ril_s *p, struct ril_msg *message) |
| { |
| guint count = g_queue_get_length(p->command_queue); |
| struct ril_request *req; |
| gboolean found = FALSE; |
| guint i, len; |
| gint id; |
| |
| for (i = 0; i < count; i++) { |
| req = g_queue_peek_nth(p->command_queue, i); |
| |
| if (req->id == message->serial_no) { |
| found = TRUE; |
| message->req = req->req; |
| |
| if (message->error != RIL_E_SUCCESS) |
| RIL_TRACE(p, "[%d,%04d]< %s failed %s", |
| p->slot, message->serial_no, |
| request_id_to_string(p, message->req), |
| ril_error_to_string(message->error)); |
| |
| req = g_queue_pop_nth(p->command_queue, i); |
| if (req->callback) |
| req->callback(message, req->user_data); |
| |
| /* gril may have been destroyed in the request callback */ |
| if (p->destroyed) { |
| ril_request_destroy(req); |
| return; |
| } |
| |
| len = g_queue_get_length(p->out_queue); |
| |
| for (i = 0; i < len; i++) { |
| id = GPOINTER_TO_INT(g_queue_peek_nth( |
| p->out_queue, i)); |
| if (id == req->id) { |
| g_queue_pop_nth(p->out_queue, i); |
| break; |
| } |
| } |
| |
| ril_request_destroy(req); |
| |
| if (g_queue_peek_head(p->command_queue)) |
| ril_wakeup_writer(p); |
| |
| break; |
| } |
| } |
| |
| if (found == FALSE) |
| ofono_error("No matching request for reply: %s serial_no: %d!", |
| request_id_to_string(p, message->req), |
| message->serial_no); |
| |
| } |
| |
| static gboolean node_check_destroyed(struct ril_notify_node *node, |
| gpointer userdata) |
| { |
| gboolean val = GPOINTER_TO_UINT(userdata); |
| |
| if (node->destroyed == val) |
| return TRUE; |
| |
| return FALSE; |
| } |
| |
| static void handle_unsol_req(struct ril_s *p, struct ril_msg *message) |
| { |
| struct ril_notify *notify; |
| |
| if (p->notify_list == NULL) |
| return; |
| |
| p->in_notify = TRUE; |
| |
| notify = g_hash_table_lookup(p->notify_list, &message->req); |
| if (notify != NULL) { |
| GSList *list_item; |
| |
| for (list_item = notify->nodes; list_item; |
| list_item = g_slist_next(list_item)) { |
| struct ril_notify_node *node = list_item->data; |
| |
| if (node->destroyed) |
| continue; |
| |
| node->callback(message, node->user_data); |
| } |
| } else { |
| /* Only log events not being listended for... */ |
| DBG("RIL Event slot %d: %s\n", |
| p->slot, unsol_request_to_string(p, message->req)); |
| } |
| |
| p->in_notify = FALSE; |
| |
| /* Now destroy nodes possibly removed by callbacks */ |
| if (notify != NULL) |
| ril_unregister_all(p, FALSE, node_check_destroyed, |
| GUINT_TO_POINTER(TRUE)); |
| } |
| |
| static void dispatch(struct ril_s *p, struct ril_msg *message) |
| { |
| int32_t *unsolicited_field, *id_num_field; |
| gchar *bufp = message->buf; |
| gchar *datap; |
| gsize data_len; |
| |
| /* This could be done with a struct/union... */ |
| unsolicited_field = (int32_t *) (void *) bufp; |
| if (*unsolicited_field) |
| message->unsolicited = TRUE; |
| else |
| message->unsolicited = FALSE; |
| |
| bufp += 4; |
| |
| id_num_field = (int32_t *) (void *) bufp; |
| if (message->unsolicited) { |
| message->req = (int) *id_num_field; |
| |
| /* |
| * A RIL Unsolicited Event is two UINT32 fields ( unsolicited, |
| * and req/ev ), so subtract the length of the header from the |
| * overall length to calculate the length of the Event Data. |
| */ |
| data_len = message->buf_len - 8; |
| } else { |
| message->serial_no = (int) *id_num_field; |
| |
| bufp += 4; |
| message->error = *((int32_t *) (void *) bufp); |
| |
| /* |
| * A RIL Solicited Response is three UINT32 fields ( unsolicied, |
| * serial_no and error ), so subtract the length of the header |
| * from the overall length to calculate the length of the Event |
| * Data. |
| */ |
| data_len = message->buf_len - 12; |
| } |
| |
| /* advance to start of data.. */ |
| bufp += 4; |
| |
| /* |
| * Now, use buffer for event data if present |
| */ |
| if (data_len) { |
| datap = g_try_malloc(data_len); |
| if (datap == NULL) |
| goto error; |
| |
| /* Copy event bytes from message->buf into new buffer */ |
| memcpy(datap, bufp, data_len); |
| |
| /* Free buffer that includes header */ |
| g_free(message->buf); |
| |
| /* ...and replace with new buffer */ |
| message->buf = datap; |
| message->buf_len = data_len; |
| } else { |
| /* Free buffer that includes header */ |
| g_free(message->buf); |
| |
| /* To know if there was no data when parsing */ |
| message->buf = NULL; |
| message->buf_len = 0; |
| } |
| |
| if (message->unsolicited == TRUE) |
| handle_unsol_req(p, message); |
| else |
| handle_response(p, message); |
| |
| error: |
| g_free(message->buf); |
| g_free(message); |
| } |
| |
| static struct ril_msg *read_fixed_record(struct ril_s *p, |
| const guchar *bytes, gsize *len) |
| { |
| struct ril_msg *message; |
| unsigned message_len, plen; |
| |
| /* First four bytes are length in TCP byte order (Big Endian) */ |
| plen = ntohl(*((uint32_t *) (void *) bytes)); |
| bytes += 4; |
| |
| /* |
| * TODO: Verify that 8k is the max message size from rild. |
| * |
| * This condition shouldn't happen. If it does |
| * there are three options: |
| * |
| * 1) Exit; ofono will restart via DBus (this is what we do now) |
| * 2) Consume the bytes & continue |
| * 3) force a disconnect |
| */ |
| if (plen > GRIL_BUFFER_SIZE - 4) { |
| ofono_error("ERROR RIL parcel bigger than buffer (%u), exiting", |
| plen); |
| exit(1); |
| } |
| |
| /* |
| * If we don't have the whole fixed record in the ringbuffer |
| * then return NULL & leave ringbuffer as is. |
| */ |
| |
| message_len = *len - 4; |
| if (message_len < plen) |
| return NULL; |
| |
| message = g_malloc(sizeof(struct ril_msg)); |
| |
| /* allocate ril_msg->buffer */ |
| message->buf_len = plen; |
| message->buf = g_malloc(plen); |
| |
| /* Copy bytes into message buffer */ |
| memmove(message->buf, (const void *) bytes, plen); |
| |
| /* Indicate to caller size of record we extracted */ |
| *len = plen + 4; |
| return message; |
| } |
| |
| static void new_bytes(struct ring_buffer *rbuf, gpointer user_data) |
| { |
| struct ril_msg *message; |
| struct ril_s *p = user_data; |
| unsigned int len = ring_buffer_len(rbuf); |
| unsigned int wrap = ring_buffer_len_no_wrap(rbuf); |
| guchar *buf = ring_buffer_read_ptr(rbuf, p->read_so_far); |
| |
| p->in_read_handler = TRUE; |
| |
| while (p->suspended == FALSE && (p->read_so_far < len)) { |
| gsize rbytes = MIN(len - p->read_so_far, wrap - p->read_so_far); |
| |
| if (rbytes < 4) { |
| DBG("Not enough bytes for header length: len: %d", len); |
| return; |
| } |
| |
| /* |
| * This function attempts to read the next full length |
| * fixed message from the stream. if not all bytes are |
| * available, it returns NULL. otherwise it allocates |
| * and returns a ril_message with the copied bytes, and |
| * drains those bytes from the ring_buffer |
| */ |
| message = read_fixed_record(p, buf, &rbytes); |
| |
| /* wait for the rest of the record... */ |
| if (message == NULL) |
| break; |
| |
| buf += rbytes; |
| p->read_so_far += rbytes; |
| |
| /* TODO: need to better understand how wrap works! */ |
| if (p->read_so_far == wrap) { |
| buf = ring_buffer_read_ptr(rbuf, p->read_so_far); |
| wrap = len; |
| } |
| |
| dispatch(p, message); |
| |
| ring_buffer_drain(rbuf, p->read_so_far); |
| |
| len -= p->read_so_far; |
| wrap -= p->read_so_far; |
| p->read_so_far = 0; |
| } |
| |
| p->in_read_handler = FALSE; |
| |
| if (p->destroyed) |
| g_free(p); |
| } |
| |
| /* |
| * This function is a GIOFunc and may be called directly or via an IO watch. |
| * The return value controls whether the watch stays active ( TRUE ), or is |
| * removed ( FALSE ). |
| */ |
| static gboolean can_write_data(gpointer data) |
| { |
| struct ril_s *ril = data; |
| struct ril_request *req; |
| gsize bytes_written, towrite, len; |
| guint qlen, oqlen; |
| gint id; |
| gboolean written = TRUE; |
| guint i, j; |
| |
| qlen = g_queue_get_length(ril->command_queue); |
| if (qlen < 1) |
| return FALSE; |
| |
| /* if the whole request was not written */ |
| if (ril->req_bytes_written != 0) { |
| |
| for (i = 0; i < qlen; i++) { |
| req = g_queue_peek_nth(ril->command_queue, i); |
| if (req) { |
| id = GPOINTER_TO_INT(g_queue_peek_head( |
| ril->out_queue)); |
| if (req->id == id) |
| goto out; |
| } else { |
| return FALSE; |
| } |
| } |
| } |
| /* if no requests already sent */ |
| oqlen = g_queue_get_length(ril->out_queue); |
| if (oqlen < 1) { |
| req = g_queue_peek_head(ril->command_queue); |
| if (req == NULL) |
| return FALSE; |
| |
| g_queue_push_head(ril->out_queue, GINT_TO_POINTER(req->id)); |
| |
| goto out; |
| } |
| |
| for (i = 0; i < qlen; i++) { |
| req = g_queue_peek_nth(ril->command_queue, i); |
| if (req == NULL) |
| return FALSE; |
| |
| for (j = 0; j < oqlen; j++) { |
| id = GPOINTER_TO_INT( |
| g_queue_peek_nth(ril->out_queue, j)); |
| if (req->id == id) { |
| written = TRUE; |
| break; |
| } else { |
| written = FALSE; |
| } |
| } |
| |
| if (written == FALSE) |
| break; |
| } |
| |
| /* watcher fired though requests already written */ |
| if (written == TRUE) |
| return FALSE; |
| |
| g_queue_push_head(ril->out_queue, GINT_TO_POINTER(req->id)); |
| |
| out: |
| len = req->data_len; |
| |
| towrite = len - ril->req_bytes_written; |
| |
| #ifdef WRITE_SCHEDULER_DEBUG |
| if (towrite > 5) |
| towrite = 5; |
| #endif |
| |
| bytes_written = g_ril_io_write(ril->io, |
| req->data + ril->req_bytes_written, |
| towrite); |
| |
| if (bytes_written == 0) |
| return FALSE; |
| |
| ril->req_bytes_written += bytes_written; |
| if (bytes_written < towrite) |
| return TRUE; |
| else |
| ril->req_bytes_written = 0; |
| |
| return FALSE; |
| } |
| |
| static void ril_wakeup_writer(struct ril_s *ril) |
| { |
| g_ril_io_set_write_handler(ril->io, can_write_data, ril); |
| } |
| |
| static void ril_suspend(struct ril_s *ril) |
| { |
| ril->suspended = TRUE; |
| |
| g_ril_io_set_write_handler(ril->io, NULL, NULL); |
| g_ril_io_set_read_handler(ril->io, NULL, NULL); |
| g_ril_io_set_debug(ril->io, NULL, NULL); |
| } |
| |
| static gboolean ril_set_debug(struct ril_s *ril, |
| GRilDebugFunc func, gpointer user_data) |
| { |
| if (ril->io == NULL) |
| return FALSE; |
| |
| g_ril_io_set_debug(ril->io, func, user_data); |
| |
| return TRUE; |
| } |
| |
| static void ril_unref(struct ril_s *ril) |
| { |
| gboolean is_zero; |
| |
| is_zero = g_atomic_int_dec_and_test(&ril->ref_count); |
| |
| if (is_zero == FALSE) |
| return; |
| |
| if (ril->io) { |
| ril_suspend(ril); |
| g_ril_io_unref(ril->io); |
| ril->io = NULL; |
| ril_cleanup(ril); |
| } |
| |
| if (ril->in_read_handler) |
| ril->destroyed = TRUE; |
| else |
| g_free(ril); |
| } |
| |
| static gboolean node_compare_by_group(struct ril_notify_node *node, |
| gpointer userdata) |
| { |
| guint group = GPOINTER_TO_UINT(userdata); |
| |
| if (node->gid == group) |
| return TRUE; |
| |
| return FALSE; |
| } |
| |
| static struct ril_s *create_ril(const char *sock_path, unsigned int uid, |
| unsigned int gid) |
| |
| { |
| struct ril_s *ril; |
| struct sockaddr_un addr; |
| int sk; |
| int r; |
| GIOChannel *io; |
| |
| ril = g_try_new0(struct ril_s, 1); |
| if (ril == NULL) |
| return ril; |
| |
| ril->ref_count = 1; |
| ril->next_cmd_id = 1; |
| ril->next_notify_id = 1; |
| ril->next_gid = 0; |
| ril->req_bytes_written = 0; |
| ril->trace = FALSE; |
| |
| /* sock_path is allowed to be NULL for unit tests */ |
| if (sock_path == NULL) |
| return ril; |
| |
| sk = socket(AF_UNIX, SOCK_STREAM, 0); |
| if (sk < 0) { |
| ofono_error("create_ril: can't create unix socket: %s (%d)\n", |
| strerror(errno), errno); |
| goto error; |
| } |
| |
| memset(&addr, 0, sizeof(addr)); |
| addr.sun_family = AF_UNIX; |
| strncpy(addr.sun_path, sock_path, sizeof(addr.sun_path) - 1); |
| |
| /* Drop root user last, otherwise we won't be able to change egid */ |
| if (gid != 0 && setegid(gid) < 0) |
| ofono_error("%s: setegid(%d) failed: %s (%d)", |
| __func__, gid, strerror(errno), errno); |
| |
| if (uid != 0 && seteuid(uid) < 0) |
| ofono_error("%s: seteuid(%d) failed: %s (%d)", |
| __func__, uid, strerror(errno), errno); |
| |
| r = connect(sk, (struct sockaddr *) &addr, sizeof(addr)); |
| |
| /* Switch back to root as needed */ |
| if (uid && seteuid(0) < 0) |
| ofono_error("%s: seteuid(0) failed: %s (%d)", |
| __func__, strerror(errno), errno); |
| |
| if (gid && setegid(0) < 0) |
| ofono_error("%s: setegid(0) failed: %s (%d)", |
| __func__, strerror(errno), errno); |
| |
| if (r < 0) { |
| ofono_error("create_ril: can't connect to RILD: %s (%d)\n", |
| strerror(errno), errno); |
| goto error; |
| } |
| |
| io = g_io_channel_unix_new(sk); |
| if (io == NULL) { |
| ofono_error("create_ril: can't open RILD io channel: %s (%d)\n", |
| strerror(errno), errno); |
| goto error; |
| } |
| |
| g_io_channel_set_close_on_unref(io, TRUE); |
| g_io_channel_set_flags(io, G_IO_FLAG_NONBLOCK, NULL); |
| |
| ril->io = g_ril_io_new(io); |
| g_io_channel_unref(io); |
| |
| if (ril->io == NULL) { |
| ofono_error("create_ril: can't create ril->io"); |
| goto error; |
| } |
| |
| g_ril_io_set_disconnect_function(ril->io, io_disconnect, ril); |
| |
| ril->command_queue = g_queue_new(); |
| if (ril->command_queue == NULL) { |
| ofono_error("create_ril: Couldn't create command_queue."); |
| goto error; |
| } |
| |
| ril->out_queue = g_queue_new(); |
| if (ril->out_queue == NULL) { |
| ofono_error("create_ril: Couldn't create out_queue."); |
| goto error; |
| } |
| |
| ril->notify_list = g_hash_table_new_full(g_int_hash, g_int_equal, |
| g_free, |
| ril_notify_destroy); |
| |
| g_ril_io_set_read_handler(ril->io, new_bytes, ril); |
| |
| return ril; |
| |
| error: |
| ril_unref(ril); |
| |
| return NULL; |
| } |
| |
| static struct ril_notify *ril_notify_create(struct ril_s *ril, |
| const int req) |
| { |
| struct ril_notify *notify; |
| int *key; |
| |
| notify = g_try_new0(struct ril_notify, 1); |
| if (notify == NULL) |
| return 0; |
| |
| key = g_try_new0(int, 1); |
| if (key == NULL) |
| return 0; |
| |
| *key = req; |
| |
| g_hash_table_insert(ril->notify_list, key, notify); |
| |
| return notify; |
| } |
| |
| static void ril_cancel_group(struct ril_s *ril, guint group) |
| { |
| int n = 0; |
| guint len, i; |
| struct ril_request *req; |
| gboolean sent; |
| |
| if (ril->command_queue == NULL) |
| return; |
| |
| while ((req = g_queue_peek_nth(ril->command_queue, n)) != NULL) { |
| if (req->id == 0 || req->gid != group) { |
| n += 1; |
| continue; |
| } |
| |
| req->callback = NULL; |
| sent = FALSE; |
| |
| len = g_queue_get_length(ril->out_queue); |
| for (i = 0; i < len; i++) { |
| if (GPOINTER_TO_INT( |
| g_queue_peek_nth(ril->out_queue, i)) |
| == req->id) { |
| n += 1; |
| sent = TRUE; |
| break; |
| } |
| } |
| |
| if (sent) |
| continue; |
| |
| g_queue_remove(ril->command_queue, req); |
| ril_request_destroy(req); |
| } |
| } |
| |
| static guint ril_register(struct ril_s *ril, guint group, |
| const int req, GRilNotifyFunc func, |
| gpointer user_data) |
| { |
| struct ril_notify *notify; |
| struct ril_notify_node *node; |
| |
| if (ril->notify_list == NULL) |
| return 0; |
| |
| if (func == NULL) |
| return 0; |
| |
| notify = g_hash_table_lookup(ril->notify_list, &req); |
| |
| if (notify == NULL) |
| notify = ril_notify_create(ril, req); |
| |
| if (notify == NULL) |
| return 0; |
| |
| node = g_try_new0(struct ril_notify_node, 1); |
| if (node == NULL) |
| return 0; |
| |
| node->id = ril->next_notify_id++; |
| node->gid = group; |
| node->callback = func; |
| node->user_data = user_data; |
| |
| notify->nodes = g_slist_prepend(notify->nodes, node); |
| |
| return node->id; |
| } |
| |
| static gboolean ril_unregister(struct ril_s *ril, gboolean mark_only, |
| guint group, guint id) |
| { |
| GHashTableIter iter; |
| struct ril_notify *notify; |
| struct ril_notify_node *node; |
| gpointer key, value; |
| GSList *l; |
| |
| if (ril->notify_list == NULL) |
| return FALSE; |
| |
| g_hash_table_iter_init(&iter, ril->notify_list); |
| |
| while (g_hash_table_iter_next(&iter, &key, &value)) { |
| notify = value; |
| |
| l = g_slist_find_custom(notify->nodes, GUINT_TO_POINTER(id), |
| ril_notify_node_compare_by_id); |
| |
| if (l == NULL) |
| continue; |
| |
| node = l->data; |
| |
| if (node->gid != group) |
| return FALSE; |
| |
| if (mark_only) { |
| node->destroyed = TRUE; |
| return TRUE; |
| } |
| |
| ril_notify_node_destroy(node, NULL); |
| notify->nodes = g_slist_remove(notify->nodes, node); |
| |
| if (notify->nodes == NULL) |
| g_hash_table_iter_remove(&iter); |
| |
| return TRUE; |
| } |
| |
| return FALSE; |
| } |
| |
| void g_ril_init_parcel(const struct ril_msg *message, struct parcel *rilp) |
| { |
| /* Set up Parcel struct for proper parsing */ |
| rilp->data = message->buf; |
| rilp->size = message->buf_len; |
| rilp->capacity = message->buf_len; |
| rilp->offset = 0; |
| rilp->malformed = 0; |
| } |
| |
| GRil *g_ril_new_with_ucred(const char *sock_path, enum ofono_ril_vendor vendor, |
| unsigned int uid, unsigned int gid) |
| { |
| GRil *ril; |
| |
| ril = g_try_new0(GRil, 1); |
| if (ril == NULL) |
| return NULL; |
| |
| ril->parent = create_ril(sock_path, uid, gid); |
| if (ril->parent == NULL) { |
| g_free(ril); |
| return NULL; |
| } |
| |
| ril->group = ril->parent->next_gid++; |
| ril->ref_count = 1; |
| |
| ril->parent->vendor = vendor; |
| |
| return ril; |
| } |
| |
| GRil *g_ril_new(const char *sock_path, enum ofono_ril_vendor vendor) |
| { |
| return g_ril_new_with_ucred(sock_path, vendor, 0, 0); |
| } |
| |
| GRil *g_ril_clone(GRil *clone) |
| { |
| GRil *ril; |
| |
| if (clone == NULL) |
| return NULL; |
| |
| ril = g_try_new0(GRil, 1); |
| if (ril == NULL) |
| return NULL; |
| |
| ril->parent = clone->parent; |
| ril->group = ril->parent->next_gid++; |
| ril->ref_count = 1; |
| g_atomic_int_inc(&ril->parent->ref_count); |
| |
| return ril; |
| } |
| |
| GIOChannel *g_ril_get_channel(GRil *ril) |
| { |
| if (ril == NULL || ril->parent->io == NULL) |
| return NULL; |
| |
| return g_ril_io_get_channel(ril->parent->io); |
| |
| } |
| |
| GRilIO *g_ril_get_io(GRil *ril) |
| { |
| if (ril == NULL) |
| return NULL; |
| |
| return ril->parent->io; |
| } |
| |
| GRil *g_ril_ref(GRil *ril) |
| { |
| if (ril == NULL) |
| return NULL; |
| |
| g_atomic_int_inc(&ril->ref_count); |
| |
| return ril; |
| } |
| |
| gint g_ril_send(GRil *ril, const gint reqid, struct parcel *rilp, |
| GRilResponseFunc func, gpointer user_data, |
| GDestroyNotify notify) |
| { |
| struct ril_request *r; |
| struct ril_s *p; |
| |
| if (ril == NULL |
| || ril->parent == NULL |
| || ril->parent->command_queue == NULL) |
| return 0; |
| |
| p = ril->parent; |
| |
| r = ril_request_create(p, ril->group, reqid, p->next_cmd_id, rilp, |
| func, user_data, notify, FALSE); |
| |
| if (rilp != NULL) |
| parcel_free(rilp); |
| |
| if (r == NULL) |
| return 0; |
| |
| p->next_cmd_id++; |
| |
| g_queue_push_tail(p->command_queue, r); |
| |
| ril_wakeup_writer(p); |
| |
| if (rilp == NULL) |
| g_ril_print_request_no_args(ril, r->id, reqid); |
| else |
| g_ril_print_request(ril, r->id, reqid); |
| |
| return r->id; |
| } |
| |
| void g_ril_unref(GRil *ril) |
| { |
| gboolean is_zero; |
| |
| if (ril == NULL) |
| return; |
| |
| is_zero = g_atomic_int_dec_and_test(&ril->ref_count); |
| |
| if (is_zero == FALSE) |
| return; |
| |
| ril_cancel_group(ril->parent, ril->group); |
| g_ril_unregister_all(ril); |
| ril_unref(ril->parent); |
| |
| g_free(ril); |
| } |
| |
| gboolean g_ril_get_trace(GRil *ril) |
| { |
| |
| if (ril == NULL || ril->parent == NULL) |
| return FALSE; |
| |
| return ril->parent->trace; |
| } |
| |
| gboolean g_ril_set_trace(GRil *ril, gboolean trace) |
| { |
| |
| if (ril == NULL || ril->parent == NULL) |
| return FALSE; |
| |
| return ril->parent->trace = trace; |
| } |
| |
| gboolean g_ril_set_slot(GRil *ril, int slot) |
| { |
| if (ril == NULL || ril->parent == NULL) |
| return FALSE; |
| |
| ril->parent->slot = slot; |
| return TRUE; |
| } |
| |
| int g_ril_get_slot(GRil *ril) |
| { |
| if (ril == NULL) |
| return 0; |
| |
| return ril->parent->slot; |
| } |
| |
| gboolean g_ril_set_debugf(GRil *ril, |
| GRilDebugFunc func, gpointer user_data) |
| { |
| |
| if (ril == NULL || ril->group != 0) |
| return FALSE; |
| |
| return ril_set_debug(ril->parent, func, user_data); |
| } |
| |
| gboolean g_ril_set_vendor_print_msg_id_funcs(GRil *ril, |
| GRilMsgIdToStrFunc req_to_string, |
| GRilMsgIdToStrFunc unsol_to_string) |
| { |
| if (ril == NULL || ril->parent == NULL) |
| return FALSE; |
| |
| ril->parent->req_to_string = req_to_string; |
| ril->parent->unsol_to_string = unsol_to_string; |
| |
| return TRUE; |
| } |
| |
| guint g_ril_register(GRil *ril, const int req, |
| GRilNotifyFunc func, gpointer user_data) |
| { |
| if (ril == NULL) |
| return 0; |
| |
| return ril_register(ril->parent, ril->group, req, |
| func, user_data); |
| } |
| |
| gboolean g_ril_unregister(GRil *ril, guint id) |
| { |
| if (ril == NULL) |
| return FALSE; |
| |
| return ril_unregister(ril->parent, ril->parent->in_notify, |
| ril->group, id); |
| } |
| |
| gboolean g_ril_unregister_all(GRil *ril) |
| { |
| if (ril == NULL) |
| return FALSE; |
| |
| return ril_unregister_all(ril->parent, |
| ril->parent->in_notify, |
| node_compare_by_group, |
| GUINT_TO_POINTER(ril->group)); |
| } |
| |
| enum ofono_ril_vendor g_ril_vendor(GRil *ril) |
| { |
| if (ril == NULL) |
| return OFONO_RIL_VENDOR_AOSP; |
| |
| return ril->parent->vendor; |
| } |
| |
| const char *g_ril_request_id_to_string(GRil *ril, int req) |
| { |
| return request_id_to_string(ril->parent, req); |
| } |
| |
| const char *g_ril_unsol_request_to_string(GRil *ril, int req) |
| { |
| return unsol_request_to_string(ril->parent, req); |
| } |