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