| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <fuchsia/io/cpp/fidl.h> |
| #include <lib/fdio/directory.h> |
| #include <lib/sys/service/cpp/service_aggregate.h> |
| #include <lib/sys/service/cpp/service_watcher.h> |
| |
| namespace sys { |
| |
| zx_status_t ServiceWatcher::Begin(const ServiceAggregateBase& service_aggregate, |
| async_dispatcher_t* dispatcher) { |
| zx::channel client_end, server_end; |
| zx_status_t status = zx::channel::create(0, &client_end, &server_end); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| fidl::SynchronousInterfacePtr<fuchsia::io::Directory> dir; |
| dir.Bind(zx::channel(fdio_service_clone(service_aggregate.channel().get()))); |
| zx_status_t fidl_status; |
| status = dir->Watch(fuchsia::io::WATCH_MASK_EXISTING | fuchsia::io::WATCH_MASK_ADDED | |
| fuchsia::io::WATCH_MASK_REMOVED, |
| 0, std::move(server_end), &fidl_status); |
| if (status != ZX_OK) { |
| return status; |
| } else if (fidl_status != ZX_OK) { |
| return fidl_status; |
| } |
| |
| buf_.resize(fuchsia::io::MAX_BUF); |
| client_end_ = std::move(client_end); |
| wait_.set_object(client_end_.get()); |
| wait_.set_trigger(ZX_CHANNEL_READABLE); |
| return wait_.Begin(dispatcher); |
| } |
| |
| zx_status_t ServiceWatcher::Cancel() { return wait_.Cancel(); } |
| |
| void ServiceWatcher::OnWatchedEvent(async_dispatcher_t* dispatcher, async::WaitBase* wait, |
| zx_status_t status, const zx_packet_signal_t* signal) { |
| if (status != ZX_OK || !(signal->observed & ZX_CHANNEL_READABLE)) { |
| return; |
| } |
| uint32_t size = buf_.size(); |
| status = client_end_.read(0, buf_.data(), nullptr, size, 0, &size, nullptr); |
| if (status != ZX_OK) { |
| return; |
| } |
| |
| for (auto i = buf_.begin(), end = buf_.begin() + size; std::distance(i, end) > 2;) { |
| // Process message structure, as described by fuchsia::io::WatchedEvent. |
| uint8_t event = *i++; |
| uint8_t len = *i++; |
| // Restrict the length to the remaining size of the buffer. |
| len = std::min<uint8_t>(len, std::max(0l, std::distance(i, end))); |
| // If the entry is valid, invoke the callback. |
| if (len != 1 || *i != '.') { |
| std::string instance(reinterpret_cast<char*>(i.base()), len); |
| callback_(event, std::move(instance)); |
| } |
| i += len; |
| } |
| |
| wait_.Begin(dispatcher); |
| } |
| |
| } // namespace sys |