OSDN Git Service

Rename IPCHandlerUnix to IPCHandlerLinux
[android-x86/system-bt.git] / service / ipc / ipc_handler_linux.cpp
1 //
2 //  Copyright (C) 2015 Google, Inc.
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 #include "service/ipc/ipc_handler_linux.h"
18
19 #include <sys/socket.h>
20 #include <sys/un.h>
21
22 #include <base/bind.h>
23
24 #include "osi/include/socket_utils/sockets.h"
25 #include "service/daemon.h"
26 #include "service/ipc/linux_ipc_host.h"
27 #include "service/settings.h"
28
29 namespace ipc {
30
31 IPCHandlerLinux::IPCHandlerLinux(bluetooth::Adapter* adapter,
32                                IPCManager::Delegate* delegate)
33     : IPCHandler(adapter, delegate),
34       running_(false),
35       thread_("IPCHandlerLinux"),
36       keep_running_(true) {
37 }
38
39 IPCHandlerLinux::~IPCHandlerLinux() {
40   // This will only be set if the Settings::create_ipc_socket_path() was
41   // originally provided.
42   if (!socket_path_.empty())
43     unlink(socket_path_.value().c_str());
44 }
45
46 bool IPCHandlerLinux::Run() {
47   CHECK(!running_);
48
49   const std::string& android_suffix =
50       bluetooth::Daemon::Get()->GetSettings()->android_ipc_socket_suffix();
51   const base::FilePath& path =
52       bluetooth::Daemon::Get()->GetSettings()->create_ipc_socket_path();
53
54   // Both flags cannot be set at the same time.
55   CHECK(android_suffix.empty() || path.empty());
56   if (android_suffix.empty() && path.empty()) {
57     LOG(ERROR) << "No domain socket path provided";
58     return false;
59   }
60
61   CHECK(base::MessageLoop::current());  // An origin event loop is required.
62   origin_task_runner_ = base::MessageLoop::current()->task_runner();
63
64   if (!android_suffix.empty()) {
65     int server_fd = osi_android_get_control_socket(android_suffix.c_str());
66     if (server_fd == -1) {
67       LOG(ERROR) << "Unable to get Android socket from: " << android_suffix;
68       return false;
69     }
70     LOG(INFO) << "Binding to Android server socket:" << android_suffix;
71     socket_.reset(server_fd);
72   } else {
73     LOG(INFO) << "Creating a Unix domain socket:" << path.value();
74
75     // TODO(armansito): This is opens the door to potentially unlinking files in
76     // the current directory that we're not supposed to. For now we will have an
77     // assumption that the daemon runs in a sandbox but we should generally do
78     // this properly.
79     unlink(path.value().c_str());
80
81     base::ScopedFD server_socket(socket(PF_UNIX, SOCK_SEQPACKET, 0));
82     if (!server_socket.is_valid()) {
83       LOG(ERROR) << "Failed to open domain socket for IPC";
84       return false;
85     }
86
87     struct sockaddr_un address;
88     memset(&address, 0, sizeof(address));
89     address.sun_family = AF_UNIX;
90     strncpy(address.sun_path, path.value().c_str(),
91             sizeof(address.sun_path) - 1);
92     if (bind(server_socket.get(), (struct sockaddr*)&address, sizeof(address)) <
93         0) {
94       LOG(ERROR) << "Failed to bind IPC socket to address: " << strerror(errno);
95       return false;
96     }
97
98     socket_.swap(server_socket);
99     socket_path_ = path;
100   }
101
102   CHECK(socket_.is_valid());
103
104   running_ = true;  // Set this here before launching the thread.
105
106   // Start an IO thread and post the listening task.
107   base::Thread::Options options(base::MessageLoop::TYPE_IO, 0);
108   if (!thread_.StartWithOptions(options)) {
109     LOG(ERROR) << "Failed to start IPCHandlerLinux thread";
110     running_ = false;
111     return false;
112   }
113
114   thread_.task_runner()->PostTask(
115       FROM_HERE,
116       base::Bind(&IPCHandlerLinux::StartListeningOnThread, this));
117
118   return true;
119 }
120
121 void IPCHandlerLinux::Stop() {
122   keep_running_ = false;
123
124   // At this moment the listening thread might be blocking on the accept
125   // syscall. Shutdown and close the server socket before joining the thread to
126   // interrupt accept so that the main thread doesn't keep blocking.
127   shutdown(socket_.get(), SHUT_RDWR);
128   socket_.reset();
129
130   // Join and clean up the thread.
131   thread_.Stop();
132
133   // Thread exited. Notify the delegate. Post this on the event loop so that the
134   // callback isn't reentrant.
135   NotifyStoppedOnOriginThread();
136 }
137
138 void IPCHandlerLinux::StartListeningOnThread() {
139   CHECK(socket_.is_valid());
140   CHECK(adapter());
141   CHECK(running_);
142
143   LOG(INFO) << "Listening to incoming connections";
144
145   int status = listen(socket_.get(), SOMAXCONN);
146   if (status < 0) {
147     LOG(ERROR) << "Failed to listen on domain socket: " << strerror(errno);
148     origin_task_runner_->PostTask(
149         FROM_HERE,
150         base::Bind(&IPCHandlerLinux::ShutDownOnOriginThread, this));
151     return;
152   }
153
154   NotifyStartedOnOriginThread();
155
156   // TODO(armansito): The code below can cause the daemon to run indefinitely if
157   // the thread is joined while it's in the middle of the EventLoop() call. The
158   // EventLoop() won't exit until a client terminates the connection, however
159   // this can be fixed by using the |thread_|'s MessageLoopForIO instead (since
160   // it gets stopped along with the thread).
161   // TODO(icoolidge): accept simultaneous clients
162   while (keep_running_.load()) {
163     int client_socket = accept4(socket_.get(), nullptr, nullptr, SOCK_NONBLOCK);
164     if (client_socket < 0) {
165       LOG(ERROR) << "Failed to accept client connection: " << strerror(errno);
166       continue;
167     }
168
169     LOG(INFO) << "Established client connection: fd=" << client_socket;
170
171     LinuxIPCHost ipc_host(client_socket, adapter());
172
173     // TODO(armansito): Use |thread_|'s MessageLoopForIO instead of using a
174     // custom event loop to poll from the socket.
175     ipc_host.EventLoop();
176   }
177 }
178
179 void IPCHandlerLinux::ShutDownOnOriginThread() {
180   LOG(INFO) << "Shutting down IPCHandlerLinux thread";
181   thread_.Stop();
182   running_ = false;
183
184   NotifyStoppedOnCurrentThread();
185 }
186
187 void IPCHandlerLinux::NotifyStartedOnOriginThread() {
188   if (!delegate())
189     return;
190
191   origin_task_runner_->PostTask(
192       FROM_HERE,
193       base::Bind(&IPCHandlerLinux::NotifyStartedOnCurrentThread, this));
194 }
195
196 void IPCHandlerLinux::NotifyStartedOnCurrentThread() {
197   if (delegate())
198     delegate()->OnIPCHandlerStarted(IPCManager::TYPE_LINUX);
199 }
200
201 void IPCHandlerLinux::NotifyStoppedOnOriginThread() {
202   if (!delegate())
203     return;
204
205   origin_task_runner_->PostTask(
206       FROM_HERE,
207       base::Bind(&IPCHandlerLinux::NotifyStoppedOnCurrentThread, this));
208 }
209
210 void IPCHandlerLinux::NotifyStoppedOnCurrentThread() {
211   if (delegate())
212     delegate()->OnIPCHandlerStopped(IPCManager::TYPE_LINUX);
213 }
214
215 }  // namespace ipc