00001
00021 #ifdef HAVE_CONFIG_H
00022 #include "config.h"
00023 #endif
00024
00025 #include <dlfcn.h>
00026 #include <stdio.h>
00027 #include <stdlib.h>
00028 #include <string.h>
00029 #include <dirent.h>
00030 #include <errno.h>
00031
00032 #include "lightmediascanner.h"
00033 #include "lightmediascanner_private.h"
00034 #include "lightmediascanner_plugin.h"
00035
00036 #define DEFAULT_SLAVE_TIMEOUT 1000
00037 #define DEFAULT_COMMIT_INTERVAL 100
00038
00039 static int
00040 _parser_load(struct parser *p, const char *so_path)
00041 {
00042 lms_plugin_t *(*plugin_open)(void);
00043 char *errmsg;
00044
00045 memset(p, 0, sizeof(*p));
00046
00047 p->dl_handle = dlopen(so_path, RTLD_NOW | RTLD_LOCAL);
00048 errmsg = dlerror();
00049 if (errmsg) {
00050 fprintf(stderr, "ERROR: could not dlopen() %s\n", errmsg);
00051 return -1;
00052 }
00053
00054 plugin_open = dlsym(p->dl_handle, "lms_plugin_open");
00055 errmsg = dlerror();
00056 if (errmsg) {
00057 fprintf(stderr, "ERROR: could not find plugin entry point %s\n",
00058 errmsg);
00059 return -2;
00060 }
00061
00062 p->so_path = strdup(so_path);
00063 if (!p->so_path) {
00064 perror("strdup");
00065 return -3;
00066 }
00067
00068 p->plugin = plugin_open();
00069 if (!p->plugin) {
00070 fprintf(stderr, "ERROR: plugin \"%s\" failed to init.\n", so_path);
00071 return -4;
00072 }
00073
00074 return 0;
00075 }
00076
00077 static int
00078 _parser_unload(struct parser *p)
00079 {
00080 int r;
00081
00082 r = 0;
00083 if (p->plugin) {
00084 if (p->plugin->close(p->plugin) != 0) {
00085 fprintf(stderr, "ERROR: plugin \"%s\" failed to deinit.\n",
00086 p->so_path);
00087 r -= 1;
00088 }
00089 }
00090
00091 if (p->dl_handle) {
00092 char *errmsg;
00093
00094 dlclose(p->dl_handle);
00095 errmsg = dlerror();
00096 if (errmsg) {
00097 fprintf(stderr, "ERROR: could not dlclose() plugin \"%s\": %s\n",
00098 errmsg, p->so_path);
00099 r -= 1;
00100 }
00101 }
00102
00103 if (p->so_path)
00104 free(p->so_path);
00105
00106 return r;
00107 }
00108
00109
00110
00111
00112
00120 lms_t *
00121 lms_new(const char *db_path)
00122 {
00123 lms_t *lms;
00124
00125 lms = calloc(1, sizeof(lms_t));
00126 if (!lms) {
00127 perror("calloc");
00128 return NULL;
00129 }
00130
00131 lms->cs_conv = lms_charset_conv_new();
00132 if (!lms->cs_conv) {
00133 free(lms);
00134 return NULL;
00135 }
00136
00137 lms->commit_interval = DEFAULT_COMMIT_INTERVAL;
00138 lms->slave_timeout = DEFAULT_SLAVE_TIMEOUT;
00139 lms->db_path = strdup(db_path);
00140 if (!lms->db_path) {
00141 perror("strdup");
00142 lms_charset_conv_free(lms->cs_conv);
00143 free(lms);
00144 return NULL;
00145 }
00146
00147 return lms;
00148 }
00149
00158 int
00159 lms_free(lms_t *lms)
00160 {
00161 int i;
00162
00163 if (!lms)
00164 return 0;
00165
00166 if (lms->is_processing)
00167 return -1;
00168
00169 if (lms->parsers) {
00170 for (i = 0; i < lms->n_parsers; i++)
00171 _parser_unload(lms->parsers + i);
00172
00173 free(lms->parsers);
00174 }
00175
00176 if (lms->progress.data && lms->progress.free_data)
00177 lms->progress.free_data(lms->progress.data);
00178
00179 free(lms->db_path);
00180 lms_charset_conv_free(lms->cs_conv);
00181 free(lms);
00182 return 0;
00183 }
00184
00194 void
00195 lms_set_progress_callback(lms_t *lms, lms_progress_callback_t cb, const void *data, lms_free_callback_t free_data)
00196 {
00197 if (!lms) {
00198 if (data && free_data)
00199 free_data((void *)data);
00200 return;
00201 }
00202
00203 if (lms->progress.data && lms->progress.free_data)
00204 lms->progress.free_data(lms->progress.data);
00205
00206 lms->progress.cb = cb;
00207 lms->progress.data = (void *)data;
00208 lms->progress.free_data = free_data;
00209 }
00210
00220 lms_plugin_t *
00221 lms_parser_add(lms_t *lms, const char *so_path)
00222 {
00223 struct parser *parser;
00224
00225 if (!lms)
00226 return NULL;
00227
00228 if (!so_path)
00229 return NULL;
00230
00231 if (lms->is_processing) {
00232 fprintf(stderr, "ERROR: do not add parsers while it's processing.\n");
00233 return NULL;
00234 }
00235
00236 lms->parsers = realloc(lms->parsers,
00237 (lms->n_parsers + 1) * sizeof(struct parser));
00238 if (!lms->parsers) {
00239 perror("realloc");
00240 return NULL;
00241 }
00242
00243 parser = lms->parsers + lms->n_parsers;
00244 if (_parser_load(parser, so_path) != 0) {
00245 _parser_unload(parser);
00246 return NULL;
00247 }
00248
00249 lms->n_parsers++;
00250 return parser->plugin;
00251 }
00252
00253 static int
00254 lms_parser_find(char *buf, int buf_size, const char *name)
00255 {
00256 int r;
00257
00258 r = snprintf(buf, buf_size, "%s/%s.so", PLUGINSDIR, name);
00259 if (r >= buf_size)
00260 return 0;
00261
00262 return 1;
00263 }
00264
00265
00278 lms_plugin_t *
00279 lms_parser_find_and_add(lms_t *lms, const char *name)
00280 {
00281 char so_path[PATH_MAX];
00282
00283 if (!lms)
00284 return NULL;
00285 if (!name)
00286 return NULL;
00287
00288 if (!lms_parser_find(so_path, sizeof(so_path), name))
00289 return NULL;
00290 return lms_parser_add(lms, so_path);
00291 }
00292
00293 int
00294 lms_parser_del_int(lms_t *lms, int i)
00295 {
00296 struct parser *parser;
00297
00298 parser = lms->parsers + i;
00299 _parser_unload(parser);
00300 lms->n_parsers--;
00301
00302 if (lms->n_parsers == 0) {
00303 free(lms->parsers);
00304 lms->parsers = NULL;
00305 return 0;
00306 } else {
00307 int dif;
00308
00309 dif = lms->n_parsers - i;
00310 if (dif)
00311 lms->parsers = memmove(parser, parser + 1,
00312 dif * sizeof(struct parser));
00313
00314 lms->parsers = realloc(lms->parsers,
00315 lms->n_parsers * sizeof(struct parser));
00316 if (!lms->parsers) {
00317 lms->n_parsers = 0;
00318 return -1;
00319 }
00320
00321 return 0;
00322 }
00323 }
00324
00333 int
00334 lms_parser_del(lms_t *lms, lms_plugin_t *handle)
00335 {
00336 int i;
00337
00338 if (!lms)
00339 return -1;
00340 if (!handle)
00341 return -2;
00342 if (!lms->parsers)
00343 return -3;
00344 if (lms->is_processing) {
00345 fprintf(stderr, "ERROR: do not del parsers while it's processing.\n");
00346 return -4;
00347 }
00348
00349 for (i = 0; i < lms->n_parsers; i++)
00350 if (lms->parsers[i].plugin == handle)
00351 return lms_parser_del_int(lms, i);
00352
00353 return -3;
00354 }
00355
00365 int
00366 lms_is_processing(const lms_t *lms)
00367 {
00368 if (!lms) {
00369 fprintf(stderr, "ERROR: lms_is_processing(NULL)\n");
00370 return -1;
00371 }
00372
00373 return lms->is_processing;
00374 }
00375
00384 const char *
00385 lms_get_db_path(const lms_t *lms)
00386 {
00387 if (!lms) {
00388 fprintf(stderr, "ERROR: lms_get_db_path(NULL)\n");
00389 return NULL;
00390 }
00391
00392 return lms->db_path;
00393 }
00394
00406 int
00407 lms_get_slave_timeout(const lms_t *lms)
00408 {
00409 if (!lms) {
00410 fprintf(stderr, "ERROR: lms_get_slave_timeout(NULL)\n");
00411 return -1;
00412 }
00413
00414 return lms->slave_timeout;
00415 }
00416
00427 void lms_set_slave_timeout(lms_t *lms, int ms)
00428 {
00429 if (!lms) {
00430 fprintf(stderr, "ERROR: lms_set_slave_timeout(NULL, %d)\n", ms);
00431 return;
00432 }
00433
00434 lms->slave_timeout = ms;
00435 }
00436
00448 unsigned int
00449 lms_get_commit_interval(const lms_t *lms)
00450 {
00451 if (!lms) {
00452 fprintf(stderr, "ERROR: lms_get_commit_interval(NULL)\n");
00453 return (unsigned int)-1;
00454 }
00455
00456 return lms->commit_interval;
00457 }
00458
00471 void
00472 lms_set_commit_interval(lms_t *lms, unsigned int transactions)
00473 {
00474 if (!lms) {
00475 fprintf(stderr, "ERROR: lms_set_commit_interval(NULL, %u)\n",
00476 transactions);
00477 return;
00478 }
00479
00480 lms->commit_interval = transactions;
00481 }
00482
00495 int
00496 lms_charset_add(lms_t *lms, const char *charset)
00497 {
00498 if (!lms) {
00499 fprintf(stderr, "ERROR: lms_charset_add(NULL)\n");
00500 return -1;
00501 }
00502
00503 return lms_charset_conv_add(lms->cs_conv, charset);
00504 }
00505
00518 int
00519 lms_charset_del(lms_t *lms, const char *charset)
00520 {
00521 if (!lms) {
00522 fprintf(stderr, "ERROR: lms_charset_del(NULL)\n");
00523 return -1;
00524 }
00525
00526 return lms_charset_conv_del(lms->cs_conv, charset);
00527 }
00528
00539 void
00540 lms_parsers_list(int (*cb)(void *data, const char *path), const void *data)
00541 {
00542 void *datap = (void *)data;
00543 char path[PATH_MAX] = PLUGINSDIR;
00544 int base;
00545 DIR *d;
00546 struct dirent *de;
00547
00548 if (!cb)
00549 return;
00550
00551 base = sizeof(PLUGINSDIR) - 1;
00552 if (base + sizeof("/.so") >= PATH_MAX) {
00553 fprintf(stderr, "ERROR: path is too long '%s'\n", path);
00554 return;
00555 }
00556
00557 d = opendir(path);
00558 if (!d) {
00559 fprintf(stderr, "ERROR: could not open directory %s: %s\n",
00560 path, strerror(errno));
00561 return;
00562 }
00563
00564 path[base] = '/';
00565 base++;
00566
00567 while ((de = readdir(d)) != NULL) {
00568 int len;
00569
00570 if (de->d_name[0] == '.')
00571 continue;
00572
00573 len = strlen(de->d_name);
00574 if (len < 3 || memcmp(de->d_name + len - 3, ".so", 3) != 0)
00575 continue;
00576
00577 memcpy(path + base, de->d_name, len + 1);
00578 if (!cb(datap, path))
00579 break;
00580 }
00581 closedir(d);
00582 }
00583
00584 struct lms_parsers_list_by_category_data {
00585 const char *category;
00586 int (*cb)(void *data, const char *path, const struct lms_parser_info *info);
00587 void *data;
00588 };
00589
00590 static int
00591 _lms_parsers_list_by_category(void *data, const char *path)
00592 {
00593 struct lms_parsers_list_by_category_data *d = data;
00594 struct lms_parser_info *info;
00595 int r;
00596
00597 info = lms_parser_info(path);
00598 if (!info)
00599 return 1;
00600
00601 r = 1;
00602 if (info->categories) {
00603 const char * const *itr;
00604 for (itr = info->categories; *itr != NULL; itr++)
00605 if (strcmp(d->category, *itr) == 0) {
00606 r = d->cb(d->data, path, info);
00607 break;
00608 }
00609 }
00610
00611 lms_parser_info_free(info);
00612
00613 return r;
00614 }
00615
00628 void
00629 lms_parsers_list_by_category(const char *category, int (*cb)(void *data, const char *path, const struct lms_parser_info *info), const void *data)
00630 {
00631 struct lms_parsers_list_by_category_data d;
00632
00633 if (!category || !cb)
00634 return;
00635
00636 d.category = category;
00637 d.cb = cb;
00638 d.data = (void *)data;
00639
00640 lms_parsers_list(_lms_parsers_list_by_category, &d);
00641 }
00642
00643 static int
00644 _lms_string_array_count(const char * const *array, int *size)
00645 {
00646 int count, align_overflow;
00647
00648 *size = 0;
00649 if (!array)
00650 return 0;
00651
00652 count = 0;
00653 for (; *array != NULL; array++) {
00654 *size += sizeof(char *) + strlen(*array) + 1;
00655 count++;
00656 }
00657 if (count) {
00658
00659 count++;
00660 *size += sizeof(char *);
00661 }
00662
00663 align_overflow = *size % sizeof(char *);
00664 if (align_overflow)
00665 *size += sizeof(char *) - align_overflow;
00666
00667 return count;
00668 }
00669
00670 static void
00671 _lms_string_array_copy(char **dst, const char * const *src, int count)
00672 {
00673 char *d;
00674
00675 d = (char *)(dst + count);
00676
00677 for (; count > 1; count--, dst++, src++) {
00678 int len;
00679
00680 len = strlen(*src) + 1;
00681 *dst = d;
00682 memcpy(*dst, *src, len);
00683 d += len;
00684 }
00685
00686 *dst = NULL;
00687 }
00688
00698 struct lms_parser_info *
00699 lms_parser_info(const char *so_path)
00700 {
00701 const struct lms_plugin_info *(*plugin_info)(void);
00702 const struct lms_plugin_info *pinfo;
00703 struct lms_parser_info *ret;
00704 const char *errmsg;
00705 void *dl_handle;
00706 int len, path_len, name_len, desc_len, ver_len, uri_len;
00707 int cats_count, cats_size, authors_count, authors_size;
00708
00709 if (!so_path)
00710 return NULL;
00711
00712 dl_handle = dlopen(so_path, RTLD_NOW | RTLD_LOCAL);
00713 errmsg = dlerror();
00714 if (errmsg) {
00715 fprintf(stderr, "ERROR: could not dlopen() %s\n", errmsg);
00716 return NULL;
00717 }
00718
00719 ret = NULL;
00720 plugin_info = dlsym(dl_handle, "lms_plugin_info");
00721 errmsg = dlerror();
00722 if (errmsg) {
00723 fprintf(stderr, "ERROR: could not find plugin info function %s\n",
00724 errmsg);
00725 goto close_and_exit;
00726 }
00727
00728 if (!plugin_info) {
00729 fprintf(stderr, "ERROR: lms_plugin_info is NULL\n");
00730 goto close_and_exit;
00731 }
00732
00733 pinfo = plugin_info();
00734 if (!pinfo) {
00735 fprintf(stderr, "ERROR: lms_plugin_info() returned NULL\n");
00736 goto close_and_exit;
00737 }
00738
00739 path_len = strlen(so_path) + 1;
00740 name_len = pinfo->name ? strlen(pinfo->name) + 1 : 0;
00741 desc_len = pinfo->description ? strlen(pinfo->description) + 1 : 0;
00742 ver_len = pinfo->version ? strlen(pinfo->version) + 1 : 0;
00743 uri_len = pinfo->uri ? strlen(pinfo->uri) + 1 : 0;
00744
00745 cats_count = _lms_string_array_count(pinfo->categories, &cats_size);
00746 authors_count = _lms_string_array_count(pinfo->authors, &authors_size);
00747
00748 len = path_len + name_len + desc_len + ver_len + uri_len + cats_size +
00749 authors_size;
00750 ret = malloc(sizeof(*ret) + len);
00751 if (!ret) {
00752 fprintf(stderr, "ERROR: could not alloc %d bytes: %s",
00753 sizeof(*ret) + len, strerror(errno));
00754 goto close_and_exit;
00755 }
00756
00757 len = 0;
00758
00759 if (cats_count) {
00760 ret->categories = (const char * const *)
00761 ((char *)ret + sizeof(*ret) + len);
00762 _lms_string_array_copy(
00763 (char **)ret->categories, pinfo->categories, cats_count);
00764 len += cats_size;
00765 } else
00766 ret->categories = NULL;
00767
00768 if (authors_count) {
00769 ret->authors = (const char * const *)
00770 ((char *)ret + sizeof(*ret) + len);
00771 _lms_string_array_copy(
00772 (char **)ret->authors, pinfo->authors, authors_count);
00773 len += authors_size;
00774 } else
00775 ret->authors = NULL;
00776
00777 ret->path = (char *)ret + sizeof(*ret) + len;
00778 memcpy((char *)ret->path, so_path, path_len);
00779 len += path_len;
00780
00781 if (pinfo->name) {
00782 ret->name = (char *)ret + sizeof(*ret) + len;
00783 memcpy((char *)ret->name, pinfo->name, name_len);
00784 len += name_len;
00785 } else
00786 ret->name = NULL;
00787
00788 if (pinfo->description) {
00789 ret->description = (char *)ret + sizeof(*ret) + len;
00790 memcpy((char *)ret->description, pinfo->description, desc_len);
00791 len += desc_len;
00792 } else
00793 ret->description = NULL;
00794
00795 if (pinfo->version) {
00796 ret->version = (char *)ret + sizeof(*ret) + len;
00797 memcpy((char *)ret->version, pinfo->version, ver_len);
00798 len += ver_len;
00799 } else
00800 ret->version = NULL;
00801
00802 if (pinfo->uri) {
00803 ret->uri = (char *)ret + sizeof(*ret) + len;
00804 memcpy((char *)ret->uri, pinfo->uri, uri_len);
00805 len += uri_len;
00806 } else
00807 ret->uri = NULL;
00808
00809 close_and_exit:
00810 dlclose(dl_handle);
00811 return ret;
00812 }
00813
00823 struct lms_parser_info *
00824 lms_parser_info_find(const char *name)
00825 {
00826 char so_path[PATH_MAX];
00827
00828 if (!name)
00829 return NULL;
00830
00831 if (!lms_parser_find(so_path, sizeof(so_path), name))
00832 return NULL;
00833
00834 return lms_parser_info(so_path);
00835 }
00836
00842 void
00843 lms_parser_info_free(struct lms_parser_info *info)
00844 {
00845 free(info);
00846 }