xref: /unit/src/nxt_script.c (revision 2450:14277f21a722)
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