| // Copyright 2016 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/llcpp/fidl.h> |
| #include <lib/fdio/watcher.h> |
| #include <zircon/types.h> |
| |
| #include <fbl/span.h> |
| |
| #include "fdio_unistd.h" |
| |
| namespace fio = fuchsia_io; |
| |
| __EXPORT |
| zx_status_t fdio_watch_directory(int dirfd, watchdir_func_t cb, zx_time_t deadline, void* cookie) { |
| fdio_ptr io = fd_to_io(dirfd); |
| if (io == nullptr) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| zx_handle_t handle; |
| if (zx_status_t status = io->borrow_channel(&handle); status != ZX_OK) { |
| return status; |
| } |
| fidl::UnownedClientEnd<fio::Directory> directory(handle); |
| if (!directory.is_valid()) { |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| |
| zx::status endpoints = fidl::CreateEndpoints<fio::DirectoryWatcher>(); |
| if (endpoints.is_error()) { |
| return endpoints.status_value(); |
| } |
| |
| auto result = |
| fidl::WireCall(directory).Watch(fio::wire::kWatchMaskAll, 0, endpoints->client.TakeChannel()); |
| if (zx_status_t status = result.status(); status != ZX_OK) { |
| return status; |
| } |
| if (zx_status_t status = result->s; status != ZX_OK) { |
| return status; |
| } |
| |
| for (;;) { |
| uint8_t bytes[fio::wire::kMaxBuf + 1]; // Extra byte for temporary null terminator. |
| uint32_t num_bytes; |
| zx_status_t status = endpoints->server.channel().read_etc(0, &bytes, nullptr, sizeof(bytes), 0, |
| &num_bytes, nullptr); |
| if (status != ZX_OK) { |
| if (status == ZX_ERR_SHOULD_WAIT) { |
| status = endpoints->server.channel().wait_one(ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED, |
| zx::time(deadline), nullptr); |
| if (status != ZX_OK) { |
| return status; |
| } |
| continue; |
| } |
| return status; |
| } |
| |
| // Message Format: { OP, LEN, DATA[LEN] } |
| fbl::Span span(bytes, num_bytes); |
| auto it = span.begin(); |
| for (;;) { |
| if (std::distance(it, span.end()) < 2) { |
| break; |
| } |
| uint8_t event = *it++; |
| uint8_t len = *it++; |
| uint8_t* name = it; |
| |
| if (std::distance(it, span.end()) < len) { |
| break; |
| } |
| it += len; |
| |
| switch (event) { |
| case fio::wire::kWatchEventAdded: |
| case fio::wire::kWatchEventExisting: |
| event = WATCH_EVENT_ADD_FILE; |
| break; |
| case fio::wire::kWatchEventRemoved: |
| event = WATCH_EVENT_REMOVE_FILE; |
| break; |
| case fio::wire::kWatchEventIdle: |
| event = WATCH_EVENT_WAITING; |
| break; |
| default: |
| // unsupported event |
| continue; |
| } |
| |
| // The callback expects a null-terminated string. |
| uint8_t tmp = *it; |
| *it = 0; |
| status = cb(dirfd, event, reinterpret_cast<const char*>(name), cookie); |
| *it = tmp; |
| if (status != ZX_OK) { |
| return status; |
| } |
| } |
| } |
| } |