1/* -*- c-basic-offset: 2 -*- */
2/*
3 Copyright(C) 2012-2017 Brazil
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License version 2.1 as published by the Free Software Foundation.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public
15 License along with this library; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17*/
18#include "grn.h"
19#include "grn_ctx_impl_mrb.h"
20#include "grn_proc.h"
21#include <groonga/plugin.h>
22
23#include <stdarg.h>
24#include <stdio.h>
25#include <string.h>
26
27#include <sys/stat.h>
28#ifdef HAVE_DIRENT_H
29# include <dirent.h>
30#endif /* HAVE_DIRENT_H */
31
32#ifndef S_ISREG
33# ifdef _S_IFREG
34# define S_ISREG(mode) (mode & _S_IFREG)
35# endif /* _S_IFREG */
36#endif /* !S_ISREG */
37
38#include "grn_db.h"
39#include "grn_plugin.h"
40#include "grn_ctx_impl.h"
41#include "grn_util.h"
42
43#ifdef GRN_WITH_MRUBY
44# include <mruby.h>
45#endif /* GRN_WITH_MRUBY */
46
47static grn_hash *grn_plugins = NULL;
48static grn_critical_section grn_plugins_lock;
49static grn_ctx grn_plugins_ctx;
50
51#ifdef HAVE_DLFCN_H
52# include <dlfcn.h>
53# define grn_dl_open(filename) dlopen(filename, RTLD_LAZY | RTLD_LOCAL)
54# define grn_dl_open_error_label() dlerror()
55# define grn_dl_close(dl) (dlclose(dl) == 0)
56# define grn_dl_close_error_label() dlerror()
57# define grn_dl_sym(dl, symbol) dlsym(dl, symbol)
58# define grn_dl_sym_error_label() dlerror()
59# define grn_dl_clear_error() dlerror()
60#else
61# define grn_dl_open(filename) LoadLibrary(filename)
62# define grn_dl_open_error_label() "LoadLibrary"
63# define grn_dl_close(dl) (FreeLibrary(dl) != 0)
64# define grn_dl_close_error_label() "FreeLibrary"
65# define grn_dl_sym(dl, symbol) ((void *)GetProcAddress(dl, symbol))
66# define grn_dl_sym_error_label() "GetProcAddress"
67# define grn_dl_clear_error()
68#endif
69
70#define GRN_PLUGIN_KEY_SIZE(filename) (strlen((filename)) + 1)
71
72static char grn_plugins_dir[GRN_ENV_BUFFER_SIZE];
73
74void
75grn_plugin_init_from_env(void)
76{
77 grn_getenv("GRN_PLUGINS_DIR",
78 grn_plugins_dir,
79 GRN_ENV_BUFFER_SIZE);
80}
81
82static int
83compute_name_size(const char *name, int name_size)
84{
85 if (name_size < 0) {
86 if (name) {
87 name_size = strlen(name);
88 } else {
89 name_size = 0;
90 }
91 }
92 return name_size;
93}
94
95grn_id
96grn_plugin_reference(grn_ctx *ctx, const char *filename)
97{
98 grn_id id;
99 grn_plugin **plugin = NULL;
100
101 CRITICAL_SECTION_ENTER(grn_plugins_lock);
102 id = grn_hash_get(&grn_plugins_ctx, grn_plugins,
103 filename, GRN_PLUGIN_KEY_SIZE(filename),
104 (void **)&plugin);
105 if (plugin) {
106 (*plugin)->refcount++;
107 }
108 CRITICAL_SECTION_LEAVE(grn_plugins_lock);
109
110 return id;
111}
112
113const char *
114grn_plugin_path(grn_ctx *ctx, grn_id id)
115{
116 const char *path;
117 grn_plugin *plugin;
118 int value_size;
119 const char *system_plugins_dir;
120 size_t system_plugins_dir_size;
121
122 if (id == GRN_ID_NIL) {
123 return NULL;
124 }
125
126 CRITICAL_SECTION_ENTER(grn_plugins_lock);
127 value_size = grn_hash_get_value(&grn_plugins_ctx, grn_plugins, id, &plugin);
128 CRITICAL_SECTION_LEAVE(grn_plugins_lock);
129
130 if (!plugin) {
131 return NULL;
132 }
133
134 path = plugin->path;
135 system_plugins_dir = grn_plugin_get_system_plugins_dir();
136 system_plugins_dir_size = strlen(system_plugins_dir);
137 if (strncmp(system_plugins_dir, path, system_plugins_dir_size) == 0) {
138 const char *plugin_name = path + system_plugins_dir_size;
139 while (plugin_name[0] == '/') {
140 plugin_name++;
141 }
142 /* TODO: remove suffix too? */
143 return plugin_name;
144 } else {
145 return path;
146 }
147}
148
149#define GRN_PLUGIN_FUNC_PREFIX "grn_plugin_impl_"
150
151static grn_rc
152grn_plugin_call_init(grn_ctx *ctx, grn_id id)
153{
154 grn_plugin *plugin;
155 int size;
156
157 size = grn_hash_get_value(&grn_plugins_ctx, grn_plugins, id, &plugin);
158 if (size == 0) {
159 return GRN_INVALID_ARGUMENT;
160 }
161
162 if (plugin->init_func) {
163 return plugin->init_func(ctx);
164 }
165
166 return GRN_SUCCESS;
167}
168
169#ifdef GRN_WITH_MRUBY
170static grn_rc
171grn_plugin_call_register_mrb(grn_ctx *ctx, grn_id id, grn_plugin *plugin)
172{
173 grn_mrb_data *data;
174 mrb_state *mrb;
175 struct RClass *module;
176 struct RClass *plugin_loader_class;
177 int arena_index;
178
179 grn_ctx_impl_mrb_ensure_init(ctx);
180 if (ctx->rc != GRN_SUCCESS) {
181 return ctx->rc;
182 }
183
184 data = &(ctx->impl->mrb);
185 mrb = data->state;
186 module = data->module;
187
188 {
189 int added;
190 grn_hash_add(ctx, ctx->impl->mrb.registered_plugins,
191 &id, sizeof(grn_id), NULL, &added);
192 if (!added) {
193 return ctx->rc;
194 }
195 }
196
197 arena_index = mrb_gc_arena_save(mrb);
198 plugin_loader_class = mrb_class_get_under(mrb, module, "PluginLoader");
199 mrb_funcall(mrb, mrb_obj_value(plugin_loader_class),
200 "load_file", 1, mrb_str_new_cstr(mrb, ctx->impl->plugin_path));
201 mrb_gc_arena_restore(mrb, arena_index);
202 return ctx->rc;
203}
204#endif /*GRN_WITH_MRUBY */
205
206static grn_rc
207grn_plugin_call_register(grn_ctx *ctx, grn_id id)
208{
209 grn_plugin *plugin;
210 int size;
211
212 CRITICAL_SECTION_ENTER(grn_plugins_lock);
213 size = grn_hash_get_value(&grn_plugins_ctx, grn_plugins, id, &plugin);
214 CRITICAL_SECTION_LEAVE(grn_plugins_lock);
215
216 if (size == 0) {
217 return GRN_INVALID_ARGUMENT;
218 }
219
220#ifdef GRN_WITH_MRUBY
221 if (!plugin->dl) {
222 return grn_plugin_call_register_mrb(ctx, id, plugin);
223 }
224#endif /* GRN_WITH_MRUBY */
225
226 if (plugin->register_func) {
227 return plugin->register_func(ctx);
228 }
229
230 return GRN_SUCCESS;
231}
232
233static grn_rc
234grn_plugin_call_fin(grn_ctx *ctx, grn_id id)
235{
236 grn_plugin *plugin;
237 int size;
238
239 size = grn_hash_get_value(&grn_plugins_ctx, grn_plugins, id, &plugin);
240 if (size == 0) {
241 return GRN_INVALID_ARGUMENT;
242 }
243
244 if (plugin->fin_func) {
245 return plugin->fin_func(ctx);
246 }
247
248 return GRN_SUCCESS;
249}
250
251static grn_rc
252grn_plugin_initialize(grn_ctx *ctx, grn_plugin *plugin,
253 grn_dl dl, grn_id id, const char *path)
254{
255 plugin->dl = dl;
256
257#define GET_SYMBOL(type) do { \
258 grn_dl_clear_error(); \
259 plugin->type ## _func = grn_dl_sym(dl, GRN_PLUGIN_FUNC_PREFIX #type); \
260 if (!plugin->type ## _func) { \
261 const char *label; \
262 label = grn_dl_sym_error_label(); \
263 SERR("%s", label); \
264 } \
265} while (0)
266
267 GET_SYMBOL(init);
268 GET_SYMBOL(register);
269 GET_SYMBOL(fin);
270
271#undef GET_SYMBOL
272
273 if (!plugin->init_func || !plugin->register_func || !plugin->fin_func) {
274 ERR(GRN_INVALID_FORMAT,
275 "init func (%s) %sfound, "
276 "register func (%s) %sfound and "
277 "fin func (%s) %sfound",
278 GRN_PLUGIN_FUNC_PREFIX "init", plugin->init_func ? "" : "not ",
279 GRN_PLUGIN_FUNC_PREFIX "register", plugin->register_func ? "" : "not ",
280 GRN_PLUGIN_FUNC_PREFIX "fin", plugin->fin_func ? "" : "not ");
281 }
282
283 if (!ctx->rc) {
284 ctx->impl->plugin_path = path;
285 grn_plugin_call_init(ctx, id);
286 ctx->impl->plugin_path = NULL;
287 }
288
289 return ctx->rc;
290}
291
292#ifdef GRN_WITH_MRUBY
293static grn_id
294grn_plugin_open_mrb(grn_ctx *ctx, const char *filename, size_t filename_size)
295{
296 grn_ctx *plugins_ctx = &grn_plugins_ctx;
297 grn_id id = GRN_ID_NIL;
298 grn_plugin **plugin = NULL;
299
300 grn_ctx_impl_mrb_ensure_init(ctx);
301 if (ctx->rc != GRN_SUCCESS) {
302 return GRN_ID_NIL;
303 }
304
305 if (!ctx->impl->mrb.state) {
306 ERR(GRN_FUNCTION_NOT_IMPLEMENTED, "mruby support isn't enabled");
307 return GRN_ID_NIL;
308 }
309
310 id = grn_hash_add(plugins_ctx, grn_plugins, filename, filename_size,
311 (void **)&plugin, NULL);
312 if (!id) {
313 return id;
314 }
315
316 {
317 grn_ctx *ctx = plugins_ctx;
318 *plugin = GRN_MALLOCN(grn_plugin, 1);
319 }
320 if (!*plugin) {
321 grn_hash_delete_by_id(plugins_ctx, grn_plugins, id, NULL);
322 return GRN_ID_NIL;
323 }
324
325 grn_memcpy((*plugin)->path, filename, filename_size);
326 (*plugin)->dl = NULL;
327 (*plugin)->init_func = NULL;
328 (*plugin)->register_func = NULL;
329 (*plugin)->fin_func = NULL;
330 (*plugin)->refcount = 1;
331
332 return id;
333}
334#endif /* GRN_WITH_MRUBY */
335
336grn_id
337grn_plugin_open(grn_ctx *ctx, const char *filename)
338{
339 grn_ctx *plugins_ctx = &grn_plugins_ctx;
340 grn_id id = GRN_ID_NIL;
341 grn_dl dl;
342 grn_plugin **plugin = NULL;
343 size_t filename_size;
344
345 filename_size = GRN_PLUGIN_KEY_SIZE(filename);
346
347 CRITICAL_SECTION_ENTER(grn_plugins_lock);
348 if ((id = grn_hash_get(plugins_ctx, grn_plugins, filename, filename_size,
349 (void **)&plugin))) {
350 (*plugin)->refcount++;
351 goto exit;
352 }
353
354#ifdef GRN_WITH_MRUBY
355 {
356 const char *mrb_suffix;
357 mrb_suffix = grn_plugin_get_ruby_suffix();
358 if (filename_size > strlen(mrb_suffix) &&
359 strcmp(filename + (strlen(filename) - strlen(mrb_suffix)),
360 mrb_suffix) == 0) {
361 id = grn_plugin_open_mrb(ctx, filename, filename_size);
362 goto exit;
363 }
364 }
365#endif /* GRN_WITH_MRUBY */
366
367 if ((dl = grn_dl_open(filename))) {
368 if ((id = grn_hash_add(plugins_ctx, grn_plugins, filename, filename_size,
369 (void **)&plugin, NULL))) {
370 {
371 grn_ctx *ctx = plugins_ctx;
372 *plugin = GRN_MALLOCN(grn_plugin, 1);
373 }
374 if (*plugin) {
375 grn_memcpy((*plugin)->path, filename, filename_size);
376 if (grn_plugin_initialize(ctx, *plugin, dl, id, filename)) {
377 {
378 grn_ctx *ctx = plugins_ctx;
379 GRN_FREE(*plugin);
380 }
381 *plugin = NULL;
382 }
383 }
384 if (!*plugin) {
385 grn_hash_delete_by_id(plugins_ctx, grn_plugins, id, NULL);
386 if (grn_dl_close(dl)) {
387 /* Now, __FILE__ set in plugin is invalid. */
388 ctx->errline = 0;
389 ctx->errfile = NULL;
390 } else {
391 const char *label;
392 label = grn_dl_close_error_label();
393 SERR("%s", label);
394 }
395 id = GRN_ID_NIL;
396 } else {
397 (*plugin)->refcount = 1;
398 }
399 } else {
400 if (!grn_dl_close(dl)) {
401 const char *label;
402 label = grn_dl_close_error_label();
403 SERR("%s", label);
404 }
405 }
406 } else {
407 const char *label;
408 label = grn_dl_open_error_label();
409 SERR("%s", label);
410 }
411
412exit:
413 CRITICAL_SECTION_LEAVE(grn_plugins_lock);
414
415 return id;
416}
417
418grn_rc
419grn_plugin_close(grn_ctx *ctx, grn_id id)
420{
421 grn_ctx *plugins_ctx = &grn_plugins_ctx;
422 grn_rc rc;
423 grn_plugin *plugin;
424
425 if (id == GRN_ID_NIL) {
426 return GRN_INVALID_ARGUMENT;
427 }
428
429 CRITICAL_SECTION_ENTER(grn_plugins_lock);
430 if (!grn_hash_get_value(plugins_ctx, grn_plugins, id, &plugin)) {
431 rc = GRN_INVALID_ARGUMENT;
432 goto exit;
433 }
434 if (--plugin->refcount) {
435 rc = GRN_SUCCESS;
436 goto exit;
437 }
438 if (plugin->dl) {
439 grn_plugin_call_fin(ctx, id);
440 if (!grn_dl_close(plugin->dl)) {
441 const char *label;
442 label = grn_dl_close_error_label();
443 SERR("%s", label);
444 }
445 }
446 {
447 grn_ctx *ctx = plugins_ctx;
448 GRN_FREE(plugin);
449 }
450 rc = grn_hash_delete_by_id(plugins_ctx, grn_plugins, id, NULL);
451
452exit:
453 CRITICAL_SECTION_LEAVE(grn_plugins_lock);
454
455 return rc;
456}
457
458void *
459grn_plugin_sym(grn_ctx *ctx, grn_id id, const char *symbol)
460{
461 grn_plugin *plugin;
462 grn_dl_symbol func;
463
464 if (id == GRN_ID_NIL) {
465 return NULL;
466 }
467
468 CRITICAL_SECTION_ENTER(grn_plugins_lock);
469 if (!grn_hash_get_value(&grn_plugins_ctx, grn_plugins, id, &plugin)) {
470 func = NULL;
471 goto exit;
472 }
473 grn_dl_clear_error();
474 if (!(func = grn_dl_sym(plugin->dl, symbol))) {
475 const char *label;
476 label = grn_dl_sym_error_label();
477 SERR("%s", label);
478 }
479
480exit:
481 CRITICAL_SECTION_LEAVE(grn_plugins_lock);
482
483 return func;
484}
485
486grn_rc
487grn_plugins_init(void)
488{
489 CRITICAL_SECTION_INIT(grn_plugins_lock);
490 grn_ctx_init(&grn_plugins_ctx, 0);
491 grn_plugins = grn_hash_create(&grn_plugins_ctx, NULL,
492 PATH_MAX, sizeof(grn_plugin *),
493 GRN_OBJ_KEY_VAR_SIZE);
494 if (!grn_plugins) {
495 grn_ctx_fin(&grn_plugins_ctx);
496 return GRN_NO_MEMORY_AVAILABLE;
497 }
498 return GRN_SUCCESS;
499}
500
501grn_rc
502grn_plugins_fin(void)
503{
504 grn_rc rc;
505 if (!grn_plugins) { return GRN_INVALID_ARGUMENT; }
506 GRN_HASH_EACH(&grn_plugins_ctx, grn_plugins, id, NULL, NULL, NULL, {
507 grn_plugin_close(&grn_plugins_ctx, id);
508 });
509 rc = grn_hash_close(&grn_plugins_ctx, grn_plugins);
510 grn_ctx_fin(&grn_plugins_ctx);
511 CRITICAL_SECTION_FIN(grn_plugins_lock);
512 return rc;
513}
514
515const char *
516grn_plugin_get_suffix(void)
517{
518 return GRN_PLUGIN_SUFFIX;
519}
520
521const char *
522grn_plugin_get_ruby_suffix(void)
523{
524 return ".rb";
525}
526
527grn_rc
528grn_plugin_register_by_path(grn_ctx *ctx, const char *path)
529{
530 grn_obj *db;
531 if (!ctx || !ctx->impl || !(db = ctx->impl->db)) {
532 ERR(GRN_INVALID_ARGUMENT, "db not initialized");
533 return ctx->rc;
534 }
535 GRN_API_ENTER;
536 if (GRN_DB_P(db)) {
537 grn_id id;
538 id = grn_plugin_open(ctx, path);
539 if (id) {
540 ctx->impl->plugin_path = path;
541 ctx->rc = grn_plugin_call_register(ctx, id);
542 ctx->impl->plugin_path = NULL;
543 grn_plugin_close(ctx, id);
544 }
545 } else {
546 ERR(GRN_INVALID_ARGUMENT, "invalid db assigned");
547 }
548 GRN_API_RETURN(ctx->rc);
549}
550
551#ifdef WIN32
552static char *windows_plugins_dir = NULL;
553static char windows_plugins_dir_buffer[PATH_MAX];
554static const char *
555grn_plugin_get_default_system_plugins_dir(void)
556{
557 if (!windows_plugins_dir) {
558 const char *base_dir;
559 const char *relative_path = GRN_RELATIVE_PLUGINS_DIR;
560 size_t base_dir_length;
561
562 base_dir = grn_windows_base_dir();
563 base_dir_length = strlen(base_dir);
564 grn_strcpy(windows_plugins_dir_buffer, PATH_MAX, base_dir);
565 grn_strcat(windows_plugins_dir_buffer, PATH_MAX, "/");
566 grn_strcat(windows_plugins_dir_buffer, PATH_MAX, relative_path);
567 windows_plugins_dir = windows_plugins_dir_buffer;
568 }
569 return windows_plugins_dir;
570}
571
572#else /* WIN32 */
573static const char *
574grn_plugin_get_default_system_plugins_dir(void)
575{
576 return GRN_PLUGINS_DIR;
577}
578#endif /* WIN32 */
579
580const char *
581grn_plugin_get_system_plugins_dir(void)
582{
583 if (grn_plugins_dir[0]) {
584 return grn_plugins_dir;
585 } else {
586 return grn_plugin_get_default_system_plugins_dir();
587 }
588}
589
590static char *
591grn_plugin_find_path_raw(grn_ctx *ctx, const char *path)
592{
593 struct stat path_stat;
594
595 if (stat(path, &path_stat) != 0) {
596 return NULL;
597 }
598
599 if (!S_ISREG(path_stat.st_mode)) {
600 return NULL;
601 }
602
603 return GRN_STRDUP(path);
604}
605
606#ifdef GRN_WITH_MRUBY
607static char *
608grn_plugin_find_path_mrb(grn_ctx *ctx, const char *path, size_t path_len)
609{
610 char mrb_path[PATH_MAX];
611 const char *mrb_suffix;
612 size_t mrb_path_len;
613
614 grn_ctx_impl_mrb_ensure_init(ctx);
615 if (ctx->rc != GRN_SUCCESS) {
616 return NULL;
617 }
618
619 if (!ctx->impl->mrb.state) {
620 return NULL;
621 }
622
623 mrb_suffix = grn_plugin_get_ruby_suffix();
624 mrb_path_len = path_len + strlen(mrb_suffix);
625 if (mrb_path_len >= PATH_MAX) {
626 ERR(GRN_FILENAME_TOO_LONG,
627 "too long plugin path: <%s%s>",
628 path, mrb_suffix);
629 return NULL;
630 }
631
632 grn_strcpy(mrb_path, PATH_MAX, path);
633 grn_strcat(mrb_path, PATH_MAX, mrb_suffix);
634 return grn_plugin_find_path_raw(ctx, mrb_path);
635}
636#else /* GRN_WITH_MRUBY */
637static char *
638grn_plugin_find_path_mrb(grn_ctx *ctx, const char *path, size_t path_len)
639{
640 return NULL;
641}
642#endif /* GRN_WITH_MRUBY */
643
644static char *
645grn_plugin_find_path_so(grn_ctx *ctx, const char *path, size_t path_len)
646{
647 char so_path[PATH_MAX];
648 const char *so_suffix;
649 size_t so_path_len;
650
651 so_suffix = grn_plugin_get_suffix();
652 so_path_len = path_len + strlen(so_suffix);
653 if (so_path_len >= PATH_MAX) {
654 ERR(GRN_FILENAME_TOO_LONG,
655 "too long plugin path: <%s%s>",
656 path, so_suffix);
657 return NULL;
658 }
659
660 grn_strcpy(so_path, PATH_MAX, path);
661 grn_strcat(so_path, PATH_MAX, so_suffix);
662 return grn_plugin_find_path_raw(ctx, so_path);
663}
664
665static char *
666grn_plugin_find_path_libs_so(grn_ctx *ctx, const char *path, size_t path_len)
667{
668 char libs_so_path[PATH_MAX];
669 const char *base_name;
670 const char *so_suffix;
671 const char *libs_path = "/.libs";
672 size_t libs_so_path_len;
673
674 base_name = strrchr(path, '/');
675 if (!base_name) {
676 return NULL;
677 }
678
679 so_suffix = grn_plugin_get_suffix();
680 libs_so_path_len =
681 base_name - path +
682 strlen(libs_path) +
683 strlen(base_name) +
684 strlen(so_suffix);
685 if (libs_so_path_len >= PATH_MAX) {
686 ERR(GRN_FILENAME_TOO_LONG,
687 "too long plugin path: <%.*s/.libs%s%s>",
688 (int)(base_name - path), path, base_name, so_suffix);
689 return NULL;
690 }
691
692 libs_so_path[0] = '\0';
693 grn_strncat(libs_so_path, PATH_MAX, path, base_name - path);
694 grn_strcat(libs_so_path, PATH_MAX, libs_path);
695 grn_strcat(libs_so_path, PATH_MAX, base_name);
696 grn_strcat(libs_so_path, PATH_MAX, so_suffix);
697 return grn_plugin_find_path_raw(ctx, libs_so_path);
698}
699
700char *
701grn_plugin_find_path(grn_ctx *ctx, const char *name)
702{
703 const char *plugins_dir;
704 char dir_last_char;
705 char path[PATH_MAX];
706 int name_length, max_name_length;
707 char *found_path = NULL;
708 size_t path_len;
709
710 GRN_API_ENTER;
711 if (name[0] == '/') {
712 path[0] = '\0';
713 } else {
714 plugins_dir = grn_plugin_get_system_plugins_dir();
715 grn_strcpy(path, PATH_MAX, plugins_dir);
716
717 dir_last_char = plugins_dir[strlen(path) - 1];
718 if (dir_last_char != '/') {
719 grn_strcat(path, PATH_MAX, "/");
720 }
721 }
722
723 name_length = strlen(name);
724 max_name_length = PATH_MAX - strlen(path) - 1;
725 if (name_length > max_name_length) {
726 ERR(GRN_INVALID_ARGUMENT,
727 "plugin name is too long: %d (max: %d) <%s%s>",
728 name_length, max_name_length,
729 path, name);
730 goto exit;
731 }
732 grn_strcat(path, PATH_MAX, name);
733
734 found_path = grn_plugin_find_path_raw(ctx, path);
735 if (found_path) {
736 goto exit;
737 }
738
739 path_len = strlen(path);
740
741 found_path = grn_plugin_find_path_so(ctx, path, path_len);
742 if (found_path) {
743 goto exit;
744 }
745 if (ctx->rc) {
746 goto exit;
747 }
748
749 found_path = grn_plugin_find_path_libs_so(ctx, path, path_len);
750 if (found_path) {
751 goto exit;
752 }
753 if (ctx->rc) {
754 goto exit;
755 }
756
757 found_path = grn_plugin_find_path_mrb(ctx, path, path_len);
758 if (found_path) {
759 goto exit;
760 }
761 if (ctx->rc) {
762 goto exit;
763 }
764
765exit :
766 GRN_API_RETURN(found_path);
767}
768
769static void
770grn_plugin_set_name_resolve_error(grn_ctx *ctx, const char *name,
771 const char *tag)
772{
773 const char *prefix, *prefix_separator, *suffix;
774
775 if (name[0] == '/') {
776 prefix = "";
777 prefix_separator = "";
778 suffix = "";
779 } else {
780 prefix = grn_plugin_get_system_plugins_dir();
781 if (prefix[strlen(prefix) - 1] != '/') {
782 prefix_separator = "/";
783 } else {
784 prefix_separator = "";
785 }
786 suffix = grn_plugin_get_suffix();
787 }
788 ERR(GRN_NO_SUCH_FILE_OR_DIRECTORY,
789 "%s cannot find plugin file: <%s%s%s%s>",
790 tag, prefix, prefix_separator, name, suffix);
791}
792
793grn_rc
794grn_plugin_register(grn_ctx *ctx, const char *name)
795{
796 grn_rc rc;
797 char *path;
798
799 GRN_API_ENTER;
800 path = grn_plugin_find_path(ctx, name);
801 if (path) {
802 rc = grn_plugin_register_by_path(ctx, path);
803 GRN_FREE(path);
804 } else {
805 if (ctx->rc == GRN_SUCCESS) {
806 grn_plugin_set_name_resolve_error(ctx, name, "[plugin][register]");
807 }
808 rc = ctx->rc;
809 }
810 GRN_API_RETURN(rc);
811}
812
813grn_rc
814grn_plugin_unregister_by_path(grn_ctx *ctx, const char *path)
815{
816 grn_obj *db;
817 grn_id plugin_id;
818
819 if (!ctx || !ctx->impl) {
820 ERR(GRN_INVALID_ARGUMENT, "[plugin][unregister] ctx isn't initialized");
821 return ctx->rc;
822 }
823
824 db = ctx->impl->db;
825 if (!db) {
826 ERR(GRN_INVALID_ARGUMENT, "[plugin][unregister] DB isn't initialized");
827 return ctx->rc;
828 }
829
830 GRN_API_ENTER;
831
832 CRITICAL_SECTION_ENTER(grn_plugins_lock);
833 plugin_id = grn_hash_get(&grn_plugins_ctx, grn_plugins,
834 path, GRN_PLUGIN_KEY_SIZE(path),
835 NULL);
836 CRITICAL_SECTION_LEAVE(grn_plugins_lock);
837
838 if (plugin_id == GRN_ID_NIL) {
839 GRN_API_RETURN(ctx->rc);
840 }
841
842 {
843 grn_table_cursor *cursor;
844 grn_id id;
845
846 cursor = grn_table_cursor_open(ctx, db,
847 NULL, 0,
848 NULL, 0,
849 0, -1, GRN_CURSOR_BY_ID);
850 if (!cursor) {
851 GRN_API_RETURN(ctx->rc);
852 }
853
854 while ((id = grn_table_cursor_next(ctx, cursor))) {
855 grn_obj *obj;
856 obj = grn_ctx_at(ctx, id);
857 if (!obj) {
858 continue;
859 }
860 if (obj->header.type == GRN_PROC && DB_OBJ(obj)->range == plugin_id) {
861 grn_obj_remove(ctx, obj);
862 } else {
863 grn_obj_unlink(ctx, obj);
864 }
865 }
866 grn_table_cursor_close(ctx, cursor);
867 }
868
869 GRN_API_RETURN(ctx->rc);
870}
871
872grn_rc
873grn_plugin_unregister(grn_ctx *ctx, const char *name)
874{
875 grn_rc rc;
876 char *path;
877
878 GRN_API_ENTER;
879 path = grn_plugin_find_path(ctx, name);
880 if (path) {
881 rc = grn_plugin_unregister_by_path(ctx, path);
882 GRN_FREE(path);
883 } else {
884 if (ctx->rc == GRN_SUCCESS) {
885 grn_plugin_set_name_resolve_error(ctx, name, "[plugin][unregister]");
886 }
887 rc = ctx->rc;
888 }
889 GRN_API_RETURN(rc);
890}
891
892void
893grn_plugin_ensure_registered(grn_ctx *ctx, grn_obj *proc)
894{
895#ifdef GRN_WITH_MRUBY
896 grn_id plugin_id;
897 grn_plugin *plugin = NULL;
898
899 if (!(proc->header.flags & GRN_OBJ_CUSTOM_NAME)) {
900 return;
901 }
902
903 plugin_id = DB_OBJ(proc)->range;
904 CRITICAL_SECTION_ENTER(grn_plugins_lock);
905 {
906 const char *value;
907 value = grn_hash_get_value_(&grn_plugins_ctx, grn_plugins, plugin_id, NULL);
908 if (value) {
909 plugin = *((grn_plugin **)value);
910 }
911 }
912 CRITICAL_SECTION_LEAVE(grn_plugins_lock);
913
914 if (!plugin) {
915 return;
916 }
917
918 if (plugin->dl) {
919 return;
920 }
921
922 grn_ctx_impl_mrb_ensure_init(ctx);
923 if (ctx->rc != GRN_SUCCESS) {
924 return;
925 }
926
927 if (!ctx->impl->mrb.state) {
928 return;
929 }
930
931 {
932 grn_id id;
933 int added;
934 id = DB_OBJ(proc)->id;
935 grn_hash_add(ctx, ctx->impl->mrb.checked_procs,
936 &id, sizeof(grn_id), NULL, &added);
937 if (!added) {
938 return;
939 }
940 }
941
942 ctx->impl->plugin_path = plugin->path;
943 grn_plugin_call_register_mrb(ctx, plugin_id, plugin);
944 ctx->impl->plugin_path = NULL;
945#endif /* GRN_WITH_MRUBY */
946}
947
948grn_rc
949grn_plugin_get_names(grn_ctx *ctx, grn_obj *names)
950{
951 grn_hash *processed_paths;
952 const char *system_plugins_dir;
953 const char *native_plugin_suffix;
954 const char *ruby_plugin_suffix;
955 grn_bool is_close_opened_object_mode = GRN_FALSE;
956
957 GRN_API_ENTER;
958
959 if (ctx->rc) {
960 GRN_API_RETURN(ctx->rc);
961 }
962
963 if (grn_thread_get_limit() == 1) {
964 is_close_opened_object_mode = GRN_TRUE;
965 }
966
967 processed_paths = grn_hash_create(ctx, NULL, GRN_TABLE_MAX_KEY_SIZE, 0,
968 GRN_OBJ_TABLE_HASH_KEY |
969 GRN_OBJ_KEY_VAR_SIZE);
970 if (!processed_paths) {
971 GRN_API_RETURN(ctx->rc);
972 }
973
974 system_plugins_dir = grn_plugin_get_system_plugins_dir();
975 native_plugin_suffix = grn_plugin_get_suffix();
976 ruby_plugin_suffix = grn_plugin_get_ruby_suffix();
977
978 GRN_TABLE_EACH_BEGIN_FLAGS(ctx, grn_ctx_db(ctx), cursor, id,
979 GRN_CURSOR_BY_ID | GRN_CURSOR_ASCENDING) {
980 void *name;
981 int name_size;
982 grn_obj *object;
983 const char *path;
984 grn_id processed_path_id;
985
986 if (grn_id_is_builtin(ctx, id)) {
987 continue;
988 }
989
990 name_size = grn_table_cursor_get_key(ctx, cursor, &name);
991 if (grn_obj_name_is_column(ctx, name, name_size)) {
992 continue;
993 }
994
995 if (is_close_opened_object_mode) {
996 grn_ctx_push_temporary_open_space(ctx);
997 }
998
999 object = grn_ctx_at(ctx, id);
1000 if (!object) {
1001 ERRCLR(ctx);
1002 goto next_loop;
1003 }
1004
1005 if (!grn_obj_is_proc(ctx, object)) {
1006 goto next_loop;
1007 }
1008
1009 path = grn_obj_path(ctx, object);
1010 if (!path) {
1011 goto next_loop;
1012 }
1013
1014 processed_path_id = grn_hash_get(ctx, processed_paths,
1015 path, strlen(path),
1016 NULL);
1017 if (processed_path_id != GRN_ID_NIL) {
1018 goto next_loop;
1019 }
1020
1021 grn_hash_add(ctx, processed_paths,
1022 path, strlen(path),
1023 NULL, NULL);
1024
1025 {
1026 const char *relative_path;
1027 const char *libs_path = "/.libs/";
1028 const char *start_libs;
1029 char name[PATH_MAX];
1030
1031 name[0] = '\0';
1032 if (strncmp(path, system_plugins_dir, strlen(system_plugins_dir)) == 0) {
1033 relative_path = path + strlen(system_plugins_dir);
1034 } else {
1035 relative_path = path;
1036 }
1037 start_libs = strstr(relative_path, libs_path);
1038 if (start_libs) {
1039 grn_strncat(name, PATH_MAX, relative_path, start_libs - relative_path);
1040 grn_strcat(name, PATH_MAX, "/");
1041 grn_strcat(name, PATH_MAX, start_libs + strlen(libs_path));
1042 } else {
1043 grn_strcat(name, PATH_MAX, relative_path);
1044 }
1045 if (strlen(name) > strlen(native_plugin_suffix) &&
1046 strcmp(name + strlen(name) - strlen(native_plugin_suffix),
1047 native_plugin_suffix) == 0) {
1048 name[strlen(name) - strlen(native_plugin_suffix)] = '\0';
1049 } else if (strlen(name) > strlen(ruby_plugin_suffix) &&
1050 strcmp(name + strlen(name) - strlen(ruby_plugin_suffix),
1051 ruby_plugin_suffix) == 0) {
1052 name[strlen(name) - strlen(ruby_plugin_suffix)] = '\0';
1053 }
1054 grn_vector_add_element(ctx, names,
1055 name, strlen(name),
1056 0, GRN_DB_TEXT);
1057 }
1058
1059 next_loop :
1060 if (is_close_opened_object_mode) {
1061 grn_ctx_pop_temporary_open_space(ctx);
1062 }
1063 } GRN_TABLE_EACH_END(ctx, cursor);
1064
1065 grn_hash_close(ctx, processed_paths);
1066
1067 GRN_API_RETURN(ctx->rc);
1068}
1069
1070void *
1071grn_plugin_malloc(grn_ctx *ctx, size_t size, const char *file, int line,
1072 const char *func)
1073{
1074 return grn_malloc(ctx, size, file, line, func);
1075}
1076
1077void *
1078grn_plugin_calloc(grn_ctx *ctx, size_t size, const char *file, int line,
1079 const char *func)
1080{
1081 return grn_calloc(ctx, size, file, line, func);
1082}
1083
1084void *
1085grn_plugin_realloc(grn_ctx *ctx, void *ptr, size_t size,
1086 const char *file, int line, const char *func)
1087{
1088 return grn_realloc(ctx, ptr, size, file, line, func);
1089}
1090
1091void
1092grn_plugin_free(grn_ctx *ctx, void *ptr, const char *file, int line,
1093 const char *func)
1094{
1095 grn_free(ctx, ptr, file, line, func);
1096}
1097
1098void
1099grn_plugin_set_error(grn_ctx *ctx, grn_log_level level, grn_rc error_code,
1100 const char *file, int line, const char *func,
1101 const char *format, ...)
1102{
1103 char old_error_message[GRN_CTX_MSGSIZE];
1104
1105 ctx->errlvl = level;
1106 ctx->rc = error_code;
1107 ctx->errfile = file;
1108 ctx->errline = line;
1109 ctx->errfunc = func;
1110
1111 grn_strcpy(old_error_message, GRN_CTX_MSGSIZE, ctx->errbuf);
1112
1113 {
1114 va_list ap;
1115 va_start(ap, format);
1116 grn_ctx_logv(ctx, format, ap);
1117 va_end(ap);
1118 }
1119
1120 if (grn_ctx_impl_should_log(ctx)) {
1121 grn_ctx_impl_set_current_error_message(ctx);
1122 if (grn_logger_pass(ctx, level)) {
1123 char new_error_message[GRN_CTX_MSGSIZE];
1124 grn_strcpy(new_error_message, GRN_CTX_MSGSIZE, ctx->errbuf);
1125 grn_strcpy(ctx->errbuf, GRN_CTX_MSGSIZE, old_error_message);
1126 {
1127 va_list ap;
1128 va_start(ap, format);
1129 grn_logger_putv(ctx, level, file, line, func, format, ap);
1130 va_end(ap);
1131 }
1132 grn_strcpy(ctx->errbuf, GRN_CTX_MSGSIZE, new_error_message);
1133 }
1134 if (level <= GRN_LOG_ERROR) {
1135 grn_plugin_logtrace(ctx, level);
1136 }
1137 }
1138}
1139
1140void
1141grn_plugin_clear_error(grn_ctx *ctx)
1142{
1143 ERRCLR(ctx);
1144}
1145
1146void
1147grn_plugin_backtrace(grn_ctx *ctx)
1148{
1149 BACKTRACE(ctx);
1150}
1151
1152void
1153grn_plugin_logtrace(grn_ctx *ctx, grn_log_level level)
1154{
1155 if (level <= GRN_LOG_ERROR) {
1156 grn_plugin_backtrace(ctx);
1157 LOGTRACE(ctx, level);
1158 }
1159}
1160
1161struct _grn_plugin_mutex {
1162 grn_critical_section critical_section;
1163};
1164
1165grn_plugin_mutex *
1166grn_plugin_mutex_open(grn_ctx *ctx)
1167{
1168 grn_plugin_mutex * const mutex =
1169 GRN_PLUGIN_MALLOC(ctx, sizeof(grn_plugin_mutex));
1170 if (mutex != NULL) {
1171 CRITICAL_SECTION_INIT(mutex->critical_section);
1172 }
1173 return mutex;
1174}
1175
1176grn_plugin_mutex *
1177grn_plugin_mutex_create(grn_ctx *ctx)
1178{
1179 return grn_plugin_mutex_open(ctx);
1180}
1181
1182void
1183grn_plugin_mutex_close(grn_ctx *ctx, grn_plugin_mutex *mutex)
1184{
1185 if (mutex != NULL) {
1186 CRITICAL_SECTION_FIN(mutex->critical_section);
1187 GRN_PLUGIN_FREE(ctx, mutex);
1188 }
1189}
1190
1191void
1192grn_plugin_mutex_destroy(grn_ctx *ctx, grn_plugin_mutex *mutex)
1193{
1194 grn_plugin_mutex_close(ctx, mutex);
1195}
1196
1197void
1198grn_plugin_mutex_lock(grn_ctx *ctx, grn_plugin_mutex *mutex)
1199{
1200 if (mutex != NULL) {
1201 CRITICAL_SECTION_ENTER(mutex->critical_section);
1202 }
1203}
1204
1205void
1206grn_plugin_mutex_unlock(grn_ctx *ctx, grn_plugin_mutex *mutex)
1207{
1208 if (mutex != NULL) {
1209 CRITICAL_SECTION_LEAVE(mutex->critical_section);
1210 }
1211}
1212
1213grn_obj *
1214grn_plugin_proc_alloc(grn_ctx *ctx, grn_user_data *user_data,
1215 grn_id domain, unsigned char flags)
1216{
1217 return grn_proc_alloc(ctx, user_data, domain, flags);
1218}
1219
1220grn_obj *
1221grn_plugin_proc_get_vars(grn_ctx *ctx, grn_user_data *user_data)
1222{
1223 return grn_proc_get_vars(ctx, user_data);
1224}
1225
1226grn_obj *
1227grn_plugin_proc_get_var(grn_ctx *ctx, grn_user_data *user_data,
1228 const char *name, int name_size)
1229{
1230 name_size = compute_name_size(name, name_size);
1231 return grn_proc_get_var(ctx, user_data, name, name_size);
1232}
1233
1234grn_bool
1235grn_plugin_proc_get_var_bool(grn_ctx *ctx,
1236 grn_user_data *user_data,
1237 const char *name,
1238 int name_size,
1239 grn_bool default_value)
1240{
1241 grn_obj *var;
1242
1243 var = grn_plugin_proc_get_var(ctx, user_data, name, name_size);
1244 return grn_proc_option_value_bool(ctx, var, default_value);
1245}
1246
1247int32_t
1248grn_plugin_proc_get_var_int32(grn_ctx *ctx,
1249 grn_user_data *user_data,
1250 const char *name,
1251 int name_size,
1252 int32_t default_value)
1253{
1254 grn_obj *var;
1255
1256 var = grn_plugin_proc_get_var(ctx, user_data, name, name_size);
1257 return grn_proc_option_value_int32(ctx, var, default_value);
1258}
1259
1260const char *
1261grn_plugin_proc_get_var_string(grn_ctx *ctx,
1262 grn_user_data *user_data,
1263 const char *name,
1264 int name_size,
1265 size_t *size)
1266{
1267 grn_obj *var;
1268
1269 var = grn_plugin_proc_get_var(ctx, user_data, name, name_size);
1270 return grn_proc_option_value_string(ctx, var, size);
1271}
1272
1273grn_content_type
1274grn_plugin_proc_get_var_content_type(grn_ctx *ctx,
1275 grn_user_data *user_data,
1276 const char *name,
1277 int name_size,
1278 grn_content_type default_value)
1279{
1280 grn_obj *var;
1281
1282 var = grn_plugin_proc_get_var(ctx, user_data, name, name_size);
1283 return grn_proc_option_value_content_type(ctx, var, default_value);
1284}
1285
1286grn_obj *
1287grn_plugin_proc_get_var_by_offset(grn_ctx *ctx, grn_user_data *user_data,
1288 unsigned int offset)
1289{
1290 return grn_proc_get_var_by_offset(ctx, user_data, offset);
1291}
1292
1293grn_obj *
1294grn_plugin_proc_get_caller(grn_ctx *ctx, grn_user_data *user_data)
1295{
1296 grn_obj *caller = NULL;
1297 GRN_API_ENTER;
1298 grn_proc_get_info(ctx, user_data, NULL, NULL, &caller);
1299 GRN_API_RETURN(caller);
1300}
1301
1302const char *
1303grn_plugin_win32_base_dir(void)
1304{
1305 return grn_plugin_windows_base_dir();
1306}
1307
1308const char *
1309grn_plugin_windows_base_dir(void)
1310{
1311#ifdef WIN32
1312 return grn_windows_base_dir();
1313#else /* WIN32 */
1314 return NULL;
1315#endif /* WIN32 */
1316}
1317
1318/*
1319 grn_plugin_charlen() takes the length of a string, unlike grn_charlen_().
1320 */
1321int
1322grn_plugin_charlen(grn_ctx *ctx, const char *str_ptr,
1323 unsigned int str_length, grn_encoding encoding)
1324{
1325 return grn_charlen_(ctx, str_ptr, str_ptr + str_length, encoding);
1326}
1327
1328/*
1329 grn_plugin_isspace() takes the length of a string, unlike grn_isspace().
1330 */
1331int
1332grn_plugin_isspace(grn_ctx *ctx, const char *str_ptr,
1333 unsigned int str_length, grn_encoding encoding)
1334{
1335 if ((str_ptr == NULL) || (str_length == 0)) {
1336 return 0;
1337 }
1338 switch ((unsigned char)str_ptr[0]) {
1339 case ' ' :
1340 case '\f' :
1341 case '\n' :
1342 case '\r' :
1343 case '\t' :
1344 case '\v' :
1345 return 1;
1346 case 0x81 :
1347 if ((encoding == GRN_ENC_SJIS) && (str_length >= 2) &&
1348 ((unsigned char)str_ptr[1] == 0x40)) {
1349 return 2;
1350 }
1351 break;
1352 case 0xA1 :
1353 if ((encoding == GRN_ENC_EUC_JP) && (str_length >= 2) &&
1354 ((unsigned char)str_ptr[1] == 0xA1)) {
1355 return 2;
1356 }
1357 break;
1358 case 0xE3 :
1359 if ((encoding == GRN_ENC_UTF8) && (str_length >= 3) &&
1360 ((unsigned char)str_ptr[1] == 0x80) &&
1361 ((unsigned char)str_ptr[2] == 0x80)) {
1362 return 3;
1363 }
1364 break;
1365 default :
1366 break;
1367 }
1368 return 0;
1369}
1370
1371grn_rc
1372grn_plugin_expr_var_init(grn_ctx *ctx,
1373 grn_expr_var *var,
1374 const char *name,
1375 int name_size)
1376{
1377 var->name = name;
1378 var->name_size = compute_name_size(name, name_size);
1379 GRN_TEXT_INIT(&var->value, 0);
1380 return GRN_SUCCESS;
1381}
1382
1383grn_obj *
1384grn_plugin_command_create(grn_ctx *ctx,
1385 const char *name,
1386 int name_size,
1387 grn_proc_func func,
1388 unsigned int n_vars,
1389 grn_expr_var *vars)
1390{
1391 grn_obj *proc;
1392 name_size = compute_name_size(name, name_size);
1393 proc = grn_proc_create(ctx, name, name_size, GRN_PROC_COMMAND,
1394 func, NULL, NULL, n_vars, vars);
1395 return proc;
1396}
1397