1
2 /*
3 * Copyright (C) NGINX, Inc.
4 * Copyright (C) Zhidao HONG
5 */
6
7 #include <nxt_main.h>
8 #include <nxt_conf.h>
9 #include <nxt_script.h>
10 #include <dirent.h>
11
12
13 struct nxt_script_s {
14 nxt_str_t text;
15 };
16
17
18 typedef struct {
19 nxt_str_t name;
20 nxt_conf_value_t *value;
21 nxt_mp_t *mp;
22 } nxt_script_info_t;
23
24
25 typedef struct {
26 nxt_str_t name;
27 nxt_fd_t fd;
28 } nxt_script_item_t;
29
30
31 static nxt_script_t *nxt_script_get(nxt_task_t *task, nxt_str_t *name,
32 nxt_fd_t fd);
33 static nxt_conf_value_t *nxt_script_details(nxt_mp_t *mp, nxt_script_t *cert);
34 static void nxt_script_buf_completion(nxt_task_t *task, void *obj, void *data);
35
36
37 static nxt_lvlhsh_t nxt_script_info;
38
39
40 static njs_vm_ops_t nxt_js_ops = {
41 NULL,
42 NULL,
43 nxt_js_module_loader,
44 NULL,
45 };
46
47
48 nxt_script_t *
nxt_script_new(nxt_task_t * task,nxt_str_t * name,u_char * data,size_t size,u_char * error)49 nxt_script_new(nxt_task_t *task, nxt_str_t *name, u_char *data, size_t size,
50 u_char *error)
51 {
52 u_char *start;
53 njs_vm_t *vm;
54 njs_str_t mod_name;
55 njs_mod_t *mod;
56 njs_vm_opt_t opts;
57 nxt_script_t *script;
58
59 njs_vm_opt_init(&opts);
60
61 opts.backtrace = 1;
62
63 opts.file.start = (u_char *) "default";
64 opts.file.length = 7;
65
66 opts.ops = &nxt_js_ops;
67
68 vm = njs_vm_create(&opts);
69 if (nxt_slow_path(vm == NULL)) {
70 return NULL;
71 }
72
73 mod_name.length = name->length;
74 mod_name.start = name->start;
75
76 start = data;
77
78 mod = njs_vm_compile_module(vm, &mod_name, &start, start + size);
79
80 if (nxt_slow_path(mod == NULL)) {
81 (void) nxt_js_error(vm, error);
82 nxt_alert(task, "JS compile module(%V) failed: %s", name, error);
83
84 goto fail;
85 }
86
87 script = nxt_zalloc(sizeof(nxt_script_t) + size);
88 if (nxt_slow_path(script == NULL)) {
89 goto fail;
90 }
91
92 script->text.length = size;
93 script->text.start = (u_char *) script + sizeof(nxt_script_t);
94
95 nxt_memcpy(script->text.start, data, size);
96
97 njs_vm_destroy(vm);
98
99 return script;
100
101 fail:
102
103 njs_vm_destroy(vm);
104
105 return NULL;
106 }
107
108
109 static nxt_script_t *
nxt_script_get(nxt_task_t * task,nxt_str_t * name,nxt_fd_t fd)110 nxt_script_get(nxt_task_t *task, nxt_str_t *name, nxt_fd_t fd)
111 {
112 nxt_int_t ret;
113 nxt_str_t text;
114 nxt_script_t *script;
115 u_char error[NXT_MAX_ERROR_STR];
116
117 ret = nxt_script_file_read(fd, &text);
118 if (nxt_slow_path(ret != NXT_OK)) {
119 return NULL;
120 }
121
122 script = nxt_script_new(task, name, text.start, text.length, error);
123
124 nxt_free(text.start);
125
126 return script;
127 }
128
129
130 void
nxt_script_destroy(nxt_script_t * script)131 nxt_script_destroy(nxt_script_t *script)
132 {
133 nxt_free(script);
134 }
135
136
137 static nxt_int_t
nxt_script_info_hash_test(nxt_lvlhsh_query_t * lhq,void * data)138 nxt_script_info_hash_test(nxt_lvlhsh_query_t *lhq, void *data)
139 {
140 nxt_script_info_t *info;
141
142 info = data;
143
144 if (nxt_strcasestr_eq(&lhq->key, &info->name)) {
145 return NXT_OK;
146 }
147
148 return NXT_DECLINED;
149 }
150
151
152 static const nxt_lvlhsh_proto_t nxt_script_info_hash_proto
153 nxt_aligned(64) =
154 {
155 NXT_LVLHSH_DEFAULT,
156 nxt_script_info_hash_test,
157 nxt_lvlhsh_alloc,
158 nxt_lvlhsh_free,
159 };
160
161
162 void
nxt_script_info_init(nxt_task_t * task,nxt_array_t * scripts)163 nxt_script_info_init(nxt_task_t *task, nxt_array_t *scripts)
164 {
165 uint32_t i;
166 nxt_script_t *script;
167 nxt_script_item_t *item;
168
169 item = scripts->elts;
170
171 for (i = 0; i < scripts->nelts; i++) {
172 script = nxt_script_get(task, &item->name, item->fd);
173
174 if (nxt_slow_path(script == NULL)) {
175 continue;
176 }
177
178 (void) nxt_script_info_save(&item->name, script);
179
180 nxt_script_destroy(script);
181
182 item++;
183 }
184 }
185
186
187 nxt_int_t
nxt_script_info_save(nxt_str_t * name,nxt_script_t * script)188 nxt_script_info_save(nxt_str_t *name, nxt_script_t *script)
189 {
190 nxt_mp_t *mp;
191 nxt_int_t ret;
192 nxt_conf_value_t *value;
193 nxt_script_info_t *info;
194 nxt_lvlhsh_query_t lhq;
195
196 mp = nxt_mp_create(1024, 128, 256, 32);
197 if (nxt_slow_path(mp == NULL)) {
198 return NXT_ERROR;
199 }
200
201 info = nxt_mp_get(mp, sizeof(nxt_script_info_t));
202 if (nxt_slow_path(info == NULL)) {
203 goto fail;
204 }
205
206 name = nxt_str_dup(mp, &info->name, name);
207 if (nxt_slow_path(name == NULL)) {
208 goto fail;
209 }
210
211 value = nxt_script_details(mp, script);
212 if (nxt_slow_path(value == NULL)) {
213 goto fail;
214 }
215
216 info->mp = mp;
217 info->value = value;
218
219 lhq.key_hash = nxt_djb_hash(name->start, name->length);
220 lhq.replace = 1;
221 lhq.key = *name;
222 lhq.value = info;
223 lhq.proto = &nxt_script_info_hash_proto;
224
225 ret = nxt_lvlhsh_insert(&nxt_script_info, &lhq);
226 if (nxt_slow_path(ret != NXT_OK)) {
227 goto fail;
228 }
229
230 if (lhq.value != info) {
231 info = lhq.value;
232 nxt_mp_destroy(info->mp);
233 }
234
235 return NXT_OK;
236
237 fail:
238
239 nxt_mp_destroy(mp);
240 return NXT_ERROR;
241 }
242
243
244 nxt_conf_value_t *
nxt_script_info_get(nxt_str_t * name)245 nxt_script_info_get(nxt_str_t *name)
246 {
247 nxt_int_t ret;
248 nxt_script_info_t *info;
249 nxt_lvlhsh_query_t lhq;
250
251 lhq.key_hash = nxt_djb_hash(name->start, name->length);
252 lhq.key = *name;
253 lhq.proto = &nxt_script_info_hash_proto;
254
255 ret = nxt_lvlhsh_find(&nxt_script_info, &lhq);
256 if (ret != NXT_OK) {
257 return NULL;
258 }
259
260 info = lhq.value;
261
262 return info->value;
263 }
264
265
266 nxt_conf_value_t *
nxt_script_info_get_all(nxt_mp_t * mp)267 nxt_script_info_get_all(nxt_mp_t *mp)
268 {
269 uint32_t i;
270 nxt_conf_value_t *all;
271 nxt_script_info_t *info;
272 nxt_lvlhsh_each_t lhe;
273
274 nxt_lvlhsh_each_init(&lhe, &nxt_script_info_hash_proto);
275
276 for (i = 0; /* void */; i++) {
277 info = nxt_lvlhsh_each(&nxt_script_info, &lhe);
278
279 if (info == NULL) {
280 break;
281 }
282 }
283
284 all = nxt_conf_create_object(mp, i);
285 if (nxt_slow_path(all == NULL)) {
286 return NULL;
287 }
288
289 nxt_lvlhsh_each_init(&lhe, &nxt_script_info_hash_proto);
290
291 for (i = 0; /* void */; i++) {
292 info = nxt_lvlhsh_each(&nxt_script_info, &lhe);
293
294 if (info == NULL) {
295 break;
296 }
297
298 nxt_conf_set_member(all, &info->name, info->value, i);
299 }
300
301 return all;
302 }
303
304
305 static nxt_conf_value_t *
nxt_script_details(nxt_mp_t * mp,nxt_script_t * script)306 nxt_script_details(nxt_mp_t *mp, nxt_script_t *script)
307 {
308 nxt_conf_value_t *value;
309
310 value = nxt_conf_create_object(mp, 0);
311 if (nxt_slow_path(value == NULL)) {
312 return NULL;
313 }
314
315 nxt_conf_set_string_dup(value, mp, &script->text);
316
317 return value;
318 }
319
320
321 nxt_int_t
nxt_script_info_delete(nxt_str_t * name)322 nxt_script_info_delete(nxt_str_t *name)
323 {
324 nxt_int_t ret;
325 nxt_script_info_t *info;
326 nxt_lvlhsh_query_t lhq;
327
328 lhq.key_hash = nxt_djb_hash(name->start, name->length);
329 lhq.key = *name;
330 lhq.proto = &nxt_script_info_hash_proto;
331
332 ret = nxt_lvlhsh_delete(&nxt_script_info, &lhq);
333
334 if (ret == NXT_OK) {
335 info = lhq.value;
336 nxt_mp_destroy(info->mp);
337 }
338
339 return ret;
340 }
341
342
343 nxt_array_t *
nxt_script_store_load(nxt_task_t * task,nxt_mp_t * mp)344 nxt_script_store_load(nxt_task_t *task, nxt_mp_t *mp)
345 {
346 DIR *dir;
347 size_t size, alloc;
348 u_char *buf, *p;
349 nxt_str_t name;
350 nxt_int_t ret;
351 nxt_file_t file;
352 nxt_array_t *scripts;
353 nxt_runtime_t *rt;
354 struct dirent *de;
355 nxt_script_item_t *item;
356
357 rt = task->thread->runtime;
358
359 if (nxt_slow_path(rt->scripts.start == NULL)) {
360 nxt_alert(task, "no scripts storage directory");
361 return NULL;
362 }
363
364 scripts = nxt_array_create(mp, 16, sizeof(nxt_script_item_t));
365 if (nxt_slow_path(scripts == NULL)) {
366 return NULL;
367 }
368
369 buf = NULL;
370 alloc = 0;
371
372 dir = opendir((char *) rt->scripts.start);
373 if (nxt_slow_path(dir == NULL)) {
374 nxt_alert(task, "opendir(\"%s\") failed %E",
375 rt->scripts.start, nxt_errno);
376 goto fail;
377 }
378
379 for ( ;; ) {
380 de = readdir(dir);
381 if (de == NULL) {
382 break;
383 }
384
385 nxt_debug(task, "readdir(\"%s\"): \"%s\"",
386 rt->scripts.start, de->d_name);
387
388 name.length = nxt_strlen(de->d_name);
389 name.start = (u_char *) de->d_name;
390
391 if (nxt_str_eq(&name, ".", 1) || nxt_str_eq(&name, "..", 2)) {
392 continue;
393 }
394
395 item = nxt_array_add(scripts);
396 if (nxt_slow_path(item == NULL)) {
397 goto fail;
398 }
399
400 item->fd = -1;
401
402 size = rt->scripts.length + name.length + 1;
403
404 if (size > alloc) {
405 size += 32;
406
407 p = nxt_realloc(buf, size);
408 if (p == NULL) {
409 goto fail;
410 }
411
412 alloc = size;
413 buf = p;
414 }
415
416 p = nxt_cpymem(buf, rt->scripts.start, rt->scripts.length);
417 p = nxt_cpymem(p, name.start, name.length + 1);
418
419 nxt_memzero(&file, sizeof(nxt_file_t));
420
421 file.name = buf;
422
423 ret = nxt_file_open(task, &file, NXT_FILE_RDONLY, NXT_FILE_OPEN,
424 NXT_FILE_OWNER_ACCESS);
425
426 if (nxt_slow_path(ret != NXT_OK)) {
427 nxt_array_remove_last(scripts);
428 continue;
429 }
430
431 item->fd = file.fd;
432
433 if (nxt_slow_path(nxt_str_dup(mp, &item->name, &name) == NULL)) {
434 goto fail;
435 }
436 }
437
438 if (buf != NULL) {
439 nxt_free(buf);
440 }
441
442 (void) closedir(dir);
443
444 return scripts;
445
446 fail:
447
448 if (buf != NULL) {
449 nxt_free(buf);
450 }
451
452 if (dir != NULL) {
453 (void) closedir(dir);
454 }
455
456 nxt_script_store_release(scripts);
457
458 return NULL;
459 }
460
461
462 void
nxt_script_store_release(nxt_array_t * scripts)463 nxt_script_store_release(nxt_array_t *scripts)
464 {
465 uint32_t i;
466 nxt_script_item_t *item;
467
468 item = scripts->elts;
469
470 for (i = 0; i < scripts->nelts; i++) {
471 nxt_fd_close(item[i].fd);
472 }
473
474 nxt_array_destroy(scripts);
475 }
476
477
478 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)479 nxt_script_store_get(nxt_task_t *task, nxt_str_t *name, nxt_mp_t *mp,
480 nxt_port_rpc_handler_t handler, void *ctx)
481 {
482 uint32_t stream;
483 nxt_int_t ret;
484 nxt_buf_t *b;
485 nxt_port_t *main_port, *recv_port;
486 nxt_runtime_t *rt;
487
488 b = nxt_buf_mem_alloc(mp, name->length + 1, 0);
489 if (nxt_slow_path(b == NULL)) {
490 goto fail;
491 }
492
493 nxt_mp_retain(mp);
494 b->completion_handler = nxt_script_buf_completion;
495
496 nxt_buf_cpystr(b, name);
497 *b->mem.free++ = '\0';
498
499 rt = task->thread->runtime;
500 main_port = rt->port_by_type[NXT_PROCESS_MAIN];
501 recv_port = rt->port_by_type[rt->type];
502
503 stream = nxt_port_rpc_register_handler(task, recv_port, handler, handler,
504 -1, ctx);
505 if (nxt_slow_path(stream == 0)) {
506 goto fail;
507 }
508
509 ret = nxt_port_socket_write(task, main_port, NXT_PORT_MSG_SCRIPT_GET, -1,
510 stream, recv_port->id, b);
511
512 if (nxt_slow_path(ret != NXT_OK)) {
513 nxt_port_rpc_cancel(task, recv_port, stream);
514 goto fail;
515 }
516
517 return;
518
519 fail:
520
521 handler(task, NULL, ctx);
522 }
523
524
525 static void
nxt_script_buf_completion(nxt_task_t * task,void * obj,void * data)526 nxt_script_buf_completion(nxt_task_t *task, void *obj, void *data)
527 {
528 nxt_mp_t *mp;
529 nxt_buf_t *b;
530
531 b = obj;
532 mp = b->data;
533 nxt_assert(b->next == NULL);
534
535 nxt_mp_free(mp, b);
536 nxt_mp_release(mp);
537 }
538
539
540 void
nxt_script_store_get_handler(nxt_task_t * task,nxt_port_recv_msg_t * msg)541 nxt_script_store_get_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
542 {
543 u_char *p;
544 nxt_int_t ret;
545 nxt_str_t name;
546 nxt_file_t file;
547 nxt_port_t *port;
548 nxt_runtime_t *rt;
549 nxt_port_msg_type_t type;
550
551 port = nxt_runtime_port_find(task->thread->runtime, msg->port_msg.pid,
552 msg->port_msg.reply_port);
553
554 if (nxt_slow_path(port == NULL)) {
555 nxt_alert(task, "process port not found (pid %PI, reply_port %d)",
556 msg->port_msg.pid, msg->port_msg.reply_port);
557 return;
558 }
559
560 if (nxt_slow_path(port->type != NXT_PROCESS_CONTROLLER
561 && port->type != NXT_PROCESS_ROUTER))
562 {
563 nxt_alert(task, "process %PI cannot store scripts",
564 msg->port_msg.pid);
565 return;
566 }
567
568 nxt_memzero(&file, sizeof(nxt_file_t));
569
570 file.fd = -1;
571 type = NXT_PORT_MSG_RPC_ERROR;
572
573 rt = task->thread->runtime;
574
575 if (nxt_slow_path(rt->certs.start == NULL)) {
576 nxt_alert(task, "no scripts storage directory");
577 goto error;
578 }
579
580 name.start = msg->buf->mem.pos;
581 name.length = nxt_strlen(name.start);
582
583 file.name = nxt_malloc(rt->scripts.length + name.length + 1);
584 if (nxt_slow_path(file.name == NULL)) {
585 goto error;
586 }
587
588 p = nxt_cpymem(file.name, rt->scripts.start, rt->scripts.length);
589 p = nxt_cpymem(p, name.start, name.length + 1);
590
591 ret = nxt_file_open(task, &file, NXT_FILE_RDWR, NXT_FILE_CREATE_OR_OPEN,
592 NXT_FILE_OWNER_ACCESS);
593
594 nxt_free(file.name);
595
596 if (nxt_fast_path(ret == NXT_OK)) {
597 type = NXT_PORT_MSG_RPC_READY_LAST | NXT_PORT_MSG_CLOSE_FD;
598 }
599
600 error:
601
602 (void) nxt_port_socket_write(task, port, type, file.fd,
603 msg->port_msg.stream, 0, NULL);
604 }
605
606
607 void
nxt_script_store_delete(nxt_task_t * task,nxt_str_t * name,nxt_mp_t * mp)608 nxt_script_store_delete(nxt_task_t *task, nxt_str_t *name, nxt_mp_t *mp)
609 {
610 nxt_buf_t *b;
611 nxt_port_t *main_port;
612 nxt_runtime_t *rt;
613
614 b = nxt_buf_mem_alloc(mp, name->length + 1, 0);
615
616 if (nxt_fast_path(b != NULL)) {
617 nxt_buf_cpystr(b, name);
618 *b->mem.free++ = '\0';
619
620 rt = task->thread->runtime;
621 main_port = rt->port_by_type[NXT_PROCESS_MAIN];
622
623 (void) nxt_port_socket_write(task, main_port,
624 NXT_PORT_MSG_SCRIPT_DELETE, -1, 0, 0, b);
625 }
626 }
627
628
629 void
nxt_script_store_delete_handler(nxt_task_t * task,nxt_port_recv_msg_t * msg)630 nxt_script_store_delete_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
631 {
632 u_char *p;
633 nxt_str_t name;
634 nxt_port_t *ctl_port;
635 nxt_runtime_t *rt;
636 nxt_file_name_t *path;
637
638 rt = task->thread->runtime;
639 ctl_port = rt->port_by_type[NXT_PROCESS_CONTROLLER];
640
641 if (nxt_slow_path(ctl_port == NULL)) {
642 nxt_alert(task, "controller port not found");
643 return;
644 }
645
646 if (nxt_slow_path(nxt_recv_msg_cmsg_pid(msg) != ctl_port->pid)) {
647 nxt_alert(task, "process %PI cannot delete scripts",
648 nxt_recv_msg_cmsg_pid(msg));
649 return;
650 }
651
652 if (nxt_slow_path(rt->scripts.start == NULL)) {
653 nxt_alert(task, "no scripts storage directory");
654 return;
655 }
656
657 name.start = msg->buf->mem.pos;
658 name.length = nxt_strlen(name.start);
659
660 path = nxt_malloc(rt->scripts.length + name.length + 1);
661
662 if (nxt_fast_path(path != NULL)) {
663 p = nxt_cpymem(path, rt->scripts.start, rt->scripts.length);
664 p = nxt_cpymem(p, name.start, name.length + 1);
665
666 (void) nxt_file_delete(path);
667
668 nxt_free(path);
669 }
670 }
671
672
673 nxt_int_t
nxt_script_file_read(nxt_fd_t fd,nxt_str_t * str)674 nxt_script_file_read(nxt_fd_t fd, nxt_str_t *str)
675 {
676 ssize_t n;
677 nxt_int_t ret;
678 nxt_file_t file;
679 nxt_file_info_t fi;
680
681 nxt_memzero(&file, sizeof(nxt_file_t));
682
683 file.fd = fd;
684
685 ret = nxt_file_info(&file, &fi);
686 if (nxt_slow_path(ret != NXT_OK)) {
687 return NXT_ERROR;
688 }
689
690 if (nxt_slow_path(!nxt_is_file(&fi))) {
691 nxt_str_null(str);
692 return NXT_DECLINED;
693 }
694
695 str->length = nxt_file_size(&fi);
696 str->start = nxt_malloc(str->length);
697 if (nxt_slow_path(str->start == NULL)) {
698 return NXT_ERROR;
699 }
700
701 n = nxt_file_read(&file, str->start, str->length, 0);
702
703 if (nxt_slow_path(n != (ssize_t) str->length)) {
704 nxt_free(str->start);
705 return NXT_ERROR;
706 }
707
708 return NXT_OK;
709 }
710