sync-utils.c revision 9411a562e1ab772732a4d5147c9103a638837c82
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
32struct SyncSocket {
33    // Helper for performing synchronous I/O on the socket.
34    IoLooper* iolooper;
35
36    /* Opened socket handle. */
37    int fd;
38};
39
40SyncSocket*
41syncsocket_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
53SyncSocket*
54syncsocket_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            } else {
78                iolooper_free(looper);
79                return NULL;
80            }
81        } else if (errno != EINTR) {
82            return NULL;
83        }
84    }
85
86    // We're now connected. Lets initialize SyncSocket instance
87    // for this connection.
88    sync_socket = malloc(sizeof(SyncSocket));
89    if (sync_socket == NULL) {
90        derror("PANIC: not enough memory\n");
91        exit(1);
92    }
93
94    sync_socket->iolooper = looper;
95    sync_socket->fd = fd;
96
97    return sync_socket;
98}
99
100void
101syncsocket_close(SyncSocket* ssocket)
102{
103    if (ssocket != NULL && ssocket->fd >= 0) {
104        if (ssocket->iolooper != NULL) {
105            iolooper_reset(ssocket->iolooper);
106        }
107        socket_close(ssocket->fd);
108        ssocket->fd = -1;
109    }
110}
111
112void
113syncsocket_free(SyncSocket* ssocket)
114{
115    if (ssocket != NULL) {
116        syncsocket_close(ssocket);
117        if (ssocket->iolooper != NULL) {
118            iolooper_free(ssocket->iolooper);
119        }
120        free(ssocket);
121    }
122}
123
124int
125syncsocket_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
135int
136syncsocket_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
146int
147syncsocket_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
157int
158syncsocket_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
168ssize_t
169syncsocket_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 = read(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
198ssize_t
199syncsocket_read(SyncSocket* ssocket, void* buf, size_t size, int timeout)
200{
201    return syncsocket_read_absolute(ssocket, buf, size, iolooper_now() + timeout);
202}
203
204ssize_t
205syncsocket_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 = write(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
251ssize_t
252syncsocket_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
257ssize_t
258syncsocket_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
282ssize_t
283syncsocket_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
289int
290syncsocket_get_socket(SyncSocket* ssocket)
291{
292    return (ssocket != NULL) ? ssocket->fd : -1;
293}
294