Charset conversion support.
[platform/upstream/lightmediascanner.git] / src / lib / lightmediascanner.c
1 /**
2  * Copyright (C) 2007 by INdT
3  *
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.
8  *
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.
13  *
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.
17  *
18  * @author Gustavo Sverzut Barbieri <gustavo.barbieri@openbossa.org>
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #define _GNU_SOURCE
26 #include <sys/types.h>
27 #include <sys/wait.h>
28 #include <sys/stat.h>
29 #include <unistd.h>
30 #include <dirent.h>
31 #include <limits.h>
32 #include <signal.h>
33 #include <poll.h>
34 #include <dlfcn.h>
35
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39
40 #include "lightmediascanner.h"
41 #include "lightmediascanner_plugin.h"
42 #include "lightmediascanner_db_private.h"
43 #include "lightmediascanner_charset_conv.h"
44
45 #define PATH_SIZE PATH_MAX
46 #define DEFAULT_SLAVE_TIMEOUT 1000
47 #define DEFAULT_COMMIT_INTERVAL 100
48
49 struct fds {
50     int r;
51     int w;
52 };
53
54 /* info to be carried along lms_process() */
55 struct pinfo {
56     struct fds master;
57     struct fds slave;
58     struct pollfd poll;
59     lms_t *lms;
60     pid_t child;
61 };
62
63 struct parser {
64     lms_plugin_t *plugin;
65     void *dl_handle;
66     char *so_path;
67 };
68
69 struct lms {
70     struct parser *parsers;
71     int n_parsers;
72     lms_charset_conv_t *cs_conv;
73     char *db_path;
74     int slave_timeout;
75     unsigned int commit_interval;
76     unsigned int is_processing:1;
77 };
78
79 struct db {
80     sqlite3 *handle;
81     sqlite3_stmt *transaction_begin;
82     sqlite3_stmt *transaction_commit;
83     sqlite3_stmt *transaction_rollback;
84     sqlite3_stmt *get_file_info;
85     sqlite3_stmt *insert_file_info;
86     sqlite3_stmt *update_file_info;
87     sqlite3_stmt *delete_file_info;
88     sqlite3_stmt *clear_file_dtime;
89 };
90
91 /***********************************************************************
92  * Master-Slave communication.
93  ***********************************************************************/
94
95 static int
96 _master_send_path(const struct fds *master, int plen, int dlen, const char *p)
97 {
98     int lengths[2];
99
100     lengths[0] = plen;
101     lengths[1] = dlen;
102
103     if (write(master->w, lengths, sizeof(lengths)) < 0) {
104         perror("write");
105         return -1;
106     }
107
108     if (write(master->w, p, plen) < 0) {
109         perror("write");
110         return -1;
111     }
112
113     return 0;
114 }
115
116 static int
117 _master_send_finish(const struct fds *master)
118 {
119     const int lengths[2] = {-1, -1};
120
121     if (write(master->w, lengths, sizeof(lengths)) < 0) {
122         perror("write");
123         return -1;
124     }
125     return 0;
126 }
127
128 static int
129 _master_recv_reply(const struct fds *master, struct pollfd *pfd, int *reply, int timeout)
130 {
131     int r;
132
133     r = poll(pfd, 1, timeout);
134     if (r < 0) {
135         perror("poll");
136         return -1;
137     }
138
139     if (r == 0)
140         return 1;
141
142     if (read(master->r, reply, sizeof(*reply)) != sizeof(*reply)) {
143         perror("read");
144         return -2;
145     }
146
147     return 0;
148 }
149
150 static int
151 _slave_send_reply(const struct fds *slave, int reply)
152 {
153     if (write(slave->w, &reply, sizeof(reply)) == 0) {
154         perror("write");
155         return -1;
156     }
157     return 0;
158 }
159
160 static int
161 _slave_recv_path(const struct fds *slave, int *plen, int *dlen, char *path)
162 {
163     int lengths[2], r;
164
165     r = read(slave->r, lengths, sizeof(lengths));
166     if (r != sizeof(lengths)) {
167         perror("read");
168         return -1;
169     }
170     *plen = lengths[0];
171     *dlen = lengths[1];
172
173     if (*plen == -1)
174         return 0;
175
176     if (*plen > PATH_SIZE) {
177         fprintf(stderr, "ERROR: path too long (%d/%d)\n", *plen, PATH_SIZE);
178         return -2;
179     }
180
181     r = read(slave->r, path, *plen);
182     if (r != *plen) {
183         fprintf(stderr, "ERROR: could not read whole path %d/%d\n", r, *plen);
184         return -3;
185     }
186
187     path[*plen] = 0;
188     return 0;
189 }
190
191
192 /***********************************************************************
193  * Slave-side.
194  ***********************************************************************/
195
196 static int
197 _db_create_tables_if_required(sqlite3 *db)
198 {
199     char *errmsg;
200     int r;
201
202     errmsg = NULL;
203     r = sqlite3_exec(db,
204                      "CREATE TABLE IF NOT EXISTS lms_internal ("
205                      "tab TEXT NOT NULL UNIQUE, "
206                      "version INTEGER NOT NULL"
207                      ")",
208                      NULL, NULL, &errmsg);
209     if (r != SQLITE_OK) {
210         fprintf(stderr, "ERROR: could not create 'lms_internal' table: %s\n",
211                 errmsg);
212         sqlite3_free(errmsg);
213         return -1;
214     }
215
216     r = sqlite3_exec(db,
217                      "CREATE TABLE IF NOT EXISTS files ("
218                      "id INTEGER PRIMARY KEY AUTOINCREMENT, "
219                      "path BLOB NOT NULL UNIQUE, "
220                      "mtime INTEGER NOT NULL, "
221                      "dtime INTEGER NOT NULL, "
222                      "size INTEGER NOT NULL"
223                      ")",
224                      NULL, NULL, &errmsg);
225     if (r != SQLITE_OK) {
226         fprintf(stderr, "ERROR: could not create 'files' table: %s\n", errmsg);
227         sqlite3_free(errmsg);
228         return -2;
229     }
230
231     r = sqlite3_exec(db,
232                      "CREATE INDEX IF NOT EXISTS files_path_idx ON files ("
233                      "path"
234                      ")",
235                      NULL, NULL, &errmsg);
236     if (r != SQLITE_OK) {
237         fprintf(stderr, "ERROR: could not create 'files_path_idx' index: %s\n",
238                 errmsg);
239         sqlite3_free(errmsg);
240         return -3;
241     }
242
243     return 0;
244 }
245
246 static int
247 _db_compile_all_stmts(struct db *db)
248 {
249     db->transaction_begin = lms_db_compile_stmt(db->handle,
250         "BEGIN TRANSACTION");
251     if (!db->transaction_begin)
252         return -1;
253
254     db->transaction_commit = lms_db_compile_stmt(db->handle,
255         "COMMIT");
256     if (!db->transaction_commit)
257         return -2;
258
259     db->transaction_rollback = lms_db_compile_stmt(db->handle,
260         "ROLLBACK");
261     if (!db->transaction_rollback)
262         return -3;
263
264     db->get_file_info = lms_db_compile_stmt(db->handle,
265         "SELECT id, mtime, dtime, size FROM files WHERE path = ?");
266     if (!db->get_file_info)
267         return -4;
268
269     db->insert_file_info = lms_db_compile_stmt(db->handle,
270         "INSERT INTO files (path, mtime, dtime, size) VALUES(?, ?, ?, ?)");
271     if (!db->insert_file_info)
272         return -5;
273
274     db->update_file_info = lms_db_compile_stmt(db->handle,
275         "UPDATE files SET mtime = ?, dtime = ?, size = ? WHERE id = ?");
276     if (!db->update_file_info)
277         return -6;
278
279     db->delete_file_info = lms_db_compile_stmt(db->handle,
280         "DELETE FROM files WHERE id = ?");
281     if (!db->delete_file_info)
282         return -6;
283
284     db->clear_file_dtime = lms_db_compile_stmt(db->handle,
285         "UPDATE files SET dtime = 0 WHERE id = ?");
286     if (!db->clear_file_dtime)
287         return -7;
288
289     return 0;
290 }
291
292 static struct db *
293 _db_open(const char *db_path)
294 {
295     struct db *db;
296
297     db = calloc(1, sizeof(*db));
298     if (!db) {
299         perror("calloc");
300         return NULL;
301     }
302
303     if (sqlite3_open(db_path, &db->handle) != SQLITE_OK) {
304         fprintf(stderr, "ERROR: could not open DB \"%s\": %s\n",
305                 db_path, sqlite3_errmsg(db->handle));
306         goto error;
307     }
308
309     if (_db_create_tables_if_required(db->handle) != 0) {
310         fprintf(stderr, "ERROR: could not setup tables and indexes.\n");
311         goto error;
312     }
313
314     return db;
315
316   error:
317     sqlite3_close(db->handle);
318     free(db);
319     return NULL;
320 }
321
322 static int
323 _db_close(struct db *db)
324 {
325     if (db->transaction_begin)
326         lms_db_finalize_stmt(db->transaction_begin, "transaction_begin");
327
328     if (db->transaction_commit)
329         lms_db_finalize_stmt(db->transaction_commit, "transaction_commit");
330
331     if (db->transaction_rollback)
332         lms_db_finalize_stmt(db->transaction_rollback, "transaction_rollback");
333
334     if (db->get_file_info)
335         lms_db_finalize_stmt(db->get_file_info, "get_file_info");
336
337     if (db->insert_file_info)
338         lms_db_finalize_stmt(db->insert_file_info, "insert_file_info");
339
340     if (db->update_file_info)
341         lms_db_finalize_stmt(db->update_file_info, "update_file_info");
342
343     if (db->delete_file_info)
344         lms_db_finalize_stmt(db->delete_file_info, "delete_file_info");
345
346     if (db->clear_file_dtime)
347         lms_db_finalize_stmt(db->clear_file_dtime, "clear_file_dtime");
348
349     if (sqlite3_close(db->handle) != SQLITE_OK) {
350         fprintf(stderr, "ERROR: clould not close DB: %s\n",
351                 sqlite3_errmsg(db->handle));
352         return -1;
353     }
354     free(db);
355
356     return 0;
357 }
358
359 static int
360 _db_begin_transaction(struct db *db)
361 {
362     sqlite3_stmt *stmt;
363     int r, ret;
364
365     stmt = db->transaction_begin;
366
367     ret = 0;
368     r = sqlite3_step(stmt);
369     if (r != SQLITE_DONE) {
370         fprintf(stderr, "ERROR: could not begin transaction: %s\n",
371                 sqlite3_errmsg(db->handle));
372         ret = -1;
373     }
374
375     r = sqlite3_reset(stmt);
376     if (r != SQLITE_OK)
377         fprintf(stderr, "ERROR: could not reset SQL statement: %s\n",
378                 sqlite3_errmsg(db->handle));
379
380     return ret;
381 }
382
383 static int
384 _db_end_transaction(struct db *db)
385 {
386     sqlite3_stmt *stmt;
387     int r, ret;
388
389     stmt = db->transaction_commit;
390
391     ret = 0;
392     r = sqlite3_step(stmt);
393     if (r != SQLITE_DONE) {
394         fprintf(stderr, "ERROR: could not end transaction: %s\n",
395                 sqlite3_errmsg(db->handle));
396         ret = -1;
397     }
398
399     r = sqlite3_reset(stmt);
400     if (r != SQLITE_OK)
401         fprintf(stderr, "ERROR: could not reset SQL statement: %s\n",
402                 sqlite3_errmsg(db->handle));
403
404     return ret;
405 }
406
407 static int
408 _db_get_file_info(struct db *db, struct lms_file_info *finfo)
409 {
410     sqlite3_stmt *stmt;
411     int r, ret;
412
413     stmt = db->get_file_info;
414
415     ret = lms_db_bind_blob(stmt, 1, finfo->path, finfo->path_len);
416     if (ret != 0)
417         goto done;
418
419     r = sqlite3_step(stmt);
420     if (r == SQLITE_DONE) {
421         ret = 1;
422         finfo->id = -1;
423         goto done;
424     }
425
426     if (r != SQLITE_ROW) {
427         fprintf(stderr, "ERROR: could not get file info from table: %s\n",
428                 sqlite3_errmsg(db->handle));
429         ret = -2;
430         goto done;
431     }
432
433     finfo->id = sqlite3_column_int64(stmt, 0);
434     finfo->mtime = sqlite3_column_int(stmt, 1);
435     finfo->dtime = sqlite3_column_int(stmt, 2);
436     finfo->size = sqlite3_column_int(stmt, 3);
437     ret = 0;
438
439   done:
440     lms_db_reset_stmt(stmt);
441
442     return ret;
443 }
444
445 static int
446 _db_update_file_info(struct db *db, struct lms_file_info *finfo)
447 {
448     sqlite3_stmt *stmt;
449     int r, ret;
450
451     stmt = db->update_file_info;
452
453     ret = lms_db_bind_int(stmt, 1, finfo->mtime);
454     if (ret != 0)
455         goto done;
456
457     ret = lms_db_bind_int(stmt, 2, finfo->dtime);
458     if (ret != 0)
459         goto done;
460
461     ret = lms_db_bind_int(stmt, 3, finfo->size);
462     if (ret != 0)
463         goto done;
464
465     ret = lms_db_bind_int(stmt, 4, finfo->id);
466     if (ret != 0)
467         goto done;
468
469     r = sqlite3_step(stmt);
470     if (r != SQLITE_DONE) {
471         fprintf(stderr, "ERROR: could not update file info: %s\n",
472                 sqlite3_errmsg(db->handle));
473         ret = -5;
474         goto done;
475     }
476
477     ret = 0;
478
479   done:
480     lms_db_reset_stmt(stmt);
481
482     return ret;
483 }
484
485 static int
486 _db_insert_file_info(struct db *db, struct lms_file_info *finfo)
487 {
488     sqlite3_stmt *stmt;
489     int r, ret;
490
491     stmt = db->insert_file_info;
492
493     ret = lms_db_bind_blob(stmt, 1, finfo->path, finfo->path_len);
494     if (ret != 0)
495         goto done;
496
497     ret = lms_db_bind_int(stmt, 2, finfo->mtime);
498     if (ret != 0)
499         goto done;
500
501     ret = lms_db_bind_int(stmt, 3, finfo->dtime);
502     if (ret != 0)
503         goto done;
504
505     ret = lms_db_bind_int(stmt, 4, finfo->size);
506     if (ret != 0)
507         goto done;
508
509     r = sqlite3_step(stmt);
510     if (r != SQLITE_DONE) {
511         fprintf(stderr, "ERROR: could not insert file info: %s\n",
512                 sqlite3_errmsg(db->handle));
513         ret = -5;
514         goto done;
515     }
516
517     finfo->id = sqlite3_last_insert_rowid(db->handle);
518     ret = 0;
519
520   done:
521     lms_db_reset_stmt(stmt);
522
523     return ret;
524 }
525
526 static int
527 _db_delete_file_info(struct db *db, struct lms_file_info *finfo)
528 {
529     sqlite3_stmt *stmt;
530     int r, ret;
531
532     stmt = db->delete_file_info;
533
534     ret = lms_db_bind_int64(stmt, 1, finfo->id);
535     if (ret != 0)
536         goto done;
537
538     r = sqlite3_step(stmt);
539     if (r != SQLITE_DONE) {
540         fprintf(stderr, "ERROR: could not delete file info: %s\n",
541                 sqlite3_errmsg(db->handle));
542         ret = -2;
543         goto done;
544     }
545     ret = 0;
546
547   done:
548     lms_db_reset_stmt(stmt);
549
550     return ret;
551 }
552
553 static int
554 _db_clear_file_dtime(struct db *db, struct lms_file_info *finfo)
555 {
556     sqlite3_stmt *stmt;
557     int r, ret;
558
559     stmt = db->clear_file_dtime;
560
561     ret = lms_db_bind_int64(stmt, 1, finfo->id);
562     if (ret != 0)
563         goto done;
564
565     r = sqlite3_step(stmt);
566     if (r != SQLITE_DONE) {
567         fprintf(stderr, "ERROR: could not clear file dtime: %s\n",
568                 sqlite3_errmsg(db->handle));
569         ret = -2;
570         goto done;
571     }
572
573     finfo->dtime = 0;
574     ret = 0;
575
576   done:
577     lms_db_reset_stmt(stmt);
578
579     return ret;
580 }
581
582 static int
583 _retrieve_file_status(struct db *db, struct lms_file_info *finfo)
584 {
585     struct stat st;
586     int r;
587
588     if (stat(finfo->path, &st) != 0) {
589         perror("stat");
590         return -1;
591     }
592
593     r = _db_get_file_info(db, finfo);
594     if (r == 0) {
595         if (st.st_mtime <= finfo->mtime && finfo->size == st.st_size)
596             return 0;
597         else {
598             finfo->mtime = st.st_mtime;
599             finfo->size = st.st_size;
600             return 1;
601         }
602     } else if (r == 1) {
603         finfo->mtime = st.st_mtime;
604         finfo->size = st.st_size;
605         return 1;
606     } else
607         return -2;
608 }
609
610 static void
611 _ctxt_init(struct lms_context *ctxt, const lms_t *lms, const struct db *db)
612 {
613     ctxt->cs_conv = lms->cs_conv;
614     ctxt->db = db->handle;
615 }
616
617 static int _parser_del_int(lms_t *lms, int i);
618
619 static int
620 _parsers_setup(lms_t *lms, struct db *db)
621 {
622     struct lms_context ctxt;
623     int i;
624
625     _ctxt_init(&ctxt, lms, db);
626
627     for (i = 0; i < lms->n_parsers; i++) {
628         lms_plugin_t *plugin;
629         int r;
630
631         plugin = lms->parsers[i].plugin;
632         r = plugin->setup(plugin, &ctxt);
633         if (r != 0) {
634             fprintf(stderr, "ERROR: parser \"%s\" failed to setup: %d.\n",
635                     plugin->name, r);
636             plugin->finish(plugin, &ctxt);
637             _parser_del_int(lms, i);
638             i--; /* cancel i++ */
639         }
640     }
641
642     return 0;
643 }
644
645 static int
646 _parsers_start(lms_t *lms, struct db *db)
647 {
648     struct lms_context ctxt;
649     int i;
650
651     _ctxt_init(&ctxt, lms, db);
652
653     for (i = 0; i < lms->n_parsers; i++) {
654         lms_plugin_t *plugin;
655         int r;
656
657         plugin = lms->parsers[i].plugin;
658         r = plugin->start(plugin, &ctxt);
659         if (r != 0) {
660             fprintf(stderr, "ERROR: parser \"%s\" failed to start: %d.\n",
661                     plugin->name, r);
662             plugin->finish(plugin, &ctxt);
663             _parser_del_int(lms, i);
664             i--; /* cancel i++ */
665         }
666     }
667
668     return 0;
669 }
670
671 static int
672 _parsers_finish(lms_t *lms, struct db *db)
673 {
674     struct lms_context ctxt;
675     int i;
676
677     _ctxt_init(&ctxt, lms, db);
678
679     for (i = 0; i < lms->n_parsers; i++) {
680         lms_plugin_t *plugin;
681         int r;
682
683         plugin = lms->parsers[i].plugin;
684         r = plugin->finish(plugin, &ctxt);
685         if (r != 0)
686             fprintf(stderr, "ERROR: parser \"%s\" failed to finish: %d.\n",
687                     plugin->name, r);
688     }
689
690     return 0;
691 }
692
693 static int
694 _parsers_check_using(lms_t *lms, void **parser_match, struct lms_file_info *finfo)
695 {
696     int used, i;
697
698     used = 0;
699     for (i = 0; i < lms->n_parsers; i++) {
700         lms_plugin_t *plugin;
701         void *r;
702
703         plugin = lms->parsers[i].plugin;
704         r = plugin->match(plugin, finfo->path, finfo->path_len, finfo->base);
705         parser_match[i] = r;
706         if (r)
707             used = 1;
708     }
709
710     return used;
711 }
712
713 static int
714 _parsers_run(lms_t *lms, struct db *db, void **parser_match, struct lms_file_info *finfo)
715 {
716     struct lms_context ctxt;
717     int i, failed, available;
718
719     _ctxt_init(&ctxt, lms, db);
720
721     failed = 0;
722     available = 0;
723     for (i = 0; i < lms->n_parsers; i++) {
724         lms_plugin_t *plugin;
725
726         plugin = lms->parsers[i].plugin;
727         if (parser_match[i]) {
728             int r;
729
730             available++;
731             r = plugin->parse(plugin, &ctxt, finfo, parser_match[i]);
732             if (r != 0)
733                 failed++;
734         }
735     }
736
737     if (!failed)
738         return 0;
739     else if (failed == available)
740         return -1;
741     else
742         return 1; /* non critical */
743 }
744
745 static int
746 _slave_work(lms_t *lms, struct fds *fds)
747 {
748     int r, len, base, counter;
749     char path[PATH_SIZE];
750     void **parser_match;
751     struct db *db;
752
753     db = _db_open(lms->db_path);
754     if (!db)
755         return -1;
756
757     if (_parsers_setup(lms, db) != 0) {
758         fprintf(stderr, "ERROR: could not setup parsers.\n");
759         r = -2;
760         goto end;
761     }
762
763     if (_db_compile_all_stmts(db) != 0) {
764         fprintf(stderr, "ERROR: could not compile statements.\n");
765         r = -3;
766         goto end;
767     }
768
769     if (_parsers_start(lms, db) != 0) {
770         fprintf(stderr, "ERROR: could not start parsers.\n");
771         r = -4;
772         goto end;
773     }
774     if (lms->n_parsers < 1) {
775         fprintf(stderr, "ERROR: no parser could be started, exit.\n");
776         r = -5;
777         goto end;
778     }
779
780     parser_match = malloc(lms->n_parsers * sizeof(*parser_match));
781     if (!parser_match) {
782         perror("malloc");
783         r = -6;
784         goto end;
785     }
786
787     counter = 0;
788     _db_begin_transaction(db);
789
790     while (((r = _slave_recv_path(fds, &len, &base, path)) == 0) && len > 0) {
791         struct lms_file_info finfo;
792         int used, r;
793
794         finfo.path = path;
795         finfo.path_len = len;
796         finfo.base = base;
797
798         r = _retrieve_file_status(db, &finfo);
799         if (r == 0) {
800             if (finfo.dtime)
801                 _db_clear_file_dtime(db, &finfo);
802             goto inform_end;
803         } else if (r < 0) {
804             fprintf(stderr, "ERROR: could not detect file status.\n");
805             goto inform_end;
806         }
807
808         used = _parsers_check_using(lms, parser_match, &finfo);
809         if (!used)
810             goto inform_end;
811
812         finfo.dtime = 0;
813         if (finfo.id > 0)
814             r = _db_update_file_info(db, &finfo);
815         else
816             r = _db_insert_file_info(db, &finfo);
817         if (r < 0) {
818             fprintf(stderr, "ERROR: could not register path in DB\n");
819             goto inform_end;
820         }
821
822         r = _parsers_run(lms, db, parser_match, &finfo);
823         if (r < 0) {
824             fprintf(stderr, "ERROR: pid=%d failed to parse \"%s\".\n",
825                     getpid(), finfo.path);
826             _db_delete_file_info(db, &finfo);
827         }
828
829       inform_end:
830         _slave_send_reply(fds, r);
831         counter++;
832         if (counter > lms->commit_interval) {
833             _db_end_transaction(db);
834             _db_begin_transaction(db);
835             counter = 0;
836         }
837     }
838
839     free(parser_match);
840     _db_end_transaction(db);
841   end:
842     _parsers_finish(lms, db);
843     _db_close(db);
844
845     return r;
846 }
847
848
849 /***********************************************************************
850  * Master-side.
851  ***********************************************************************/
852
853 static int
854 _consume_garbage(struct pollfd *pfd)
855 {
856     int r;
857
858     while ((r = poll(pfd, 1, 0)) > 0) {
859         if (pfd->revents & (POLLERR | POLLHUP | POLLNVAL))
860             return 0;
861         else if (pfd->revents & POLLIN) {
862             char c;
863
864             read(pfd->fd, &c, sizeof(c));
865         }
866     }
867
868     return r;
869 }
870
871 static int
872 _close_fds(struct fds *fds)
873 {
874     int r;
875
876     r = 0;
877     if (close(fds->r) != 0) {
878         r--;
879         perror("close");
880     }
881
882     if (close(fds->w) != 0) {
883         r--;
884         perror("close");
885     }
886
887     return r;
888 }
889
890 static int
891 _close_pipes(struct pinfo *pinfo)
892 {
893     int r;
894
895     r = _close_fds(&pinfo->master);
896     r += _close_fds(&pinfo->slave);
897
898     return r;
899 }
900
901 static int
902 _create_pipes(struct pinfo *pinfo)
903 {
904     int fds[2];
905
906     if (pipe(fds) != 0) {
907         perror("pipe");
908         return -1;
909     }
910     pinfo->master.r = fds[0];
911     pinfo->slave.w = fds[1];
912
913     if (pipe(fds) != 0) {
914         perror("pipe");
915         close(pinfo->master.r);
916         close(pinfo->slave.w);
917         return -1;
918     }
919     pinfo->slave.r = fds[0];
920     pinfo->master.w = fds[1];
921
922     pinfo->poll.fd = pinfo->master.r;
923     pinfo->poll.events = POLLIN;
924
925     return 0;
926 }
927
928 static int
929 _create_slave(struct pinfo *pinfo)
930 {
931     int r;
932
933     pinfo->child = fork();
934     if (pinfo->child == -1) {
935         perror("fork");
936         return -1;
937     }
938
939     if (pinfo->child > 0)
940         return 0;
941
942     _close_fds(&pinfo->master);
943     nice(19);
944     r = _slave_work(pinfo->lms, &pinfo->slave);
945     lms_free(pinfo->lms);
946     _exit(r);
947     return r; /* shouldn't reach anyway... */
948 }
949
950 static int
951 _waitpid(pid_t pid)
952 {
953     int status;
954     pid_t r;
955
956     r = waitpid(pid, &status, 0);
957     if (r > -1)
958         return 0;
959     else
960         perror("waitpid");
961
962     return r;
963 }
964
965 static int
966 _finish_slave(struct pinfo *pinfo)
967 {
968     int r;
969
970     if (pinfo->child <= 0)
971         return 0;
972
973     r = _master_send_finish(&pinfo->master);
974     if (r == 0)
975         r = _waitpid(pinfo->child);
976     else {
977         r = kill(pinfo->child, SIGKILL);
978         if (r < 0)
979             perror("kill");
980         else
981             r =_waitpid(pinfo->child);
982     }
983
984     return r;
985 }
986
987 static int
988 _restart_slave(struct pinfo *pinfo)
989 {
990     int status;
991
992     if (waitpid(pinfo->child, &status, WNOHANG) > 0) {
993         if (WIFEXITED(status)) {
994             int code;
995
996             code = WEXITSTATUS(status);
997             if (code != 0) {
998                 fprintf(stderr, "ERROR: slave returned %d, exit.\n", code);
999                 pinfo->child = 0;
1000                 return -1;
1001             }
1002         } else {
1003             if (WIFSIGNALED(status)) {
1004                 int code;
1005
1006                 code = WTERMSIG(status);
1007                 fprintf(stderr, "ERROR: slave was terminated by signal %d.\n",
1008                         code);
1009             }
1010             pinfo->child = 0;
1011             return -1;
1012         }
1013     }
1014
1015     if (kill(pinfo->child, SIGKILL))
1016         perror("kill");
1017
1018     if (waitpid(pinfo->child, &status, 0) < 0)
1019         perror("waitpid");
1020
1021     _consume_garbage(&pinfo->poll);
1022     return _create_slave(pinfo);
1023 }
1024
1025 static int
1026 _strcat(int base, char *path, const char *name)
1027 {
1028     int new_len, name_len;
1029
1030     name_len = strlen(name);
1031     new_len = base + name_len;
1032
1033     if (new_len >= PATH_SIZE) {
1034         path[base] = '\0';
1035         fprintf(stderr,
1036                 "ERROR: path concatenation too long %d of %d "
1037                 "available: \"%s\" + \"%s\"\n", new_len, PATH_SIZE,
1038                 path, name);
1039         return -1;
1040     }
1041
1042     memcpy(path + base, name, name_len + 1);
1043
1044     return new_len;
1045 }
1046
1047 static int
1048 _process_file(struct pinfo *pinfo, int base, char *path, const char *name)
1049 {
1050     int new_len, reply, r;
1051
1052     new_len = _strcat(base, path, name);
1053     if (new_len < 0)
1054         return -1;
1055
1056     if (_master_send_path(&pinfo->master, new_len, base, path) != 0)
1057         return -2;
1058
1059     r = _master_recv_reply(&pinfo->master, &pinfo->poll, &reply,
1060                            pinfo->lms->slave_timeout);
1061     if (r < 0)
1062         return -3;
1063     else if (r == 1) {
1064         fprintf(stderr, "ERROR: slave took too long, restart %d\n",
1065                 pinfo->child);
1066         if (_restart_slave(pinfo) != 0)
1067             return -4;
1068         return 1;
1069     } else {
1070         if (reply < 0) {
1071             /* XXX callback library users to inform error. */
1072             fprintf(stderr, "ERROR: pid=%d failed to parse \"%s\".\n",
1073                     getpid(), path);
1074             return (-reply) << 8;
1075         } else
1076             return reply;
1077     }
1078 }
1079
1080 static int
1081 _process_dir(struct pinfo *pinfo, int base, char *path, const char *name)
1082 {
1083     DIR *dir;
1084     struct dirent *de;
1085     int new_len, r;
1086
1087     new_len = _strcat(base, path, name);
1088     if (new_len < 0)
1089         return -1;
1090     else if (new_len + 1 >= PATH_SIZE) {
1091         fprintf(stderr, "ERROR: path too long\n");
1092         return 2;
1093     }
1094
1095     dir = opendir(path);
1096     if (dir == NULL) {
1097         perror("opendir");
1098         return 3;
1099     }
1100
1101     path[new_len] = '/';
1102     new_len++;
1103
1104     r = 0;
1105     while ((de = readdir(dir)) != NULL) {
1106         if (de->d_name[0] == '.')
1107             continue;
1108         if (de->d_type == DT_REG) {
1109             if (_process_file(pinfo, new_len, path, de->d_name) < 0) {
1110                 path[new_len - 1] = '\0';
1111                 fprintf(stderr,
1112                         "ERROR: unrecoverable error parsing file, "
1113                         "exit \"%s\".\n", path);
1114                 r = -4;
1115                 goto end;
1116             }
1117         } else if (de->d_type == DT_DIR || de->d_type == DT_UNKNOWN) {
1118             if (_process_dir(pinfo, new_len, path, de->d_name) < 0) {
1119                 path[new_len - 1] = '\0';
1120                 fprintf(stderr,
1121                         "ERROR: unrecoverable error parsing dir, "
1122                         "exit \"%s\".\n", path);
1123                 r = -5;
1124                 goto end;
1125             }
1126         }
1127     }
1128
1129   end:
1130     closedir(dir);
1131     return r;
1132 }
1133
1134
1135 /***********************************************************************
1136  * Plugins handling.
1137  ***********************************************************************/
1138
1139 static int
1140 _parser_load(struct parser *p, const char *so_path)
1141 {
1142     lms_plugin_t *(*plugin_open)(void);
1143     char *errmsg;
1144
1145     memset(p, 0, sizeof(*p));
1146
1147     p->dl_handle = dlopen(so_path, RTLD_NOW | RTLD_LOCAL);
1148     errmsg = dlerror();
1149     if (errmsg) {
1150         fprintf(stderr, "ERROR: could not dlopen() %s\n", errmsg);
1151         return -1;
1152     }
1153
1154     plugin_open = dlsym(p->dl_handle, "lms_plugin_open");
1155     errmsg = dlerror();
1156     if (errmsg) {
1157         fprintf(stderr, "ERROR: could not find plugin entry point %s\n",
1158                 errmsg);
1159         return -2;
1160     }
1161
1162     p->so_path = strdup(so_path);
1163     if (!p->so_path) {
1164         perror("strdup");
1165         return -3;
1166     }
1167
1168     p->plugin = plugin_open();
1169     if (!p->plugin) {
1170         fprintf(stderr, "ERROR: plugin \"%s\" failed to init.\n", so_path);
1171         return -4;
1172     }
1173
1174     return 0;
1175 }
1176
1177 static int
1178 _parser_unload(struct parser *p)
1179 {
1180     int r;
1181
1182     r = 0;
1183     if (p->plugin) {
1184         if (p->plugin->close(p->plugin) != 0) {
1185             fprintf(stderr, "ERROR: plugin \"%s\" failed to deinit.\n",
1186                     p->so_path);
1187             r -= 1;
1188         }
1189     }
1190
1191     if (p->dl_handle) {
1192         char *errmsg;
1193
1194         dlclose(p->dl_handle);
1195         errmsg = dlerror();
1196         if (errmsg) {
1197             fprintf(stderr, "ERROR: could not dlclose() plugin \"%s\": %s\n",
1198                     errmsg, p->so_path);
1199             r -= 1;
1200         }
1201     }
1202
1203     if (p->so_path)
1204         free(p->so_path);
1205
1206     return r;
1207 }
1208
1209
1210 /***********************************************************************
1211  * Public API.
1212  ***********************************************************************/
1213 lms_t *
1214 lms_new(const char *db_path)
1215 {
1216     lms_t *lms;
1217
1218     lms = calloc(1, sizeof(lms_t));
1219     if (!lms) {
1220         perror("calloc");
1221         return NULL;
1222     }
1223
1224     lms->cs_conv = lms_charset_conv_new();
1225     if (!lms->cs_conv) {
1226         free(lms);
1227         return NULL;
1228     }
1229
1230     lms->commit_interval = DEFAULT_COMMIT_INTERVAL;
1231     lms->slave_timeout = DEFAULT_SLAVE_TIMEOUT;
1232     lms->db_path = strdup(db_path);
1233     if (!lms->db_path) {
1234         perror("strdup");
1235         lms_charset_conv_free(lms->cs_conv);
1236         free(lms);
1237         return NULL;
1238     }
1239
1240     return lms;
1241 }
1242
1243 int
1244 lms_free(lms_t *lms)
1245 {
1246     int i;
1247
1248     if (!lms)
1249         return 0;
1250
1251     if (lms->is_processing)
1252         return -1;
1253
1254     if (lms->parsers) {
1255         for (i = 0; i < lms->n_parsers; i++)
1256             _parser_unload(lms->parsers + i);
1257
1258         free(lms->parsers);
1259     }
1260
1261     free(lms->db_path);
1262     lms_charset_conv_free(lms->cs_conv);
1263     free(lms);
1264     return 0;
1265 }
1266
1267 lms_plugin_t *
1268 lms_parser_add(lms_t *lms, const char *so_path)
1269 {
1270     struct parser *parser;
1271
1272     if (!lms)
1273         return NULL;
1274
1275     if (!so_path)
1276         return NULL;
1277
1278     if (lms->is_processing) {
1279         fprintf(stderr, "ERROR: do not add parsers while it's processing.\n");
1280         return NULL;
1281     }
1282
1283     lms->parsers = realloc(lms->parsers,
1284                            (lms->n_parsers + 1) * sizeof(struct parser));
1285     if (!lms->parsers) {
1286         perror("realloc");
1287         return NULL;
1288     }
1289
1290     parser = lms->parsers + lms->n_parsers;
1291     if (_parser_load(parser, so_path) != 0) {
1292         _parser_unload(parser);
1293         return NULL;
1294     }
1295
1296     lms->n_parsers++;
1297     return parser->plugin;
1298 }
1299
1300 lms_plugin_t *
1301 lms_parser_find_and_add(lms_t *lms, const char *name)
1302 {
1303     char so_path[PATH_MAX];
1304
1305     if (!lms)
1306         return NULL;
1307     if (!name)
1308         return NULL;
1309
1310     snprintf(so_path, sizeof(so_path), "%s/%s.so", PLUGINSDIR, name);
1311     return lms_parser_add(lms, so_path);
1312 }
1313
1314 static int
1315 _parser_del_int(lms_t *lms, int i)
1316 {
1317     struct parser *parser;
1318
1319     parser = lms->parsers + i;
1320     _parser_unload(parser);
1321     lms->n_parsers--;
1322
1323     if (lms->n_parsers == 0) {
1324         free(lms->parsers);
1325         lms->parsers = NULL;
1326         return 0;
1327     } else {
1328         int dif;
1329
1330         dif = lms->n_parsers - i;
1331         if (dif)
1332             lms->parsers = memmove(parser, parser + 1,
1333                                    dif * sizeof(struct parser));
1334
1335         lms->parsers = realloc(lms->parsers,
1336                                lms->n_parsers * sizeof(struct parser));
1337         if (!lms->parsers) {
1338             lms->n_parsers = 0;
1339             return -1;
1340         }
1341
1342         return 0;
1343     }
1344 }
1345
1346 int
1347 lms_parser_del(lms_t *lms, lms_plugin_t *handle)
1348 {
1349     int i;
1350
1351     if (!lms)
1352         return -1;
1353     if (!handle)
1354         return -2;
1355     if (!lms->parsers)
1356         return -3;
1357     if (lms->is_processing) {
1358         fprintf(stderr, "ERROR: do not del parsers while it's processing.\n");
1359         return -4;
1360     }
1361
1362     for (i = 0; i < lms->n_parsers; i++)
1363         if (lms->parsers[i].plugin == handle)
1364             return _parser_del_int(lms, i);
1365
1366     return -3;
1367 }
1368
1369 int
1370 lms_process(lms_t *lms, const char *top_path)
1371 {
1372     struct pinfo pinfo;
1373     int r, len;
1374     char path[PATH_SIZE], *bname;
1375
1376     if (!lms) {
1377         r = -1;
1378         goto end;
1379     }
1380
1381     if (!top_path) {
1382         r = -2;
1383         goto end;
1384     }
1385
1386     if (lms->is_processing) {
1387         fprintf(stderr, "ERROR: is already processing.\n");
1388         r = -3;
1389         goto end;
1390     }
1391
1392     if (!lms->parsers) {
1393         fprintf(stderr, "ERROR: no plugins registered.\n");
1394         r = -4;
1395         goto end;
1396     }
1397
1398     pinfo.lms = lms;
1399
1400     if (_create_pipes(&pinfo) != 0) {
1401         r = -5;
1402         goto end;
1403     }
1404
1405     if (_create_slave(&pinfo) != 0) {
1406         r = -6;
1407         goto close_pipes;
1408     }
1409
1410     if (realpath(top_path, path) == NULL) {
1411         perror("realpath");
1412         r = -7;
1413         goto finish_slave;
1414     }
1415
1416     /* search '/' backwards, split dirname and basename, note realpath usage */
1417     len = strlen(path);
1418     for (; len >= 0 && path[len] != '/'; len--);
1419     len++;
1420     bname = strdup(path + len);
1421     if (bname == NULL) {
1422         perror("strdup");
1423         r = -8;
1424         goto finish_slave;
1425     }
1426
1427     lms->is_processing = 1;
1428     r = _process_dir(&pinfo, len, path, bname);
1429     lms->is_processing = 0;
1430     free(bname);
1431
1432   finish_slave:
1433     _finish_slave(&pinfo);
1434   close_pipes:
1435     _close_pipes(&pinfo);
1436   end:
1437     return r;
1438 }
1439
1440 int
1441 lms_is_processing(const lms_t *lms)
1442 {
1443     if (!lms) {
1444         fprintf(stderr, "ERROR: lms_is_processing(NULL)\n");
1445         return -1;
1446     }
1447
1448     return lms->is_processing;
1449 }
1450
1451 const char *
1452 lms_get_db_path(const lms_t *lms)
1453 {
1454     if (!lms) {
1455         fprintf(stderr, "ERROR: lms_get_db_path(NULL)\n");
1456         return NULL;
1457     }
1458
1459     return lms->db_path;
1460 }
1461
1462 int
1463 lms_get_slave_timeout(const lms_t *lms)
1464 {
1465     if (!lms) {
1466         fprintf(stderr, "ERROR: lms_get_slave_timeout(NULL)\n");
1467         return -1;
1468     }
1469
1470     return lms->slave_timeout;
1471 }
1472
1473 void lms_set_slave_timeout(lms_t *lms, int ms)
1474 {
1475     if (!lms) {
1476         fprintf(stderr, "ERROR: lms_set_slave_timeout(NULL, %d)\n", ms);
1477         return;
1478     }
1479
1480     lms->slave_timeout = ms;
1481 }
1482
1483 unsigned int
1484 lms_get_commit_interval(const lms_t *lms)
1485 {
1486     if (!lms) {
1487         fprintf(stderr, "ERROR: lms_get_commit_interval(NULL)\n");
1488         return (unsigned int)-1;
1489     }
1490
1491     return lms->commit_interval;
1492 }
1493
1494 void
1495 lms_set_commit_interval(lms_t *lms, unsigned int transactions)
1496 {
1497     if (!lms) {
1498         fprintf(stderr, "ERROR: lms_set_commit_interval(NULL, %u)\n",
1499                 transactions);
1500         return;
1501     }
1502
1503     lms->commit_interval = transactions;
1504 }
1505
1506 int
1507 lms_charset_add(lms_t *lms, const char *charset)
1508 {
1509     if (!lms) {
1510         fprintf(stderr, "ERROR: lms_charset_add(NULL)\n");
1511         return -1;
1512     }
1513
1514     return lms_charset_conv_add(lms->cs_conv, charset);
1515 }
1516
1517 int
1518 lms_charset_del(lms_t *lms, const char *charset)
1519 {
1520     if (!lms) {
1521         fprintf(stderr, "ERROR: lms_charset_del(NULL)\n");
1522         return -1;
1523     }
1524
1525     return lms_charset_conv_del(lms->cs_conv, charset);
1526 }