diff options
Diffstat (limited to 'src/nxt_script.c')
-rw-r--r-- | src/nxt_script.c | 709 |
1 files changed, 709 insertions, 0 deletions
diff --git a/src/nxt_script.c b/src/nxt_script.c new file mode 100644 index 00000000..70045a22 --- /dev/null +++ b/src/nxt_script.c @@ -0,0 +1,709 @@ + +/* + * 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; +} |