1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <lib/async/wait.h>
6 #include <lib/fidl-async/bind.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <zircon/syscalls.h>
10 
11 typedef struct fidl_binding {
12     async_wait_t wait;
13     fidl_dispatch_t* dispatch;
14     async_dispatcher_t* dispatcher;
15     void* ctx;
16     const void* ops;
17 } fidl_binding_t;
18 
19 typedef struct fidl_connection {
20     fidl_txn_t txn;
21     zx_handle_t channel;
22     zx_txid_t txid;
23     fidl_binding_t* binding;
24 } fidl_connection_t;
25 
fidl_reply(fidl_txn_t * txn,const fidl_msg_t * msg)26 static zx_status_t fidl_reply(fidl_txn_t* txn, const fidl_msg_t* msg) {
27     fidl_connection_t* conn = (fidl_connection_t*)txn;
28     if (conn->txid == 0u)
29         return ZX_ERR_BAD_STATE;
30     if (msg->num_bytes < sizeof(fidl_message_header_t))
31         return ZX_ERR_INVALID_ARGS;
32     fidl_message_header_t* hdr = (fidl_message_header_t*)msg->bytes;
33     hdr->txid = conn->txid;
34     conn->txid = 0u;
35     return zx_channel_write(conn->channel, 0, msg->bytes, msg->num_bytes,
36                             msg->handles, msg->num_handles);
37 }
38 
fidl_binding_destroy(fidl_binding_t * binding)39 static void fidl_binding_destroy(fidl_binding_t* binding) {
40     zx_handle_close(binding->wait.object);
41     free(binding);
42 }
43 
fidl_message_handler(async_dispatcher_t * dispatcher,async_wait_t * wait,zx_status_t status,const zx_packet_signal_t * signal)44 static void fidl_message_handler(async_dispatcher_t* dispatcher,
45                                  async_wait_t* wait,
46                                  zx_status_t status,
47                                  const zx_packet_signal_t* signal) {
48     fidl_binding_t* binding = (fidl_binding_t*)wait;
49     if (status != ZX_OK) {
50         goto shutdown;
51     }
52 
53     if (signal->observed & ZX_CHANNEL_READABLE) {
54         char bytes[ZX_CHANNEL_MAX_MSG_BYTES];
55         zx_handle_t handles[ZX_CHANNEL_MAX_MSG_HANDLES];
56         for (uint64_t i = 0; i < signal->count; i++) {
57             fidl_msg_t msg = {
58                 .bytes = bytes,
59                 .handles = handles,
60                 .num_bytes = 0u,
61                 .num_handles = 0u,
62             };
63             status = zx_channel_read(wait->object, 0, bytes, handles,
64                                      ZX_CHANNEL_MAX_MSG_BYTES,
65                                      ZX_CHANNEL_MAX_MSG_HANDLES,
66                                      &msg.num_bytes, &msg.num_handles);
67             if (status == ZX_ERR_SHOULD_WAIT) {
68                 break;
69             }
70             if (status != ZX_OK || msg.num_bytes < sizeof(fidl_message_header_t)) {
71                 goto shutdown;
72             }
73             fidl_message_header_t* hdr = (fidl_message_header_t*)msg.bytes;
74             fidl_connection_t conn = {
75                 .txn.reply = fidl_reply,
76                 .channel = wait->object,
77                 .txid = hdr->txid,
78                 .binding = binding,
79             };
80             status = binding->dispatch(binding->ctx, &conn.txn, &msg, binding->ops);
81             switch (status) {
82             case ZX_OK:
83                 status = async_begin_wait(dispatcher, wait);
84                 if (status != ZX_OK) {
85                     goto shutdown;
86                 }
87                 return;
88             case ZX_ERR_ASYNC:
89                 return;
90             default:
91                 goto shutdown;
92             }
93         }
94     }
95 
96 shutdown:
97     fidl_binding_destroy(binding);
98 }
99 
fidl_bind(async_dispatcher_t * dispatcher,zx_handle_t channel,fidl_dispatch_t * dispatch,void * ctx,const void * ops)100 zx_status_t fidl_bind(async_dispatcher_t* dispatcher, zx_handle_t channel,
101                       fidl_dispatch_t* dispatch, void* ctx, const void* ops) {
102     fidl_binding_t* binding = calloc(1, sizeof(fidl_binding_t));
103     binding->wait.handler = fidl_message_handler;
104     binding->wait.object = channel;
105     binding->wait.trigger = ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED;
106     binding->dispatch = dispatch;
107     binding->dispatcher = dispatcher;
108     binding->ctx = ctx;
109     binding->ops = ops;
110     zx_status_t status = async_begin_wait(dispatcher, &binding->wait);
111     if (status != ZX_OK) {
112         fidl_binding_destroy(binding);
113     }
114     return status;
115 }
116 
117 typedef struct fidl_async_txn {
118     fidl_connection_t connection;
119 } fidl_async_txn_t;
120 
fidl_async_txn_create(fidl_txn_t * txn)121 fidl_async_txn_t* fidl_async_txn_create(fidl_txn_t* txn) {
122     fidl_connection_t* connection = (fidl_connection_t*) txn;
123 
124     fidl_async_txn_t* async_txn = calloc(1, sizeof(fidl_async_txn_t));
125     memcpy(&async_txn->connection, connection, sizeof(*connection));
126 
127     return async_txn;
128 }
129 
fidl_async_txn_borrow(fidl_async_txn_t * async_txn)130 fidl_txn_t* fidl_async_txn_borrow(fidl_async_txn_t* async_txn) {
131     return &async_txn->connection.txn;
132 }
133 
fidl_async_txn_complete(fidl_async_txn_t * async_txn,bool rebind)134 zx_status_t fidl_async_txn_complete(fidl_async_txn_t* async_txn, bool rebind) {
135     zx_status_t status = ZX_OK;
136     if (rebind) {
137         status = async_begin_wait(async_txn->connection.binding->dispatcher,
138                                   &async_txn->connection.binding->wait);
139         if (status == ZX_OK) {
140             return ZX_OK;
141         }
142     }
143 
144     fidl_binding_destroy(async_txn->connection.binding);
145     free(async_txn);
146     return status;
147 }
148