summaryrefslogtreecommitdiffhomepage
path: root/src/nxt_script.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nxt_script.c')
-rw-r--r--src/nxt_script.c709
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;
+}