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 216