ipc_handler_linux.cc revision fe65fb7978bc9257a36d1e5eae59c5f412dbdb49
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
17fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray#include "service/ipc/ipc_handler_unix.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
24fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray#include "service/daemon.h"
25fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray#include "service/ipc/unix_ipc_host.h"
26fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray#include "service/settings.h"
27fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray
28fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguraynamespace ipc {
29fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray
30fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman UgurayIPCHandlerUnix::IPCHandlerUnix(bluetooth::CoreStack* core_stack)
31fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray    : IPCHandler(core_stack),
32fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray      running_(false),
33fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray      thread_("IPCHandlerUnix") {
34fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray}
35fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray
36fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman UgurayIPCHandlerUnix::~IPCHandlerUnix() {
37fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray}
38fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray
39fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguraybool IPCHandlerUnix::Run() {
40fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray  CHECK(!running_);
41fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray
42fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray  const base::FilePath& path =
43fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray      bluetooth::Daemon::Get()->settings()->ipc_socket_path();
44fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray  if (path.empty()) {
45fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray    LOG(ERROR) << "No domain socket path provided";
46fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray    return false;
47fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray  }
48fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray
49fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray  CHECK(base::MessageLoop::current());  // An origin event loop is required.
50fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray  origin_task_runner_ = base::MessageLoop::current()->task_runner();
51fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray
52fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray  // TODO(armansito): This is opens the door to potentially unlinking files in
53fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray  // the current directory that we're not supposed to. For now we will have an
54fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray  // assumption that the daemon runs in a sandbox but we should generally do
55fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray  // this properly.
56fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray  //
57fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray  // Also, the daemon should clean this up properly as it shuts down.
58fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray  unlink(path.value().c_str());
59fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray
60fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray  base::ScopedFD server_socket(socket(PF_UNIX, SOCK_SEQPACKET, 0));
61fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray  if (!server_socket.is_valid()) {
62fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray    LOG(ERROR) << "Failed to open domain socket for IPC";
63fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray    return false;
64fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray  }
65fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray
66fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray  struct sockaddr_un address;
67fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray  memset(&address, 0, sizeof(address));
68fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray  address.sun_family = AF_UNIX;
69fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray  strncpy(address.sun_path, path.value().c_str(), sizeof(address.sun_path) - 1);
70fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray  if (bind(server_socket.get(), (struct sockaddr*)&address,
71fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray           sizeof(address)) < 0) {
72fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray    LOG(ERROR) << "Failed to bind IPC socket to address: " << strerror(errno);
73fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray    return false;
74fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray  }
75fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray
76fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray  socket_.swap(server_socket);
77fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray  running_ = true;  // Set this here before launching the thread.
78fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray
79fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray  // Start an IO thread and post the listening task.
80fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray  base::Thread::Options options(base::MessageLoop::TYPE_IO, 0);
81fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray  if (!thread_.StartWithOptions(options)) {
82fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray    LOG(ERROR) << "Failed to start IPCHandlerUnix thread";
83fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray    running_ = false;
84fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray    return false;
85fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray  }
86fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray
87fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray  thread_.task_runner()->PostTask(
88fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray      FROM_HERE,
89fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray      base::Bind(&IPCHandlerUnix::StartListeningOnThread, this));
90fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray
91fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray  return true;
92fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray}
93fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray
94fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Ugurayvoid IPCHandlerUnix::StartListeningOnThread() {
95fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray  CHECK(socket_.is_valid());
96fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray  CHECK(core_stack_);
97fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray  CHECK(running_);
98fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray
99fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray  LOG(INFO) << "Listening to incoming connections";
100fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray
101fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray  int status = listen(socket_.get(), SOMAXCONN);
102fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray  if (status < 0) {
103fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray    LOG(ERROR) << "Failed to listen on domain socket: " << strerror(errno);
104fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray    origin_task_runner_->PostTask(
105fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray        FROM_HERE,
106fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray        base::Bind(&IPCHandlerUnix::ShutDownOnOriginThread, this));
107fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray    return;
108fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray  }
109fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray
110fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray  // TODO(icoolidge): accept simultaneous clients
111fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray  while (true) {
112fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray    int client_socket = accept4(socket_.get(), nullptr, nullptr, SOCK_NONBLOCK);
113fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray    if (status == -1) {
114fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray      LOG(ERROR) << "Failed to accept client connection: " << strerror(errno);
115fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray      continue;
116fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray    }
117fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray
118fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray    LOG(INFO) << "Established client connection: fd=" << client_socket;
119fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray    UnixIPCHost ipc_host(client_socket, core_stack_);
120fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray    // TODO(armansito): Use |thread_|'s MessageLoopForIO instead of using a
121fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray    // custom event loop to poll from the socket.
122fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray    ipc_host.EventLoop();
123fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray  }
124fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray}
125fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray
126fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Ugurayvoid IPCHandlerUnix::ShutDownOnOriginThread() {
127fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray  LOG(INFO) << "Shutting down IPCHandlerUnix thread";
128fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray  thread_.Stop();
129fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray  running_ = false;
130fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray
131fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray  // TODO(armansito): Notify the upper layer so that they can perform clean-up
132fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray  // tasks on unexpected shut-down.
133fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray}
134fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray
135fe65fb7978bc9257a36d1e5eae59c5f412dbdb49Arman Uguray}  // namespace ipc
136