1 //
2 // Copyright 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 "abstract_message_loop.h"
25 #include "osi/include/socket_utils/sockets.h"
26 #include "service/daemon.h"
27 #include "service/ipc/linux_ipc_host.h"
28 #include "service/settings.h"
29
30 namespace ipc {
31
IPCHandlerLinux(bluetooth::Adapter * adapter,IPCManager::Delegate * delegate)32 IPCHandlerLinux::IPCHandlerLinux(bluetooth::Adapter* adapter,
33 IPCManager::Delegate* delegate)
34 : IPCHandler(adapter, delegate),
35 running_(false),
36 thread_("IPCHandlerLinux"),
37 keep_running_(true) {}
38
~IPCHandlerLinux()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()) unlink(socket_path_.value().c_str());
43 }
44
Run()45 bool IPCHandlerLinux::Run() {
46 CHECK(!running_);
47
48 const std::string& android_suffix =
49 bluetooth::Daemon::Get()->GetSettings()->android_ipc_socket_suffix();
50 const base::FilePath& path =
51 bluetooth::Daemon::Get()->GetSettings()->create_ipc_socket_path();
52
53 // Both flags cannot be set at the same time.
54 CHECK(android_suffix.empty() || path.empty());
55 if (android_suffix.empty() && path.empty()) {
56 LOG(ERROR) << "No domain socket path provided";
57 return false;
58 }
59
60 // An origin event loop is required.
61 CHECK(btbase::AbstractMessageLoop::current_task_runner());
62 origin_task_runner_ = btbase::AbstractMessageLoop::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 PLOG(ERROR) << "Failed to bind IPC socket to address";
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;
108 btbase::set_message_loop_type_IO(options);
109 if (!thread_.StartWithOptions(options)) {
110 LOG(ERROR) << "Failed to start IPCHandlerLinux thread";
111 running_ = false;
112 return false;
113 }
114
115 thread_.task_runner()->PostTask(
116 FROM_HERE, base::Bind(&IPCHandlerLinux::StartListeningOnThread, this));
117
118 return true;
119 }
120
Stop()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
StartListeningOnThread()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 PLOG(ERROR) << "Failed to listen on domain socket";
148 origin_task_runner_->PostTask(
149 FROM_HERE, base::Bind(&IPCHandlerLinux::ShutDownOnOriginThread, this));
150 return;
151 }
152
153 NotifyStartedOnOriginThread();
154
155 // TODO(armansito): The code below can cause the daemon to run indefinitely if
156 // the thread is joined while it's in the middle of the EventLoop() call. The
157 // EventLoop() won't exit until a client terminates the connection, however
158 // this can be fixed by using the |thread_|'s MessageLoopForIO instead (since
159 // it gets stopped along with the thread).
160 // TODO(icoolidge): accept simultaneous clients
161 while (keep_running_.load()) {
162 int client_socket = accept4(socket_.get(), nullptr, nullptr, SOCK_NONBLOCK);
163 if (client_socket < 0) {
164 PLOG(ERROR) << "Failed to accept client connection";
165 continue;
166 }
167
168 LOG(INFO) << "Established client connection: fd=" << client_socket;
169
170 LinuxIPCHost ipc_host(client_socket, adapter());
171
172 // TODO(armansito): Use |thread_|'s MessageLoopForIO instead of using a
173 // custom event loop to poll from the socket.
174 ipc_host.EventLoop();
175 }
176 }
177
ShutDownOnOriginThread()178 void IPCHandlerLinux::ShutDownOnOriginThread() {
179 LOG(INFO) << "Shutting down IPCHandlerLinux thread";
180 thread_.Stop();
181 running_ = false;
182
183 NotifyStoppedOnCurrentThread();
184 }
185
NotifyStartedOnOriginThread()186 void IPCHandlerLinux::NotifyStartedOnOriginThread() {
187 if (!delegate()) return;
188
189 origin_task_runner_->PostTask(
190 FROM_HERE,
191 base::Bind(&IPCHandlerLinux::NotifyStartedOnCurrentThread, this));
192 }
193
NotifyStartedOnCurrentThread()194 void IPCHandlerLinux::NotifyStartedOnCurrentThread() {
195 if (delegate()) delegate()->OnIPCHandlerStarted(IPCManager::TYPE_LINUX);
196 }
197
NotifyStoppedOnOriginThread()198 void IPCHandlerLinux::NotifyStoppedOnOriginThread() {
199 if (!delegate()) return;
200
201 origin_task_runner_->PostTask(
202 FROM_HERE,
203 base::Bind(&IPCHandlerLinux::NotifyStoppedOnCurrentThread, this));
204 }
205
NotifyStoppedOnCurrentThread()206 void IPCHandlerLinux::NotifyStoppedOnCurrentThread() {
207 if (delegate()) delegate()->OnIPCHandlerStopped(IPCManager::TYPE_LINUX);
208 }
209
210 } // namespace ipc
211