Groonga 3.0.9 Source Code Document
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
plugin.c
Go to the documentation of this file.
1 /* -*- c-basic-offset: 2 -*- */
2 /*
3  Copyright(C) 2012-2013 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 "groonga_in.h"
19 #include "groonga/plugin.h"
20 
21 #include <stdarg.h>
22 #include <stdio.h>
23 #include <string.h>
24 
25 #include "db.h"
26 #include "plugin_in.h"
27 #include "ctx_impl.h"
28 #include "util.h"
29 
30 static grn_hash *grn_plugins = NULL;
31 static grn_critical_section grn_plugins_lock;
32 
33 #define PATHLEN(filename) (strlen(filename) + 1)
34 
35 #ifdef HAVE_DLFCN_H
36 # include <dlfcn.h>
37 # define grn_dl_open(filename) dlopen(filename, RTLD_LAZY | RTLD_LOCAL)
38 # define grn_dl_open_error_label() dlerror()
39 # define grn_dl_close(dl) (dlclose(dl) == 0)
40 # define grn_dl_close_error_label() dlerror()
41 # define grn_dl_sym(dl, symbol) dlsym(dl, symbol)
42 # define grn_dl_sym_error_label() dlerror()
43 # define grn_dl_clear_error() dlerror()
44 #else
45 # define grn_dl_open(filename) LoadLibrary(filename)
46 # define grn_dl_open_error_label() "LoadLibrary"
47 # define grn_dl_close(dl) (FreeLibrary(dl) != 0)
48 # define grn_dl_close_error_label() "FreeLibrary"
49 # define grn_dl_sym(dl, symbol) ((void *)GetProcAddress(dl, symbol))
50 # define grn_dl_sym_error_label() "GetProcAddress"
51 # define grn_dl_clear_error()
52 #endif
53 
54 grn_id
55 grn_plugin_reference(grn_ctx *ctx, const char *filename)
56 {
57  grn_id id;
58  grn_plugin **plugin = NULL;
59 
60  CRITICAL_SECTION_ENTER(grn_plugins_lock);
61  id = grn_hash_get(&grn_gctx, grn_plugins, filename, PATHLEN(filename),
62  (void **)&plugin);
63  if (plugin) {
64  (*plugin)->refcount++;
65  }
66  CRITICAL_SECTION_LEAVE(grn_plugins_lock);
67 
68  return id;
69 }
70 
71 const char *
73 {
74  const char *path;
75  uint32_t key_size;
76  const char *system_plugins_dir;
77  size_t system_plugins_dir_size;
78 
79  CRITICAL_SECTION_ENTER(grn_plugins_lock);
80  path = _grn_hash_key(&grn_gctx, grn_plugins, id, &key_size);
81  CRITICAL_SECTION_LEAVE(grn_plugins_lock);
82 
83  if (!path) {
84  return NULL;
85  }
86 
87  system_plugins_dir = grn_plugin_get_system_plugins_dir();
88  system_plugins_dir_size = strlen(system_plugins_dir);
89  if (strncmp(system_plugins_dir, path, system_plugins_dir_size) == 0) {
90  const char *plugin_name = path + system_plugins_dir_size;
91  while (plugin_name[0] == '/') {
92  plugin_name++;
93  }
94  /* TODO: remove suffix too? */
95  return plugin_name;
96  } else {
97  return path;
98  }
99 }
100 
101 #define GRN_PLUGIN_FUNC_PREFIX "grn_plugin_impl_"
102 
103 static grn_rc
104 grn_plugin_call_init (grn_ctx *ctx, grn_id id)
105 {
106  grn_plugin *plugin;
107  if (!grn_hash_get_value(&grn_gctx, grn_plugins, id, &plugin)) {
108  return GRN_INVALID_ARGUMENT;
109  }
110  if (plugin->init_func) {
111  return plugin->init_func(ctx);
112  }
113  return GRN_SUCCESS;
114 }
115 
116 static grn_rc
117 grn_plugin_call_register(grn_ctx *ctx, grn_id id)
118 {
119  grn_plugin *plugin;
120  if (!grn_hash_get_value(&grn_gctx, grn_plugins, id, &plugin)) {
121  return GRN_INVALID_ARGUMENT;
122  }
123  if (plugin->register_func) {
124  return plugin->register_func(ctx);
125  }
126  return GRN_SUCCESS;
127 }
128 
129 static grn_rc
130 grn_plugin_call_fin(grn_ctx *ctx, grn_id id)
131 {
132  grn_plugin *plugin;
133  if (!grn_hash_get_value(&grn_gctx, grn_plugins, id, &plugin)) {
134  return GRN_INVALID_ARGUMENT;
135  }
136  if (plugin->fin_func) {
137  return plugin->fin_func(ctx);
138  }
139  return GRN_SUCCESS;
140 }
141 
142 static grn_rc
143 grn_plugin_initialize(grn_ctx *ctx, grn_plugin *plugin,
144  grn_dl dl, grn_id id, const char *path)
145 {
146  plugin->dl = dl;
147 
148 #define GET_SYMBOL(type) do { \
149  grn_dl_clear_error(); \
150  plugin->type ## _func = grn_dl_sym(dl, GRN_PLUGIN_FUNC_PREFIX #type); \
151  if (!plugin->type ## _func) { \
152  const char *label; \
153  label = grn_dl_sym_error_label(); \
154  SERR(label); \
155  } \
156 } while (0)
157 
158  GET_SYMBOL(init);
159  GET_SYMBOL(register);
160  GET_SYMBOL(fin);
161 
162 #undef GET_SYMBOL
163 
164  if (!plugin->init_func || !plugin->register_func || !plugin->fin_func) {
166  "init func (%s) %sfound, "
167  "register func (%s) %sfound and "
168  "fin func (%s) %sfound",
169  GRN_PLUGIN_FUNC_PREFIX "init", plugin->init_func ? "" : "not ",
170  GRN_PLUGIN_FUNC_PREFIX "register", plugin->register_func ? "" : "not ",
171  GRN_PLUGIN_FUNC_PREFIX "fin", plugin->fin_func ? "" : "not ");
172  }
173 
174  if (!ctx->rc) {
175  ctx->impl->plugin_path = path;
176  grn_plugin_call_init(ctx, id);
177  ctx->impl->plugin_path = NULL;
178  }
179 
180  return ctx->rc;
181 }
182 
183 grn_id
184 grn_plugin_open(grn_ctx *ctx, const char *filename)
185 {
186  grn_id id;
187  grn_dl dl;
188  grn_plugin **plugin = NULL;
189 
190  CRITICAL_SECTION_ENTER(grn_plugins_lock);
191  if ((id = grn_hash_get(&grn_gctx, grn_plugins, filename, PATHLEN(filename),
192  (void **)&plugin))) {
193  (*plugin)->refcount++;
194  goto exit;
195  }
196 
197  if ((dl = grn_dl_open(filename))) {
198  if ((id = grn_hash_add(&grn_gctx, grn_plugins, filename, PATHLEN(filename),
199  (void **)&plugin, NULL))) {
200  *plugin = GRN_GMALLOCN(grn_plugin, 1);
201  if (*plugin) {
202  if (grn_plugin_initialize(ctx, *plugin, dl, id, filename)) {
203  GRN_GFREE(*plugin);
204  *plugin = NULL;
205  }
206  }
207  if (!*plugin) {
208  grn_hash_delete_by_id(&grn_gctx, grn_plugins, id, NULL);
209  if (grn_dl_close(dl)) {
210  /* Now, __FILE__ set in plugin is invalid. */
211  ctx->errline = 0;
212  ctx->errfile = NULL;
213  } else {
214  const char *label;
215  label = grn_dl_close_error_label();
216  SERR(label);
217  }
218  id = GRN_ID_NIL;
219  } else {
220  (*plugin)->refcount = 1;
221  }
222  } else {
223  if (!grn_dl_close(dl)) {
224  const char *label;
225  label = grn_dl_close_error_label();
226  SERR(label);
227  }
228  }
229  } else {
230  const char *label;
231  label = grn_dl_open_error_label();
232  SERR(label);
233  }
234 
235 exit:
236  CRITICAL_SECTION_LEAVE(grn_plugins_lock);
237 
238  return id;
239 }
240 
241 grn_rc
243 {
244  grn_rc rc;
245  grn_plugin *plugin;
246 
247  if (id == GRN_ID_NIL) {
248  return GRN_INVALID_ARGUMENT;
249  }
250 
251  CRITICAL_SECTION_ENTER(grn_plugins_lock);
252  if (!grn_hash_get_value(&grn_gctx, grn_plugins, id, &plugin)) {
254  goto exit;
255  }
256  if (--plugin->refcount) {
257  rc = GRN_SUCCESS;
258  goto exit;
259  }
260  grn_plugin_call_fin(ctx, id);
261  if (!grn_dl_close(plugin->dl)) {
262  const char *label;
263  label = grn_dl_close_error_label();
264  SERR(label);
265  }
266  GRN_GFREE(plugin);
267  rc = grn_hash_delete_by_id(&grn_gctx, grn_plugins, id, NULL);
268 
269 exit:
270  CRITICAL_SECTION_LEAVE(grn_plugins_lock);
271 
272  return rc;
273 }
274 
275 void *
276 grn_plugin_sym(grn_ctx *ctx, grn_id id, const char *symbol)
277 {
278  grn_plugin *plugin;
279  grn_dl_symbol func;
280 
281  if (id == GRN_ID_NIL) {
282  return NULL;
283  }
284 
285  CRITICAL_SECTION_ENTER(grn_plugins_lock);
286  if (!grn_hash_get_value(&grn_gctx, grn_plugins, id, &plugin)) {
287  func = NULL;
288  goto exit;
289  }
291  if (!(func = grn_dl_sym(plugin->dl, symbol))) {
292  const char *label;
293  label = grn_dl_sym_error_label();
294  SERR(label);
295  }
296 
297 exit:
298  CRITICAL_SECTION_LEAVE(grn_plugins_lock);
299 
300  return func;
301 }
302 
303 grn_rc
305 {
306  CRITICAL_SECTION_INIT(grn_plugins_lock);
307  grn_plugins = grn_hash_create(&grn_gctx, NULL, PATH_MAX, sizeof(grn_plugin *),
309  if (!grn_plugins) { return GRN_NO_MEMORY_AVAILABLE; }
310  return GRN_SUCCESS;
311 }
312 
313 grn_rc
315 {
316  grn_rc rc;
317  if (!grn_plugins) { return GRN_INVALID_ARGUMENT; }
318  GRN_HASH_EACH(&grn_gctx, grn_plugins, id, NULL, NULL, NULL, {
320  });
321  rc = grn_hash_close(&grn_gctx, grn_plugins);
322  CRITICAL_SECTION_FIN(grn_plugins_lock);
323  return rc;
324 }
325 
326 const char *
328 {
329  return GRN_PLUGIN_SUFFIX;
330 }
331 
332 grn_rc
333 grn_plugin_register_by_path(grn_ctx *ctx, const char *path)
334 {
335  grn_obj *db;
336  if (!ctx || !ctx->impl || !(db = ctx->impl->db)) {
337  ERR(GRN_INVALID_ARGUMENT, "db not initialized");
338  return ctx->rc;
339  }
341  if (GRN_DB_P(db)) {
342  grn_id id;
343  id = grn_plugin_open(ctx, path);
344  if (id) {
345  ctx->impl->plugin_path = path;
346  ctx->rc = grn_plugin_call_register(ctx, id);
347  ctx->impl->plugin_path = NULL;
348  grn_plugin_close(ctx, id);
349  }
350  } else {
351  ERR(GRN_INVALID_ARGUMENT, "invalid db assigned");
352  }
353  GRN_API_RETURN(ctx->rc);
354 }
355 
356 #ifdef WIN32
357 static char *win32_plugins_dir = NULL;
358 static char win32_plugins_dir_buffer[PATH_MAX];
359 const char *
361 {
362  if (!win32_plugins_dir) {
363  const char *base_dir;
364  const char *relative_path = GRN_RELATIVE_PLUGINS_DIR;
365  char *path;
366  size_t base_dir_length;
367 
368  base_dir = grn_win32_base_dir();
369  base_dir_length = strlen(base_dir);
370  strcpy(win32_plugins_dir_buffer, base_dir);
371  strcat(win32_plugins_dir_buffer, "/");
372  strcat(win32_plugins_dir_buffer, relative_path);
373  win32_plugins_dir = win32_plugins_dir_buffer;
374  }
375  return win32_plugins_dir;
376 }
377 
378 #else /* WIN32 */
379 const char *
381 {
382  return GRN_PLUGINS_DIR;
383 }
384 #endif /* WIN32 */
385 
386 char *
387 grn_plugin_find_path(grn_ctx *ctx, const char *name)
388 {
389  const char *plugins_dir;
390  char dir_last_char;
391  char path[PATH_MAX];
392  int name_length, max_name_length;
393  FILE *plugin_file;
394  char complemented_path[PATH_MAX], complemented_libs_path[PATH_MAX];
395  char *found_path = NULL;
396  size_t path_len;
397 
399  if (name[0] == '/') {
400  path[0] = '\0';
401  } else {
402  plugins_dir = getenv("GRN_PLUGINS_DIR");
403  if (!plugins_dir) {
404  plugins_dir = grn_plugin_get_system_plugins_dir();
405  }
406  strcpy(path, plugins_dir);
407 
408  dir_last_char = plugins_dir[strlen(path) - 1];
409  if (dir_last_char != '/') {
410  strcat(path, "/");
411  }
412  }
413 
414  name_length = strlen(name);
415  max_name_length = PATH_MAX - strlen(path) - 1;
416  if (name_length > max_name_length) {
418  "plugin name is too long: %d (max: %d) <%s%s>",
419  name_length, max_name_length,
420  path, name);
421  goto exit;
422  }
423  strcat(path, name);
424 
425  plugin_file = fopen(path, "r");
426  if (plugin_file) {
427  fclose(plugin_file);
428  found_path = GRN_STRDUP(path);
429  } else {
430  path_len = strlen(path);
431  path_len += strlen(grn_plugin_get_suffix());
432  if (path_len >= PATH_MAX) {
434  "too long plugin path: <%s%s>",
435  path, grn_plugin_get_suffix());
436  goto exit;
437  }
438  strcpy(complemented_path, path);
439  strcat(complemented_path, grn_plugin_get_suffix());
440  plugin_file = fopen(complemented_path, "r");
441  if (plugin_file) {
442  fclose(plugin_file);
443  found_path = GRN_STRDUP(complemented_path);
444  } else {
445  const char *base_name;
446 
447  base_name = strrchr(path, '/');
448  if (base_name) {
449  path_len = base_name - path + strlen("/.libs") + strlen(base_name);
450  path_len += strlen(grn_plugin_get_suffix());
451  if (path_len >= PATH_MAX) {
453  "too long plugin path: <%.*s/.libs%s%s>",
454  (int)(base_name - path), path, base_name, grn_plugin_get_suffix());
455  goto exit;
456  }
457  complemented_libs_path[0] = '\0';
458  strncat(complemented_libs_path, path, base_name - path);
459  strcat(complemented_libs_path, "/.libs");
460  strcat(complemented_libs_path, base_name);
461  strcat(complemented_libs_path, grn_plugin_get_suffix());
462  plugin_file = fopen(complemented_libs_path, "r");
463  if (plugin_file) {
464  fclose(plugin_file);
465  found_path = GRN_STRDUP(complemented_libs_path);
466  }
467  }
468  }
469  }
470 
471 exit :
472  GRN_API_RETURN(found_path);
473 }
474 
475 grn_rc
476 grn_plugin_register(grn_ctx *ctx, const char *name)
477 {
478  grn_rc rc;
479  char *path;
480 
482  path = grn_plugin_find_path(ctx, name);
483  if (path) {
484  rc = grn_plugin_register_by_path(ctx, path);
485  GRN_FREE(path);
486  } else {
487  if (ctx->rc == GRN_SUCCESS) {
488  const char *prefix, *prefix_separator, *suffix;
489  if (name[0] == '/') {
490  prefix = "";
491  prefix_separator = "";
492  suffix = "";
493  } else {
494  prefix = getenv("GRN_PLUGINS_DIR");
495  if (!prefix) {
497  }
498  if (prefix[strlen(prefix) - 1] != '/') {
499  prefix_separator = "/";
500  } else {
501  prefix_separator = "";
502  }
503  suffix = grn_plugin_get_suffix();
504  }
506  "cannot find plugin file: <%s%s%s%s>",
507  prefix, prefix_separator, name, suffix);
508  }
509  rc = ctx->rc;
510  }
511  GRN_API_RETURN(rc);
512 }
513 
514 void *
515 grn_plugin_malloc(grn_ctx *ctx, size_t size, const char *file, int line,
516  const char *func)
517 {
518  return grn_malloc(ctx, size, file, line, func);
519 }
520 
521 void *
522 grn_plugin_realloc(grn_ctx *ctx, void *ptr, size_t size,
523  const char *file, int line, const char *func)
524 {
525  return grn_realloc(ctx, ptr, size, file, line, func);
526 }
527 
528 void
529 grn_plugin_free(grn_ctx *ctx, void *ptr, const char *file, int line,
530  const char *func)
531 {
532  grn_free(ctx, ptr, file, line, func);
533 }
534 
535 /*
536  grn_plugin_ctx_log() is a clone of grn_ctx_log() in ctx.c. The only
537  difference is that grn_plugin_ctx_log() uses va_list instead of `...'.
538  */
539 static void
540 grn_plugin_ctx_log(grn_ctx *ctx, const char *format, va_list ap)
541 {
542  vsnprintf(ctx->errbuf, GRN_CTX_MSGSIZE, format, ap);
543 }
544 
545 void
547  const char *file, int line, const char *func,
548  const char *format, ...)
549 {
550  ctx->errlvl = level;
551  ctx->rc = error_code;
552  ctx->errfile = file;
553  ctx->errline = line;
554  ctx->errfunc = func;
555  grn_ctx_impl_err(ctx);
556 
557  {
558  va_list ap;
559  va_start(ap, format);
560  grn_plugin_ctx_log(ctx, format, ap);
561  va_end(ap);
562  }
563 }
564 
565 void
567 {
568  BACKTRACE(ctx);
569 }
570 
571 void
573 {
574  if (level <= GRN_LOG_ERROR) {
575  LOGTRACE(ctx, level);
576  }
577 }
578 
580  grn_critical_section critical_section;
581 };
582 
585 {
586  grn_plugin_mutex * const mutex =
587  GRN_PLUGIN_MALLOC(ctx, sizeof(grn_plugin_mutex));
588  if (mutex != NULL) {
589  CRITICAL_SECTION_INIT(mutex->critical_section);
590  }
591  return mutex;
592 }
593 
596 {
597  return grn_plugin_mutex_open(ctx);
598 }
599 
600 void
602 {
603  if (mutex != NULL) {
604  CRITICAL_SECTION_FIN(mutex->critical_section);
605  GRN_PLUGIN_FREE(ctx, mutex);
606  }
607 }
608 
609 void
611 {
612  grn_plugin_mutex_close(ctx, mutex);
613 }
614 
615 void
617 {
618  if (mutex != NULL) {
619  CRITICAL_SECTION_ENTER(mutex->critical_section);
620  }
621 }
622 
623 void
625 {
626  if (mutex != NULL) {
627  CRITICAL_SECTION_LEAVE(mutex->critical_section);
628  }
629 }
630 
631 grn_obj *
633  grn_id domain, grn_obj_flags flags)
634 {
635  return grn_proc_alloc(ctx, user_data, domain, flags);
636 }
637 
638 const char *
640 {
641 #ifdef WIN32
642  return grn_win32_base_dir();
643 #else /* WIN32 */
644  return NULL;
645 #endif /* WIN32 */
646 }
647 
648 /*
649  grn_plugin_charlen() takes the length of a string, unlike grn_charlen_().
650  */
651 int
652 grn_plugin_charlen(grn_ctx *ctx, const char *str_ptr,
653  unsigned int str_length, grn_encoding encoding)
654 {
655  return grn_charlen_(ctx, str_ptr, str_ptr + str_length, encoding);
656 }
657 
658 /*
659  grn_plugin_isspace() takes the length of a string, unlike grn_isspace().
660  */
661 int
662 grn_plugin_isspace(grn_ctx *ctx, const char *str_ptr,
663  unsigned int str_length, grn_encoding encoding)
664 {
665  if ((str_ptr == NULL) || (str_length == 0)) {
666  return 0;
667  }
668  switch ((unsigned char)str_ptr[0]) {
669  case ' ' :
670  case '\f' :
671  case '\n' :
672  case '\r' :
673  case '\t' :
674  case '\v' :
675  return 1;
676  case 0x81 :
677  if ((encoding == GRN_ENC_SJIS) && (str_length >= 2) &&
678  ((unsigned char)str_ptr[1] == 0x40)) {
679  return 2;
680  }
681  break;
682  case 0xA1 :
683  if ((encoding == GRN_ENC_EUC_JP) && (str_length >= 2) &&
684  ((unsigned char)str_ptr[1] == 0xA1)) {
685  return 2;
686  }
687  break;
688  case 0xE3 :
689  if ((encoding == GRN_ENC_UTF8) && (str_length >= 3) &&
690  ((unsigned char)str_ptr[1] == 0x80) &&
691  ((unsigned char)str_ptr[2] == 0x80)) {
692  return 3;
693  }
694  break;
695  default :
696  break;
697  }
698  return 0;
699 }