| /* |
| * Copyright (C) 2010 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /* |
| * Contains helper routines dealing with syncronous access to a non-blocking |
| * sokets. |
| */ |
| |
| #include "qemu-common.h" |
| #include "errno.h" |
| #include "iolooper.h" |
| #include "sockets.h" |
| #include "android/utils/debug.h" |
| #include "android/sync-utils.h" |
| #include "android/utils/system.h" |
| |
| #define D(...) do { if (VERBOSE_CHECK(init)) dprint(__VA_ARGS__); } while (0) |
| |
| struct SyncSocket { |
| // Helper for performing synchronous I/O on the socket. |
| IoLooper* iolooper; |
| |
| /* Opened socket handle. */ |
| int fd; |
| }; |
| |
| SyncSocket* |
| syncsocket_init(int fd) |
| { |
| SyncSocket* sync_socket; |
| ANEW0(sync_socket); |
| |
| socket_set_nonblock(fd); |
| sync_socket->iolooper = iolooper_new(); |
| sync_socket->fd = fd; |
| |
| return sync_socket; |
| } |
| |
| SyncSocket* |
| syncsocket_connect(int fd, SockAddress* sockaddr, int timeout) |
| { |
| IoLooper* looper; |
| int connect_status; |
| SyncSocket* sync_socket = NULL; |
| |
| socket_set_nonblock(fd); |
| |
| for(;;) { |
| connect_status = socket_connect(fd, sockaddr); |
| if (connect_status >= 0) { |
| // Connected. Create IoLooper for the helper. |
| looper = iolooper_new(); |
| break; |
| } |
| |
| if (errno == EINPROGRESS || errno == EAGAIN || errno == EWOULDBLOCK) { |
| // Connection is in progress. Wait till it's finished. |
| looper = iolooper_new(); |
| iolooper_add_write(looper, fd); |
| connect_status = iolooper_wait(looper, timeout); |
| if (connect_status > 0) { |
| iolooper_del_write(looper, fd); |
| break; |
| } else { |
| iolooper_free(looper); |
| return NULL; |
| } |
| } else if (errno != EINTR) { |
| return NULL; |
| } |
| } |
| |
| // We're now connected. Lets initialize SyncSocket instance |
| // for this connection. |
| sync_socket = malloc(sizeof(SyncSocket)); |
| if (sync_socket == NULL) { |
| derror("PANIC: not enough memory\n"); |
| exit(1); |
| } |
| |
| sync_socket->iolooper = looper; |
| sync_socket->fd = fd; |
| |
| return sync_socket; |
| } |
| |
| void |
| syncsocket_close(SyncSocket* ssocket) |
| { |
| if (ssocket != NULL && ssocket->fd >= 0) { |
| if (ssocket->iolooper != NULL) { |
| iolooper_reset(ssocket->iolooper); |
| } |
| socket_close(ssocket->fd); |
| ssocket->fd = -1; |
| } |
| } |
| |
| void |
| syncsocket_free(SyncSocket* ssocket) |
| { |
| if (ssocket != NULL) { |
| if (ssocket->iolooper != NULL) { |
| iolooper_free(ssocket->iolooper); |
| } |
| free(ssocket); |
| } |
| } |
| |
| int |
| syncsocket_start_read(SyncSocket* ssocket) |
| { |
| if (ssocket == NULL || ssocket->fd < 0 || ssocket->iolooper == NULL) { |
| errno = EINVAL; |
| return -1; |
| } |
| iolooper_add_read(ssocket->iolooper, ssocket->fd); |
| return 0; |
| } |
| |
| int |
| syncsocket_stop_read(SyncSocket* ssocket) |
| { |
| if (ssocket == NULL || ssocket->fd < 0 || ssocket->iolooper == NULL) { |
| errno = EINVAL; |
| return -1; |
| } |
| iolooper_del_read(ssocket->iolooper, ssocket->fd); |
| return 0; |
| } |
| |
| int |
| syncsocket_start_write(SyncSocket* ssocket) |
| { |
| if (ssocket == NULL || ssocket->fd < 0 || ssocket->iolooper == NULL) { |
| errno = EINVAL; |
| return -1; |
| } |
| iolooper_add_write(ssocket->iolooper, ssocket->fd); |
| return 0; |
| } |
| |
| int |
| syncsocket_stop_write(SyncSocket* ssocket) |
| { |
| if (ssocket == NULL || ssocket->fd < 0 || ssocket->iolooper == NULL) { |
| errno = EINVAL; |
| return -1; |
| } |
| iolooper_del_write(ssocket->iolooper, ssocket->fd); |
| return 0; |
| } |
| |
| ssize_t |
| syncsocket_read_absolute(SyncSocket* ssocket, |
| void* buf, |
| size_t size, |
| int64_t deadline) |
| { |
| int ret; |
| |
| if (ssocket == NULL || ssocket->fd < 0 || ssocket->iolooper == NULL) { |
| errno = EINVAL; |
| return -1; |
| } |
| |
| ret = iolooper_wait_absolute(ssocket->iolooper, deadline); |
| if (ret > 0) { |
| if (!iolooper_is_read(ssocket->iolooper, ssocket->fd)) { |
| D("%s: Internal error, iolooper_is_read() not set!", __FUNCTION__); |
| return -1; |
| } |
| do { |
| ret = socket_recv(ssocket->fd, buf, size); |
| } while( ret < 0 && errno == EINTR); |
| } else if (ret == 0) { |
| // Timed out |
| errno = ETIMEDOUT; |
| ret = -1; |
| } |
| return ret; |
| } |
| |
| ssize_t |
| syncsocket_read(SyncSocket* ssocket, void* buf, size_t size, int timeout) |
| { |
| return syncsocket_read_absolute(ssocket, buf, size, iolooper_now() + timeout); |
| } |
| |
| ssize_t |
| syncsocket_write_absolute(SyncSocket* ssocket, |
| const void* buf, |
| size_t size, |
| int64_t deadline) |
| { |
| int ret; |
| size_t written = 0; |
| |
| if (ssocket == NULL || ssocket->fd < 0 || ssocket->iolooper == NULL) { |
| errno = EINVAL; |
| return -1; |
| } |
| |
| do { |
| ret = iolooper_wait_absolute(ssocket->iolooper, deadline); |
| if (ret < 0) { |
| return ret; |
| } else if (ret == 0) { |
| // Timeout. |
| errno = ETIMEDOUT; |
| return -1; |
| } |
| if (!iolooper_is_write(ssocket->iolooper, ssocket->fd)) { |
| D("%s: Internal error, iolooper_is_write() not set!", __FUNCTION__); |
| return -1; |
| } |
| |
| do { |
| ret = socket_send(ssocket->fd, (const char*)buf + written, size - written); |
| } while( ret < 0 && errno == EINTR); |
| |
| if (ret > 0) { |
| written += ret; |
| } else if (ret < 0) { |
| if (errno != EAGAIN && errno != EWOULDBLOCK) { |
| return -1; |
| } |
| } else { |
| // Disconnected. |
| errno = ECONNRESET; |
| return -1; |
| } |
| } while (written < size); |
| return (int)written; |
| } |
| |
| ssize_t |
| syncsocket_write(SyncSocket* ssocket, const void* buf, size_t size, int timeout) |
| { |
| return syncsocket_write_absolute(ssocket, buf, size, iolooper_now() + timeout); |
| } |
| |
| ssize_t |
| syncsocket_read_line_absolute(SyncSocket* ssocket, |
| char* buffer, |
| size_t size, |
| int64_t deadline) |
| { |
| size_t read_chars = 0; |
| |
| while (read_chars < size) { |
| char ch; |
| int ret = syncsocket_read_absolute(ssocket, &ch, 1, deadline); |
| if (ret <= 0) { |
| return ret; |
| } |
| buffer[read_chars++] = ch; |
| if (ch == '\n') { |
| return read_chars; |
| } |
| } |
| |
| /* Not enough room in the input buffer!*/ |
| errno = ENOMEM; |
| return -1; |
| } |
| |
| ssize_t |
| syncsocket_read_line(SyncSocket* ssocket, char* buffer, size_t size, int timeout) |
| { |
| return syncsocket_read_line_absolute(ssocket, buffer, size, |
| iolooper_now() + timeout); |
| } |
| |
| int |
| syncsocket_get_socket(SyncSocket* ssocket) |
| { |
| return (ssocket != NULL) ? ssocket->fd : -1; |
| } |