blob: 116209df369f1317e322a8da2aa3d78ec9a61e27 [file] [log] [blame]
// 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;
}
}
}
}