process: do not change update_id on skipped files.
[platform/upstream/lightmediascanner.git] / src / lib / lightmediascanner_process.c
1 /**
2  * Copyright (C) 2008-2011 by ProFUSION embedded systems
3  * Copyright (C) 2007 by INdT
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 License
7  * as published by the Free Software Foundation; either version 2.1 of
8  * the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18  * 02110-1301 USA
19  *
20  * @author Gustavo Sverzut Barbieri <barbieri@profusion.mobi>
21  */
22
23 #include <sys/wait.h>
24 #include <sys/stat.h>
25 #include <unistd.h>
26 #include <dirent.h>
27 #include <signal.h>
28 #include <time.h>
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33
34 #include "lightmediascanner.h"
35 #include "lightmediascanner_private.h"
36 #include "lightmediascanner_db_private.h"
37
38 struct db {
39     sqlite3 *handle;
40     sqlite3_stmt *transaction_begin;
41     sqlite3_stmt *transaction_commit;
42     sqlite3_stmt *get_file_info;
43     sqlite3_stmt *insert_file_info;
44     sqlite3_stmt *update_file_info;
45     sqlite3_stmt *delete_file_info;
46     sqlite3_stmt *set_file_dtime;
47 };
48
49 /***********************************************************************
50  * Master-Slave communication.
51  ***********************************************************************/
52
53 static int
54 _master_send_path(const struct fds *master, int plen, int dlen, const char *p)
55 {
56     int lengths[2];
57
58     lengths[0] = plen;
59     lengths[1] = dlen;
60
61     if (write(master->w, lengths, sizeof(lengths)) < 0) {
62         perror("write");
63         return -1;
64     }
65
66     if (write(master->w, p, plen) < 0) {
67         perror("write");
68         return -1;
69     }
70
71     return 0;
72 }
73
74 static int
75 _master_send_finish(const struct fds *master)
76 {
77     const int lengths[2] = {-1, -1};
78
79     if (write(master->w, lengths, sizeof(lengths)) < 0) {
80         perror("write");
81         return -1;
82     }
83     return 0;
84 }
85
86 static int
87 _master_recv_reply(const struct fds *master, struct pollfd *pfd, int *reply, int timeout)
88 {
89     int r;
90
91     r = poll(pfd, 1, timeout);
92     if (r < 0) {
93         perror("poll");
94         return -1;
95     }
96
97     if (r == 0)
98         return 1;
99
100     if (read(master->r, reply, sizeof(*reply)) != sizeof(*reply)) {
101         perror("read");
102         return -2;
103     }
104
105     return 0;
106 }
107
108 static int
109 _slave_send_reply(const struct fds *slave, int reply)
110 {
111     if (write(slave->w, &reply, sizeof(reply)) == 0) {
112         perror("write");
113         return -1;
114     }
115     return 0;
116 }
117
118 static int
119 _slave_recv_path(const struct fds *slave, int *plen, int *dlen, char *path)
120 {
121     int lengths[2], r;
122
123     r = read(slave->r, lengths, sizeof(lengths));
124     if (r != sizeof(lengths)) {
125         perror("read");
126         return -1;
127     }
128     *plen = lengths[0];
129     *dlen = lengths[1];
130
131     if (*plen == -1)
132         return 0;
133
134     if (*plen > PATH_SIZE) {
135         fprintf(stderr, "ERROR: path too long (%d/%d)\n", *plen, PATH_SIZE);
136         return -2;
137     }
138
139     r = read(slave->r, path, *plen);
140     if (r != *plen) {
141         fprintf(stderr, "ERROR: could not read whole path %d/%d\n", r, *plen);
142         return -3;
143     }
144
145     path[*plen] = 0;
146     return 0;
147 }
148
149
150 /***********************************************************************
151  * Slave-side.
152  ***********************************************************************/
153
154 static int
155 _db_compile_all_stmts(struct db *db)
156 {
157     sqlite3 *handle;
158
159     handle = db->handle;
160     db->transaction_begin = lms_db_compile_stmt_begin_transaction(handle);
161     if (!db->transaction_begin)
162         return -1;
163
164     db->transaction_commit = lms_db_compile_stmt_end_transaction(handle);
165     if (!db->transaction_commit)
166         return -2;
167
168     db->get_file_info = lms_db_compile_stmt_get_file_info(handle);
169     if (!db->get_file_info)
170         return -4;
171
172     db->insert_file_info = lms_db_compile_stmt_insert_file_info(handle);
173     if (!db->insert_file_info)
174         return -5;
175
176     db->update_file_info = lms_db_compile_stmt_update_file_info(handle);
177     if (!db->update_file_info)
178         return -6;
179
180     db->delete_file_info = lms_db_compile_stmt_delete_file_info(handle);
181     if (!db->delete_file_info)
182         return -6;
183
184     db->set_file_dtime = lms_db_compile_stmt_set_file_dtime(handle);
185     if (!db->set_file_dtime)
186         return -7;
187
188     return 0;
189 }
190
191 static struct db *
192 _db_open(const char *db_path)
193 {
194     struct db *db;
195
196     db = calloc(1, sizeof(*db));
197     if (!db) {
198         perror("calloc");
199         return NULL;
200     }
201
202     if (sqlite3_open(db_path, &db->handle) != SQLITE_OK) {
203         fprintf(stderr, "ERROR: could not open DB \"%s\": %s\n",
204                 db_path, sqlite3_errmsg(db->handle));
205         goto error;
206     }
207
208     if (lms_db_create_core_tables_if_required(db->handle) != 0) {
209         fprintf(stderr, "ERROR: could not setup tables and indexes.\n");
210         goto error;
211     }
212
213     return db;
214
215   error:
216     sqlite3_close(db->handle);
217     free(db);
218     return NULL;
219 }
220
221 static int
222 _db_close(struct db *db)
223 {
224     if (db->transaction_begin)
225         lms_db_finalize_stmt(db->transaction_begin, "transaction_begin");
226
227     if (db->transaction_commit)
228         lms_db_finalize_stmt(db->transaction_commit, "transaction_commit");
229
230     if (db->get_file_info)
231         lms_db_finalize_stmt(db->get_file_info, "get_file_info");
232
233     if (db->insert_file_info)
234         lms_db_finalize_stmt(db->insert_file_info, "insert_file_info");
235
236     if (db->update_file_info)
237         lms_db_finalize_stmt(db->update_file_info, "update_file_info");
238
239     if (db->delete_file_info)
240         lms_db_finalize_stmt(db->delete_file_info, "delete_file_info");
241
242     if (db->set_file_dtime)
243         lms_db_finalize_stmt(db->set_file_dtime, "set_file_dtime");
244
245     if (sqlite3_close(db->handle) != SQLITE_OK) {
246         fprintf(stderr, "ERROR: clould not close DB: %s\n",
247                 sqlite3_errmsg(db->handle));
248         return -1;
249     }
250     free(db);
251
252     return 0;
253 }
254
255 /*
256  * Return:
257  *  0: file found and nothing changed
258  *  1: file not found or mtime/size is different
259  *  < 0: error
260  */
261 static int
262 _retrieve_file_status(struct db *db, struct lms_file_info *finfo)
263 {
264     struct stat st;
265     int r;
266
267     if (stat(finfo->path, &st) != 0) {
268         perror("stat");
269         return -1;
270     }
271
272     r = lms_db_get_file_info(db->get_file_info, finfo);
273     if (r == 0) {
274         if (st.st_mtime <= finfo->mtime && finfo->size == (size_t)st.st_size)
275             return 0;
276         else {
277             finfo->mtime = st.st_mtime;
278             finfo->size = st.st_size;
279             return 1;
280         }
281     } else if (r == 1) {
282         finfo->mtime = st.st_mtime;
283         finfo->size = st.st_size;
284         return 1;
285     } else
286         return -2;
287 }
288
289 static void
290 _ctxt_init(struct lms_context *ctxt, const lms_t *lms, sqlite3 *db)
291 {
292     ctxt->cs_conv = lms->cs_conv;
293     ctxt->db = db;
294 }
295
296 int
297 lms_parsers_setup(lms_t *lms, sqlite3 *db)
298 {
299     struct lms_context ctxt;
300     int i;
301
302     _ctxt_init(&ctxt, lms, db);
303
304     for (i = 0; i < lms->n_parsers; i++) {
305         lms_plugin_t *plugin;
306         int r;
307
308         plugin = lms->parsers[i].plugin;
309         r = plugin->setup(plugin, &ctxt);
310         if (r != 0) {
311             fprintf(stderr, "ERROR: parser \"%s\" failed to setup: %d.\n",
312                     plugin->name, r);
313             plugin->finish(plugin, &ctxt);
314             lms_parser_del_int(lms, i);
315             i--; /* cancel i++ */
316         }
317     }
318
319     return 0;
320 }
321
322 int
323 lms_parsers_start(lms_t *lms, sqlite3 *db)
324 {
325     struct lms_context ctxt;
326     int i;
327
328     _ctxt_init(&ctxt, lms, db);
329
330     for (i = 0; i < lms->n_parsers; i++) {
331         lms_plugin_t *plugin;
332         int r;
333
334         plugin = lms->parsers[i].plugin;
335         r = plugin->start(plugin, &ctxt);
336         if (r != 0) {
337             fprintf(stderr, "ERROR: parser \"%s\" failed to start: %d.\n",
338                     plugin->name, r);
339             plugin->finish(plugin, &ctxt);
340             lms_parser_del_int(lms, i);
341             i--; /* cancel i++ */
342         }
343     }
344
345     return 0;
346 }
347
348 int
349 lms_parsers_finish(lms_t *lms, sqlite3 *db)
350 {
351     struct lms_context ctxt;
352     int i;
353
354     _ctxt_init(&ctxt, lms, db);
355
356     for (i = 0; i < lms->n_parsers; i++) {
357         lms_plugin_t *plugin;
358         int r;
359
360         plugin = lms->parsers[i].plugin;
361         r = plugin->finish(plugin, &ctxt);
362         if (r != 0)
363             fprintf(stderr, "ERROR: parser \"%s\" failed to finish: %d.\n",
364                     plugin->name, r);
365     }
366
367     return 0;
368 }
369
370 int
371 lms_parsers_check_using(lms_t *lms, void **parser_match, struct lms_file_info *finfo)
372 {
373     int used, i;
374
375     used = 0;
376     for (i = 0; i < lms->n_parsers; i++) {
377         lms_plugin_t *plugin;
378         void *r;
379
380         plugin = lms->parsers[i].plugin;
381         r = plugin->match(plugin, finfo->path, finfo->path_len, finfo->base);
382         parser_match[i] = r;
383         if (r)
384             used = 1;
385     }
386
387     return used;
388 }
389
390 int
391 lms_parsers_run(lms_t *lms, sqlite3 *db, void **parser_match, struct lms_file_info *finfo)
392 {
393     struct lms_context ctxt;
394     int i, failed, available;
395
396     _ctxt_init(&ctxt, lms, db);
397
398     failed = 0;
399     available = 0;
400     for (i = 0; i < lms->n_parsers; i++) {
401         lms_plugin_t *plugin;
402
403         plugin = lms->parsers[i].plugin;
404         if (parser_match[i]) {
405             int r;
406
407             available++;
408             r = plugin->parse(plugin, &ctxt, finfo, parser_match[i]);
409             if (r != 0)
410                 failed++;
411         }
412     }
413
414     if (!failed)
415         return 0;
416     else if (failed == available)
417         return -1;
418     else
419         return 1; /* non critical */
420 }
421
422 static int
423 _db_and_parsers_setup(lms_t *lms, struct db **db_ret, void ***parser_match_ret)
424 {
425     void **parser_match;
426     struct db *db;
427     int r = 0;
428
429     db = _db_open(lms->db_path);
430     if (!db) {
431         r = -1;
432         return r;
433     }
434
435     if (lms_parsers_setup(lms, db->handle) != 0) {
436         fprintf(stderr, "ERROR: could not setup parsers.\n");
437         r = -2;
438         goto err;
439     }
440
441     if (_db_compile_all_stmts(db) != 0) {
442         fprintf(stderr, "ERROR: could not compile statements.\n");
443         r = -3;
444         goto err;
445     }
446
447     if (lms_parsers_start(lms, db->handle) != 0) {
448         fprintf(stderr, "ERROR: could not start parsers.\n");
449         r = -4;
450         goto err;
451     }
452     if (lms->n_parsers < 1) {
453         fprintf(stderr, "ERROR: no parser could be started, exit.\n");
454         r = -5;
455         goto err;
456     }
457
458     parser_match = malloc(lms->n_parsers * sizeof(*parser_match));
459     if (!parser_match) {
460         perror("malloc");
461         r = -6;
462         goto err;
463     }
464
465     *parser_match_ret = parser_match;
466     *db_ret = db;
467     return r;
468
469   err:
470     lms_parsers_finish(lms, db->handle);
471     _db_close(db);
472     return r;
473 }
474
475 /*
476  * Return:
477  *  LMS_PROGRESS_STATUS_UP_TO_DATE
478  *  LMS_PROGRESS_STATUS_PROCESSED
479  *  LMS_PROGRESS_STATUS_SKIPPED
480  *  < 0 on error
481  */
482 static int
483 _db_and_parsers_process_file(lms_t *lms, struct db *db, void **parser_match,
484                              char *path, int path_len, int path_base,
485                              unsigned int update_id)
486 {
487     struct lms_file_info finfo;
488     int used, r;
489
490     finfo.path = path;
491     finfo.path_len = path_len;
492     finfo.base = path_base;
493
494     r = _retrieve_file_status(db, &finfo);
495     if (r == 0) {
496         if (!finfo.dtime)
497             return LMS_PROGRESS_STATUS_UP_TO_DATE;
498
499         finfo.dtime = 0;
500         finfo.itime = time(NULL);
501         lms_db_set_file_dtime(db->set_file_dtime, &finfo);
502         return LMS_PROGRESS_STATUS_PROCESSED;
503     } else if (r < 0) {
504         fprintf(stderr, "ERROR: could not detect file status.\n");
505         return r;
506     }
507
508     used = lms_parsers_check_using(lms, parser_match, &finfo);
509     if (!used)
510         return LMS_PROGRESS_STATUS_SKIPPED;
511
512     finfo.dtime = 0;
513     finfo.itime = time(NULL);
514     if (finfo.id > 0)
515         r = lms_db_update_file_info(db->update_file_info, &finfo, update_id);
516     else
517         r = lms_db_insert_file_info(db->insert_file_info, &finfo, update_id);
518
519     if (r < 0) {
520         fprintf(stderr, "ERROR: could not register path in DB\n");
521         return r;
522     }
523
524     r = lms_parsers_run(lms, db->handle, parser_match, &finfo);
525     if (r < 0) {
526         fprintf(stderr, "ERROR: pid=%d failed to parse \"%s\".\n",
527                 getpid(), finfo.path);
528         lms_db_delete_file_info(db->delete_file_info, &finfo);
529         return r;
530     }
531
532     return LMS_PROGRESS_STATUS_PROCESSED;
533 }
534
535 static int
536 _slave_work(struct pinfo *pinfo)
537 {
538     lms_t *lms = pinfo->common.lms;
539     struct fds *fds = &pinfo->slave;
540     int r, len, base;
541     char path[PATH_SIZE];
542     void **parser_match;
543     struct db *db;
544     unsigned int total_committed, counter;
545
546     r = _db_and_parsers_setup(lms, &db, &parser_match);
547     if (r < 0)
548         return r;
549
550     r = lms_db_update_id_get(db->handle);
551     if (r < 0) {
552         fprintf(stderr, "ERROR: could not get global update id.\n");
553         goto done;
554     }
555
556     pinfo->common.update_id = r + 1;
557
558     counter = 0;
559     total_committed = 0;
560     lms_db_begin_transaction(db->transaction_begin);
561
562     while (((r = _slave_recv_path(fds, &len, &base, path)) == 0) && len > 0) {
563         r = _db_and_parsers_process_file(
564             lms, db, parser_match, path, len, base, pinfo->common.update_id);
565
566         _slave_send_reply(fds, r);
567
568         if (r < 0 ||
569             (r == LMS_PROGRESS_STATUS_UP_TO_DATE ||
570              r == LMS_PROGRESS_STATUS_SKIPPED))
571             continue;
572
573         counter++;
574         if (counter > lms->commit_interval) {
575             if (!total_committed) {
576                 total_committed += counter;
577                 lms_db_update_id_set(db->handle, pinfo->common.update_id);
578             }
579
580             lms_db_end_transaction(db->transaction_commit);
581             lms_db_begin_transaction(db->transaction_begin);
582             counter = 0;
583         }
584     }
585
586     if (counter) {
587         total_committed += counter;
588         lms_db_update_id_set(db->handle, pinfo->common.update_id);
589     }
590
591     lms_db_end_transaction(db->transaction_commit);
592
593 done:
594     free(parser_match);
595     lms_parsers_finish(lms, db->handle);
596     _db_close(db);
597
598     return r;
599 }
600
601
602 /***********************************************************************
603  * Master-side.
604  ***********************************************************************/
605
606 static int
607 _consume_garbage(struct pollfd *pfd)
608 {
609     int r;
610
611     while ((r = poll(pfd, 1, 0)) > 0) {
612         if (pfd->revents & (POLLERR | POLLHUP | POLLNVAL))
613             return 0;
614         else if (pfd->revents & POLLIN) {
615             char c;
616
617             read(pfd->fd, &c, sizeof(c));
618         }
619     }
620
621     return r;
622 }
623
624 static int
625 _close_fds(struct fds *fds)
626 {
627     int r;
628
629     r = 0;
630     if (close(fds->r) != 0) {
631         r--;
632         perror("close");
633     }
634
635     if (close(fds->w) != 0) {
636         r--;
637         perror("close");
638     }
639
640     return r;
641 }
642
643 int
644 lms_close_pipes(struct pinfo *pinfo)
645 {
646     int r;
647
648     r = _close_fds(&pinfo->master);
649     r += _close_fds(&pinfo->slave);
650
651     return r;
652 }
653
654 int
655 lms_create_pipes(struct pinfo *pinfo)
656 {
657     int fds[2];
658
659     if (pipe(fds) != 0) {
660         perror("pipe");
661         return -1;
662     }
663     pinfo->master.r = fds[0];
664     pinfo->slave.w = fds[1];
665
666     if (pipe(fds) != 0) {
667         perror("pipe");
668         close(pinfo->master.r);
669         close(pinfo->slave.w);
670         return -1;
671     }
672     pinfo->slave.r = fds[0];
673     pinfo->master.w = fds[1];
674
675     pinfo->poll.fd = pinfo->master.r;
676     pinfo->poll.events = POLLIN;
677
678     return 0;
679 }
680
681 int
682 lms_create_slave(struct pinfo *pinfo, int (*work)(struct pinfo *pinfo))
683 {
684     int r;
685
686     pinfo->child = fork();
687     if (pinfo->child == -1) {
688         perror("fork");
689         return -1;
690     }
691
692     if (pinfo->child > 0)
693         return 0;
694
695     _close_fds(&pinfo->master);
696     nice(19);
697     r = work(pinfo);
698     lms_free(pinfo->common.lms);
699     _exit(r);
700     return r; /* shouldn't reach anyway... */
701 }
702
703 static int
704 _waitpid(pid_t pid)
705 {
706     int status;
707     pid_t r;
708
709     r = waitpid(pid, &status, 0);
710     if (r > -1)
711         return 0;
712     else
713         perror("waitpid");
714
715     return r;
716 }
717
718 int
719 lms_finish_slave(struct pinfo *pinfo, int (*finish)(const struct fds *fds))
720 {
721     int r;
722
723     if (pinfo->child <= 0)
724         return 0;
725
726     r = finish(&pinfo->master);
727     if (r == 0)
728         r = _waitpid(pinfo->child);
729     else {
730         r = kill(pinfo->child, SIGKILL);
731         if (r < 0)
732             perror("kill");
733         else
734             r =_waitpid(pinfo->child);
735     }
736     pinfo->child = 0;
737
738     return r;
739 }
740
741 int
742 lms_restart_slave(struct pinfo *pinfo, int (*work)(struct pinfo *pinfo))
743 {
744     int status;
745
746     if (waitpid(pinfo->child, &status, WNOHANG) > 0) {
747         if (WIFEXITED(status)) {
748             int code;
749
750             code = WEXITSTATUS(status);
751             if (code != 0) {
752                 fprintf(stderr, "ERROR: slave returned %d, exit.\n", code);
753                 pinfo->child = 0;
754                 return -1;
755             }
756         } else {
757             if (WIFSIGNALED(status)) {
758                 int code;
759
760                 code = WTERMSIG(status);
761                 fprintf(stderr, "ERROR: slave was terminated by signal %d.\n",
762                         code);
763             }
764             pinfo->child = 0;
765             return -1;
766         }
767     }
768
769     if (kill(pinfo->child, SIGKILL))
770         perror("kill");
771
772     if (waitpid(pinfo->child, &status, 0) < 0)
773         perror("waitpid");
774
775     _consume_garbage(&pinfo->poll);
776     return lms_create_slave(pinfo, work);
777 }
778
779 static int
780 _strcat(int base, char *path, const char *name)
781 {
782     int new_len, name_len;
783
784     name_len = strlen(name);
785     new_len = base + name_len;
786
787     if (new_len >= PATH_SIZE) {
788         path[base] = '\0';
789         fprintf(stderr,
790                 "ERROR: path concatenation too long %d of %d "
791                 "available: \"%s\" + \"%s\"\n", new_len, PATH_SIZE,
792                 path, name);
793         return -1;
794     }
795
796     memcpy(path + base, name, name_len + 1);
797
798     return new_len;
799 }
800
801 static inline void
802 _report_progress(struct cinfo *info, const char *path, int path_len, lms_progress_status_t status)
803 {
804     lms_progress_callback_t cb;
805     lms_t *lms = info->lms;
806
807     cb = lms->progress.cb;
808     if (!cb)
809         return;
810
811     cb(lms, path, path_len, status, lms->progress.data);
812 }
813
814 static int
815 _process_file(struct cinfo *info, int base, char *path, const char *name)
816 {
817     struct pinfo *pinfo = (struct pinfo *)info;
818     int new_len, reply, r;
819
820     new_len = _strcat(base, path, name);
821     if (new_len < 0)
822         return -1;
823
824     if (_master_send_path(&pinfo->master, new_len, base, path) != 0)
825         return -2;
826
827     r = _master_recv_reply(&pinfo->master, &pinfo->poll, &reply,
828                            pinfo->common.lms->slave_timeout);
829     if (r < 0) {
830         _report_progress(info, path, new_len, LMS_PROGRESS_STATUS_ERROR_COMM);
831         return -3;
832     } else if (r == 1) {
833         fprintf(stderr, "ERROR: slave took too long, restart %d\n",
834                 pinfo->child);
835         _report_progress(info, path, new_len, LMS_PROGRESS_STATUS_KILLED);
836         if (lms_restart_slave(pinfo, _slave_work) != 0)
837             return -4;
838         return 1;
839     } else {
840         if (reply < 0) {
841             fprintf(stderr, "ERROR: pid=%d failed to parse \"%s\".\n",
842                     getpid(), path);
843             _report_progress(
844                 info, path, new_len, LMS_PROGRESS_STATUS_ERROR_PARSE);
845             return reply;
846         }
847         _report_progress(info, path, new_len, r);
848         return reply;
849     }
850 }
851
852 static int
853 _process_file_single_process(struct cinfo *info, int base, char *path, const char *name)
854 {
855     struct sinfo *sinfo = (struct sinfo *)info;
856     int new_len, r;
857
858     void **parser_match = sinfo->parser_match;
859     struct db *db = sinfo->db;
860     lms_t *lms = sinfo->common.lms;
861
862     new_len = _strcat(base, path, name);
863     if (new_len < 0)
864         return -1;
865
866     r = _db_and_parsers_process_file(lms, db, parser_match, path, new_len,
867                                      base, sinfo->common.update_id);
868     if (r < 0) {
869         fprintf(stderr, "ERROR: pid=%d failed to parse \"%s\".\n",
870                 getpid(), path);
871         _report_progress(info, path, new_len, LMS_PROGRESS_STATUS_ERROR_PARSE);
872         return r;
873     }
874
875     if (r != LMS_PROGRESS_STATUS_UP_TO_DATE)
876         sinfo->commit_counter++;
877
878     if (sinfo->commit_counter > lms->commit_interval) {
879         if (!sinfo->total_committed) {
880             sinfo->total_committed += sinfo->commit_counter;
881             lms_db_update_id_set(db->handle, sinfo->common.update_id);
882         }
883
884         lms_db_end_transaction(db->transaction_commit);
885         lms_db_begin_transaction(db->transaction_begin);
886         sinfo->commit_counter = 0;
887     }
888
889     _report_progress(info, path, new_len, r);
890
891     return r;
892 }
893
894 static int _process_dir(struct cinfo *info, int base, char *path, const char *name, process_file_callback_t process_file);
895
896 static int
897 _process_unknown(struct cinfo *info, int base, char *path, const char *name, process_file_callback_t process_file)
898 {
899     struct stat st;
900     int new_len;
901
902     new_len = _strcat(base, path, name);
903     if (new_len < 0)
904         return -1;
905
906     if (stat(path, &st) != 0) {
907         perror("stat");
908         return -2;
909     }
910
911     if (S_ISREG(st.st_mode)) {
912         int r = process_file(info, base, path, name);
913         if (r >= 0) /* if success and ignore non-fatal errors */
914             return 0;
915         return r;
916     } else if (S_ISDIR(st.st_mode)) {
917         int r = _process_dir(info, base, path, name, process_file);
918         if (r >= 0) /* ignore non-fatal errors */
919             return 0;
920         return r;
921     } else {
922         fprintf(stderr,
923                 "INFO: %s is neither a directory nor a regular file.\n", path);
924         return -3;
925     }
926 }
927
928 static int
929 _process_dir(struct cinfo *info, int base, char *path, const char *name, process_file_callback_t process_file)
930 {
931     lms_t *lms = info->lms;
932     struct dirent *de;
933     int new_len, r;
934     DIR *dir;
935
936     new_len = _strcat(base, path, name);
937     if (new_len < 0)
938         return -1;
939     else if (new_len + 1 >= PATH_SIZE) {
940         fprintf(stderr, "ERROR: path too long\n");
941         return 2;
942     }
943
944     dir = opendir(path);
945     if (dir == NULL) {
946         perror("opendir");
947         return 3;
948     }
949
950     path[new_len] = '/';
951     new_len++;
952
953     r = 0;
954     while ((de = readdir(dir)) != NULL && !lms->stop_processing) {
955         if (de->d_name[0] == '.')
956             continue;
957         if (de->d_type == DT_REG) {
958             if (process_file(info, new_len, path, de->d_name) < 0) {
959                 fprintf(stderr,
960                         "ERROR: unrecoverable error parsing file, "
961                         "exit \"%s\".\n", path);
962                 path[new_len - 1] = '\0';
963                 r = -4;
964                 goto end;
965             }
966         } else if (de->d_type == DT_DIR) {
967             if (_process_dir(
968                     info, new_len, path, de->d_name, process_file) < 0) {
969                 fprintf(stderr,
970                         "ERROR: unrecoverable error parsing dir, "
971                         "exit \"%s\".\n", path);
972                 path[new_len - 1] = '\0';
973                 r = -5;
974                 goto end;
975             }
976         } else if (de->d_type == DT_UNKNOWN) {
977             if (_process_unknown(
978                     info, new_len, path, de->d_name, process_file) < 0) {
979                 fprintf(stderr,
980                         "ERROR: unrecoverable error parsing DT_UNKNOWN, "
981                         "exit \"%s\".\n", path);
982                 path[new_len - 1] = '\0';
983                 r = -6;
984                 goto end;
985             }
986         }
987     }
988
989   end:
990     closedir(dir);
991     return r;
992 }
993
994 static int
995 _lms_process_check_valid(lms_t *lms, const char *path)
996 {
997     if (!lms)
998         return -1;
999
1000     if (!path)
1001         return -2;
1002
1003     if (lms->is_processing) {
1004         fprintf(stderr, "ERROR: is already processing.\n");
1005         return -3;
1006     }
1007
1008     if (!lms->parsers) {
1009         fprintf(stderr, "ERROR: no plugins registered.\n");
1010         return -4;
1011     }
1012
1013     return 0;
1014 }
1015
1016 static int
1017 _process_trigger(struct cinfo *info, const char *top_path, process_file_callback_t process_file)
1018 {
1019     char path[PATH_SIZE], *bname;
1020     lms_t *lms = info->lms;
1021     int len;
1022     int r;
1023
1024     if (realpath(top_path, path) == NULL) {
1025         perror("realpath");
1026         return -1;
1027     }
1028
1029     /* search '/' backwards, split dirname and basename, note realpath usage */
1030     len = strlen(path);
1031     for (; len >= 0 && path[len] != '/'; len--);
1032     len++;
1033     bname = strdup(path + len);
1034     if (bname == NULL) {
1035         perror("strdup");
1036         return -2;
1037     }
1038
1039     lms->is_processing = 1;
1040     lms->stop_processing = 0;
1041     r = _process_unknown(info, len, path, bname, process_file);
1042     lms->is_processing = 0;
1043     lms->stop_processing = 0;
1044     free(bname);
1045
1046     return r;
1047 }
1048
1049 /**
1050  * Process the given directory or file.
1051  *
1052  * This will add or update media found in the given directory or its children.
1053  *
1054  * @param lms previously allocated Light Media Scanner instance.
1055  * @param top_path top directory or file to scan.
1056  *
1057  * @return On success 0 is returned.
1058  */
1059 int
1060 lms_process(lms_t *lms, const char *top_path)
1061 {
1062     struct pinfo pinfo;
1063     int r;
1064
1065     r = _lms_process_check_valid(lms, top_path);
1066     if (r < 0)
1067         return r;
1068
1069     pinfo.common.lms = lms;
1070
1071     if (lms_create_pipes(&pinfo) != 0) {
1072         r = -1;
1073         goto end;
1074     }
1075
1076     if (lms_create_slave(&pinfo, _slave_work) != 0) {
1077         r = -2;
1078         goto close_pipes;
1079     }
1080
1081     r = _process_trigger(&pinfo.common, top_path, _process_file);
1082
1083     lms_finish_slave(&pinfo, _master_send_finish);
1084   close_pipes:
1085     lms_close_pipes(&pinfo);
1086   end:
1087     return r;
1088 }
1089
1090 /**
1091  * Process the given directory or file *without fork()-ing* into child process.
1092  *
1093  * This will add or update media found in the given directory or its children.
1094  * Note that if a parser hangs during the process, this call will also hang.
1095  *
1096  * @param lms previously allocated Light Media Scanner instance.
1097  * @param top_path top directory or file to scan.
1098  *
1099  * @return On success 0 is returned.
1100  */
1101 int
1102 lms_process_single_process(lms_t *lms, const char *top_path)
1103 {
1104     struct sinfo sinfo;
1105     int r;
1106
1107     r = _lms_process_check_valid(lms, top_path);
1108     if (r < 0)
1109         return r;
1110
1111     sinfo.common.lms = lms;
1112     sinfo.commit_counter = 0;
1113     sinfo.total_committed = 0;
1114
1115     r = _db_and_parsers_setup(sinfo.common.lms, &sinfo.db, &sinfo.parser_match);
1116     if (r < 0)
1117         return r;
1118
1119     r = lms_db_update_id_get(sinfo.db->handle);
1120     if (r < 0) {
1121         fprintf(stderr, "ERROR: could not get global update id.\n");
1122         goto done;
1123     }
1124
1125     sinfo.common.update_id = r + 1;
1126
1127     lms_db_begin_transaction(sinfo.db->transaction_begin);
1128
1129     r = _process_trigger(&sinfo.common, top_path, _process_file_single_process);
1130
1131     /* Check only if there are remaining commits to do */
1132     if (sinfo.commit_counter) {
1133         sinfo.total_committed += sinfo.commit_counter;
1134         lms_db_update_id_set(sinfo.db->handle, sinfo.common.update_id);
1135     }
1136
1137     lms_db_end_transaction(sinfo.db->transaction_commit);
1138
1139 done:
1140     free(sinfo.parser_match);
1141     lms_parsers_finish(lms, sinfo.db->handle);
1142     _db_close(sinfo.db);
1143     return r;
1144 }
1145
1146 void
1147 lms_stop_processing(lms_t *lms)
1148 {
1149     if (!lms)
1150         return;
1151     if (!lms->is_processing)
1152         return;
1153
1154     lms->stop_processing = 1;
1155 }