1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
|
// Copyright (C) 2021 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.
#include "net/posix/posix_async_socket.h"
#include <errno.h> // for errno
#include <fcntl.h> // for fcntl, FD_CLOEXEC, F_GETFL
#include <string.h> // for strerror
#include <sys/socket.h> // for getsockopt, send, MSG_NOSIGNAL
#include <unistd.h> // for close, read
#include <functional> // for __base
#include "model/setup/async_manager.h" // for AsyncManager
#include "os/log.h" // for LOG_INFO
#include "osi/include/osi.h" // for OSI_NO_INTR
/* set for very verbose debugging */
#ifndef DEBUG
#define DD(...) (void)0
#else
#define DD(...) LOG_INFO(__VA_ARGS__)
#endif
namespace android {
namespace net {
PosixAsyncSocket::PosixAsyncSocket(int fd, AsyncManager* am)
: fd_(fd), am_(am), watching_(false) {
int flags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
flags = fcntl(fd, F_GETFD);
fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
#ifdef SO_NOSIGPIPE
// Disable SIGPIPE generation on Darwin.
// When writing to a broken pipe, send() will return -1 and
// set errno to EPIPE.
flags = 1;
setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (const char*)&flags, sizeof(flags));
#endif
}
PosixAsyncSocket::PosixAsyncSocket(PosixAsyncSocket&& other) {
fd_ = other.fd_;
watching_ = other.watching_.load();
am_ = other.am_;
other.fd_ = -1;
other.watching_ = false;
}
PosixAsyncSocket::~PosixAsyncSocket() { Close(); }
ssize_t PosixAsyncSocket::Recv(uint8_t* buffer, uint64_t bufferSize) {
errno = 0;
ssize_t res = 0;
OSI_NO_INTR(res = read(fd_, buffer, bufferSize));
if (res < 0) {
DD("Recv < 0: %s (%d)", strerror(errno), fd_);
}
DD("%zd bytes (%d)", res, fd_);
return res;
};
ssize_t PosixAsyncSocket::Send(const uint8_t* buffer, uint64_t bufferSize) {
errno = 0;
ssize_t res = 0;
#ifdef MSG_NOSIGNAL
// Prevent SIGPIPE generation on Linux when writing to a broken pipe.
// ::send() will return -1/EPIPE instead.
const int sendFlags = MSG_NOSIGNAL;
#else
// For Darwin, this is handled by setting SO_NOSIGPIPE when creating
// the socket.
const int sendFlags = 0;
#endif
OSI_NO_INTR(res = send(fd_, buffer, bufferSize, sendFlags));
DD("%zd bytes (%d)", res, fd_);
return res;
}
bool PosixAsyncSocket::Connected() {
if (fd_ == -1) {
return false;
}
char buf;
if (recv(fd_, &buf, 1, MSG_PEEK | MSG_DONTWAIT) != 1) {
DD("Recv not 1, could be connected: %s (%d)", strerror(errno), fd_);
if (errno == EAGAIN || errno == EWOULDBLOCK) {
return true;
}
return false;
}
// We saw a byte in the queue, we are likely connected.
return true;
}
void PosixAsyncSocket::Close() {
if (fd_ == -1) {
return;
}
StopWatching();
// Clear out error
int error_code = 0;
socklen_t error_code_size = sizeof(error_code);
getsockopt(fd_, SOL_SOCKET, SO_ERROR, reinterpret_cast<void*>(&error_code),
&error_code_size);
// shutdown sockets if possible,
OSI_NO_INTR(shutdown(fd_, SHUT_RDWR));
error_code = ::close(fd_);
if (error_code == -1) {
LOG_INFO("Failed to close: %s (%d)", strerror(errno), fd_);
}
LOG_INFO("(%d)", fd_);
fd_ = -1;
}
bool PosixAsyncSocket::WatchForNonBlockingRead(
const ReadCallback& on_read_ready_callback) {
bool expected = false;
if (watching_.compare_exchange_strong(expected, true)) {
return am_->WatchFdForNonBlockingReads(
fd_, [on_read_ready_callback, this](int /* fd */) {
on_read_ready_callback(this);
}) == 0;
}
return false;
}
void PosixAsyncSocket::StopWatching() {
bool expected = true;
if (watching_.compare_exchange_strong(expected, false)) {
am_->StopWatchingFileDescriptor(fd_);
}
}
} // namespace net
} // namespace android
|