1fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray// 2fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray// Copyright (C) 2015 Google, Inc. 3fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray// 4fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray// Licensed under the Apache License, Version 2.0 (the "License"); 5fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray// you may not use this file except in compliance with the License. 6fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray// You may obtain a copy of the License at: 7fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray// 8fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray// http://www.apache.org/licenses/LICENSE-2.0 9fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray// 10fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray// Unless required by applicable law or agreed to in writing, software 11fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray// distributed under the License is distributed on an "AS IS" BASIS, 12fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray// See the License for the specific language governing permissions and 14fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray// limitations under the License. 15fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray// 16fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray 17e415c050edbb2710e8807dd2602c851412953268Scott James Remnant#include "service/ipc/ipc_handler_linux.h" 18fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray 19fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray#include <sys/socket.h> 20fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray#include <sys/un.h> 21fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray 22fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray#include <base/bind.h> 23fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray 24c6760d82357f46943406c579f5b1c291a20afdebIan Coolidge#include "osi/include/socket_utils/sockets.h" 25fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray#include "service/daemon.h" 26e415c050edbb2710e8807dd2602c851412953268Scott James Remnant#include "service/ipc/linux_ipc_host.h" 27fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray#include "service/settings.h" 28fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray 29fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguraynamespace ipc { 30fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray 31e415c050edbb2710e8807dd2602c851412953268Scott James RemnantIPCHandlerLinux::IPCHandlerLinux(bluetooth::Adapter* adapter, 32911d1ae03efec2d54c3b1b605589d790d1745488Myles Watson IPCManager::Delegate* delegate) 33d6a4b0c950f44d3eab34825880d26c19e362d22bArman Uguray : IPCHandler(adapter, delegate), 34fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray running_(false), 35e415c050edbb2710e8807dd2602c851412953268Scott James Remnant thread_("IPCHandlerLinux"), 36911d1ae03efec2d54c3b1b605589d790d1745488Myles Watson keep_running_(true) {} 37fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray 38e415c050edbb2710e8807dd2602c851412953268Scott James RemnantIPCHandlerLinux::~IPCHandlerLinux() { 39e0d08c9fecdf92c386b52375501a306c8f67d63eArman Uguray // This will only be set if the Settings::create_ipc_socket_path() was 40e0d08c9fecdf92c386b52375501a306c8f67d63eArman Uguray // originally provided. 41911d1ae03efec2d54c3b1b605589d790d1745488Myles Watson if (!socket_path_.empty()) unlink(socket_path_.value().c_str()); 42fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray} 43fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray 44e415c050edbb2710e8807dd2602c851412953268Scott James Remnantbool IPCHandlerLinux::Run() { 45fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray CHECK(!running_); 46fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray 47c6760d82357f46943406c579f5b1c291a20afdebIan Coolidge const std::string& android_suffix = 48f8881fee3d08cb50896b22adc0841223694d51d2Arman Uguray bluetooth::Daemon::Get()->GetSettings()->android_ipc_socket_suffix(); 49fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray const base::FilePath& path = 50f8881fee3d08cb50896b22adc0841223694d51d2Arman Uguray bluetooth::Daemon::Get()->GetSettings()->create_ipc_socket_path(); 51c6760d82357f46943406c579f5b1c291a20afdebIan Coolidge 52c6760d82357f46943406c579f5b1c291a20afdebIan Coolidge // Both flags cannot be set at the same time. 53c6760d82357f46943406c579f5b1c291a20afdebIan Coolidge CHECK(android_suffix.empty() || path.empty()); 54c6760d82357f46943406c579f5b1c291a20afdebIan Coolidge if (android_suffix.empty() && path.empty()) { 55fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray LOG(ERROR) << "No domain socket path provided"; 56fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray return false; 57fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray } 58fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray 59fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray CHECK(base::MessageLoop::current()); // An origin event loop is required. 60fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray origin_task_runner_ = base::MessageLoop::current()->task_runner(); 61fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray 62c6760d82357f46943406c579f5b1c291a20afdebIan Coolidge if (!android_suffix.empty()) { 63c6760d82357f46943406c579f5b1c291a20afdebIan Coolidge int server_fd = osi_android_get_control_socket(android_suffix.c_str()); 64c6760d82357f46943406c579f5b1c291a20afdebIan Coolidge if (server_fd == -1) { 65c6760d82357f46943406c579f5b1c291a20afdebIan Coolidge LOG(ERROR) << "Unable to get Android socket from: " << android_suffix; 66c6760d82357f46943406c579f5b1c291a20afdebIan Coolidge return false; 67c6760d82357f46943406c579f5b1c291a20afdebIan Coolidge } 68c6760d82357f46943406c579f5b1c291a20afdebIan Coolidge LOG(INFO) << "Binding to Android server socket:" << android_suffix; 69c6760d82357f46943406c579f5b1c291a20afdebIan Coolidge socket_.reset(server_fd); 70c6760d82357f46943406c579f5b1c291a20afdebIan Coolidge } else { 71c6760d82357f46943406c579f5b1c291a20afdebIan Coolidge LOG(INFO) << "Creating a Unix domain socket:" << path.value(); 72c6760d82357f46943406c579f5b1c291a20afdebIan Coolidge 73c6760d82357f46943406c579f5b1c291a20afdebIan Coolidge // TODO(armansito): This is opens the door to potentially unlinking files in 74c6760d82357f46943406c579f5b1c291a20afdebIan Coolidge // the current directory that we're not supposed to. For now we will have an 75c6760d82357f46943406c579f5b1c291a20afdebIan Coolidge // assumption that the daemon runs in a sandbox but we should generally do 76c6760d82357f46943406c579f5b1c291a20afdebIan Coolidge // this properly. 77c6760d82357f46943406c579f5b1c291a20afdebIan Coolidge unlink(path.value().c_str()); 78c6760d82357f46943406c579f5b1c291a20afdebIan Coolidge 79c6760d82357f46943406c579f5b1c291a20afdebIan Coolidge base::ScopedFD server_socket(socket(PF_UNIX, SOCK_SEQPACKET, 0)); 80c6760d82357f46943406c579f5b1c291a20afdebIan Coolidge if (!server_socket.is_valid()) { 81c6760d82357f46943406c579f5b1c291a20afdebIan Coolidge LOG(ERROR) << "Failed to open domain socket for IPC"; 82c6760d82357f46943406c579f5b1c291a20afdebIan Coolidge return false; 83c6760d82357f46943406c579f5b1c291a20afdebIan Coolidge } 84c6760d82357f46943406c579f5b1c291a20afdebIan Coolidge 85c6760d82357f46943406c579f5b1c291a20afdebIan Coolidge struct sockaddr_un address; 86c6760d82357f46943406c579f5b1c291a20afdebIan Coolidge memset(&address, 0, sizeof(address)); 87c6760d82357f46943406c579f5b1c291a20afdebIan Coolidge address.sun_family = AF_UNIX; 88c6760d82357f46943406c579f5b1c291a20afdebIan Coolidge strncpy(address.sun_path, path.value().c_str(), 89c6760d82357f46943406c579f5b1c291a20afdebIan Coolidge sizeof(address.sun_path) - 1); 90c6760d82357f46943406c579f5b1c291a20afdebIan Coolidge if (bind(server_socket.get(), (struct sockaddr*)&address, sizeof(address)) < 91c6760d82357f46943406c579f5b1c291a20afdebIan Coolidge 0) { 92c6760d82357f46943406c579f5b1c291a20afdebIan Coolidge LOG(ERROR) << "Failed to bind IPC socket to address: " << strerror(errno); 93c6760d82357f46943406c579f5b1c291a20afdebIan Coolidge return false; 94c6760d82357f46943406c579f5b1c291a20afdebIan Coolidge } 95c6760d82357f46943406c579f5b1c291a20afdebIan Coolidge 96c6760d82357f46943406c579f5b1c291a20afdebIan Coolidge socket_.swap(server_socket); 97e0d08c9fecdf92c386b52375501a306c8f67d63eArman Uguray socket_path_ = path; 98fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray } 99fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray 100c6760d82357f46943406c579f5b1c291a20afdebIan Coolidge CHECK(socket_.is_valid()); 101fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray 102fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray running_ = true; // Set this here before launching the thread. 103fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray 104fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray // Start an IO thread and post the listening task. 105fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray base::Thread::Options options(base::MessageLoop::TYPE_IO, 0); 106fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray if (!thread_.StartWithOptions(options)) { 107e415c050edbb2710e8807dd2602c851412953268Scott James Remnant LOG(ERROR) << "Failed to start IPCHandlerLinux thread"; 108fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray running_ = false; 109fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray return false; 110fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray } 111fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray 112fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray thread_.task_runner()->PostTask( 113911d1ae03efec2d54c3b1b605589d790d1745488Myles Watson FROM_HERE, base::Bind(&IPCHandlerLinux::StartListeningOnThread, this)); 114fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray 115fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray return true; 116fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray} 117fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray 118e415c050edbb2710e8807dd2602c851412953268Scott James Remnantvoid IPCHandlerLinux::Stop() { 119e0d08c9fecdf92c386b52375501a306c8f67d63eArman Uguray keep_running_ = false; 120e0d08c9fecdf92c386b52375501a306c8f67d63eArman Uguray 121e0d08c9fecdf92c386b52375501a306c8f67d63eArman Uguray // At this moment the listening thread might be blocking on the accept 122e0d08c9fecdf92c386b52375501a306c8f67d63eArman Uguray // syscall. Shutdown and close the server socket before joining the thread to 123e0d08c9fecdf92c386b52375501a306c8f67d63eArman Uguray // interrupt accept so that the main thread doesn't keep blocking. 124e0d08c9fecdf92c386b52375501a306c8f67d63eArman Uguray shutdown(socket_.get(), SHUT_RDWR); 125e0d08c9fecdf92c386b52375501a306c8f67d63eArman Uguray socket_.reset(); 126e0d08c9fecdf92c386b52375501a306c8f67d63eArman Uguray 127e0d08c9fecdf92c386b52375501a306c8f67d63eArman Uguray // Join and clean up the thread. 128e0d08c9fecdf92c386b52375501a306c8f67d63eArman Uguray thread_.Stop(); 129e0d08c9fecdf92c386b52375501a306c8f67d63eArman Uguray 130e0d08c9fecdf92c386b52375501a306c8f67d63eArman Uguray // Thread exited. Notify the delegate. Post this on the event loop so that the 131e0d08c9fecdf92c386b52375501a306c8f67d63eArman Uguray // callback isn't reentrant. 132e0d08c9fecdf92c386b52375501a306c8f67d63eArman Uguray NotifyStoppedOnOriginThread(); 133e0d08c9fecdf92c386b52375501a306c8f67d63eArman Uguray} 134e0d08c9fecdf92c386b52375501a306c8f67d63eArman Uguray 135e415c050edbb2710e8807dd2602c851412953268Scott James Remnantvoid IPCHandlerLinux::StartListeningOnThread() { 136fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray CHECK(socket_.is_valid()); 137d6a4b0c950f44d3eab34825880d26c19e362d22bArman Uguray CHECK(adapter()); 138fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray CHECK(running_); 139fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray 140fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray LOG(INFO) << "Listening to incoming connections"; 141fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray 142fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray int status = listen(socket_.get(), SOMAXCONN); 143fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray if (status < 0) { 144fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray LOG(ERROR) << "Failed to listen on domain socket: " << strerror(errno); 145fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray origin_task_runner_->PostTask( 146911d1ae03efec2d54c3b1b605589d790d1745488Myles Watson FROM_HERE, base::Bind(&IPCHandlerLinux::ShutDownOnOriginThread, this)); 147fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray return; 148fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray } 149fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray 150e0d08c9fecdf92c386b52375501a306c8f67d63eArman Uguray NotifyStartedOnOriginThread(); 151e0d08c9fecdf92c386b52375501a306c8f67d63eArman Uguray 152e0d08c9fecdf92c386b52375501a306c8f67d63eArman Uguray // TODO(armansito): The code below can cause the daemon to run indefinitely if 153e0d08c9fecdf92c386b52375501a306c8f67d63eArman Uguray // the thread is joined while it's in the middle of the EventLoop() call. The 154e0d08c9fecdf92c386b52375501a306c8f67d63eArman Uguray // EventLoop() won't exit until a client terminates the connection, however 155e0d08c9fecdf92c386b52375501a306c8f67d63eArman Uguray // this can be fixed by using the |thread_|'s MessageLoopForIO instead (since 156e0d08c9fecdf92c386b52375501a306c8f67d63eArman Uguray // it gets stopped along with the thread). 157fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray // TODO(icoolidge): accept simultaneous clients 158e0d08c9fecdf92c386b52375501a306c8f67d63eArman Uguray while (keep_running_.load()) { 159fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray int client_socket = accept4(socket_.get(), nullptr, nullptr, SOCK_NONBLOCK); 160e0d08c9fecdf92c386b52375501a306c8f67d63eArman Uguray if (client_socket < 0) { 161fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray LOG(ERROR) << "Failed to accept client connection: " << strerror(errno); 162fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray continue; 163fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray } 164fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray 165fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray LOG(INFO) << "Established client connection: fd=" << client_socket; 166d6a4b0c950f44d3eab34825880d26c19e362d22bArman Uguray 167e415c050edbb2710e8807dd2602c851412953268Scott James Remnant LinuxIPCHost ipc_host(client_socket, adapter()); 168d6a4b0c950f44d3eab34825880d26c19e362d22bArman Uguray 169fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray // TODO(armansito): Use |thread_|'s MessageLoopForIO instead of using a 170fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray // custom event loop to poll from the socket. 171fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray ipc_host.EventLoop(); 172fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray } 173fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray} 174fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray 175e415c050edbb2710e8807dd2602c851412953268Scott James Remnantvoid IPCHandlerLinux::ShutDownOnOriginThread() { 176e415c050edbb2710e8807dd2602c851412953268Scott James Remnant LOG(INFO) << "Shutting down IPCHandlerLinux thread"; 177fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray thread_.Stop(); 178fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray running_ = false; 179fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray 180e0d08c9fecdf92c386b52375501a306c8f67d63eArman Uguray NotifyStoppedOnCurrentThread(); 181e0d08c9fecdf92c386b52375501a306c8f67d63eArman Uguray} 182e0d08c9fecdf92c386b52375501a306c8f67d63eArman Uguray 183e415c050edbb2710e8807dd2602c851412953268Scott James Remnantvoid IPCHandlerLinux::NotifyStartedOnOriginThread() { 184911d1ae03efec2d54c3b1b605589d790d1745488Myles Watson if (!delegate()) return; 185e0d08c9fecdf92c386b52375501a306c8f67d63eArman Uguray 186e0d08c9fecdf92c386b52375501a306c8f67d63eArman Uguray origin_task_runner_->PostTask( 187e0d08c9fecdf92c386b52375501a306c8f67d63eArman Uguray FROM_HERE, 188e415c050edbb2710e8807dd2602c851412953268Scott James Remnant base::Bind(&IPCHandlerLinux::NotifyStartedOnCurrentThread, this)); 189e0d08c9fecdf92c386b52375501a306c8f67d63eArman Uguray} 190e0d08c9fecdf92c386b52375501a306c8f67d63eArman Uguray 191e415c050edbb2710e8807dd2602c851412953268Scott James Remnantvoid IPCHandlerLinux::NotifyStartedOnCurrentThread() { 192911d1ae03efec2d54c3b1b605589d790d1745488Myles Watson if (delegate()) delegate()->OnIPCHandlerStarted(IPCManager::TYPE_LINUX); 193e0d08c9fecdf92c386b52375501a306c8f67d63eArman Uguray} 194e0d08c9fecdf92c386b52375501a306c8f67d63eArman Uguray 195e415c050edbb2710e8807dd2602c851412953268Scott James Remnantvoid IPCHandlerLinux::NotifyStoppedOnOriginThread() { 196911d1ae03efec2d54c3b1b605589d790d1745488Myles Watson if (!delegate()) return; 197e0d08c9fecdf92c386b52375501a306c8f67d63eArman Uguray 198e0d08c9fecdf92c386b52375501a306c8f67d63eArman Uguray origin_task_runner_->PostTask( 199e0d08c9fecdf92c386b52375501a306c8f67d63eArman Uguray FROM_HERE, 200e415c050edbb2710e8807dd2602c851412953268Scott James Remnant base::Bind(&IPCHandlerLinux::NotifyStoppedOnCurrentThread, this)); 201e0d08c9fecdf92c386b52375501a306c8f67d63eArman Uguray} 202e0d08c9fecdf92c386b52375501a306c8f67d63eArman Uguray 203e415c050edbb2710e8807dd2602c851412953268Scott James Remnantvoid IPCHandlerLinux::NotifyStoppedOnCurrentThread() { 204911d1ae03efec2d54c3b1b605589d790d1745488Myles Watson if (delegate()) delegate()->OnIPCHandlerStopped(IPCManager::TYPE_LINUX); 205fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray} 206fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray 207fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray} // namespace ipc 208