2014-08-12 12:03:04 -07:00
|
|
|
/**
|
|
|
|
* bitcoind.js
|
|
|
|
* Copyright (c) 2014, BitPay (MIT License)
|
|
|
|
*
|
|
|
|
* bitcoindjs.cc:
|
|
|
|
* A bitcoind node.js binding.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "nan.h"
|
|
|
|
|
2014-08-29 16:20:38 -07:00
|
|
|
#include "bitcoindjs.h"
|
|
|
|
|
2014-08-20 17:00:09 -07:00
|
|
|
/**
|
|
|
|
* Bitcoin headers
|
|
|
|
*/
|
|
|
|
|
2014-09-17 12:14:20 -07:00
|
|
|
#if defined(HAVE_CONFIG_H)
|
|
|
|
#include "bitcoin-config.h"
|
|
|
|
#endif
|
|
|
|
|
2014-08-19 17:11:35 -07:00
|
|
|
#include "core.h"
|
|
|
|
#include "addrman.h"
|
|
|
|
#include "checkpoints.h"
|
|
|
|
#include "crypter.h"
|
|
|
|
#include "main.h"
|
|
|
|
// #include "random.h"
|
|
|
|
// #include "timedata.h"
|
2014-09-17 12:14:20 -07:00
|
|
|
|
|
|
|
#ifdef ENABLE_WALLET
|
|
|
|
#include "db.h"
|
|
|
|
#include "wallet.h"
|
2014-08-19 17:11:35 -07:00
|
|
|
#include "walletdb.h"
|
2014-09-17 12:14:20 -07:00
|
|
|
#endif
|
|
|
|
|
|
|
|
// #include "walletdb.h"
|
2014-08-19 17:11:35 -07:00
|
|
|
#include "alert.h"
|
|
|
|
#include "checkqueue.h"
|
2014-09-17 12:14:20 -07:00
|
|
|
// #include "db.h"
|
2014-08-19 17:11:35 -07:00
|
|
|
#include "miner.h"
|
|
|
|
#include "rpcclient.h"
|
|
|
|
#include "tinyformat.h"
|
2014-09-17 12:14:20 -07:00
|
|
|
// #include "wallet.h"
|
2014-08-19 17:11:35 -07:00
|
|
|
#include "allocators.h"
|
|
|
|
#include "clientversion.h"
|
|
|
|
#include "hash.h"
|
|
|
|
#include "mruset.h"
|
|
|
|
#include "rpcprotocol.h"
|
|
|
|
#include "txdb.h"
|
|
|
|
#include "base58.h"
|
|
|
|
#include "coincontrol.h"
|
|
|
|
#include "init.h"
|
|
|
|
#include "netbase.h"
|
|
|
|
#include "rpcserver.h"
|
|
|
|
#include "txmempool.h"
|
|
|
|
#include "bloom.h"
|
|
|
|
#include "coins.h"
|
|
|
|
#include "key.h"
|
|
|
|
#include "net.h"
|
|
|
|
#include "script.h"
|
|
|
|
#include "ui_interface.h"
|
|
|
|
// #include "chainparamsbase.h"
|
|
|
|
#include "compat.h"
|
|
|
|
#include "keystore.h"
|
|
|
|
#include "noui.h"
|
|
|
|
#include "serialize.h"
|
|
|
|
#include "uint256.h"
|
|
|
|
#include "chainparams.h"
|
|
|
|
#include "core.h"
|
|
|
|
#include "leveldbwrapper.h"
|
|
|
|
// #include "pow.h"
|
|
|
|
#include "sync.h"
|
|
|
|
#include "util.h"
|
|
|
|
// #include "chainparamsseeds.h"
|
|
|
|
// #include "core_io.h"
|
|
|
|
#include "limitedmap.h"
|
|
|
|
#include "protocol.h"
|
|
|
|
#include "threadsafety.h"
|
|
|
|
#include "version.h"
|
|
|
|
|
2014-08-20 17:00:09 -07:00
|
|
|
/**
|
|
|
|
* Bitcoin Globals
|
|
|
|
* Relevant:
|
|
|
|
* ~/bitcoin/src/init.cpp
|
|
|
|
* ~/bitcoin/src/bitcoind.cpp
|
|
|
|
* ~/bitcoin/src/main.h
|
|
|
|
*/
|
|
|
|
|
2014-09-17 12:14:20 -07:00
|
|
|
#include <stdint.h>
|
|
|
|
#include <signal.h>
|
|
|
|
|
|
|
|
#include <boost/algorithm/string/predicate.hpp>
|
|
|
|
#include <boost/filesystem.hpp>
|
|
|
|
#include <boost/interprocess/sync/file_lock.hpp>
|
|
|
|
#include <openssl/crypto.h>
|
|
|
|
|
|
|
|
#define MIN_CORE_FILEDESCRIPTORS 150
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
using namespace boost;
|
|
|
|
|
2014-09-17 11:23:38 -07:00
|
|
|
extern void ThreadImport(std::vector<boost::filesystem::path>);
|
|
|
|
extern void DetectShutdownThread(boost::thread_group*);
|
|
|
|
extern void StartNode(boost::thread_group&);
|
|
|
|
extern void ThreadScriptCheck();
|
|
|
|
extern void StartShutdown();
|
2014-09-17 14:08:26 -07:00
|
|
|
extern bool ShutdownRequested();
|
2014-09-17 11:23:38 -07:00
|
|
|
extern bool AppInit2(boost::thread_group&);
|
|
|
|
extern bool AppInit(int, char**);
|
|
|
|
extern bool SoftSetBoolArg(const std::string&, bool);
|
|
|
|
extern void PrintExceptionContinue(std::exception*, const char*);
|
|
|
|
extern void Shutdown();
|
2014-09-12 18:13:21 -07:00
|
|
|
extern void noui_connect();
|
2014-08-29 16:20:38 -07:00
|
|
|
extern int nScriptCheckThreads;
|
2014-09-12 18:13:21 -07:00
|
|
|
extern bool fDaemon;
|
2014-09-12 15:44:57 -07:00
|
|
|
extern std::map<std::string, std::string> mapArgs;
|
2014-08-20 17:00:09 -07:00
|
|
|
#ifdef ENABLE_WALLET
|
|
|
|
extern std::string strWalletFile;
|
|
|
|
extern CWallet *pwalletMain;
|
|
|
|
#endif
|
|
|
|
|
2014-08-19 16:40:19 -07:00
|
|
|
#include <node.h>
|
|
|
|
#include <string>
|
|
|
|
|
2014-08-12 12:03:04 -07:00
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
|
|
|
using namespace node;
|
|
|
|
using namespace v8;
|
|
|
|
|
|
|
|
NAN_METHOD(StartBitcoind);
|
|
|
|
|
2014-08-20 16:47:18 -07:00
|
|
|
static void
|
2014-09-02 19:00:31 -07:00
|
|
|
async_start_node_work(uv_work_t *req);
|
2014-08-19 16:40:19 -07:00
|
|
|
|
2014-08-20 16:47:18 -07:00
|
|
|
static void
|
2014-09-02 19:00:31 -07:00
|
|
|
async_start_node_after(uv_work_t *req);
|
2014-08-12 12:03:04 -07:00
|
|
|
|
2014-09-11 17:18:36 -07:00
|
|
|
static void
|
|
|
|
async_stop_node_work(uv_work_t *req);
|
|
|
|
|
|
|
|
static void
|
|
|
|
async_stop_node_after(uv_work_t *req);
|
|
|
|
|
2014-08-20 16:47:18 -07:00
|
|
|
static int
|
|
|
|
start_node(void);
|
|
|
|
|
2014-09-17 14:08:26 -07:00
|
|
|
static void
|
|
|
|
start_node_thread(void);
|
2014-09-17 12:52:35 -07:00
|
|
|
|
2014-09-02 19:00:31 -07:00
|
|
|
static void
|
|
|
|
open_pipes(int **out_pipe, int **log_pipe);
|
|
|
|
|
|
|
|
static void
|
|
|
|
parse_logs(int **out_pipe, int **log_pipe);
|
2014-08-29 13:53:08 -07:00
|
|
|
|
2014-08-29 16:20:38 -07:00
|
|
|
static void
|
|
|
|
async_parse_logs(uv_work_t *req);
|
|
|
|
|
|
|
|
static void
|
|
|
|
async_parse_logs_after(uv_work_t *req);
|
|
|
|
|
2014-08-12 12:03:04 -07:00
|
|
|
extern "C" void
|
|
|
|
init(Handle<Object>);
|
|
|
|
|
2014-09-17 14:08:26 -07:00
|
|
|
static bool shutdownComplete = false;
|
|
|
|
|
2014-08-20 17:00:09 -07:00
|
|
|
/**
|
2014-09-02 19:00:31 -07:00
|
|
|
* async_node_data
|
2014-08-20 17:00:09 -07:00
|
|
|
* Where the uv async request data resides.
|
|
|
|
*/
|
|
|
|
|
2014-09-02 19:00:31 -07:00
|
|
|
struct async_node_data {
|
|
|
|
char *err_msg;
|
|
|
|
char *result;
|
2014-09-04 15:16:32 -07:00
|
|
|
Persistent<Function> callback;
|
2014-09-02 19:00:31 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* async_log_data
|
|
|
|
* Where the uv async request data resides.
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct async_log_data {
|
|
|
|
int **out_pipe;
|
|
|
|
int **log_pipe;
|
|
|
|
char *err_msg;
|
2014-08-19 16:40:19 -07:00
|
|
|
char *result;
|
2014-09-04 15:16:32 -07:00
|
|
|
Persistent<Function> callback;
|
2014-08-19 16:40:19 -07:00
|
|
|
};
|
|
|
|
|
2014-08-12 12:03:04 -07:00
|
|
|
/**
|
|
|
|
* StartBitcoind
|
|
|
|
* bitcoind.start(callback)
|
|
|
|
*/
|
|
|
|
|
|
|
|
NAN_METHOD(StartBitcoind) {
|
|
|
|
NanScope();
|
|
|
|
|
|
|
|
if (args.Length() < 1 || !args[0]->IsFunction()) {
|
|
|
|
return NanThrowError(
|
|
|
|
"Usage: bitcoind.start(callback)");
|
|
|
|
}
|
|
|
|
|
2014-09-02 19:00:31 -07:00
|
|
|
Local<Function> callback = Local<Function>::Cast(args[0]);
|
2014-08-29 13:53:08 -07:00
|
|
|
|
2014-09-02 19:00:31 -07:00
|
|
|
//
|
|
|
|
// Setup pipes to differentiate our logs from bitcoind's.
|
|
|
|
// Run in a separate thread.
|
|
|
|
//
|
2014-08-29 16:20:38 -07:00
|
|
|
|
2014-09-17 14:08:26 -07:00
|
|
|
#if OUTPUT_REDIR
|
2014-09-02 19:00:31 -07:00
|
|
|
int *out_pipe = (int *)malloc(2 * sizeof(int));
|
|
|
|
int *log_pipe = (int *)malloc(2 * sizeof(int));
|
2014-08-29 16:20:38 -07:00
|
|
|
|
2014-09-02 19:00:31 -07:00
|
|
|
open_pipes(&out_pipe, &log_pipe);
|
2014-08-29 16:20:38 -07:00
|
|
|
|
2014-09-02 19:00:31 -07:00
|
|
|
uv_work_t *req_parse_logs = new uv_work_t();
|
|
|
|
async_log_data* data_parse_logs = new async_log_data();
|
|
|
|
data_parse_logs->out_pipe = &out_pipe;
|
|
|
|
data_parse_logs->log_pipe = &log_pipe;
|
2014-09-04 15:16:32 -07:00
|
|
|
data_parse_logs->err_msg = NULL;
|
|
|
|
data_parse_logs->result = NULL;
|
2014-09-02 19:00:31 -07:00
|
|
|
data_parse_logs->callback = Persistent<Function>::New(callback);
|
|
|
|
req_parse_logs->data = data_parse_logs;
|
2014-09-17 14:08:26 -07:00
|
|
|
|
2014-09-02 19:00:31 -07:00
|
|
|
int status_parse_logs = uv_queue_work(uv_default_loop(),
|
|
|
|
req_parse_logs, async_parse_logs,
|
|
|
|
(uv_after_work_cb)async_parse_logs_after);
|
2014-09-17 14:08:26 -07:00
|
|
|
|
2014-09-02 19:00:31 -07:00
|
|
|
assert(status_parse_logs == 0);
|
2014-09-02 19:29:36 -07:00
|
|
|
#endif
|
2014-08-19 16:40:19 -07:00
|
|
|
|
2014-09-02 19:00:31 -07:00
|
|
|
//
|
|
|
|
// Run bitcoind's StartNode() on a separate thread.
|
|
|
|
//
|
2014-08-19 16:40:19 -07:00
|
|
|
|
2014-09-02 19:00:31 -07:00
|
|
|
async_node_data* data_start_node = new async_node_data();
|
2014-09-04 15:16:32 -07:00
|
|
|
data_start_node->err_msg = NULL;
|
|
|
|
data_start_node->result = NULL;
|
2014-09-02 19:00:31 -07:00
|
|
|
data_start_node->callback = Persistent<Function>::New(callback);
|
2014-08-19 16:40:19 -07:00
|
|
|
|
2014-09-02 19:00:31 -07:00
|
|
|
uv_work_t *req_start_node = new uv_work_t();
|
|
|
|
req_start_node->data = data_start_node;
|
2014-08-19 16:40:19 -07:00
|
|
|
|
2014-09-02 19:00:31 -07:00
|
|
|
int status_start_node = uv_queue_work(uv_default_loop(),
|
|
|
|
req_start_node, async_start_node_work,
|
|
|
|
(uv_after_work_cb)async_start_node_after);
|
|
|
|
|
|
|
|
assert(status_start_node == 0);
|
2014-08-12 12:03:04 -07:00
|
|
|
|
2014-09-17 14:08:26 -07:00
|
|
|
#if OUTPUT_REDIR
|
2014-09-02 19:28:20 -07:00
|
|
|
NanReturnValue(NanNew<Number>(log_pipe[1]));
|
2014-09-17 14:08:26 -07:00
|
|
|
#else
|
|
|
|
NanReturnValue(NanNew<Number>(-1));
|
|
|
|
#endif
|
2014-08-12 12:03:04 -07:00
|
|
|
}
|
|
|
|
|
2014-08-20 17:00:09 -07:00
|
|
|
/**
|
2014-09-02 19:00:31 -07:00
|
|
|
* async_start_node_work()
|
2014-08-20 17:00:09 -07:00
|
|
|
* Call start_node() and start all our boost threads.
|
|
|
|
*/
|
|
|
|
|
2014-08-20 16:47:18 -07:00
|
|
|
static void
|
2014-09-02 19:00:31 -07:00
|
|
|
async_start_node_work(uv_work_t *req) {
|
|
|
|
async_node_data* node_data = static_cast<async_node_data*>(req->data);
|
2014-09-05 15:07:38 -07:00
|
|
|
start_node();
|
2014-09-02 19:00:31 -07:00
|
|
|
node_data->result = (char *)strdup("start_node(): bitcoind opened.");
|
2014-08-19 16:40:19 -07:00
|
|
|
}
|
|
|
|
|
2014-08-20 17:00:09 -07:00
|
|
|
/**
|
2014-09-02 19:00:31 -07:00
|
|
|
* async_start_node_after()
|
2014-08-20 17:00:09 -07:00
|
|
|
* Execute our callback.
|
|
|
|
*/
|
|
|
|
|
2014-08-20 16:47:18 -07:00
|
|
|
static void
|
2014-09-02 19:00:31 -07:00
|
|
|
async_start_node_after(uv_work_t *req) {
|
2014-08-19 16:40:19 -07:00
|
|
|
NanScope();
|
2014-09-02 19:00:31 -07:00
|
|
|
async_node_data* node_data = static_cast<async_node_data*>(req->data);
|
2014-08-19 16:40:19 -07:00
|
|
|
|
2014-09-04 15:16:32 -07:00
|
|
|
if (node_data->err_msg != NULL) {
|
2014-09-02 19:00:31 -07:00
|
|
|
Local<Value> err = Exception::Error(String::New(node_data->err_msg));
|
|
|
|
free(node_data->err_msg);
|
2014-08-19 16:40:19 -07:00
|
|
|
const unsigned argc = 1;
|
2014-09-02 19:00:31 -07:00
|
|
|
Local<Value> argv[argc] = { err };
|
2014-08-19 16:40:19 -07:00
|
|
|
TryCatch try_catch;
|
2014-09-02 19:00:31 -07:00
|
|
|
node_data->callback->Call(Context::GetCurrent()->Global(), argc, argv);
|
2014-08-19 16:40:19 -07:00
|
|
|
if (try_catch.HasCaught()) {
|
|
|
|
node::FatalException(try_catch);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
const unsigned argc = 2;
|
2014-09-02 19:00:31 -07:00
|
|
|
Local<Value> argv[argc] = {
|
2014-08-19 16:40:19 -07:00
|
|
|
Local<Value>::New(Null()),
|
2014-09-02 19:00:31 -07:00
|
|
|
Local<Value>::New(String::New(node_data->result))
|
2014-08-19 16:40:19 -07:00
|
|
|
};
|
|
|
|
TryCatch try_catch;
|
2014-09-02 19:00:31 -07:00
|
|
|
node_data->callback->Call(Context::GetCurrent()->Global(), argc, argv);
|
2014-08-19 16:40:19 -07:00
|
|
|
if (try_catch.HasCaught()) {
|
|
|
|
node::FatalException(try_catch);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-04 15:16:32 -07:00
|
|
|
// node_data->callback.Dispose();
|
2014-08-19 16:40:19 -07:00
|
|
|
|
2014-09-02 19:00:31 -07:00
|
|
|
if (node_data->result != NULL) {
|
|
|
|
free(node_data->result);
|
2014-08-19 16:40:19 -07:00
|
|
|
}
|
2014-08-12 12:03:04 -07:00
|
|
|
|
2014-09-02 19:00:31 -07:00
|
|
|
delete node_data;
|
2014-08-19 16:40:19 -07:00
|
|
|
delete req;
|
2014-08-12 12:03:04 -07:00
|
|
|
}
|
|
|
|
|
2014-09-17 14:08:26 -07:00
|
|
|
/**
|
|
|
|
* IsStopping()
|
|
|
|
* bitcoind.stopping()
|
|
|
|
*/
|
|
|
|
|
|
|
|
NAN_METHOD(IsStopping) {
|
|
|
|
NanScope();
|
|
|
|
NanReturnValue(NanNew<Boolean>(ShutdownRequested()));
|
|
|
|
}
|
|
|
|
|
2014-09-17 12:52:35 -07:00
|
|
|
/**
|
|
|
|
* IsStopped()
|
|
|
|
* bitcoind.stopped()
|
|
|
|
*/
|
|
|
|
|
2014-09-17 14:08:26 -07:00
|
|
|
NAN_METHOD(IsStopped) {
|
2014-09-17 12:52:35 -07:00
|
|
|
NanScope();
|
2014-09-17 14:08:26 -07:00
|
|
|
NanReturnValue(NanNew<Boolean>(shutdownComplete));
|
2014-09-17 12:52:35 -07:00
|
|
|
}
|
|
|
|
|
2014-08-20 17:00:09 -07:00
|
|
|
/**
|
|
|
|
* start_node(void)
|
2014-09-17 14:08:26 -07:00
|
|
|
* start_node_thread(void)
|
2014-08-20 17:00:09 -07:00
|
|
|
* A reimplementation of AppInit2 minus
|
|
|
|
* the logging and argument parsing.
|
|
|
|
*/
|
2014-09-02 19:00:31 -07:00
|
|
|
|
2014-08-20 16:47:18 -07:00
|
|
|
static int
|
|
|
|
start_node(void) {
|
2014-09-17 12:14:20 -07:00
|
|
|
noui_connect();
|
|
|
|
|
2014-09-17 14:08:26 -07:00
|
|
|
(boost::thread *)new boost::thread(boost::bind(&start_node_thread));
|
2014-09-17 12:14:20 -07:00
|
|
|
|
2014-09-17 14:08:26 -07:00
|
|
|
// horrible fix for a race condition
|
|
|
|
sleep(2);
|
|
|
|
signal(SIGINT, SIG_DFL);
|
|
|
|
signal(SIGHUP, SIG_DFL);
|
2014-09-17 12:14:20 -07:00
|
|
|
|
2014-09-17 12:52:35 -07:00
|
|
|
return 0;
|
|
|
|
}
|
2014-09-17 12:14:20 -07:00
|
|
|
|
2014-09-17 14:08:26 -07:00
|
|
|
static void
|
|
|
|
start_node_thread(void) {
|
2014-09-17 12:52:35 -07:00
|
|
|
boost::thread_group threadGroup;
|
|
|
|
boost::thread* detectShutdownThread = NULL;
|
2014-09-17 12:14:20 -07:00
|
|
|
|
2014-09-17 12:52:35 -07:00
|
|
|
const int argc = 0;
|
|
|
|
const char *argv[argc + 1] = {
|
|
|
|
//"-server",
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
ParseParameters(argc, argv);
|
|
|
|
ReadConfigFile(mapArgs, mapMultiArgs);
|
|
|
|
if (!SelectParamsFromCommandLine()) {
|
2014-09-17 14:08:26 -07:00
|
|
|
return;
|
2014-09-17 12:14:20 -07:00
|
|
|
}
|
2014-09-17 12:52:35 -07:00
|
|
|
// CreatePidFile(GetPidFile(), getpid());
|
|
|
|
detectShutdownThread = new boost::thread(
|
|
|
|
boost::bind(&DetectShutdownThread, &threadGroup));
|
2014-09-17 12:14:20 -07:00
|
|
|
|
2014-09-17 12:52:35 -07:00
|
|
|
int fRet = AppInit2(threadGroup);
|
2014-09-17 12:14:20 -07:00
|
|
|
|
|
|
|
if (!fRet) {
|
|
|
|
if (detectShutdownThread)
|
|
|
|
detectShutdownThread->interrupt();
|
|
|
|
threadGroup.interrupt_all();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (detectShutdownThread) {
|
|
|
|
detectShutdownThread->join();
|
|
|
|
delete detectShutdownThread;
|
|
|
|
detectShutdownThread = NULL;
|
|
|
|
}
|
|
|
|
Shutdown();
|
2014-09-17 14:08:26 -07:00
|
|
|
shutdownComplete = true;
|
2014-08-20 16:47:18 -07:00
|
|
|
}
|
|
|
|
|
2014-08-29 13:53:08 -07:00
|
|
|
/**
|
2014-09-02 19:00:31 -07:00
|
|
|
* parse_logs(int **out_pipe, int **log_pipe)
|
|
|
|
* Differentiate our logs and bitcoind's logs.
|
|
|
|
* Send bitcoind's logs to a pipe instead.
|
2014-08-29 13:53:08 -07:00
|
|
|
*/
|
|
|
|
|
|
|
|
const char bitcoind_char[256] = {
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
2014-09-17 14:31:20 -07:00
|
|
|
0, 0, 0, 0, 0, 0, 0, /* <- ' ' */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '.',
|
2014-08-29 13:53:08 -07:00
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ':', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
2014-09-17 14:31:20 -07:00
|
|
|
'b', 'c', 'd', 0, 0, 0, 0, 'i', 'j', 0, 0, 0, 'n', 'o', 0, 0, 0, 's', 't', 0,
|
2014-08-29 13:53:08 -07:00
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
2014-09-17 14:31:20 -07:00
|
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
2014-08-29 13:53:08 -07:00
|
|
|
};
|
|
|
|
|
2014-09-02 19:00:31 -07:00
|
|
|
static void
|
|
|
|
open_pipes(int **out_pipe, int **log_pipe) {
|
|
|
|
pipe(*out_pipe);
|
2014-09-17 15:33:21 -07:00
|
|
|
dup2((*out_pipe)[1], STDOUT_FILENO);
|
|
|
|
dup2((*out_pipe)[1], STDERR_FILENO);
|
2014-09-02 19:00:31 -07:00
|
|
|
pipe(*log_pipe);
|
|
|
|
}
|
2014-08-29 13:53:08 -07:00
|
|
|
|
2014-09-02 19:00:31 -07:00
|
|
|
static void
|
|
|
|
parse_logs(int **out_pipe, int **log_pipe) {
|
2014-08-29 13:53:08 -07:00
|
|
|
unsigned int rtotal = 0;
|
|
|
|
ssize_t r = 0;
|
|
|
|
size_t rcount = 80 * sizeof(char);
|
|
|
|
char *buf = (char *)malloc(rcount);
|
2014-09-17 15:33:21 -07:00
|
|
|
char cur[13];
|
2014-08-29 13:53:08 -07:00
|
|
|
unsigned int cp = 0;
|
|
|
|
unsigned int reallocs = 0;
|
|
|
|
|
2014-09-17 15:33:21 -07:00
|
|
|
while ((r = read((*out_pipe)[0], buf, rcount))) {
|
2014-08-29 13:53:08 -07:00
|
|
|
unsigned int i;
|
|
|
|
char *rbuf;
|
|
|
|
|
2014-09-02 19:13:33 -07:00
|
|
|
if (r == -1) {
|
2014-09-17 14:31:20 -07:00
|
|
|
fprintf(stderr, "bitcoind.js: error=\"parse_logs(): bad read.\"\n");
|
2014-09-02 19:13:33 -07:00
|
|
|
sleep(1);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (r <= 0) continue;
|
|
|
|
|
2014-08-29 13:53:08 -07:00
|
|
|
// Grab the buffer at the start of the bytes that were read:
|
2014-09-17 15:33:21 -07:00
|
|
|
rbuf = (char *)(buf + rtotal);
|
2014-08-29 13:53:08 -07:00
|
|
|
|
|
|
|
// If these are our logs, write them to stdout:
|
|
|
|
for (i = 0; i < r; i++) {
|
|
|
|
// A naive semi-boyer-moore string search (is it a bitcoind: char?):
|
|
|
|
unsigned char ch = rbuf[i];
|
|
|
|
if (bitcoind_char[ch]) {
|
|
|
|
cur[cp] = rbuf[0];
|
|
|
|
cp++;
|
|
|
|
cur[cp] = '\0';
|
2014-09-17 14:31:20 -07:00
|
|
|
if (strcmp(cur, "bitcoind.js:") == 0) {
|
2014-08-29 13:53:08 -07:00
|
|
|
size_t wcount = r;
|
|
|
|
ssize_t w = 0;
|
|
|
|
ssize_t wtotal = 0;
|
|
|
|
// undo redirection
|
2014-09-17 15:33:21 -07:00
|
|
|
close((*out_pipe)[0]);
|
|
|
|
close((*out_pipe)[1]);
|
2014-08-29 13:53:08 -07:00
|
|
|
w = write(STDOUT_FILENO, cur, cp);
|
|
|
|
wtotal += w;
|
|
|
|
while ((w = write(STDOUT_FILENO, rbuf + i + wtotal, wcount))) {
|
2014-09-02 19:13:33 -07:00
|
|
|
if (w == -1) {
|
2014-09-17 14:31:20 -07:00
|
|
|
fprintf(stderr, "bitcoind.js: error=\"parse_logs(): bad write.\"\n");
|
2014-09-02 19:13:33 -07:00
|
|
|
sleep(1);
|
|
|
|
break;
|
|
|
|
}
|
2014-08-29 13:53:08 -07:00
|
|
|
if (w == 0 || (size_t)wtotal == rcount) break;
|
|
|
|
wtotal += w;
|
|
|
|
}
|
|
|
|
// reopen redirection
|
2014-09-02 19:00:31 -07:00
|
|
|
pipe(*out_pipe);
|
2014-09-17 15:33:21 -07:00
|
|
|
dup2((*out_pipe)[1], STDOUT_FILENO);
|
|
|
|
dup2((*out_pipe)[1], STDERR_FILENO);
|
2014-08-29 13:53:08 -07:00
|
|
|
break;
|
2014-09-17 11:31:10 -07:00
|
|
|
} else if (cp == sizeof cur - 1) {
|
2014-08-29 13:53:08 -07:00
|
|
|
cp = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If these logs are from bitcoind, write them to the log pipe:
|
|
|
|
for (i = 0; i < r; i++) {
|
|
|
|
if ((rbuf[i] == '\r' && rbuf[i] == '\n')
|
|
|
|
|| rbuf[i] == '\r' || rbuf[i] == '\n') {
|
|
|
|
size_t wcount = r;
|
|
|
|
ssize_t w = 0;
|
|
|
|
ssize_t wtotal = 0;
|
2014-09-17 15:33:21 -07:00
|
|
|
while ((w = write((*log_pipe)[1], rbuf + i + wtotal + 1, wcount))) {
|
2014-09-02 19:13:33 -07:00
|
|
|
if (w == -1) {
|
2014-09-17 14:31:20 -07:00
|
|
|
fprintf(stderr, "bitcoind.js: error=\"parse_logs(): bad write.\"\n");
|
2014-09-02 19:13:33 -07:00
|
|
|
sleep(1);
|
|
|
|
break;
|
|
|
|
}
|
2014-08-29 13:53:08 -07:00
|
|
|
if (w == 0 || (size_t)wtotal == rcount) break;
|
|
|
|
wtotal += w;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
rtotal += r;
|
|
|
|
while (rtotal > rcount) {
|
|
|
|
reallocs++;
|
|
|
|
rcount = (rcount * 2) / reallocs;
|
|
|
|
buf = (char *)realloc(buf, rcount);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-02 19:00:31 -07:00
|
|
|
free(buf);
|
2014-08-29 13:53:08 -07:00
|
|
|
}
|
|
|
|
|
2014-08-29 16:20:38 -07:00
|
|
|
static void
|
|
|
|
async_parse_logs(uv_work_t *req) {
|
2014-09-02 19:00:31 -07:00
|
|
|
async_log_data* log_data = static_cast<async_log_data*>(req->data);
|
|
|
|
parse_logs(log_data->out_pipe, log_data->log_pipe);
|
|
|
|
log_data->err_msg = (char *)strdup("parse_logs(): failed.");
|
2014-08-29 16:20:38 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
async_parse_logs_after(uv_work_t *req) {
|
|
|
|
NanScope();
|
2014-09-02 19:00:31 -07:00
|
|
|
async_log_data* log_data = static_cast<async_log_data*>(req->data);
|
2014-08-29 16:20:38 -07:00
|
|
|
|
2014-09-04 15:16:32 -07:00
|
|
|
if (log_data->err_msg != NULL) {
|
2014-09-02 19:00:31 -07:00
|
|
|
Local<Value> err = Exception::Error(String::New(log_data->err_msg));
|
|
|
|
free(log_data->err_msg);
|
2014-08-29 16:20:38 -07:00
|
|
|
const unsigned argc = 1;
|
2014-09-02 19:00:31 -07:00
|
|
|
Local<Value> argv[argc] = { err };
|
2014-08-29 16:20:38 -07:00
|
|
|
TryCatch try_catch;
|
2014-09-02 19:00:31 -07:00
|
|
|
log_data->callback->Call(Context::GetCurrent()->Global(), argc, argv);
|
2014-08-29 16:20:38 -07:00
|
|
|
if (try_catch.HasCaught()) {
|
|
|
|
node::FatalException(try_catch);
|
|
|
|
}
|
2014-09-02 19:00:31 -07:00
|
|
|
} else {
|
|
|
|
assert(0 && "parse_logs(): should never happen.");
|
2014-08-29 16:20:38 -07:00
|
|
|
}
|
|
|
|
|
2014-09-04 15:16:32 -07:00
|
|
|
// log_data->callback.Dispose();
|
2014-08-29 16:20:38 -07:00
|
|
|
|
2014-09-02 19:00:31 -07:00
|
|
|
delete log_data;
|
2014-08-29 16:20:38 -07:00
|
|
|
delete req;
|
|
|
|
}
|
|
|
|
|
2014-09-11 17:18:36 -07:00
|
|
|
/**
|
|
|
|
* StopBitcoind
|
|
|
|
* bitcoind.stop(callback)
|
|
|
|
*/
|
|
|
|
|
|
|
|
NAN_METHOD(StopBitcoind) {
|
|
|
|
NanScope();
|
|
|
|
|
|
|
|
if (args.Length() < 1 || !args[0]->IsFunction()) {
|
|
|
|
return NanThrowError(
|
|
|
|
"Usage: bitcoind.stop(callback)");
|
|
|
|
}
|
|
|
|
|
|
|
|
Local<Function> callback = Local<Function>::Cast(args[0]);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Run bitcoind's StartShutdown() on a separate thread.
|
|
|
|
//
|
|
|
|
|
|
|
|
async_node_data* data_stop_node = new async_node_data();
|
|
|
|
data_stop_node->err_msg = NULL;
|
|
|
|
data_stop_node->result = NULL;
|
|
|
|
data_stop_node->callback = Persistent<Function>::New(callback);
|
|
|
|
|
|
|
|
uv_work_t *req_stop_node = new uv_work_t();
|
|
|
|
req_stop_node->data = data_stop_node;
|
|
|
|
|
|
|
|
int status_stop_node = uv_queue_work(uv_default_loop(),
|
|
|
|
req_stop_node, async_stop_node_work,
|
|
|
|
(uv_after_work_cb)async_stop_node_after);
|
|
|
|
|
|
|
|
assert(status_stop_node == 0);
|
|
|
|
|
|
|
|
NanReturnValue(Undefined());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* async_stop_node_work()
|
|
|
|
* Call StartShutdown() to join the boost threads, which will call Shutdown().
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void
|
|
|
|
async_stop_node_work(uv_work_t *req) {
|
|
|
|
async_node_data* node_data = static_cast<async_node_data*>(req->data);
|
|
|
|
StartShutdown();
|
|
|
|
node_data->result = (char *)strdup("stop_node(): bitcoind shutdown.");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* async_stop_node_after()
|
|
|
|
* Execute our callback.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void
|
|
|
|
async_stop_node_after(uv_work_t *req) {
|
|
|
|
NanScope();
|
|
|
|
async_node_data* node_data = static_cast<async_node_data*>(req->data);
|
|
|
|
|
|
|
|
if (node_data->err_msg != NULL) {
|
|
|
|
Local<Value> err = Exception::Error(String::New(node_data->err_msg));
|
|
|
|
free(node_data->err_msg);
|
|
|
|
const unsigned argc = 1;
|
|
|
|
Local<Value> argv[argc] = { err };
|
|
|
|
TryCatch try_catch;
|
|
|
|
node_data->callback->Call(Context::GetCurrent()->Global(), argc, argv);
|
|
|
|
if (try_catch.HasCaught()) {
|
|
|
|
node::FatalException(try_catch);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
const unsigned argc = 2;
|
|
|
|
Local<Value> argv[argc] = {
|
|
|
|
Local<Value>::New(Null()),
|
|
|
|
Local<Value>::New(String::New(node_data->result))
|
|
|
|
};
|
|
|
|
TryCatch try_catch;
|
|
|
|
node_data->callback->Call(Context::GetCurrent()->Global(), argc, argv);
|
|
|
|
if (try_catch.HasCaught()) {
|
|
|
|
node::FatalException(try_catch);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
node_data->callback.Dispose();
|
|
|
|
|
|
|
|
if (node_data->result != NULL) {
|
|
|
|
free(node_data->result);
|
|
|
|
}
|
|
|
|
|
|
|
|
delete node_data;
|
|
|
|
delete req;
|
|
|
|
}
|
|
|
|
|
2014-08-12 12:03:04 -07:00
|
|
|
/**
|
|
|
|
* Init
|
|
|
|
*/
|
|
|
|
|
|
|
|
extern "C" void
|
|
|
|
init(Handle<Object> target) {
|
|
|
|
NanScope();
|
|
|
|
NODE_SET_METHOD(target, "start", StartBitcoind);
|
2014-09-11 17:18:36 -07:00
|
|
|
NODE_SET_METHOD(target, "stop", StopBitcoind);
|
2014-09-17 14:08:26 -07:00
|
|
|
NODE_SET_METHOD(target, "stopping", IsStopping);
|
2014-09-17 12:52:35 -07:00
|
|
|
NODE_SET_METHOD(target, "stopped", IsStopped);
|
2014-08-12 12:03:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
NODE_MODULE(bitcoindjs, init)
|