/* take it out of global pending list */
chan_add_ref(client, ref); /* add local ref */
list_delete(&client->node);
chan_del_ref(client, &client->node_ref); /* drop list ref */
}
struct ipc_port* port = containerof(phandle, struct ipc_port, handle);
LTRACEF("shutting down port %p\n", port);
/* detach it from global list if it is in the list */
mutex_acquire(&ipc_port_lock); if (list_in_list(&port->node)) {
list_delete(&port->node);
}
mutex_release(&ipc_port_lock);
/* tear down pending connections */ struct ipc_chan *server, *temp;
list_for_every_entry_safe(&port->pending_list, server, temp, struct ipc_chan, node) { /* Check if the port being shutdown has been registered by an *application.Ifso,detachtheclientconnectionandputit *backinthewaitingforportlist
*/ if (is_startup_port) { bool client_connecting = false; /* Get a local ref to the client*/ struct obj_ref tmp_client_ref =
OBJ_REF_INITIAL_VALUE(tmp_client_ref);
chan_add_ref(server->peer, &tmp_client_ref);
/* Add client to waiting_for_port list */
mutex_acquire(&ipc_port_lock); if (client->state == IPC_CHAN_STATE_CONNECTING)
add_to_waiting_for_port_list_locked(client);
mutex_release(&ipc_port_lock);
}
/* Drop local ref */
chan_del_ref(client, &tmp_client_ref);
}
/* remove connection from the list */
mutex_acquire(&server->mlock);
list_delete(&server->node);
chan_del_ref(server, &server->node_ref); /* drop list ref */
mutex_release(&server->mlock);
/* pending server channel in not in user context table *butweneedtodecrementreftogetridofit
*/
handle_decref(&server->handle);
}
}
/* invoke port shutdown first */
port_shutdown(phandle);
struct ipc_port* port = containerof(phandle, struct ipc_port, handle);
/* pending list should be empty and nodeshouldnotbeinthelist
*/
DEBUG_ASSERT(list_is_empty(&port->pending_list));
DEBUG_ASSERT(!list_in_list(&port->node));
LTRACEF("destroying port %p ('%s')\n", port, port->path);
free(port);
}
/* *Makespecifiedportpublicallyavailableforoperation.
*/ int ipc_port_publish(struct handle* phandle) { int ret = NO_ERROR;
struct ipc_port* port = containerof(phandle, struct ipc_port, handle);
DEBUG_ASSERT(!list_in_list(&port->node));
/* Check for duplicates */
mutex_acquire(&ipc_port_lock); if (port_find_locked(port->path)) {
LTRACEF("path already exists\n");
ret = ERR_ALREADY_EXISTS;
} else {
port->state = IPC_PORT_STATE_LISTENING;
list_add_tail(&ipc_port_list, &port->node);
/* go through pending connection list and pick those we can handle */ struct ipc_chan *client, *temp; struct obj_ref tmp_client_ref = OBJ_REF_INITIAL_VALUE(tmp_client_ref);
list_for_every_entry_safe(&waiting_for_port_chan_list, client, temp, struct ipc_chan, node) { if (strcmp(client->path, port->path)) continue;
/* try to attach port */ int err = port_attach_client(port, client); if (err) { /* failed to attach port: close channel */
LTRACEF("failed (%d) to attach_port\n", err);
chan_shutdown(client);
}
chan_del_ref(client, &tmp_client_ref); /* drop local ref */
}
}
mutex_release(&ipc_port_lock);
/* copy path from user space */
ret = (int)strlcpy_from_user(tmp_path, path, sizeof(tmp_path)); if (ret < 0) return (long)ret;
if ((uint)ret >= sizeof(tmp_path)) { /* string is too long */ return ERR_INVALID_ARGS;
}
/* create new port */
ret = ipc_port_create(&tapp->props.uuid, tmp_path, (uint)num_recv_bufs,
recv_buf_size, flags, &port_handle); if (ret != NO_ERROR) goto err_port_create;
/* install handle into user context */
ret = uctx_handle_install(ctx, port_handle, &handle_id); if (ret != NO_ERROR) goto err_install;
/* publish for normal operation */
ret = ipc_port_publish(port_handle); if (ret != NO_ERROR) goto err_publish;
staticvoid chan_shutdown_locked(struct ipc_chan* chan) { /* Remove channel from any list it might be in */ if (list_in_list(&chan->node)) {
list_delete(&chan->node);
chan_del_ref(chan, &chan->node_ref);
if (chan->flags & IPC_CHAN_FLAG_SERVER) { /* If we are shutting down the server side of the channel pair *anditwasinthelist(assumeacceptpending_list)wealso *needtoremovehandlerefbecauseitisdangling.Wecannot *callhandle_decrefherebecauseoflocksbutwecanjust *removechan_del_refdirectlywhichwouldgiveusthesame *effect.Inadditionwealsoshoulddetachpeer.
*/ if (chan->peer) {
chan_del_ref(chan->peer, &chan->peer_ref);
chan->peer = NULL;
}
chan_del_ref(chan, &chan->handle_ref);
}
}
switch (chan->state) { case IPC_CHAN_STATE_CONNECTED: case IPC_CHAN_STATE_CONNECTING:
chan->state = IPC_CHAN_STATE_DISCONNECTING;
handle_notify(&chan->handle); break; case IPC_CHAN_STATE_ACCEPTING:
chan->state = IPC_CHAN_STATE_DISCONNECTING; break; default: /* no op */ break;
}
}
mutex_acquire(&chan->mlock); /* peer is closing connection */ if (chan->state == IPC_CHAN_STATE_DISCONNECTING) {
events |= IPC_HANDLE_POLL_HUP;
}
/* server accepted our connection */ if (chan->aux_state & IPC_CHAN_AUX_STATE_CONNECTED) {
events |= IPC_HANDLE_POLL_READY;
}
/* have a pending message? */ if (chan->msg_queue && !ipc_msg_queue_is_empty(chan->msg_queue)) {
events |= IPC_HANDLE_POLL_MSG;
}
/* check if we were send blocked */ if (chan->aux_state & IPC_CHAN_AUX_STATE_SEND_UNBLOCKED) {
events |= IPC_HANDLE_POLL_SEND_UNBLOCKED;
}
events &= emask; if (finalize) { if (events & IPC_HANDLE_POLL_READY) {
chan->aux_state &= ~IPC_CHAN_AUX_STATE_CONNECTED;
} if (events & IPC_HANDLE_POLL_SEND_UNBLOCKED) {
chan->aux_state &= ~IPC_CHAN_AUX_STATE_SEND_UNBLOCKED;
}
}
mutex_release(&chan->mlock); return events;
}
/* *Checkifconnectiontospecifiedportisallowed
*/ int ipc_port_check_access_peer_id(uint32_t port_flags, conststruct trusty_peer_id* peer_id,
size_t peer_id_len) { if (!peer_id) return ERR_ACCESS_DENIED;
if (is_ns_client(peer_id, peer_id_len)) { /* check if this port allows connection from NS clients */ if (port_flags & IPC_PORT_ALLOW_NS_CONNECT) return NO_ERROR;
} else { /* check if this port allows connection from Trusted Apps */ if (port_flags & IPC_PORT_ALLOW_TA_CONNECT) return NO_ERROR;
}
if (port->state != IPC_PORT_STATE_LISTENING) {
LTRACEF("port %s is not in listening state (%d)\n", port->path,
port->state); return ERR_NOT_READY;
}
/* check if we are allowed to connect */
ret = ipc_port_check_access_peer_id(
port->flags, trusty_peer_id_storage_to_trusty_peer_id(&client->id),
client->id_len); if (ret != NO_ERROR) {
LTRACEF("access denied: %d\n", ret); return ret;
}
server = chan_alloc(IPC_CHAN_FLAG_SERVER, NULL, 0); if (!server) {
LTRACEF("failed to alloc server\n"); return ERR_NO_MEMORY;
}
/* allocate msg queues */
ret = ipc_msg_queue_create(port->num_recv_bufs, port->recv_buf_size,
&client->msg_queue); if (ret != NO_ERROR) {
LTRACEF("failed to alloc mq: %d\n", ret); goto err_client_mq;
}
ret = ipc_msg_queue_create(port->num_recv_bufs, port->recv_buf_size,
&server->msg_queue); if (ret != NO_ERROR) {
LTRACEF("failed to alloc mq: %d\n", ret); goto err_server_mq;
}
if (!client_peer_id) { /* client peer ID is required */
TRACEF("client peer ID is required\n"); return ERR_INVALID_ARGS;
}
size_t len = strnlen(path, max_path); if (len == 0 || len >= max_path) { /* unterminated string */
TRACEF("invalid path specified\n"); return ERR_INVALID_ARGS;
} /* After this point path is zero terminated */
/* allocate channel pair */
client = chan_alloc(0, client_peer_id, client_peer_id_len); if (!client) {
TRACEF("failed to alloc client\n"); return ERR_NO_MEMORY;
}
LTRACEF("Connecting to '%s'\n", path);
mutex_acquire(&ipc_port_lock);
port = port_find_locked(path); if (port) { /* found */
ret = port_attach_client(port, client); if (ret) goto err_attach_client;
} else { /* *Checkifanapphasregisteredtobestartedonconnections *tothisport
*/
ret = trusty_app_request_start_by_port(path, client_peer_id,
client_peer_id_len); switch (ret) { case NO_ERROR: case ERR_ALREADY_STARTED: break; case ERR_NOT_FOUND: /* *Apphasnotbeenloadedyet,butwewaitforitifthecaller *askedto
*/ if (flags & IPC_CONNECT_WAIT_FOR_PORT) { break;
}
__FALLTHROUGH; default: goto err_find_ports;
}
/* port not found, add connection to waiting_for_port_chan_list */
client->path = strdup(path); if (!client->path) {
ret = ERR_NO_MEMORY; goto err_alloc_path;
}
/* add it to waiting for port list */
add_to_waiting_for_port_list_locked(client);
}
ret = (int)strlcpy_from_user(tmp_path, path, sizeof(tmp_path)); if (ret < 0) return (long)ret;
if ((uint)ret >= sizeof(tmp_path)) return (long)ERR_INVALID_ARGS;
/* try to connect to event first */
ret = event_source_open(&tapp->props.uuid, tmp_path, sizeof(tmp_path), 0,
&chandle); if (ret == NO_ERROR) { goto install;
}
/* if an error is other then ERR_NOT_FOUND return immediately */ if (ret != ERR_NOT_FOUND) { return ret;
}
/* then regular port */
ret = ipc_port_connect_async(&tapp->props.uuid, tmp_path, sizeof(tmp_path),
flags, &chandle); if (ret != NO_ERROR) return (long)ret;
install:
ret = uctx_handle_install(ctx, chandle, &handle_id); if (ret != NO_ERROR) { /* Failed to install handle into user context */
handle_decref(chandle); return (long)ret;
}
handle_decref(chandle); return (long)handle_id;
}
/* *Calledbyusertasktoacceptincommingconnection
*/ int ipc_port_accept(struct handle* phandle, struct handle** chandle_ptr, const uuid_t** uuid_ptr) { conststruct trusty_peer_id* id_ptr;
size_t id_len; int ret = ipc_port_accept_peer_id(phandle, chandle_ptr, &id_ptr, &id_len); if (ret != NO_ERROR) return ret;
/* verify that we received a uuid */ if (id_ptr->kind != TRUSTY_PEER_ID_KIND_UUID) goto err_bad_state; if (id_len != sizeof(struct trusty_peer_id_uuid)) goto err_bad_state;
if (!phandle || !ipc_is_port(phandle)) {
LTRACEF("invalid port handle %p\n", phandle); return ERR_INVALID_ARGS;
}
port = containerof(phandle, struct ipc_port, handle);
if (port->state != IPC_PORT_STATE_LISTENING) { /* Not in listening state: caller should close port. *isitreallypossibletogethere?
*/ return ERR_CHANNEL_CLOSED;
}
/* get next pending connection */
mutex_acquire(&ipc_port_lock);
server = list_remove_head_type(&port->pending_list, struct ipc_chan, node);
mutex_release(&ipc_port_lock); if (!server) { /* TODO: should we block waiting for a new connection if one *isnotpending?ifso,needanoptionalargumentmaybe.
*/ return ERR_NO_MSG;
}
/* it must be a server side channel */
DEBUG_ASSERT(server->flags & IPC_CHAN_FLAG_SERVER);
chan_del_ref(server, &server->node_ref); /* drop list ref */
client = server->peer;
/* there must be a client, it must be in CONNECTING state and
server must be in ACCEPTING state */
ASSERT(client);
mutex_acquire(&client->mlock); if (server->state != IPC_CHAN_STATE_ACCEPTING ||
client->state != IPC_CHAN_STATE_CONNECTING) {
LTRACEF("Drop connection: client %p (0x%x) to server %p (0x%x):\n",
client, client->state, server, server->state);
mutex_release(&client->mlock);
handle_decref(&server->handle); return ERR_CHANNEL_CLOSED;
}
/* move both client and server into connected state */
server->state = IPC_CHAN_STATE_CONNECTED;
client->state = IPC_CHAN_STATE_CONNECTED;
client->aux_state |= IPC_CHAN_AUX_STATE_CONNECTED;
/* init server channel handle and return it to caller */
*chandle_ptr = &server->handle;
*peer_id_ptr = trusty_peer_id_storage_to_trusty_peer_id(&client->id);
*peer_id_len = client->id_len;
/* copy the size of the peer id from userspace */
ret = copy_from_user(&peer_id_size_in, user_peer_id_size, sizeof(uint32_t)); if (ret != NO_ERROR) return ret;
/* return if the size is too small */ if (peer_id_size_in < sizeof(struct trusty_peer_id)) return ERR_INVALID_ARGS;
/* retrieve the handle from the user context */
ret = uctx_handle_get(ctx, handle_id, &phandle); if (ret != NO_ERROR) return ret;
/* accept the connection request */
size_t peer_id_len;
ret = ipc_port_accept_peer_id(phandle, &chandle, &peer_id_ptr,
&peer_id_len); if (ret != NO_ERROR) goto err_accept;
/* make sure the size we got can fit the returned peer id */ if (peer_id_size_in < peer_id_len) {
ret = ERR_INVALID_ARGS; goto err_size_check;
}
/* install the returned handle into the user context */
ret = uctx_handle_install(ctx, chandle, &new_id); if (ret != NO_ERROR) goto err_install;
/* copy the peer id into userspace */
ret = copy_to_user(user_peer_id, peer_id_ptr, peer_id_len); if (ret != NO_ERROR) goto err_id_copy;
/* copy the peer id size into userspace */ const uint32_t peer_id_size_out = sizeof(struct trusty_peer_id_uuid);
ret = copy_to_user(user_peer_id_size, &peer_id_size_out, sizeof(uint32_t)); if (ret != NO_ERROR) goto err_id_copy;
/* clean up */
handle_decref(chandle);
handle_decref(phandle); return (long)new_id;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.