1 /*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 /*
18 * Contains helper routines dealing with syncronous access to a non-blocking
19 * sokets.
20 */
21
22 #include "qemu-common.h"
23 #include "errno.h"
24 #include "iolooper.h"
25 #include "sockets.h"
26 #include "android/utils/debug.h"
27 #include "android/sync-utils.h"
28 #include "android/utils/system.h"
29
30 #define D(...) do { if (VERBOSE_CHECK(init)) dprint(__VA_ARGS__); } while (0)
31
32 struct SyncSocket {
33 // Helper for performing synchronous I/O on the socket.
34 IoLooper* iolooper;
35
36 /* Opened socket handle. */
37 int fd;
38 };
39
40 SyncSocket*
syncsocket_init(int fd)41 syncsocket_init(int fd)
42 {
43 SyncSocket* sync_socket;
44 ANEW0(sync_socket);
45
46 socket_set_nonblock(fd);
47 sync_socket->iolooper = iolooper_new();
48 sync_socket->fd = fd;
49
50 return sync_socket;
51 }
52
53 SyncSocket*
syncsocket_connect(int fd,SockAddress * sockaddr,int timeout)54 syncsocket_connect(int fd, SockAddress* sockaddr, int timeout)
55 {
56 IoLooper* looper;
57 int connect_status;
58 SyncSocket* sync_socket = NULL;
59
60 socket_set_nonblock(fd);
61
62 for(;;) {
63 connect_status = socket_connect(fd, sockaddr);
64 if (connect_status >= 0) {
65 // Connected. Create IoLooper for the helper.
66 looper = iolooper_new();
67 break;
68 }
69
70 if (errno == EINPROGRESS || errno == EAGAIN || errno == EWOULDBLOCK) {
71 // Connection is in progress. Wait till it's finished.
72 looper = iolooper_new();
73 iolooper_add_write(looper, fd);
74 connect_status = iolooper_wait(looper, timeout);
75 if (connect_status > 0) {
76 iolooper_del_write(looper, fd);
77 break;
78 } else {
79 iolooper_free(looper);
80 return NULL;
81 }
82 } else if (errno != EINTR) {
83 return NULL;
84 }
85 }
86
87 // We're now connected. Lets initialize SyncSocket instance
88 // for this connection.
89 sync_socket = malloc(sizeof(SyncSocket));
90 if (sync_socket == NULL) {
91 derror("PANIC: not enough memory\n");
92 exit(1);
93 }
94
95 sync_socket->iolooper = looper;
96 sync_socket->fd = fd;
97
98 return sync_socket;
99 }
100
101 void
syncsocket_close(SyncSocket * ssocket)102 syncsocket_close(SyncSocket* ssocket)
103 {
104 if (ssocket != NULL && ssocket->fd >= 0) {
105 if (ssocket->iolooper != NULL) {
106 iolooper_reset(ssocket->iolooper);
107 }
108 socket_close(ssocket->fd);
109 ssocket->fd = -1;
110 }
111 }
112
113 void
syncsocket_free(SyncSocket * ssocket)114 syncsocket_free(SyncSocket* ssocket)
115 {
116 if (ssocket != NULL) {
117 if (ssocket->iolooper != NULL) {
118 iolooper_free(ssocket->iolooper);
119 }
120 free(ssocket);
121 }
122 }
123
124 int
syncsocket_start_read(SyncSocket * ssocket)125 syncsocket_start_read(SyncSocket* ssocket)
126 {
127 if (ssocket == NULL || ssocket->fd < 0 || ssocket->iolooper == NULL) {
128 errno = EINVAL;
129 return -1;
130 }
131 iolooper_add_read(ssocket->iolooper, ssocket->fd);
132 return 0;
133 }
134
135 int
syncsocket_stop_read(SyncSocket * ssocket)136 syncsocket_stop_read(SyncSocket* ssocket)
137 {
138 if (ssocket == NULL || ssocket->fd < 0 || ssocket->iolooper == NULL) {
139 errno = EINVAL;
140 return -1;
141 }
142 iolooper_del_read(ssocket->iolooper, ssocket->fd);
143 return 0;
144 }
145
146 int
syncsocket_start_write(SyncSocket * ssocket)147 syncsocket_start_write(SyncSocket* ssocket)
148 {
149 if (ssocket == NULL || ssocket->fd < 0 || ssocket->iolooper == NULL) {
150 errno = EINVAL;
151 return -1;
152 }
153 iolooper_add_write(ssocket->iolooper, ssocket->fd);
154 return 0;
155 }
156
157 int
syncsocket_stop_write(SyncSocket * ssocket)158 syncsocket_stop_write(SyncSocket* ssocket)
159 {
160 if (ssocket == NULL || ssocket->fd < 0 || ssocket->iolooper == NULL) {
161 errno = EINVAL;
162 return -1;
163 }
164 iolooper_del_write(ssocket->iolooper, ssocket->fd);
165 return 0;
166 }
167
168 ssize_t
syncsocket_read_absolute(SyncSocket * ssocket,void * buf,size_t size,int64_t deadline)169 syncsocket_read_absolute(SyncSocket* ssocket,
170 void* buf,
171 size_t size,
172 int64_t deadline)
173 {
174 int ret;
175
176 if (ssocket == NULL || ssocket->fd < 0 || ssocket->iolooper == NULL) {
177 errno = EINVAL;
178 return -1;
179 }
180
181 ret = iolooper_wait_absolute(ssocket->iolooper, deadline);
182 if (ret > 0) {
183 if (!iolooper_is_read(ssocket->iolooper, ssocket->fd)) {
184 D("%s: Internal error, iolooper_is_read() not set!", __FUNCTION__);
185 return -1;
186 }
187 do {
188 ret = socket_recv(ssocket->fd, buf, size);
189 } while( ret < 0 && errno == EINTR);
190 } else if (ret == 0) {
191 // Timed out
192 errno = ETIMEDOUT;
193 ret = -1;
194 }
195 return ret;
196 }
197
198 ssize_t
syncsocket_read(SyncSocket * ssocket,void * buf,size_t size,int timeout)199 syncsocket_read(SyncSocket* ssocket, void* buf, size_t size, int timeout)
200 {
201 return syncsocket_read_absolute(ssocket, buf, size, iolooper_now() + timeout);
202 }
203
204 ssize_t
syncsocket_write_absolute(SyncSocket * ssocket,const void * buf,size_t size,int64_t deadline)205 syncsocket_write_absolute(SyncSocket* ssocket,
206 const void* buf,
207 size_t size,
208 int64_t deadline)
209 {
210 int ret;
211 size_t written = 0;
212
213 if (ssocket == NULL || ssocket->fd < 0 || ssocket->iolooper == NULL) {
214 errno = EINVAL;
215 return -1;
216 }
217
218 do {
219 ret = iolooper_wait_absolute(ssocket->iolooper, deadline);
220 if (ret < 0) {
221 return ret;
222 } else if (ret == 0) {
223 // Timeout.
224 errno = ETIMEDOUT;
225 return -1;
226 }
227 if (!iolooper_is_write(ssocket->iolooper, ssocket->fd)) {
228 D("%s: Internal error, iolooper_is_write() not set!", __FUNCTION__);
229 return -1;
230 }
231
232 do {
233 ret = socket_send(ssocket->fd, (const char*)buf + written, size - written);
234 } while( ret < 0 && errno == EINTR);
235
236 if (ret > 0) {
237 written += ret;
238 } else if (ret < 0) {
239 if (errno != EAGAIN && errno != EWOULDBLOCK) {
240 return -1;
241 }
242 } else {
243 // Disconnected.
244 errno = ECONNRESET;
245 return -1;
246 }
247 } while (written < size);
248 return (int)written;
249 }
250
251 ssize_t
syncsocket_write(SyncSocket * ssocket,const void * buf,size_t size,int timeout)252 syncsocket_write(SyncSocket* ssocket, const void* buf, size_t size, int timeout)
253 {
254 return syncsocket_write_absolute(ssocket, buf, size, iolooper_now() + timeout);
255 }
256
257 ssize_t
syncsocket_read_line_absolute(SyncSocket * ssocket,char * buffer,size_t size,int64_t deadline)258 syncsocket_read_line_absolute(SyncSocket* ssocket,
259 char* buffer,
260 size_t size,
261 int64_t deadline)
262 {
263 size_t read_chars = 0;
264
265 while (read_chars < size) {
266 char ch;
267 int ret = syncsocket_read_absolute(ssocket, &ch, 1, deadline);
268 if (ret <= 0) {
269 return ret;
270 }
271 buffer[read_chars++] = ch;
272 if (ch == '\n') {
273 return read_chars;
274 }
275 }
276
277 /* Not enough room in the input buffer!*/
278 errno = ENOMEM;
279 return -1;
280 }
281
282 ssize_t
syncsocket_read_line(SyncSocket * ssocket,char * buffer,size_t size,int timeout)283 syncsocket_read_line(SyncSocket* ssocket, char* buffer, size_t size, int timeout)
284 {
285 return syncsocket_read_line_absolute(ssocket, buffer, size,
286 iolooper_now() + timeout);
287 }
288
289 int
syncsocket_get_socket(SyncSocket * ssocket)290 syncsocket_get_socket(SyncSocket* ssocket)
291 {
292 return (ssocket != NULL) ? ssocket->fd : -1;
293 }
294