2 * Copyright (C) 2007 by INdT
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program 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
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 * @author Gustavo Sverzut Barbieri <gustavo.barbieri@openbossa.org>
36 #include "lightmediascanner.h"
37 #include "lightmediascanner_private.h"
38 #include "lightmediascanner_db_private.h"
42 sqlite3_stmt *transaction_begin;
43 sqlite3_stmt *transaction_commit;
44 sqlite3_stmt *get_file_info;
45 sqlite3_stmt *insert_file_info;
46 sqlite3_stmt *update_file_info;
47 sqlite3_stmt *delete_file_info;
48 sqlite3_stmt *set_file_dtime;
51 /***********************************************************************
52 * Master-Slave communication.
53 ***********************************************************************/
56 _master_send_path(const struct fds *master, int plen, int dlen, const char *p)
63 if (write(master->w, lengths, sizeof(lengths)) < 0) {
68 if (write(master->w, p, plen) < 0) {
77 _master_send_finish(const struct fds *master)
79 const int lengths[2] = {-1, -1};
81 if (write(master->w, lengths, sizeof(lengths)) < 0) {
89 _master_recv_reply(const struct fds *master, struct pollfd *pfd, int *reply, int timeout)
93 r = poll(pfd, 1, timeout);
102 if (read(master->r, reply, sizeof(*reply)) != sizeof(*reply)) {
111 _slave_send_reply(const struct fds *slave, int reply)
113 if (write(slave->w, &reply, sizeof(reply)) == 0) {
121 _slave_recv_path(const struct fds *slave, int *plen, int *dlen, char *path)
125 r = read(slave->r, lengths, sizeof(lengths));
126 if (r != sizeof(lengths)) {
136 if (*plen > PATH_SIZE) {
137 fprintf(stderr, "ERROR: path too long (%d/%d)\n", *plen, PATH_SIZE);
141 r = read(slave->r, path, *plen);
143 fprintf(stderr, "ERROR: could not read whole path %d/%d\n", r, *plen);
152 /***********************************************************************
154 ***********************************************************************/
157 _db_compile_all_stmts(struct db *db)
162 db->transaction_begin = lms_db_compile_stmt_begin_transaction(handle);
163 if (!db->transaction_begin)
166 db->transaction_commit = lms_db_compile_stmt_end_transaction(handle);
167 if (!db->transaction_commit)
170 db->get_file_info = lms_db_compile_stmt_get_file_info(handle);
171 if (!db->get_file_info)
174 db->insert_file_info = lms_db_compile_stmt_insert_file_info(handle);
175 if (!db->insert_file_info)
178 db->update_file_info = lms_db_compile_stmt_update_file_info(handle);
179 if (!db->update_file_info)
182 db->delete_file_info = lms_db_compile_stmt_delete_file_info(handle);
183 if (!db->delete_file_info)
186 db->set_file_dtime = lms_db_compile_stmt_set_file_dtime(handle);
187 if (!db->set_file_dtime)
194 _db_open(const char *db_path)
198 db = calloc(1, sizeof(*db));
204 if (sqlite3_open(db_path, &db->handle) != SQLITE_OK) {
205 fprintf(stderr, "ERROR: could not open DB \"%s\": %s\n",
206 db_path, sqlite3_errmsg(db->handle));
210 if (lms_db_create_core_tables_if_required(db->handle) != 0) {
211 fprintf(stderr, "ERROR: could not setup tables and indexes.\n");
218 sqlite3_close(db->handle);
224 _db_close(struct db *db)
226 if (db->transaction_begin)
227 lms_db_finalize_stmt(db->transaction_begin, "transaction_begin");
229 if (db->transaction_commit)
230 lms_db_finalize_stmt(db->transaction_commit, "transaction_commit");
232 if (db->get_file_info)
233 lms_db_finalize_stmt(db->get_file_info, "get_file_info");
235 if (db->insert_file_info)
236 lms_db_finalize_stmt(db->insert_file_info, "insert_file_info");
238 if (db->update_file_info)
239 lms_db_finalize_stmt(db->update_file_info, "update_file_info");
241 if (db->delete_file_info)
242 lms_db_finalize_stmt(db->delete_file_info, "delete_file_info");
244 if (db->set_file_dtime)
245 lms_db_finalize_stmt(db->set_file_dtime, "set_file_dtime");
247 if (sqlite3_close(db->handle) != SQLITE_OK) {
248 fprintf(stderr, "ERROR: clould not close DB: %s\n",
249 sqlite3_errmsg(db->handle));
258 _retrieve_file_status(struct db *db, struct lms_file_info *finfo)
263 if (stat(finfo->path, &st) != 0) {
268 r = lms_db_get_file_info(db->get_file_info, finfo);
270 if (st.st_mtime <= finfo->mtime && finfo->size == st.st_size)
273 finfo->mtime = st.st_mtime;
274 finfo->size = st.st_size;
278 finfo->mtime = st.st_mtime;
279 finfo->size = st.st_size;
286 _ctxt_init(struct lms_context *ctxt, const lms_t *lms, sqlite3 *db)
288 ctxt->cs_conv = lms->cs_conv;
293 lms_parsers_setup(lms_t *lms, sqlite3 *db)
295 struct lms_context ctxt;
298 _ctxt_init(&ctxt, lms, db);
300 for (i = 0; i < lms->n_parsers; i++) {
301 lms_plugin_t *plugin;
304 plugin = lms->parsers[i].plugin;
305 r = plugin->setup(plugin, &ctxt);
307 fprintf(stderr, "ERROR: parser \"%s\" failed to setup: %d.\n",
309 plugin->finish(plugin, &ctxt);
310 lms_parser_del_int(lms, i);
311 i--; /* cancel i++ */
319 lms_parsers_start(lms_t *lms, sqlite3 *db)
321 struct lms_context ctxt;
324 _ctxt_init(&ctxt, lms, db);
326 for (i = 0; i < lms->n_parsers; i++) {
327 lms_plugin_t *plugin;
330 plugin = lms->parsers[i].plugin;
331 r = plugin->start(plugin, &ctxt);
333 fprintf(stderr, "ERROR: parser \"%s\" failed to start: %d.\n",
335 plugin->finish(plugin, &ctxt);
336 lms_parser_del_int(lms, i);
337 i--; /* cancel i++ */
345 lms_parsers_finish(lms_t *lms, sqlite3 *db)
347 struct lms_context ctxt;
350 _ctxt_init(&ctxt, lms, db);
352 for (i = 0; i < lms->n_parsers; i++) {
353 lms_plugin_t *plugin;
356 plugin = lms->parsers[i].plugin;
357 r = plugin->finish(plugin, &ctxt);
359 fprintf(stderr, "ERROR: parser \"%s\" failed to finish: %d.\n",
367 lms_parsers_check_using(lms_t *lms, void **parser_match, struct lms_file_info *finfo)
372 for (i = 0; i < lms->n_parsers; i++) {
373 lms_plugin_t *plugin;
376 plugin = lms->parsers[i].plugin;
377 r = plugin->match(plugin, finfo->path, finfo->path_len, finfo->base);
387 lms_parsers_run(lms_t *lms, sqlite3 *db, void **parser_match, struct lms_file_info *finfo)
389 struct lms_context ctxt;
390 int i, failed, available;
392 _ctxt_init(&ctxt, lms, db);
396 for (i = 0; i < lms->n_parsers; i++) {
397 lms_plugin_t *plugin;
399 plugin = lms->parsers[i].plugin;
400 if (parser_match[i]) {
404 r = plugin->parse(plugin, &ctxt, finfo, parser_match[i]);
412 else if (failed == available)
415 return 1; /* non critical */
419 _slave_work(lms_t *lms, struct fds *fds)
421 int r, len, base, counter;
422 char path[PATH_SIZE];
426 db = _db_open(lms->db_path);
430 if (lms_parsers_setup(lms, db->handle) != 0) {
431 fprintf(stderr, "ERROR: could not setup parsers.\n");
436 if (_db_compile_all_stmts(db) != 0) {
437 fprintf(stderr, "ERROR: could not compile statements.\n");
442 if (lms_parsers_start(lms, db->handle) != 0) {
443 fprintf(stderr, "ERROR: could not start parsers.\n");
447 if (lms->n_parsers < 1) {
448 fprintf(stderr, "ERROR: no parser could be started, exit.\n");
453 parser_match = malloc(lms->n_parsers * sizeof(*parser_match));
461 lms_db_begin_transaction(db->transaction_begin);
463 while (((r = _slave_recv_path(fds, &len, &base, path)) == 0) && len > 0) {
464 struct lms_file_info finfo;
468 finfo.path_len = len;
471 r = _retrieve_file_status(db, &finfo);
475 lms_db_set_file_dtime(db->set_file_dtime, &finfo);
479 fprintf(stderr, "ERROR: could not detect file status.\n");
483 used = lms_parsers_check_using(lms, parser_match, &finfo);
489 r = lms_db_update_file_info(db->update_file_info, &finfo);
491 r = lms_db_insert_file_info(db->insert_file_info, &finfo);
493 fprintf(stderr, "ERROR: could not register path in DB\n");
497 r = lms_parsers_run(lms, db->handle, parser_match, &finfo);
499 fprintf(stderr, "ERROR: pid=%d failed to parse \"%s\".\n",
500 getpid(), finfo.path);
501 lms_db_delete_file_info(db->delete_file_info, &finfo);
505 _slave_send_reply(fds, r);
507 if (counter > lms->commit_interval) {
508 lms_db_end_transaction(db->transaction_commit);
509 lms_db_begin_transaction(db->transaction_begin);
515 lms_db_end_transaction(db->transaction_commit);
517 lms_parsers_finish(lms, db->handle);
524 /***********************************************************************
526 ***********************************************************************/
529 _consume_garbage(struct pollfd *pfd)
533 while ((r = poll(pfd, 1, 0)) > 0) {
534 if (pfd->revents & (POLLERR | POLLHUP | POLLNVAL))
536 else if (pfd->revents & POLLIN) {
539 read(pfd->fd, &c, sizeof(c));
547 _close_fds(struct fds *fds)
552 if (close(fds->r) != 0) {
557 if (close(fds->w) != 0) {
566 lms_close_pipes(struct pinfo *pinfo)
570 r = _close_fds(&pinfo->master);
571 r += _close_fds(&pinfo->slave);
577 lms_create_pipes(struct pinfo *pinfo)
581 if (pipe(fds) != 0) {
585 pinfo->master.r = fds[0];
586 pinfo->slave.w = fds[1];
588 if (pipe(fds) != 0) {
590 close(pinfo->master.r);
591 close(pinfo->slave.w);
594 pinfo->slave.r = fds[0];
595 pinfo->master.w = fds[1];
597 pinfo->poll.fd = pinfo->master.r;
598 pinfo->poll.events = POLLIN;
604 lms_create_slave(struct pinfo *pinfo, int (*work)(lms_t *lms, struct fds *fds))
608 pinfo->child = fork();
609 if (pinfo->child == -1) {
614 if (pinfo->child > 0)
617 _close_fds(&pinfo->master);
619 r = work(pinfo->lms, &pinfo->slave);
620 lms_free(pinfo->lms);
622 return r; /* shouldn't reach anyway... */
631 r = waitpid(pid, &status, 0);
641 lms_finish_slave(struct pinfo *pinfo, int (*finish)(const struct fds *fds))
645 if (pinfo->child <= 0)
648 r = finish(&pinfo->master);
650 r = _waitpid(pinfo->child);
652 r = kill(pinfo->child, SIGKILL);
656 r =_waitpid(pinfo->child);
664 lms_restart_slave(struct pinfo *pinfo, int (*work)(lms_t *lms, struct fds *fds))
668 if (waitpid(pinfo->child, &status, WNOHANG) > 0) {
669 if (WIFEXITED(status)) {
672 code = WEXITSTATUS(status);
674 fprintf(stderr, "ERROR: slave returned %d, exit.\n", code);
679 if (WIFSIGNALED(status)) {
682 code = WTERMSIG(status);
683 fprintf(stderr, "ERROR: slave was terminated by signal %d.\n",
691 if (kill(pinfo->child, SIGKILL))
694 if (waitpid(pinfo->child, &status, 0) < 0)
697 _consume_garbage(&pinfo->poll);
698 return lms_create_slave(pinfo, work);
702 _strcat(int base, char *path, const char *name)
704 int new_len, name_len;
706 name_len = strlen(name);
707 new_len = base + name_len;
709 if (new_len >= PATH_SIZE) {
712 "ERROR: path concatenation too long %d of %d "
713 "available: \"%s\" + \"%s\"\n", new_len, PATH_SIZE,
718 memcpy(path + base, name, name_len + 1);
724 _process_file(struct pinfo *pinfo, int base, char *path, const char *name)
726 int new_len, reply, r;
728 new_len = _strcat(base, path, name);
732 if (_master_send_path(&pinfo->master, new_len, base, path) != 0)
735 r = _master_recv_reply(&pinfo->master, &pinfo->poll, &reply,
736 pinfo->lms->slave_timeout);
740 fprintf(stderr, "ERROR: slave took too long, restart %d\n",
742 if (lms_restart_slave(pinfo, _slave_work) != 0)
747 /* XXX callback library users to inform error. */
748 fprintf(stderr, "ERROR: pid=%d failed to parse \"%s\".\n",
750 return (-reply) << 8;
757 _process_dir(struct pinfo *pinfo, int base, char *path, const char *name)
764 new_len = _strcat(base, path, name);
767 else if (new_len + 1 >= PATH_SIZE) {
768 fprintf(stderr, "ERROR: path too long\n");
783 while ((de = readdir(dir)) != NULL && !lms->stop_processing) {
784 if (de->d_name[0] == '.')
786 if (de->d_type == DT_REG) {
787 if (_process_file(pinfo, new_len, path, de->d_name) < 0) {
788 path[new_len - 1] = '\0';
790 "ERROR: unrecoverable error parsing file, "
791 "exit \"%s\".\n", path);
795 } else if (de->d_type == DT_DIR || de->d_type == DT_UNKNOWN) {
796 if (_process_dir(pinfo, new_len, path, de->d_name) < 0) {
797 path[new_len - 1] = '\0';
799 "ERROR: unrecoverable error parsing dir, "
800 "exit \"%s\".\n", path);
813 * Process the given directory.
815 * This will add or update media found in the given directory or its children.
817 * @param lms previously allocated Light Media Scanner instance.
818 * @param top_path top directory to scan.
820 * @return On success 0 is returned.
823 lms_process(lms_t *lms, const char *top_path)
827 char path[PATH_SIZE], *bname;
839 if (lms->is_processing) {
840 fprintf(stderr, "ERROR: is already processing.\n");
846 fprintf(stderr, "ERROR: no plugins registered.\n");
853 if (lms_create_pipes(&pinfo) != 0) {
858 if (lms_create_slave(&pinfo, _slave_work) != 0) {
863 if (realpath(top_path, path) == NULL) {
869 /* search '/' backwards, split dirname and basename, note realpath usage */
871 for (; len >= 0 && path[len] != '/'; len--);
873 bname = strdup(path + len);
880 lms->is_processing = 1;
881 lms->stop_processing = 0;
882 r = _process_dir(&pinfo, len, path, bname);
883 lms->is_processing = 0;
884 lms->stop_processing = 0;
888 lms_finish_slave(&pinfo, _master_send_finish);
890 lms_close_pipes(&pinfo);
896 lms_stop_processing(lms_t *lms)
900 if (!lms->is_processing)
903 lms->stop_processing = 1;