Home | History | Annotate | Download | only in ipc
      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