blob: dde528d2bbb5406f80bdb23b06808cec3e330ccf [file] [log] [blame]
// Copyright 2018 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 <fs/service.h>
#include <lib/fxl/logging.h>
#include <lib/fxl/strings/string_printf.h>
#include "expose.h"
namespace component {
void Property::Set(std::string value) {
value_.emplace<std::string>(std::move(value));
}
void Property::Set(ByteVector value) {
value_.emplace<ByteVector>(std::move(value));
}
void Property::Set(StringValueCallback callback) {
value_.emplace<StringValueCallback>(std::move(callback));
}
void Property::Set(VectorValueCallback callback) {
value_.emplace<VectorValueCallback>(std::move(callback));
}
fuchsia::inspect::Property Property::ToFidl(const std::string& name) const {
fuchsia::inspect::Property ret;
ret.key = name;
if (std::holds_alternative<std::string>(value_)) {
ret.value.set_str(std::get<std::string>(value_));
} else if (std::holds_alternative<ByteVector>(value_)) {
fidl::VectorPtr<uint8_t> vec;
auto& val = std::get<ByteVector>(value_);
std::copy(val.begin(), val.end(), std::back_inserter(*vec));
ret.value.set_bytes(std::move(vec));
} else if (std::holds_alternative<StringValueCallback>(value_)) {
ret.value.set_str(std::get<StringValueCallback>(value_)());
} else if (std::holds_alternative<VectorValueCallback>(value_)) {
fidl::VectorPtr<uint8_t> vec;
auto val = std::get<VectorValueCallback>(value_)();
std::copy(val.begin(), val.end(), std::back_inserter(*vec));
ret.value.set_bytes(std::move(vec));
}
return ret;
}
void Metric::SetInt(int64_t value) {
type_ = INT;
int_value_ = value;
}
void Metric::SetUInt(uint64_t value) {
type_ = UINT;
uint_value_ = value;
}
void Metric::SetDouble(double value) {
type_ = DOUBLE;
double_value_ = value;
}
void Metric::SetCallback(ValueCallback callback) {
type_ = CALLBACK;
callback_ = std::move(callback);
}
std::string Metric::ToString() const {
switch (type_) {
case INT:
return fxl::StringPrintf("%ld", int_value_);
case UINT:
return fxl::StringPrintf("%lu", uint_value_);
case DOUBLE:
return fxl::StringPrintf("%f", double_value_);
case CALLBACK:
Metric temp;
callback_(&temp);
return temp.ToString();
}
}
fuchsia::inspect::Metric Metric::ToFidl(const std::string& name) const {
fuchsia::inspect::Metric ret;
switch (type_) {
case INT:
ret.value.set_int_value(int_value_);
break;
case UINT:
ret.value.set_uint_value(uint_value_);
break;
case DOUBLE:
ret.value.set_double_value(double_value_);
break;
case CALLBACK:
Metric temp;
callback_(&temp);
return temp.ToFidl(name);
}
ret.key = name;
return ret;
}
Metric IntMetric(int64_t value) {
Metric ret;
ret.SetInt(value);
return ret;
}
Metric UIntMetric(uint64_t value) {
Metric ret;
ret.SetUInt(value);
return ret;
}
Metric DoubleMetric(double value) {
Metric ret;
ret.SetDouble(value);
return ret;
}
Metric CallbackMetric(Metric::ValueCallback callback) {
Metric ret;
ret.SetCallback(std::move(callback));
return ret;
}
Object::Object(fbl::String name) : name_(name) {
FXL_CHECK(std::find(name_.begin(), name_.end(), '\0') == name_.end())
<< "Object name cannot contain null bytes";
}
void Object::ReadData(ReadDataCallback callback) { callback(ToFidl()); }
void Object::ListChildren(ListChildrenCallback callback) {
StringOutputVector out;
PopulateChildVector(&out);
callback(std::move(out));
}
void Object::OpenChild(std::string name,
::fidl::InterfaceRequest<Inspect> child_channel,
OpenChildCallback callback) {
auto child = GetChild(name.data());
if (!child) {
callback(false);
return;
}
fbl::AutoLock lock(&(child->mutex_));
child->bindings_.AddBinding(child, std::move(child_channel));
callback(true);
}
fbl::RefPtr<Object> Object::GetChild(fbl::String name) {
fbl::AutoLock lock(&mutex_);
auto it = children_.find(name.data());
if (it != children_.end()) {
return it->second;
}
// If the child was not found yet, check all lazily initialized children.
if (lazy_object_callback_) {
ObjectVector lazy_objects;
lazy_object_callback_(&lazy_objects);
for (auto& obj : lazy_objects) {
if (name == obj->name()) {
return obj;
}
}
}
// Child not found.
return fbl::RefPtr<Object>();
}
void Object::SetChild(fbl::RefPtr<Object> child) {
fbl::AutoLock lock(&mutex_);
auto it = children_.find(child->name().data());
if (it != children_.end()) {
it->second.swap(child);
} else {
children_.insert(std::make_pair(child->name().data(), child));
}
}
fbl::RefPtr<Object> Object::TakeChild(fbl::String name) {
fbl::AutoLock lock(&mutex_);
auto it = children_.find(name.c_str());
if (it == children_.end()) {
return fbl::RefPtr<Object>();
}
auto ret = it->second;
children_.erase(it);
return ret;
}
void Object::SetChildrenCallback(ChildrenCallback callback) {
fbl::AutoLock lock(&mutex_);
lazy_object_callback_ = std::move(callback);
}
void Object::ClearChildrenCallback() {
fbl::AutoLock lock(&mutex_);
ChildrenCallback temp;
lazy_object_callback_.swap(temp);
}
bool Object::RemoveProperty(const std::string& name) {
fbl::AutoLock lock(&mutex_);
auto it = properties_.find(name.c_str());
if (it != properties_.end()) {
properties_.erase(it);
return true;
}
return false;
}
bool Object::RemoveMetric(const std::string& name) {
fbl::AutoLock lock(&mutex_);
auto it = metrics_.find(name.c_str());
if (it != metrics_.end()) {
metrics_.erase(it);
return true;
}
return false;
}
bool Object::SetProperty(const std::string& name, Property value) {
if (name.find('\0') != std::string::npos) {
FXL_DCHECK(false) << "Null bytes are not allowed in property names.";
return false;
}
fbl::AutoLock lock(&mutex_);
properties_[name.c_str()] = std::move(value);
return true;
}
bool Object::SetMetric(const std::string& name, Metric metric) {
if (name.find('\0') != std::string::npos) {
FXL_DCHECK(false) << "Null bytes are not allowed in metric names.";
return false;
}
fbl::AutoLock lock(&mutex_);
metrics_[name.c_str()] = std::move(metric);
return true;
}
void Object::GetContents(LazyEntryVector* out_vector) {
fbl::AutoLock lock(&mutex_);
out_vector->push_back({kChanId, ".channel", V_TYPE_FILE});
uint64_t index = kSpecialIdMax;
// Each child gets a unique ID, which is just its index in the directory.
// If the set of children changes between successive calls to readdir(3), it
// is possible we will miss children.
//
// This behavior is documented at the declaration point.
for (const auto& it : children_) {
out_vector->push_back({index++, it.second->name_, V_TYPE_DIR});
}
if (lazy_object_callback_) {
ObjectVector lazy_objects;
lazy_object_callback_(&lazy_objects);
for (const auto& obj : lazy_objects) {
out_vector->push_back({index++, obj->name(), V_TYPE_DIR});
}
}
}
zx_status_t Object::GetFile(fbl::RefPtr<Vnode>* out_vnode, uint64_t id,
fbl::String name) {
fbl::AutoLock lock(&mutex_);
if (id == kChanId) {
// `.channel` is a Service file that binds incoming channels to this
// Inspect implementation.
auto ref = fbl::WrapRefPtr(this);
*out_vnode = fbl::MakeRefCounted<fs::Service>([ref](zx::channel chan) {
fbl::AutoLock lock(&ref->mutex_);
ref->bindings_.AddBinding(
ref, fidl::InterfaceRequest<Inspect>(std::move(chan)));
return ZX_OK;
});
return ZX_OK;
}
// If the file isn't a special file, search for the name as a child object.
auto it = children_.find(name.c_str());
if (it == children_.end()) {
// If the named child is not found, search through the lazy objects if the
// callback is set.
if (lazy_object_callback_) {
ObjectVector lazy_objects;
lazy_object_callback_(&lazy_objects);
for (const auto& obj : lazy_objects) {
if (obj->name() == name.c_str()) {
*out_vnode = obj;
return ZX_OK;
}
}
}
return ZX_ERR_NOT_FOUND;
}
*out_vnode = it->second;
return ZX_OK;
}
void Object::PopulateChildVector(StringOutputVector* out_vector)
__TA_EXCLUDES(mutex_) {
// Lock the local child vector. No need to lock children since we are only
// reading their constant name.
fbl::AutoLock lock(&mutex_);
for (const auto& it : children_) {
out_vector->push_back(it.second->name().data());
}
if (lazy_object_callback_) {
ObjectVector lazy_objects;
lazy_object_callback_(&lazy_objects);
for (const auto& obj : lazy_objects) {
out_vector->push_back(obj->name().data());
}
}
}
fuchsia::inspect::Object Object::ToFidl() {
fbl::AutoLock lock(&mutex_);
fuchsia::inspect::Object ret;
ret.name = name_.data();
for (const auto& it : properties_) {
ret.properties.push_back(it.second.ToFidl(it.first));
}
for (const auto& it : metrics_) {
ret.metrics.push_back(it.second.ToFidl(it.first));
}
return ret;
}
Object::StringOutputVector Object::GetChildren() {
Object::StringOutputVector ret;
PopulateChildVector(&ret);
return ret;
}
} // namespace component