710 lines
15 KiB
C
710 lines
15 KiB
C
|
|
/*
|
|
* Copyright (C) NGINX, Inc.
|
|
* Copyright (C) Zhidao HONG
|
|
*/
|
|
|
|
#include <nxt_main.h>
|
|
#include <nxt_conf.h>
|
|
#include <nxt_script.h>
|
|
#include <dirent.h>
|
|
|
|
|
|
struct nxt_script_s {
|
|
nxt_str_t text;
|
|
};
|
|
|
|
|
|
typedef struct {
|
|
nxt_str_t name;
|
|
nxt_conf_value_t *value;
|
|
nxt_mp_t *mp;
|
|
} nxt_script_info_t;
|
|
|
|
|
|
typedef struct {
|
|
nxt_str_t name;
|
|
nxt_fd_t fd;
|
|
} nxt_script_item_t;
|
|
|
|
|
|
static nxt_script_t *nxt_script_get(nxt_task_t *task, nxt_str_t *name,
|
|
nxt_fd_t fd);
|
|
static nxt_conf_value_t *nxt_script_details(nxt_mp_t *mp, nxt_script_t *cert);
|
|
static void nxt_script_buf_completion(nxt_task_t *task, void *obj, void *data);
|
|
|
|
|
|
static nxt_lvlhsh_t nxt_script_info;
|
|
|
|
|
|
static njs_vm_ops_t nxt_js_ops = {
|
|
NULL,
|
|
NULL,
|
|
nxt_js_module_loader,
|
|
NULL,
|
|
};
|
|
|
|
|
|
nxt_script_t *
|
|
nxt_script_new(nxt_task_t *task, nxt_str_t *name, u_char *data, size_t size,
|
|
u_char *error)
|
|
{
|
|
u_char *start;
|
|
njs_vm_t *vm;
|
|
njs_str_t mod_name;
|
|
njs_mod_t *mod;
|
|
njs_vm_opt_t opts;
|
|
nxt_script_t *script;
|
|
|
|
njs_vm_opt_init(&opts);
|
|
|
|
opts.backtrace = 1;
|
|
|
|
opts.file.start = (u_char *) "default";
|
|
opts.file.length = 7;
|
|
|
|
opts.ops = &nxt_js_ops;
|
|
|
|
vm = njs_vm_create(&opts);
|
|
if (nxt_slow_path(vm == NULL)) {
|
|
return NULL;
|
|
}
|
|
|
|
mod_name.length = name->length;
|
|
mod_name.start = name->start;
|
|
|
|
start = data;
|
|
|
|
mod = njs_vm_compile_module(vm, &mod_name, &start, start + size);
|
|
|
|
if (nxt_slow_path(mod == NULL)) {
|
|
(void) nxt_js_error(vm, error);
|
|
nxt_alert(task, "JS compile module(%V) failed: %s", name, error);
|
|
|
|
goto fail;
|
|
}
|
|
|
|
script = nxt_zalloc(sizeof(nxt_script_t) + size);
|
|
if (nxt_slow_path(script == NULL)) {
|
|
goto fail;
|
|
}
|
|
|
|
script->text.length = size;
|
|
script->text.start = (u_char *) script + sizeof(nxt_script_t);
|
|
|
|
nxt_memcpy(script->text.start, data, size);
|
|
|
|
njs_vm_destroy(vm);
|
|
|
|
return script;
|
|
|
|
fail:
|
|
|
|
njs_vm_destroy(vm);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static nxt_script_t *
|
|
nxt_script_get(nxt_task_t *task, nxt_str_t *name, nxt_fd_t fd)
|
|
{
|
|
nxt_int_t ret;
|
|
nxt_str_t text;
|
|
nxt_script_t *script;
|
|
u_char error[NXT_MAX_ERROR_STR];
|
|
|
|
ret = nxt_script_file_read(fd, &text);
|
|
if (nxt_slow_path(ret != NXT_OK)) {
|
|
return NULL;
|
|
}
|
|
|
|
script = nxt_script_new(task, name, text.start, text.length, error);
|
|
|
|
nxt_free(text.start);
|
|
|
|
return script;
|
|
}
|
|
|
|
|
|
void
|
|
nxt_script_destroy(nxt_script_t *script)
|
|
{
|
|
nxt_free(script);
|
|
}
|
|
|
|
|
|
static nxt_int_t
|
|
nxt_script_info_hash_test(nxt_lvlhsh_query_t *lhq, void *data)
|
|
{
|
|
nxt_script_info_t *info;
|
|
|
|
info = data;
|
|
|
|
if (nxt_strcasestr_eq(&lhq->key, &info->name)) {
|
|
return NXT_OK;
|
|
}
|
|
|
|
return NXT_DECLINED;
|
|
}
|
|
|
|
|
|
static const nxt_lvlhsh_proto_t nxt_script_info_hash_proto
|
|
nxt_aligned(64) =
|
|
{
|
|
NXT_LVLHSH_DEFAULT,
|
|
nxt_script_info_hash_test,
|
|
nxt_lvlhsh_alloc,
|
|
nxt_lvlhsh_free,
|
|
};
|
|
|
|
|
|
void
|
|
nxt_script_info_init(nxt_task_t *task, nxt_array_t *scripts)
|
|
{
|
|
uint32_t i;
|
|
nxt_script_t *script;
|
|
nxt_script_item_t *item;
|
|
|
|
item = scripts->elts;
|
|
|
|
for (i = 0; i < scripts->nelts; i++) {
|
|
script = nxt_script_get(task, &item->name, item->fd);
|
|
|
|
if (nxt_slow_path(script == NULL)) {
|
|
continue;
|
|
}
|
|
|
|
(void) nxt_script_info_save(&item->name, script);
|
|
|
|
nxt_script_destroy(script);
|
|
|
|
item++;
|
|
}
|
|
}
|
|
|
|
|
|
nxt_int_t
|
|
nxt_script_info_save(nxt_str_t *name, nxt_script_t *script)
|
|
{
|
|
nxt_mp_t *mp;
|
|
nxt_int_t ret;
|
|
nxt_conf_value_t *value;
|
|
nxt_script_info_t *info;
|
|
nxt_lvlhsh_query_t lhq;
|
|
|
|
mp = nxt_mp_create(1024, 128, 256, 32);
|
|
if (nxt_slow_path(mp == NULL)) {
|
|
return NXT_ERROR;
|
|
}
|
|
|
|
info = nxt_mp_get(mp, sizeof(nxt_script_info_t));
|
|
if (nxt_slow_path(info == NULL)) {
|
|
goto fail;
|
|
}
|
|
|
|
name = nxt_str_dup(mp, &info->name, name);
|
|
if (nxt_slow_path(name == NULL)) {
|
|
goto fail;
|
|
}
|
|
|
|
value = nxt_script_details(mp, script);
|
|
if (nxt_slow_path(value == NULL)) {
|
|
goto fail;
|
|
}
|
|
|
|
info->mp = mp;
|
|
info->value = value;
|
|
|
|
lhq.key_hash = nxt_djb_hash(name->start, name->length);
|
|
lhq.replace = 1;
|
|
lhq.key = *name;
|
|
lhq.value = info;
|
|
lhq.proto = &nxt_script_info_hash_proto;
|
|
|
|
ret = nxt_lvlhsh_insert(&nxt_script_info, &lhq);
|
|
if (nxt_slow_path(ret != NXT_OK)) {
|
|
goto fail;
|
|
}
|
|
|
|
if (lhq.value != info) {
|
|
info = lhq.value;
|
|
nxt_mp_destroy(info->mp);
|
|
}
|
|
|
|
return NXT_OK;
|
|
|
|
fail:
|
|
|
|
nxt_mp_destroy(mp);
|
|
return NXT_ERROR;
|
|
}
|
|
|
|
|
|
nxt_conf_value_t *
|
|
nxt_script_info_get(nxt_str_t *name)
|
|
{
|
|
nxt_int_t ret;
|
|
nxt_script_info_t *info;
|
|
nxt_lvlhsh_query_t lhq;
|
|
|
|
lhq.key_hash = nxt_djb_hash(name->start, name->length);
|
|
lhq.key = *name;
|
|
lhq.proto = &nxt_script_info_hash_proto;
|
|
|
|
ret = nxt_lvlhsh_find(&nxt_script_info, &lhq);
|
|
if (ret != NXT_OK) {
|
|
return NULL;
|
|
}
|
|
|
|
info = lhq.value;
|
|
|
|
return info->value;
|
|
}
|
|
|
|
|
|
nxt_conf_value_t *
|
|
nxt_script_info_get_all(nxt_mp_t *mp)
|
|
{
|
|
uint32_t i;
|
|
nxt_conf_value_t *all;
|
|
nxt_script_info_t *info;
|
|
nxt_lvlhsh_each_t lhe;
|
|
|
|
nxt_lvlhsh_each_init(&lhe, &nxt_script_info_hash_proto);
|
|
|
|
for (i = 0; /* void */; i++) {
|
|
info = nxt_lvlhsh_each(&nxt_script_info, &lhe);
|
|
|
|
if (info == NULL) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
all = nxt_conf_create_object(mp, i);
|
|
if (nxt_slow_path(all == NULL)) {
|
|
return NULL;
|
|
}
|
|
|
|
nxt_lvlhsh_each_init(&lhe, &nxt_script_info_hash_proto);
|
|
|
|
for (i = 0; /* void */; i++) {
|
|
info = nxt_lvlhsh_each(&nxt_script_info, &lhe);
|
|
|
|
if (info == NULL) {
|
|
break;
|
|
}
|
|
|
|
nxt_conf_set_member(all, &info->name, info->value, i);
|
|
}
|
|
|
|
return all;
|
|
}
|
|
|
|
|
|
static nxt_conf_value_t *
|
|
nxt_script_details(nxt_mp_t *mp, nxt_script_t *script)
|
|
{
|
|
nxt_conf_value_t *value;
|
|
|
|
value = nxt_conf_create_object(mp, 0);
|
|
if (nxt_slow_path(value == NULL)) {
|
|
return NULL;
|
|
}
|
|
|
|
nxt_conf_set_string_dup(value, mp, &script->text);
|
|
|
|
return value;
|
|
}
|
|
|
|
|
|
nxt_int_t
|
|
nxt_script_info_delete(nxt_str_t *name)
|
|
{
|
|
nxt_int_t ret;
|
|
nxt_script_info_t *info;
|
|
nxt_lvlhsh_query_t lhq;
|
|
|
|
lhq.key_hash = nxt_djb_hash(name->start, name->length);
|
|
lhq.key = *name;
|
|
lhq.proto = &nxt_script_info_hash_proto;
|
|
|
|
ret = nxt_lvlhsh_delete(&nxt_script_info, &lhq);
|
|
|
|
if (ret == NXT_OK) {
|
|
info = lhq.value;
|
|
nxt_mp_destroy(info->mp);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
nxt_array_t *
|
|
nxt_script_store_load(nxt_task_t *task, nxt_mp_t *mp)
|
|
{
|
|
DIR *dir;
|
|
size_t size, alloc;
|
|
u_char *buf, *p;
|
|
nxt_str_t name;
|
|
nxt_int_t ret;
|
|
nxt_file_t file;
|
|
nxt_array_t *scripts;
|
|
nxt_runtime_t *rt;
|
|
struct dirent *de;
|
|
nxt_script_item_t *item;
|
|
|
|
rt = task->thread->runtime;
|
|
|
|
if (nxt_slow_path(rt->scripts.start == NULL)) {
|
|
nxt_alert(task, "no scripts storage directory");
|
|
return NULL;
|
|
}
|
|
|
|
scripts = nxt_array_create(mp, 16, sizeof(nxt_script_item_t));
|
|
if (nxt_slow_path(scripts == NULL)) {
|
|
return NULL;
|
|
}
|
|
|
|
buf = NULL;
|
|
alloc = 0;
|
|
|
|
dir = opendir((char *) rt->scripts.start);
|
|
if (nxt_slow_path(dir == NULL)) {
|
|
nxt_alert(task, "opendir(\"%s\") failed %E",
|
|
rt->scripts.start, nxt_errno);
|
|
goto fail;
|
|
}
|
|
|
|
for ( ;; ) {
|
|
de = readdir(dir);
|
|
if (de == NULL) {
|
|
break;
|
|
}
|
|
|
|
nxt_debug(task, "readdir(\"%s\"): \"%s\"",
|
|
rt->scripts.start, de->d_name);
|
|
|
|
name.length = nxt_strlen(de->d_name);
|
|
name.start = (u_char *) de->d_name;
|
|
|
|
if (nxt_str_eq(&name, ".", 1) || nxt_str_eq(&name, "..", 2)) {
|
|
continue;
|
|
}
|
|
|
|
item = nxt_array_add(scripts);
|
|
if (nxt_slow_path(item == NULL)) {
|
|
goto fail;
|
|
}
|
|
|
|
item->fd = -1;
|
|
|
|
size = rt->scripts.length + name.length + 1;
|
|
|
|
if (size > alloc) {
|
|
size += 32;
|
|
|
|
p = nxt_realloc(buf, size);
|
|
if (p == NULL) {
|
|
goto fail;
|
|
}
|
|
|
|
alloc = size;
|
|
buf = p;
|
|
}
|
|
|
|
p = nxt_cpymem(buf, rt->scripts.start, rt->scripts.length);
|
|
p = nxt_cpymem(p, name.start, name.length + 1);
|
|
|
|
nxt_memzero(&file, sizeof(nxt_file_t));
|
|
|
|
file.name = buf;
|
|
|
|
ret = nxt_file_open(task, &file, NXT_FILE_RDONLY, NXT_FILE_OPEN,
|
|
NXT_FILE_OWNER_ACCESS);
|
|
|
|
if (nxt_slow_path(ret != NXT_OK)) {
|
|
nxt_array_remove_last(scripts);
|
|
continue;
|
|
}
|
|
|
|
item->fd = file.fd;
|
|
|
|
if (nxt_slow_path(nxt_str_dup(mp, &item->name, &name) == NULL)) {
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
if (buf != NULL) {
|
|
nxt_free(buf);
|
|
}
|
|
|
|
(void) closedir(dir);
|
|
|
|
return scripts;
|
|
|
|
fail:
|
|
|
|
if (buf != NULL) {
|
|
nxt_free(buf);
|
|
}
|
|
|
|
if (dir != NULL) {
|
|
(void) closedir(dir);
|
|
}
|
|
|
|
nxt_script_store_release(scripts);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
void
|
|
nxt_script_store_release(nxt_array_t *scripts)
|
|
{
|
|
uint32_t i;
|
|
nxt_script_item_t *item;
|
|
|
|
item = scripts->elts;
|
|
|
|
for (i = 0; i < scripts->nelts; i++) {
|
|
nxt_fd_close(item[i].fd);
|
|
}
|
|
|
|
nxt_array_destroy(scripts);
|
|
}
|
|
|
|
|
|
void
|
|
nxt_script_store_get(nxt_task_t *task, nxt_str_t *name, nxt_mp_t *mp,
|
|
nxt_port_rpc_handler_t handler, void *ctx)
|
|
{
|
|
uint32_t stream;
|
|
nxt_int_t ret;
|
|
nxt_buf_t *b;
|
|
nxt_port_t *main_port, *recv_port;
|
|
nxt_runtime_t *rt;
|
|
|
|
b = nxt_buf_mem_alloc(mp, name->length + 1, 0);
|
|
if (nxt_slow_path(b == NULL)) {
|
|
goto fail;
|
|
}
|
|
|
|
nxt_mp_retain(mp);
|
|
b->completion_handler = nxt_script_buf_completion;
|
|
|
|
nxt_buf_cpystr(b, name);
|
|
*b->mem.free++ = '\0';
|
|
|
|
rt = task->thread->runtime;
|
|
main_port = rt->port_by_type[NXT_PROCESS_MAIN];
|
|
recv_port = rt->port_by_type[rt->type];
|
|
|
|
stream = nxt_port_rpc_register_handler(task, recv_port, handler, handler,
|
|
-1, ctx);
|
|
if (nxt_slow_path(stream == 0)) {
|
|
goto fail;
|
|
}
|
|
|
|
ret = nxt_port_socket_write(task, main_port, NXT_PORT_MSG_SCRIPT_GET, -1,
|
|
stream, recv_port->id, b);
|
|
|
|
if (nxt_slow_path(ret != NXT_OK)) {
|
|
nxt_port_rpc_cancel(task, recv_port, stream);
|
|
goto fail;
|
|
}
|
|
|
|
return;
|
|
|
|
fail:
|
|
|
|
handler(task, NULL, ctx);
|
|
}
|
|
|
|
|
|
static void
|
|
nxt_script_buf_completion(nxt_task_t *task, void *obj, void *data)
|
|
{
|
|
nxt_mp_t *mp;
|
|
nxt_buf_t *b;
|
|
|
|
b = obj;
|
|
mp = b->data;
|
|
nxt_assert(b->next == NULL);
|
|
|
|
nxt_mp_free(mp, b);
|
|
nxt_mp_release(mp);
|
|
}
|
|
|
|
|
|
void
|
|
nxt_script_store_get_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
|
|
{
|
|
u_char *p;
|
|
nxt_int_t ret;
|
|
nxt_str_t name;
|
|
nxt_file_t file;
|
|
nxt_port_t *port;
|
|
nxt_runtime_t *rt;
|
|
nxt_port_msg_type_t type;
|
|
|
|
port = nxt_runtime_port_find(task->thread->runtime, msg->port_msg.pid,
|
|
msg->port_msg.reply_port);
|
|
|
|
if (nxt_slow_path(port == NULL)) {
|
|
nxt_alert(task, "process port not found (pid %PI, reply_port %d)",
|
|
msg->port_msg.pid, msg->port_msg.reply_port);
|
|
return;
|
|
}
|
|
|
|
if (nxt_slow_path(port->type != NXT_PROCESS_CONTROLLER
|
|
&& port->type != NXT_PROCESS_ROUTER))
|
|
{
|
|
nxt_alert(task, "process %PI cannot store scripts",
|
|
msg->port_msg.pid);
|
|
return;
|
|
}
|
|
|
|
nxt_memzero(&file, sizeof(nxt_file_t));
|
|
|
|
file.fd = -1;
|
|
type = NXT_PORT_MSG_RPC_ERROR;
|
|
|
|
rt = task->thread->runtime;
|
|
|
|
if (nxt_slow_path(rt->certs.start == NULL)) {
|
|
nxt_alert(task, "no scripts storage directory");
|
|
goto error;
|
|
}
|
|
|
|
name.start = msg->buf->mem.pos;
|
|
name.length = nxt_strlen(name.start);
|
|
|
|
file.name = nxt_malloc(rt->scripts.length + name.length + 1);
|
|
if (nxt_slow_path(file.name == NULL)) {
|
|
goto error;
|
|
}
|
|
|
|
p = nxt_cpymem(file.name, rt->scripts.start, rt->scripts.length);
|
|
p = nxt_cpymem(p, name.start, name.length + 1);
|
|
|
|
ret = nxt_file_open(task, &file, NXT_FILE_RDWR, NXT_FILE_CREATE_OR_OPEN,
|
|
NXT_FILE_OWNER_ACCESS);
|
|
|
|
nxt_free(file.name);
|
|
|
|
if (nxt_fast_path(ret == NXT_OK)) {
|
|
type = NXT_PORT_MSG_RPC_READY_LAST | NXT_PORT_MSG_CLOSE_FD;
|
|
}
|
|
|
|
error:
|
|
|
|
(void) nxt_port_socket_write(task, port, type, file.fd,
|
|
msg->port_msg.stream, 0, NULL);
|
|
}
|
|
|
|
|
|
void
|
|
nxt_script_store_delete(nxt_task_t *task, nxt_str_t *name, nxt_mp_t *mp)
|
|
{
|
|
nxt_buf_t *b;
|
|
nxt_port_t *main_port;
|
|
nxt_runtime_t *rt;
|
|
|
|
b = nxt_buf_mem_alloc(mp, name->length + 1, 0);
|
|
|
|
if (nxt_fast_path(b != NULL)) {
|
|
nxt_buf_cpystr(b, name);
|
|
*b->mem.free++ = '\0';
|
|
|
|
rt = task->thread->runtime;
|
|
main_port = rt->port_by_type[NXT_PROCESS_MAIN];
|
|
|
|
(void) nxt_port_socket_write(task, main_port,
|
|
NXT_PORT_MSG_SCRIPT_DELETE, -1, 0, 0, b);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
nxt_script_store_delete_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
|
|
{
|
|
u_char *p;
|
|
nxt_str_t name;
|
|
nxt_port_t *ctl_port;
|
|
nxt_runtime_t *rt;
|
|
nxt_file_name_t *path;
|
|
|
|
rt = task->thread->runtime;
|
|
ctl_port = rt->port_by_type[NXT_PROCESS_CONTROLLER];
|
|
|
|
if (nxt_slow_path(ctl_port == NULL)) {
|
|
nxt_alert(task, "controller port not found");
|
|
return;
|
|
}
|
|
|
|
if (nxt_slow_path(nxt_recv_msg_cmsg_pid(msg) != ctl_port->pid)) {
|
|
nxt_alert(task, "process %PI cannot delete scripts",
|
|
nxt_recv_msg_cmsg_pid(msg));
|
|
return;
|
|
}
|
|
|
|
if (nxt_slow_path(rt->scripts.start == NULL)) {
|
|
nxt_alert(task, "no scripts storage directory");
|
|
return;
|
|
}
|
|
|
|
name.start = msg->buf->mem.pos;
|
|
name.length = nxt_strlen(name.start);
|
|
|
|
path = nxt_malloc(rt->scripts.length + name.length + 1);
|
|
|
|
if (nxt_fast_path(path != NULL)) {
|
|
p = nxt_cpymem(path, rt->scripts.start, rt->scripts.length);
|
|
p = nxt_cpymem(p, name.start, name.length + 1);
|
|
|
|
(void) nxt_file_delete(path);
|
|
|
|
nxt_free(path);
|
|
}
|
|
}
|
|
|
|
|
|
nxt_int_t
|
|
nxt_script_file_read(nxt_fd_t fd, nxt_str_t *str)
|
|
{
|
|
ssize_t n;
|
|
nxt_int_t ret;
|
|
nxt_file_t file;
|
|
nxt_file_info_t fi;
|
|
|
|
nxt_memzero(&file, sizeof(nxt_file_t));
|
|
|
|
file.fd = fd;
|
|
|
|
ret = nxt_file_info(&file, &fi);
|
|
if (nxt_slow_path(ret != NXT_OK)) {
|
|
return NXT_ERROR;
|
|
}
|
|
|
|
if (nxt_slow_path(!nxt_is_file(&fi))) {
|
|
nxt_str_null(str);
|
|
return NXT_DECLINED;
|
|
}
|
|
|
|
str->length = nxt_file_size(&fi);
|
|
str->start = nxt_malloc(str->length);
|
|
if (nxt_slow_path(str->start == NULL)) {
|
|
return NXT_ERROR;
|
|
}
|
|
|
|
n = nxt_file_read(&file, str->start, str->length, 0);
|
|
|
|
if (nxt_slow_path(n != (ssize_t) str->length)) {
|
|
nxt_free(str->start);
|
|
return NXT_ERROR;
|
|
}
|
|
|
|
return NXT_OK;
|
|
}
|