1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "services/device/hid/hid_service_freebsd.h"
6 
7 #if defined(OS_FREEBSD)
8 #include <dev/usb/usb_ioctl.h>
9 #else
10 #include <bus/u4b/usb_ioctl.h>
11 #endif
12 
13 #include <stdint.h>
14 #include <sys/socket.h>
15 #include <sys/un.h>
16 
17 #include <set>
18 #include <string>
19 #include <vector>
20 
21 #include "base/bind.h"
22 #include "base/files/file_descriptor_watcher_posix.h"
23 #include "base/files/file_enumerator.h"
24 #include "base/files/file_util.h"
25 #include "base/files/file.h"
26 #include "base/location.h"
27 #include "base/logging.h"
28 #include "base/posix/eintr_wrapper.h"
29 #include "base/single_thread_task_runner.h"
30 #include "base/stl_util.h"
31 #include "base/strings/pattern.h"
32 #include "base/strings/stringprintf.h"
33 #include "base/strings/sys_string_conversions.h"
34 #include "base/strings/string_util.h"
35 #include "base/strings/string_split.h"
36 #include "base/task/post_task.h"
37 #include "base/threading/scoped_blocking_call.h"
38 #include "base/threading/thread_task_runner_handle.h"
39 #include "components/device_event_log/device_event_log.h"
40 #include "services/device/hid/hid_connection_freebsd.h"
41 
42 const int kMaxPermissionChecks = 5;
43 
44 namespace device {
45 
46 struct HidServiceFreeBSD::ConnectParams {
ConnectParamsdevice::HidServiceFreeBSD::ConnectParams47   ConnectParams(scoped_refptr<HidDeviceInfo> device_info,
48                 ConnectCallback callback)
49       : device_info(std::move(device_info)),
50         callback(std::move(callback)),
51         task_runner(base::ThreadTaskRunnerHandle::Get()),
52         blocking_task_runner(
53             base::ThreadPool::CreateSequencedTaskRunner(kBlockingTaskTraits)) {}
~ConnectParamsdevice::HidServiceFreeBSD::ConnectParams54   ~ConnectParams() {}
55 
56   scoped_refptr<HidDeviceInfo> device_info;
57   ConnectCallback callback;
58   scoped_refptr<base::SequencedTaskRunner> task_runner;
59   scoped_refptr<base::SequencedTaskRunner> blocking_task_runner;
60   base::ScopedFD fd;
61 };
62 
63 class HidServiceFreeBSD::BlockingTaskRunnerHelper {
64  public:
BlockingTaskRunnerHelper(base::WeakPtr<HidServiceFreeBSD> service)65   BlockingTaskRunnerHelper(base::WeakPtr<HidServiceFreeBSD> service)
66       : service_(std::move(service)),
67         task_runner_(base::ThreadTaskRunnerHandle::Get()) {
68     DETACH_FROM_SEQUENCE(sequence_checker_);
69 
70     timer_.reset(new base::RepeatingTimer());
71     devd_buffer_ = new net::IOBufferWithSize(1024);
72   }
73 
~BlockingTaskRunnerHelper()74   ~BlockingTaskRunnerHelper() {
75   }
76 
Start()77   void Start() {
78     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
79 
80     const base::FilePath kDevRoot("/dev");
81     const std::string kUHIDPattern("/dev/uhid*");
82 
83     base::FileEnumerator enumerator(kDevRoot, false, base::FileEnumerator::FILES);
84     do {
85       const base::FilePath next_device_path(enumerator.Next());
86       const std::string next_device = next_device_path.value();
87       if (next_device.empty())
88         break;
89 
90       if (base::MatchPattern(next_device, kUHIDPattern))
91         OnDeviceAdded(next_device.substr(5));
92     } while (true);
93 
94     SetupDevdMonitor();
95 
96     task_runner_->PostTask(
97         FROM_HERE,
98         base::Bind(&HidServiceFreeBSD::FirstEnumerationComplete, service_));
99   }
100 
HaveReadWritePermissions(std::string device_id)101   bool HaveReadWritePermissions(std::string device_id) {
102     std::string device_node = "/dev/" + device_id;
103     base::internal::AssertBlockingAllowed();
104 
105     base::FilePath device_path(device_node);
106     base::File device_file;
107     int flags =
108         base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_WRITE;
109     device_file.Initialize(device_path, flags);
110     if (!device_file.IsValid())
111       return false;
112 
113     return true;
114   }
115 
OnDeviceAdded(std::string device_id)116   void OnDeviceAdded(std::string device_id) {
117     base::ScopedBlockingCall scoped_blocking_call(
118         FROM_HERE, base::BlockingType::MAY_BLOCK);
119     std::string device_node = "/dev/" + device_id;
120     uint16_t vendor_id = 0xffff;
121     uint16_t product_id = 0xffff;
122     std::string product_name = "";
123     std::string serial_number = "";
124 
125     std::vector<uint8_t> report_descriptor;
126 
127     base::internal::AssertBlockingAllowed();
128 
129     base::FilePath device_path(device_node);
130     base::File device_file;
131     int flags =
132         base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_WRITE;
133     device_file.Initialize(device_path, flags);
134     if (!device_file.IsValid()) {
135       HID_LOG(ERROR) << "Failed to open '" << device_node
136                      << "': "
137                      << base::File::ErrorToString(device_file.error_details());
138       return;
139     }
140 
141     base::ScopedFD fd;
142     fd.reset(device_file.TakePlatformFile());
143 
144     struct usb_gen_descriptor ugd;
145     ugd.ugd_data = NULL;
146     ugd.ugd_maxlen = 0xffff;
147     int result = HANDLE_EINTR(
148         ioctl(fd.get(), USB_GET_REPORT_DESC, &ugd));
149 
150     if (result < 0) {
151       HID_LOG(ERROR) << "Failed to get report descriptor size";
152       return;
153     }
154 
155     report_descriptor.resize(ugd.ugd_actlen);
156 
157     ugd.ugd_data = report_descriptor.data();
158     ugd.ugd_maxlen = ugd.ugd_actlen;
159     result = HANDLE_EINTR(
160         ioctl(fd.get(), USB_GET_REPORT_DESC, &ugd));
161 
162     if (result < 0) {
163       HID_LOG(ERROR) << "Failed to get report descriptor";
164       return;
165     }
166 
167     scoped_refptr<HidDeviceInfo> device_info(new HidDeviceInfo(
168         device_id,
169         /*physical_device_id*/"",
170 	vendor_id,
171 	product_id,
172 	product_name,
173 	serial_number,
174         device::mojom::HidBusType::kHIDBusTypeUSB,
175         report_descriptor,
176 	device_node));
177 
178     task_runner_->PostTask(FROM_HERE, base::BindOnce(&HidServiceFreeBSD::AddDevice,
179                                                  service_, device_info));
180   }
181 
OnDeviceRemoved(std::string device_id)182   void OnDeviceRemoved(std::string device_id) {
183     base::ScopedBlockingCall scoped_blocking_call(
184         FROM_HERE, base::BlockingType::MAY_BLOCK);
185     task_runner_->PostTask(
186         FROM_HERE, base::Bind(&HidServiceFreeBSD::RemoveDevice, service_,
187                               device_id));
188   }
189 
190  private:
191 
CheckPendingPermissionChange()192   void CheckPendingPermissionChange() {
193     base::internal::AssertBlockingAllowed();
194     std::map<std::string, int>::iterator it;
195     for (it = permissions_checks_attempts_.begin(); it != permissions_checks_attempts_.end();) {
196       std::string device_name = it->first;
197       bool keep = true;
198       if (HaveReadWritePermissions(device_name)) {
199         OnDeviceAdded(device_name);
200         keep = false;
201       }
202       else if (it->second-- <= 0) {
203         HID_LOG(ERROR) << "Still don't have write permissions to '" << device_name
204                        << "' after " << kMaxPermissionChecks << " attempts";
205         keep = false;
206       }
207 
208       if (keep)
209         ++it;
210       else
211         permissions_checks_attempts_.erase(it++);
212     }
213 
214     if (permissions_checks_attempts_.empty())
215       timer_->Stop();
216   }
217 
SetupDevdMonitor()218   void SetupDevdMonitor() {
219     base::internal::AssertBlockingAllowed();
220 
221     int devd_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
222     if (devd_fd < 0)
223       return;
224 
225     struct sockaddr_un sa;
226 
227     sa.sun_family = AF_UNIX;
228     strlcpy(sa.sun_path, "/var/run/devd.seqpacket.pipe", sizeof(sa.sun_path));
229     if (connect(devd_fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
230       close(devd_fd);
231       return;
232     }
233 
234     devd_fd_.reset(devd_fd);
235     file_watcher_ = base::FileDescriptorWatcher::WatchReadable(
236         devd_fd_.get(), base::Bind(&BlockingTaskRunnerHelper::OnDevdMessageCanBeRead,
237                                    base::Unretained(this)));
238   }
239 
OnDevdMessageCanBeRead()240   void OnDevdMessageCanBeRead() {
241     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
242     ssize_t bytes_read = HANDLE_EINTR(recv(devd_fd_.get(), devd_buffer_->data(),
243                                       devd_buffer_->size() - 1, MSG_WAITALL));
244     if (bytes_read < 0) {
245       if (errno != EAGAIN) {
246         HID_LOG(ERROR) << "Read failed";
247         file_watcher_.reset();
248       }
249       return;
250     }
251 
252     devd_buffer_->data()[bytes_read] = 0;
253     char *data = devd_buffer_->data();
254     // It may take some time for devd to change permissions
255     // on /dev/uhidX node. So do not fail immediately if
256     // open fail. Retry each second for kMaxPermissionChecks
257     // times before giving up entirely
258     if (base::StartsWith(data, "+uhid", base::CompareCase::SENSITIVE)) {
259       std::vector<std::string> parts = base::SplitString(
260         data, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
261       if (!parts.empty()) {
262         std::string device_name = parts[0].substr(1); // skip '+'
263         if (HaveReadWritePermissions(device_name))
264           OnDeviceAdded(parts[0].substr(1));
265         else {
266           // Do not re-add to checks
267           if (permissions_checks_attempts_.find(device_name) == permissions_checks_attempts_.end()) {
268             permissions_checks_attempts_.insert(std::pair<std::string, int>(device_name, kMaxPermissionChecks));
269             timer_->Start(FROM_HERE, base::TimeDelta::FromSeconds(1),
270                           this, &BlockingTaskRunnerHelper::CheckPendingPermissionChange);
271           }
272         }
273       }
274     }
275 
276     if (base::StartsWith(data, "-uhid", base::CompareCase::SENSITIVE)) {
277       std::vector<std::string> parts = base::SplitString(
278         data, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
279       if (!parts.empty()) {
280         std::string device_name = parts[0].substr(1); // skip '-'
281         auto it = permissions_checks_attempts_.find(device_name);
282         if (it != permissions_checks_attempts_.end()) {
283           permissions_checks_attempts_.erase(it);
284           if (permissions_checks_attempts_.empty())
285             timer_->Stop();
286         }
287         OnDeviceRemoved(parts[0].substr(1));
288       }
289     }
290   }
291 
292   SEQUENCE_CHECKER(sequence_checker_);
293 
294   // This weak pointer is only valid when checked on this task runner.
295   base::WeakPtr<HidServiceFreeBSD> service_;
296   scoped_refptr<base::SequencedTaskRunner> task_runner_;
297   std::unique_ptr<base::FileDescriptorWatcher::Controller> file_watcher_;
298   std::unique_ptr<base::RepeatingTimer> timer_;
299   base::ScopedFD devd_fd_;
300   scoped_refptr<net::IOBufferWithSize> devd_buffer_;
301   std::map<std::string, int> permissions_checks_attempts_;
302 
303   DISALLOW_COPY_AND_ASSIGN(BlockingTaskRunnerHelper);
304 };
305 
HidServiceFreeBSD()306 HidServiceFreeBSD::HidServiceFreeBSD()
307     : blocking_task_runner_(
308           base::ThreadPool::CreateSequencedTaskRunner(kBlockingTaskTraits)),
309       helper_(nullptr, base::OnTaskRunnerDeleter(blocking_task_runner_)) {
310   helper_.reset(new BlockingTaskRunnerHelper(weak_factory_.GetWeakPtr()));
311   blocking_task_runner_->PostTask(
312       FROM_HERE,
313       base::BindOnce(&BlockingTaskRunnerHelper::Start, base::Unretained(helper_.get())));
314 }
315 
~HidServiceFreeBSD()316 HidServiceFreeBSD::~HidServiceFreeBSD() {
317   blocking_task_runner_->DeleteSoon(FROM_HERE, helper_.release());
318 }
319 
GetWeakPtr()320 base::WeakPtr<HidService> HidServiceFreeBSD::GetWeakPtr() {
321   return weak_factory_.GetWeakPtr();
322 }
323 
324 // static
OpenOnBlockingThread(std::unique_ptr<ConnectParams> params)325 void HidServiceFreeBSD::OpenOnBlockingThread(
326     std::unique_ptr<ConnectParams> params) {
327   base::ScopedBlockingCall scoped_blocking_call(
328       FROM_HERE, base::BlockingType::MAY_BLOCK);
329   scoped_refptr<base::SequencedTaskRunner> task_runner = params->task_runner;
330 
331   base::FilePath device_path(params->device_info->device_node());
332   base::File device_file;
333   int flags =
334       base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_WRITE;
335   device_file.Initialize(device_path, flags);
336   if (!device_file.IsValid()) {
337     HID_LOG(EVENT) << "Failed to open '" << params->device_info->device_node()
338                    << "': "
339                    << base::File::ErrorToString(device_file.error_details());
340     task_runner->PostTask(FROM_HERE,
341 		          base::BindOnce(std::move(params->callback), nullptr));
342     return;
343   }
344   params->fd.reset(device_file.TakePlatformFile());
345   task_runner->PostTask(FROM_HERE, base::BindOnce(&HidServiceFreeBSD::FinishOpen,
346 			                          std::move(params)));
347 }
348 
Connect(const std::string & device_guid,ConnectCallback callback)349 void HidServiceFreeBSD::Connect(const std::string& device_guid,
350                             ConnectCallback callback) {
351   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
352 
353   const auto& map_entry = devices().find(device_guid);
354   if (map_entry == devices().end()) {
355     base::ThreadTaskRunnerHandle::Get()->PostTask(
356         FROM_HERE, base::BindOnce(std::move(callback), nullptr));
357     return;
358   }
359 
360   scoped_refptr<HidDeviceInfo> device_info = map_entry->second;
361 
362   auto params = std::make_unique<ConnectParams>(device_info, std::move(callback));
363   scoped_refptr<base::SequencedTaskRunner> blocking_task_runner =
364       params->blocking_task_runner;
365 
366   blocking_task_runner->PostTask(
367       FROM_HERE, base::BindOnce(&HidServiceFreeBSD::OpenOnBlockingThread,
368                                 std::move(params)));
369 }
370 
371 // static
FinishOpen(std::unique_ptr<ConnectParams> params)372 void HidServiceFreeBSD::FinishOpen(std::unique_ptr<ConnectParams> params) {
373   DCHECK(params->fd.is_valid());
374 
375   if (!base::SetNonBlocking(params->fd.get())) {
376     HID_PLOG(ERROR) << "Failed to set the non-blocking flag on the device fd";
377     std::move(params->callback).Run(nullptr);
378   }
379 
380   std::move(params->callback).Run(base::MakeRefCounted<HidConnectionFreeBSD>(
381     std::move(params->device_info),
382     std::move(params->fd),
383     std::move(params->blocking_task_runner)
384   ));
385 }
386 
387 }  // namespace device
388