blob: 882850f031937def6630d3cb07a8ddd0e337a152 [file] [log] [blame]
// Copyright (c) 2011 The Chromium OS 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 "entd/entd.h"
#include <stdio.h>
#include <iostream>
#include <base/file_util.h>
#include <event.h>
#include <glib-object.h>
#include <v8.h>
#include "cros/chromeos_cros_api.h"
#include "entd/browser.h"
#include "entd/callback_server.h"
#include "entd/flimflam.h"
#include "entd/http.h"
#include "entd/crypto_openssl.h"
#include "entd/crypto_pkcs11.h"
#include "entd/scriptable.h"
#include "entd/syslog.h"
#include "entd/tpm.h"
#include "entd/utils.h"
namespace entd {
using std::string;
static const char kOpenSSLConfigName[] = "entd";
void dispatch_OnTimeout(int fd, short flags, void* arg);
inline struct timeval CreateTimeoutInMs(uint64_t milliseconds) {
struct timeval tv = { milliseconds / 1000, milliseconds % 1000 * 1000 };
return tv;
}
static const char kDefaultLsbRelease[] = "/etc/lsb-release";
// Class to represent a pending timeout event.
class Timeout {
public:
Timeout()
: entd_(NULL), event_(NULL) {
}
virtual ~Timeout() {
v8_value_.Dispose();
delete event_;
}
virtual bool Initialize(Entd* entd, v8::Handle<v8::Value> v8_value,
uint32_t interval) {
entd_ = entd;
v8_value_ = v8::Persistent<v8::Value>::New(v8_value);
// Least significant half (on 32bit x86 :P) of the address of the timeout
// data, shifted left 16 bytes and or'd with the timeout sequence
// should give us unique timeout handles that are difficult to guess.
handle_ = (((0xFFFF & reinterpret_cast<size_t>(this)) << 16) |
++Timeout::sequence);
struct timeval tv = CreateTimeoutInMs(interval);
event_ = new struct event;
event_set(event_, 0, 0, &dispatch_OnTimeout, this);
event_add(event_, &tv);
return true;
}
virtual uint32_t GetHandle() const {
return handle_;
}
virtual v8::Local<v8::Value> GetValue() const {
return v8::Local<v8::Value>::New(v8_value_);
}
virtual Entd* GetEntd() const {
return entd_;
}
virtual void CancelEvent() {
event_del(event_);
}
private:
// Count of timeouts created so far, used to ensure globally unique handles.
static uint32_t sequence;
// Pointer to the daemon instance.
Entd* entd_;
// Integer handle used to identify this timeout to JavaScript.
uint32_t handle_;
// JS value to execute when the timeout fires.
v8::Persistent<v8::Value> v8_value_;
// libevent structure for this timeout event.
struct event* event_;
DISALLOW_COPY_AND_ASSIGN(Timeout);
};
uint32_t Timeout::sequence = 0;
// Class to handle calling native timeouts. Implemented using libevent.
class NativeTimeout
{
public:
NativeTimeout(Entd::NativeTimeoutCallback cb, void* data,
uint32_t interval_ms)
: callback_(cb), data_(data),
interval_(interval_ms * 1000),
handle_(0), event_(NULL) {
}
~NativeTimeout() {
event_del(event_);
delete event_;
}
void Initialize() {
handle_ = s_handle_counter_++;
event_ = new struct event;
event_set(event_, 0, 0, &EventCallback, this);
Start();
}
// Calling Start on a running timeout will simply reset the timer
void Start() {
struct timeval tv = CreateTimeoutInMs(interval_);
event_add(event_, &tv);
}
uint32_t GetHandle() { return handle_; }
private:
// Calls the set callback. If the callback returns 'true', start it again.
static void EventCallback(int fd, short flags, void* arg) {
NativeTimeout* timeout = reinterpret_cast<NativeTimeout*>(arg);
bool res = timeout->callback_(timeout->data_);
if (res) {
timeout->Start();
}
}
// Callback data
Entd::NativeTimeoutCallback callback_;
void* data_;
uint32_t interval_;
// Handle identifier (simple integer incrementing from 1)
uint32_t handle_;
// libevent data
struct event* event_;
// Global handle counter
static uint32_t s_handle_counter_;
DISALLOW_COPY_AND_ASSIGN(NativeTimeout);
};
uint32_t NativeTimeout::s_handle_counter_ = 1;
// Called by libevent when a signal is detected.
void dispatch_OnSignal(int fd, short flags, void* arg) {
Entd* entd = reinterpret_cast<Entd*>(arg);
entd->OnSignal(fd);
}
// Called by libevent when a timeout fires.
void dispatch_OnTimeout(int fd, short flags, void* arg) {
Timeout* timeout = reinterpret_cast<Timeout*>(arg);
// LOG(INFO) << "Fire timeout: " << timeout->GetHandle();
timeout->GetEntd()->OnTimeout(*timeout);
delete timeout;
}
// Called by v8 for entd.setTimeout().
v8::Handle<v8::Value> dispatch_SetTimeout(const v8::Arguments& args) {
Entd* entd = Entd::Unwrap(args.This());
if (!entd)
return v8::Undefined();
if (args.Length() < 2) {
ThrowException(v8::String::New("Not enough parameters"));
return v8::Undefined();
}
if (!(args[0]->IsString() || args[0]->IsFunction())) {
ThrowException(v8::String::New(
"First argument must be string or function"));
return v8::Undefined();
}
return v8::Uint32::New(entd->SetTimeout(args[0], args[1]->Uint32Value()));
}
// Called by v8 for entd.clearTimeout().
v8::Handle<v8::Value> dispatch_ClearTimeout(const v8::Arguments& args) {
Entd* entd = Entd::Unwrap(args.This());
if (!entd)
return v8::Undefined();
if (args.Length() < 1) {
ThrowException(v8::String::New("Not enough parameters"));
return v8::Undefined();
}
Timeout* timeout = entd->ClearTimeout(args[0]->Uint32Value());
if (!timeout)
return v8::False();
delete timeout;
return v8::True();
}
// Called by v8 for entd.scheduleShutdown().
v8::Handle<v8::Value> dispatch_ScheduleShutdown(const v8::Arguments& args) {
Entd* entd = Entd::Unwrap(args.This());
if (!entd)
return v8::Undefined();
uint32_t code = 0;
if (args.Length() > 0)
code = args[0]->Uint32Value();
uint32_t interval = 0;
if (args.Length() > 1)
interval = args[1]->Uint32Value();
entd->ScheduleShutdown(code, interval);
return v8::True();
}
// Called by v8 when someone trys to read from entd.hostname
v8::Handle<v8::Value> dispatch_GetHostname(v8::Local<v8::String> name,
const v8::AccessorInfo& info) {
Entd* entd = Entd::Unwrap(info.Holder());
return v8::String::New(entd->GetHostname().c_str());
}
// Called by v8 when someone trys to assign to entd.hostname
void dispatch_SetHostname(v8::Local<v8::String> name,
v8::Local<v8::Value> value,
const v8::AccessorInfo& info) {
Entd* entd = Entd::Unwrap(info.Holder());
if (!entd->SetHostname(*v8::String::Utf8Value(value)))
utils::ThrowV8Exception("Invalid hostname");
}
// Called by v8 for the global object's print() method.
v8::Handle<v8::Value> dispatch_Print(const v8::Arguments& args) {
for (int i = 0; i < args.Length(); ++i) {
v8::Handle<v8::Value> arg = args[i];
v8::String::Utf8Value value(arg);
std::cout << *value;
}
std::cout.flush();
return v8::Undefined();
}
// Called by v8 for the global object's print() method.
v8::Handle<v8::Value> dispatch_PrintLn(const v8::Arguments& args) {
for (int i = 0; i < args.Length(); ++i) {
v8::Handle<v8::Value> arg = args[i];
v8::String::Utf8Value value(arg);
std::cout << *value;
}
std::cout << "\n";
std::cout.flush();
return v8::Undefined();
}
// Called by v8 for the global object's readFromFile() method.
v8::Handle<v8::Value> dispatch_ReadFromFile(const v8::Arguments& args) {
if (args.Length() != 1) {
utils::ThrowV8Exception("Missing argument: filePath");
return v8::False();
}
std::string file = utils::ValueAsUtf8String(args[0]);
FilePath filepath = FilePath(file);
std::string filestr;
v8::Handle<v8::String> result;
if (file_util::ReadFileToString(filepath, &filestr))
result = v8::String::New(filestr.c_str(), filestr.size());
if (result.IsEmpty())
utils::ThrowV8Exception(std::string("Unable to read file: ") + file);
return result;
}
// Called by v8 for the global object's writeToFile() method.
v8::Handle<v8::Value> dispatch_WriteToFile(const v8::Arguments& args) {
if (args.Length() != 2) {
utils::ThrowV8Exception("Insufficient arguments.");
return v8::False();
}
std::string s = utils::ValueAsUtf8String(args[0]);
std::string file = utils::ValueAsUtf8String(args[1]);
int slen = s.length();
int res = -1;
if (slen > 0) {
res = file_util::WriteFile(FilePath(file), s.c_str(), slen);
}
if (res < 0 || res != slen) {
utils::ThrowV8Exception(std::string("Unable to write file: ") + file);
return v8::False();
}
return v8::True();
}
// Called by v8 for the global object's GC() method.
v8::Handle<v8::Value> dispatch_GC(const v8::Arguments& args) {
v8::V8::LowMemoryNotification();
return v8::Undefined();
}
Entd::Entd()
: lsb_release_filename_(kDefaultLsbRelease),
callback_server_(NULL),
flimflam_(new Flimflam()),
syslog_(new Syslog()) {
::g_type_init();
}
Entd::~Entd() {
// Clean up any pending timeouts.
for (unsigned int i = 0; i < timeout_list_.size(); ++i) {
Timeout* timeout = timeout_list_[i];
timeout->CancelEvent();
delete timeout;
}
CleanupTemplate();
context_.Dispose();
}
bool Entd::allow_dirty_exit = false;
bool Entd::allow_file_io = false;
bool Entd::libcros_loaded = false;
std::string Entd::libcros_location = "/opt/google/chrome/chromeos/libcros.so";
// Bind "setTimeout" and "clearTimeout" to the entd template object.
void Entd::SetTemplateBindings(v8::Handle<v8::ObjectTemplate> template_object) {
template_object->Set(v8::String::NewSymbol("setTimeout"),
v8::FunctionTemplate::New(dispatch_SetTimeout),
v8::ReadOnly);
template_object->Set(v8::String::NewSymbol("clearTimeout"),
v8::FunctionTemplate::New(dispatch_ClearTimeout),
v8::ReadOnly);
template_object->Set(v8::String::NewSymbol("scheduleShutdown"),
v8::FunctionTemplate::New(dispatch_ScheduleShutdown),
v8::ReadOnly);
template_object->SetAccessor(v8::String::NewSymbol("hostname"),
dispatch_GetHostname,
dispatch_SetHostname);
}
bool Entd::Initialize() {
if (!context_.IsEmpty()) {
LOG(ERROR) << "Entd initialized twice.";
return false;
}
event_init();
LOG(INFO) << "Initialized libevent " << event_get_version();
std::string errmsg;
Entd::libcros_loaded = chromeos::LoadLibcros(
Entd::libcros_location.c_str(), errmsg);
if (!Entd::libcros_loaded)
LOG(WARNING) << "Problem loading chromeos shared object: " << errmsg;
return true;
}
v8::Handle<v8::Object> Entd::ConstructEntd() {
v8::HandleScope handle_scope;
// Build the entd object.
if (!JSObjectWrapper<Entd>::Initialize()) {
LOG(ERROR) << "Error initializing entd";
exit(1);
}
v8::Handle<v8::Object> entd = obj();
entd->Set(v8::String::NewSymbol("isLibcrosLoaded"),
v8::Boolean::New(Entd::libcros_loaded));
// Build the flimflam object.
if (!flimflam_->Initialize()) {
LOG(ERROR) << "Error initializing entd.flimflam";
exit(1);
}
entd->Set(v8::String::NewSymbol("flimflam"),
flimflam_->obj(), v8::ReadOnly);
// Build the http object.
Http::Reference http = Http::New();
if (http.IsEmpty() || !http->Initialize(this, NULL)) {
LOG(ERROR) << "Error initializing entd.http";
exit(1);
}
entd->Set(v8::String::NewSymbol("http"),
http.js_object(), v8::ReadOnly);
// Build the syslog object.
if (!syslog_->Initialize()) {
LOG(ERROR) << "Error initializing entd.syslog";
exit(1);
}
entd->Set(v8::String::NewSymbol("syslog"),
syslog_->obj(), v8::ReadOnly);
// Build the callbackServer object.
callback_server_.reset(new CallbackServer(this));
if (!callback_server_->Initialize()) {
LOG(ERROR) << "Error initializing entd.callback_server";
exit(1);
}
entd->Set(v8::String::NewSymbol("callbackServer"),
callback_server_->obj(), v8::ReadOnly);
// Build the entd.crypto object.
v8::Handle<v8::Object> crypto_obj = v8::Object::New();
entd->Set(v8::String::NewSymbol("crypto"), crypto_obj);
// Hook up the entd.crypto.Pkcs11 constructor.
crypto_obj->Set(v8::String::NewSymbol("Pkcs11"),
crypto::Pkcs11::constructor_template()->GetFunction());
// Hook up the entd.crypto.OpenSSL constructor.
crypto_obj->Set(v8::String::NewSymbol("OpenSSL"),
crypto::OpenSSL::constructor_template()->GetFunction());
Browser::Reference browser = Browser::New();
if (!browser->Initialize(this)) {
LOG(ERROR) << "Error initializing entd.browser";
exit(1);
}
entd->Set(v8::String::NewSymbol("Browser"),
Browser::constructor_template()->GetFunction());
entd->Set(v8::String::NewSymbol("browser"), browser->js_object());
Tpm::Reference tpm = Tpm::New();
if (!tpm->Initialize()) {
LOG(ERROR) << "Error initializing entd.tpm";
exit(1);
}
entd->Set(v8::String::NewSymbol("Tpm"),
Tpm::constructor_template()->GetFunction());
entd->Set(v8::String::NewSymbol("tpm"), tpm->js_object());
return handle_scope.Close(entd);
}
bool Entd::StartScriptingEnvironment() {
v8::HandleScope handle_scope;
// Create the global object.
v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
global_template->Set(v8::String::NewSymbol("print"),
v8::FunctionTemplate::New(dispatch_Print),
v8::ReadOnly);
global_template->Set(v8::String::NewSymbol("println"),
v8::FunctionTemplate::New(dispatch_PrintLn),
v8::ReadOnly);
global_template->Set(v8::String::NewSymbol("GC"),
v8::FunctionTemplate::New(dispatch_GC),
v8::ReadOnly);
if (allow_file_io) {
global_template->Set(v8::String::NewSymbol("readFromFile"),
v8::FunctionTemplate::New(dispatch_ReadFromFile),
v8::ReadOnly);
global_template->Set(v8::String::NewSymbol("writeToFile"),
v8::FunctionTemplate::New(dispatch_WriteToFile),
v8::ReadOnly);
}
context_ = v8::Context::New(NULL, global_template);
v8::Context::Scope context_scope(context_);
v8::Handle<v8::Object> global = context_->Global();
// Construct the global entd object.
v8::Handle<v8::Object> entd = ConstructEntd();
// Set the "entd" property of the global object.
global->Set(v8::String::NewSymbol("entd"), entd, v8::ReadOnly);
// Temporary storage for the generic values used below.
v8::Handle<v8::Value> value;
// Set entd.username and entd.hostname.
entd->Set(v8::String::NewSymbol("username"),
v8::String::New(username_.c_str()), v8::ReadOnly);
// Set entd.lsbRelease to the contents the lsb_release file.
std::string lsb_file_contents;
if (!file_util::ReadFileToString(FilePath(lsb_release_filename_),
&lsb_file_contents)) {
LOG(ERROR) << "Could not read " << lsb_release_filename_;
return false;
}
entd->Set(v8::String::NewSymbol("lsbRelease"),
v8::String::New(lsb_file_contents.c_str()), v8::ReadOnly);
size_t at_pos = username_.find("@");
if (at_pos == std::string::npos || at_pos == username_.length() - 1) {
LOG(ERROR) << "Can't determine hostname from username: " << username_;
return false;
}
if (!SetHostname(username_.substr(at_pos + 1))) {
LOG(ERROR) << "Unable to set host name.";
return false;
}
// Load manifest file, if one was provided.
v8::Handle<v8::Object> manifest;
if (manifest_filename_.empty()) {
LOG(WARNING) << "No manifest file.";
} else {
if (!JsonParseFromFile(manifest_filename_, &value) || !value->IsObject()) {
LOG(ERROR) << "Error loading manifest file: " << manifest_filename_;
return false;
}
manifest = v8::Handle<v8::Object>::Cast(value);
}
// Load utility file, if one was provided.
if (!utility_filename_.empty()) {
LOG(INFO) << "Executing utility file: " << utility_filename_;
if (!ExecuteFile(utility_filename_, &value)) {
LOG(ERROR) << "Error in utility file.";
return false;
}
if (entd->Has(v8::String::NewSymbol("verifyManifest"))) {
v8::TryCatch try_catch;
v8::Handle<v8::Value> arg;
if (manifest.IsEmpty()) {
arg = v8::Null();
} else {
arg = manifest;
}
value = utils::CallV8Function(entd, "verifyManifest", 1, &arg);
if (try_catch.HasCaught()) {
// Failed due to V8 exception
utils::ReportV8Exception(&try_catch);
return false;
} else if (value.IsEmpty()) {
// Failed for other reasons
return false;
}
if (!value->IsBoolean() || value->BooleanValue() != true) {
LOG(ERROR) << "Utility file vetoed the extension manifest.";
return false;
}
}
}
// Load the policy file.
if (policy_filename_.empty()) {
LOG(ERROR) << "No policy file.";
return false;
}
if (!ExecuteFile(policy_filename_, &value)) {
LOG(ERROR) << "Error in policy file.";
return false;
}
LOG(INFO) << "Policy loaded.";
if (!crypto::OpenSSL::StaticInitialize(kOpenSSLConfigName)) {
LOG(ERROR) << "Failed to initialize OpenSSL.";
return false;
}
// Fire the onLoad event.
if (entd->Has(v8::String::NewSymbol("onLoad"))) {
v8::TryCatch try_catch;
v8::Handle<v8::Value> arg;
if (manifest.IsEmpty()) {
arg = v8::Null();
} else {
arg = manifest;
}
value = utils::CallV8Function(entd, "onLoad", 1, &arg);
if (try_catch.HasCaught()) {
// Failed due to V8 exception
utils::ReportV8Exception(&try_catch);
return false;
} else if (value.IsEmpty()) {
// Failed for other reasons
LOG(INFO) << "Policy onLoad failed for mysterious reasons.";
return false;
}
LOG(INFO) << "Policy onLoad complete.";
}
return true;
}
bool Entd::StopScriptingEnvironment() {
// Temporary storage for the a generic value used in this function.
v8::Handle<v8::Value> value;
value = context_->Global()->Get(v8::String::NewSymbol("entd"));
if (value.IsEmpty() || !value->IsObject()) {
LOG(ERROR) << "Global entd missing or not an object";
return false;
}
v8::Handle<v8::Object> entd = v8::Handle<v8::Object>::Cast(value);
if (entd->Has(v8::String::NewSymbol("onUnload"))) {
v8::TryCatch try_catch;
value = utils::CallV8Function(entd, "onUnload", 0, NULL);
if (try_catch.HasCaught()) {
// Failed due to V8 exception
utils::ReportV8Exception(&try_catch);
return false;
} else if (value.IsEmpty()) {
// Failed for other reasons
return false;
}
}
crypto::Pkcs11::Finalize();
// Unload OpenSSL modules.
crypto::OpenSSL::StaticFinalize();
return true;
}
// static
bool Entd::LibcrosLoadedOrThrow() {
if (!Entd::libcros_loaded) {
utils::ThrowV8Exception("Library not loaded: libcros");
return false;
}
return true;
}
// See comments in main.cc about the return values.
uint32_t Entd::Run() {
exit_code_ = 0;
if (!StartScriptingEnvironment())
return 1;
struct event ev_SIGHUP;
struct event ev_SIGINT;
struct event ev_SIGTERM;
if (!Entd::allow_dirty_exit) {
event_set(&ev_SIGHUP, SIGHUP, EV_SIGNAL | EV_PERSIST, &dispatch_OnSignal,
reinterpret_cast<void*>(this));
event_add(&ev_SIGHUP, NULL);
event_set(&ev_SIGINT, SIGINT, EV_SIGNAL | EV_PERSIST, &dispatch_OnSignal,
reinterpret_cast<void*>(this));
event_add(&ev_SIGINT, NULL);
event_set(&ev_SIGTERM, SIGTERM, EV_SIGNAL | EV_PERSIST, &dispatch_OnSignal,
reinterpret_cast<void*>(this));
event_add(&ev_SIGTERM, NULL);
}
// Set up the handle and context scope for JavaScript run from event handlers,
// or from StopScriptingEnvironment().
v8::HandleScope handle_scope;
v8::Context::Scope context_scope(context_);
event_loop(0);
if (!Entd::allow_dirty_exit) {
event_del(&ev_SIGHUP);
event_del(&ev_SIGINT);
event_del(&ev_SIGTERM);
}
if (!StopScriptingEnvironment()) {
if (exit_code_ == 2) {
// The policy was asking for a restart. We failed to shut down for some
// reason, but would still like to honor the restart.
return 3;
}
if (exit_code_ < 2) {
// We weren't going to restart anyway.
return 1;
}
}
return exit_code_;
}
void Entd::OnSignal(int signal) {
LOG(INFO) << "Responding to signal: " << signal;
event_loopbreak();
}
uint32_t Entd::SetTimeout(v8::Handle<v8::Value> value, uint32_t interval) {
Timeout* timeout = new Timeout();
if (!timeout->Initialize(this, value, interval))
return 0;
timeout_list_.push_back(timeout);
return timeout->GetHandle();
}
Timeout* Entd::ClearTimeout(uint32_t timeout_handle) {
for (unsigned int i = 0; i < timeout_list_.size(); ++i) {
Timeout* timeout = timeout_list_[i];
if (timeout->GetHandle() == timeout_handle) {
// LOG(INFO) << "Remove timeout handle: " << timeout_handle;
timeout->CancelEvent();
timeout_list_.erase(timeout_list_.begin() + i);
return timeout;
}
}
return NULL;
}
void Entd::ScheduleShutdown(uint32_t code, uint32_t interval) {
exit_code_ = code;
struct timeval tv = CreateTimeoutInMs(interval);
event_loopexit(&tv);
}
void Entd::OnTimeout(const Timeout& timeout) {
v8::Context::Scope context_scope(context_);
v8::HandleScope handle_scope;
uint32_t timeout_handle = timeout.GetHandle();
// LOG(INFO) << "OnTimeout: " << timeout_handle;
// Remove this from our active timeouts list first, so any attempt to
// call clearTimeout from script doesn't cause trouble. If we can't clear
// the timeout for some reason then something strange is going on. A
// timeout should only get removed from the list of timeouts by firing
// (this case) or through a ClearTimeout, which should prevent it from
// firing.
if (!ClearTimeout(timeout_handle))
LOG(ERROR) << "Failed to clear the current timeout, something is fishy.";
v8::Local<v8::Value> value = timeout.GetValue();
if (value->IsString()) {
v8::Handle<v8::String> script = v8::Handle<v8::String>::Cast(value);
v8::Handle<v8::Value> result;
char filename[20];
snprintf(&filename[0], sizeof(filename), "(timeout %u)", timeout_handle);
ExecuteString(script, filename, &result);
} else if (value->IsFunction()) {
v8::Handle<v8::Function> func = v8::Handle<v8::Function>::Cast(value);
func->Call(context_->Global(), 0, NULL);
}
}
// Initialize and start a timeout
uint32_t Entd::SetNativeTimeout(NativeTimeoutCallback cb, void* data,
uint32_t interval_ms) {
NativeTimeout* timeout = new NativeTimeout(cb, data, interval_ms);
uint32_t handle = timeout->GetHandle();
native_timeouts_[handle] = timeout;
timeout->Initialize();
return handle;
}
// Start an existing timeout (resets timer if already started)
// Returns false if timeout doesn't exist
bool Entd::StartNativeTimeout(uint32_t handle) {
std::map<uint32_t, NativeTimeout*>::iterator iter =
native_timeouts_.find(handle);
if (iter != native_timeouts_.end()) {
iter->second->Start();
return true;
}
return false;
}
// Cancel and delete an existing timeout
// Returns false if timeout doesn't exist
bool Entd::ClearNativeTimeout(uint32_t handle) {
std::map<uint32_t, NativeTimeout*>::iterator iter =
native_timeouts_.find(handle);
if (iter != native_timeouts_.end()) {
delete iter->second;
native_timeouts_.erase(iter);
return true;
}
return false;
}
bool Entd::CheckHostname(const string& new_hostname) {
if (hostname_.empty())
return utils::CheckHostnameCharset(new_hostname);
size_t new_len = new_hostname.length();
size_t current_len = hostname_.length();
if (new_len < current_len) {
// New hostname is shorter than the existing hostname, can't possibly
// be right.
return false;
}
size_t starts_at = new_len - current_len;
if (new_hostname.compare(starts_at, current_len, hostname_) != 0) {
// New host doesn't match existing one.
return false;
}
if (starts_at == 0) {
// New host and old host are exactly the same.
return true;
}
// Otherwise, the new hostname must more specific than the existing one.
if (starts_at < 2) {
// It takes at least two characters to be more specific (eg: add "a." to
// the existing hostname.)
return false;
}
if (new_hostname[starts_at - 1] != '.')
return false;
return utils::CheckHostnameCharset(new_hostname);
}
bool Entd::JsonParseFromFile(const string& filename,
v8::Handle<v8::Value>* result) {
if (!result) {
LOG(ERROR) << "Null pointer passed for result";
return false;
}
v8::Handle<v8::String> source = utils::ReadFile(filename);
if (source.IsEmpty()) {
LOG(ERROR) << "Error reading json file: " << filename;
return false;
}
return JsonParse(source, result);
}
bool Entd::JsonParse(v8::Handle<v8::String> source,
v8::Handle<v8::Value>* result) {
return CallJson("parse", source, result);
}
bool Entd::JsonStringify(v8::Handle<v8::Value> source,
v8::Handle<v8::Value>* result) {
return CallJson("stringify", source, result);
}
bool Entd::CallJson(string method_name, v8::Handle<v8::Value> arg,
v8::Handle<v8::Value>* result) {
if (!result) {
LOG(ERROR) << "Null pointer passed for result";
return false;
}
v8::Handle<v8::Value> value =
context_->Global()->Get(v8::String::NewSymbol("JSON"));
if (value.IsEmpty() || !value->IsObject()) {
LOG(ERROR) << "Global JSON property missing or not an object.";
return false;
}
v8::Handle<v8::Object> json = v8::Handle<v8::Object>::Cast(value);
value = json->Get(v8::String::NewSymbol(method_name.c_str()));
if (value.IsEmpty() || !value->IsFunction()) {
LOG(ERROR) << "JSON method missing or not a function: " << method_name;
return false;
}
v8::TryCatch try_catch;
v8::Handle<v8::Value> call_result =
utils::CallV8Function(json, method_name, 1, &arg);
if (try_catch.HasCaught()) {
// Failed due to V8 exception
utils::ReportV8Exception(&try_catch);
return false;
} else if (call_result.IsEmpty()) {
// Failed for other reasons
return false;
}
*result = call_result;
return true;
}
bool Entd::ExecuteFile(const std::string& filename,
v8::Handle<v8::Value>* result) {
v8::HandleScope handle_scope;
v8::Handle<v8::String> source = utils::ReadFile(filename);
if (source.IsEmpty()) {
PLOG(WARNING) << "Error reading source file: " << filename;
return false;
}
return ExecuteString(source, filename, result);
}
bool Entd::ExecuteString(const v8::Handle<v8::String>& source,
const std::string& filename,
v8::Handle<v8::Value>* result) {
v8::HandleScope handle_scope;
v8::TryCatch try_catch;
v8::Handle<v8::Script> script =
v8::Script::Compile(source, v8::String::New(filename.c_str()));
if (script.IsEmpty()) {
utils::ReportV8Exception(&try_catch);
return false;
}
*result = script->Run();
if (!result || result->IsEmpty()) {
utils::ReportV8Exception(&try_catch);
return false;
}
return true;
}
} // namespace entd