1 /*
2 * MOC - music on console
3 * Copyright (C) 2005, 2006 Damian Pietras <daper@daper.net>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 */
11
12 #ifdef HAVE_CONFIG_H
13 # include "config.h"
14 #endif
15
16 #include <pthread.h>
17 #include <assert.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <stdio.h>
21 #include <errno.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <time.h>
25 #include <unistd.h>
26
27 #ifdef HAVE_DB_H
28 #include <db.h>
29 #endif
30
31 /* Include dirent for various systems */
32 #ifdef HAVE_DIRENT_H
33 # include <dirent.h>
34 #else
35 # define dirent direct
36 # if HAVE_SYS_NDIR_H
37 # include <sys/ndir.h>
38 # endif
39 #endif
40
41 #define DEBUG
42
43 #include "common.h"
44 #include "server.h"
45 #include "playlist.h"
46 #include "rbtree.h"
47 #include "files.h"
48 #include "tags_cache.h"
49 #include "log.h"
50 #include "audio.h"
51
52 /* The name of the tags database in the cache directory. */
53 #define TAGS_DB "tags.db"
54
55 /* The name of the version tag file in the cache directory. */
56 #define MOC_VERSION_TAG "moc_version_tag"
57
58 /* The maximum length of the version tag (including trailing NULL). */
59 #define VERSION_TAG_MAX 64
60
61 /* Number used to create cache version tag to detect incompatibilities
62 * between cache version stored on the disk and MOC/BerkeleyDB environment.
63 *
64 * If you modify the DB structure, increase this number. You can also
65 * temporarily set it to zero to disable cache activity during structural
66 * changes which require multiple commits.
67 */
68 #define CACHE_DB_FORMAT_VERSION 1
69
70 /* How frequently to flush the tags database to disk. A value of zero
71 * disables flushing. */
72 #define DB_SYNC_COUNT 5
73
74 /* Element of a requests queue. */
75 struct request_queue_node
76 {
77 struct request_queue_node *next;
78 char *file; /* file that this request is for (malloc()ed) */
79 int tags_sel; /* which tags to read (TAGS_*) */
80 };
81
82 struct cache_record
83 {
84 time_t mod_time; /* last modification time of the file */
85 time_t atime; /* Time of last access. */
86 struct file_tags *tags;
87 };
88
request_queue_init(struct request_queue * q)89 static void request_queue_init (struct request_queue *q)
90 {
91 assert (q != NULL);
92
93 q->head = NULL;
94 q->tail = NULL;
95 }
96
request_queue_clear(struct request_queue * q)97 static void request_queue_clear (struct request_queue *q)
98 {
99 assert (q != NULL);
100
101 while (q->head) {
102 struct request_queue_node *o = q->head;
103
104 q->head = q->head->next;
105
106 free (o->file);
107 free (o);
108 }
109
110 q->tail = NULL;
111 }
112
113 /* Remove items from the queue from the beginning to the specified file. */
request_queue_clear_up_to(struct request_queue * q,const char * file)114 static void request_queue_clear_up_to (struct request_queue *q,
115 const char *file)
116 {
117 int stop = 0;
118
119 assert (q != NULL);
120
121 while (q->head && !stop) {
122 struct request_queue_node *o = q->head;
123
124 q->head = q->head->next;
125
126 if (!strcmp (o->file, file))
127 stop = 1;
128
129 free (o->file);
130 free (o);
131 }
132
133 if (!q->head)
134 q->tail = NULL;
135 }
136
request_queue_add(struct request_queue * q,const char * file,int tags_sel)137 static void request_queue_add (struct request_queue *q, const char *file,
138 int tags_sel)
139 {
140 assert (q != NULL);
141
142 if (!q->head) {
143 q->head = (struct request_queue_node *)xmalloc (
144 sizeof(struct request_queue_node));
145 q->tail = q->head;
146 }
147 else {
148 assert (q->tail != NULL);
149 assert (q->tail->next == NULL);
150
151 q->tail->next = (struct request_queue_node *)xmalloc (
152 sizeof(struct request_queue_node));
153 q->tail = q->tail->next;
154 }
155
156 q->tail->file = xstrdup (file);
157 q->tail->tags_sel = tags_sel;
158 q->tail->next = NULL;
159 }
160
request_queue_empty(const struct request_queue * q)161 static int request_queue_empty (const struct request_queue *q)
162 {
163 assert (q != NULL);
164
165 return q->head == NULL;
166 }
167
168 /* Get the file name of the first element in the queue or NULL if the queue is
169 * empty. Put tags to be read in *tags_sel. Returned memory is malloc()ed. */
request_queue_pop(struct request_queue * q,int * tags_sel)170 static char *request_queue_pop (struct request_queue *q, int *tags_sel)
171 {
172 struct request_queue_node *n;
173 char *file;
174
175 assert (q != NULL);
176
177 if (q->head == NULL)
178 return NULL;
179
180 n = q->head;
181 q->head = n->next;
182 file = n->file;
183 *tags_sel = n->tags_sel;
184 free (n);
185
186 if (q->tail == n)
187 q->tail = NULL; /* the queue is empty */
188
189 return file;
190 }
191
192 #ifdef HAVE_DB_H
strlen_null(const char * s)193 static size_t strlen_null (const char *s)
194 {
195 return s ? strlen (s) : 0;
196 }
197 #endif
198
199 #ifdef HAVE_DB_H
cache_record_serialize(const struct cache_record * rec,int * len)200 static char *cache_record_serialize (const struct cache_record *rec, int *len)
201 {
202 char *buf;
203 char *p;
204 size_t artist_len;
205 size_t album_len;
206 size_t title_len;
207
208 artist_len = strlen_null (rec->tags->artist);
209 album_len = strlen_null (rec->tags->album);
210 title_len = strlen_null (rec->tags->title);
211
212 *len = sizeof(rec->mod_time)
213 + sizeof(rec->atime)
214 + sizeof(size_t) * 3 /* lengths of title, artist, time. */
215 + artist_len
216 + album_len
217 + title_len
218 + sizeof(rec->tags->track)
219 + sizeof(rec->tags->time);
220
221 buf = p = (char *)xmalloc (*len);
222
223 memcpy (p, &rec->mod_time, sizeof(rec->mod_time));
224 p += sizeof(rec->mod_time);
225
226 memcpy (p, &rec->atime, sizeof(rec->atime));
227 p += sizeof(rec->atime);
228
229 memcpy (p, &artist_len, sizeof(artist_len));
230 p += sizeof(artist_len);
231 if (artist_len) {
232 memcpy (p, rec->tags->artist, artist_len);
233 p += artist_len;
234 }
235
236 memcpy (p, &album_len, sizeof(album_len));
237 p += sizeof(album_len);
238 if (album_len) {
239 memcpy (p, rec->tags->album, album_len);
240 p += album_len;
241 }
242
243 memcpy (p, &title_len, sizeof(title_len));
244 p += sizeof(title_len);
245 if (title_len) {
246 memcpy (p, rec->tags->title, title_len);
247 p += title_len;
248 }
249
250 memcpy (p, &rec->tags->track, sizeof(rec->tags->track));
251 p += sizeof(rec->tags->track);
252
253 memcpy (p, &rec->tags->time, sizeof(rec->tags->time));
254 p += sizeof(rec->tags->time);
255
256 return buf;
257 }
258 #endif
259
260 #ifdef HAVE_DB_H
cache_record_deserialize(struct cache_record * rec,const char * serialized,size_t size,int skip_tags)261 static int cache_record_deserialize (struct cache_record *rec,
262 const char *serialized, size_t size, int skip_tags)
263 {
264 const char *p = serialized;
265 size_t bytes_left = size;
266 size_t str_len;
267
268 assert (rec != NULL);
269 assert (serialized != NULL);
270
271 if (!skip_tags)
272 rec->tags = tags_new ();
273 else
274 rec->tags = NULL;
275
276 #define extract_num(var) \
277 if (bytes_left < sizeof(var)) \
278 goto err; \
279 memcpy (&var, p, sizeof(var)); \
280 bytes_left -= sizeof(var); \
281 p += sizeof(var);
282
283 #define extract_str(var) \
284 if (bytes_left < sizeof(str_len)) \
285 goto err; \
286 memcpy (&str_len, p, sizeof(str_len)); \
287 p += sizeof(str_len); \
288 if (bytes_left < str_len) \
289 goto err; \
290 var = xmalloc (str_len + 1); \
291 memcpy (var, p, str_len); \
292 var[str_len] = '\0'; \
293 p += str_len;
294
295 extract_num (rec->mod_time);
296 extract_num (rec->atime);
297
298 if (!skip_tags) {
299 extract_str (rec->tags->artist);
300 extract_str (rec->tags->album);
301 extract_str (rec->tags->title);
302 extract_num (rec->tags->track);
303 extract_num (rec->tags->time);
304
305 if (rec->tags->title)
306 rec->tags->filled |= TAGS_COMMENTS;
307 else {
308 if (rec->tags->artist)
309 free (rec->tags->artist);
310 rec->tags->artist = NULL;
311
312 if (rec->tags->album)
313 free (rec->tags->album);
314 rec->tags->album = NULL;
315 }
316
317 if (rec->tags->time >= 0)
318 rec->tags->filled |= TAGS_TIME;
319 }
320
321 return 1;
322
323 err:
324 logit ("Cache record deserialization error at %tdB", p - serialized);
325 tags_free (rec->tags);
326 rec->tags = NULL;
327 return 0;
328 }
329 #endif
330
331 /* Locked DB function prototype.
332 * The function must not acquire or release DB locks. */
333 #ifdef HAVE_DB_H
334 typedef void *t_locked_fn (struct tags_cache *, const char *,
335 int, int, DBT *, DBT *);
336 #endif
337
338 /* This function ensures that a DB function takes place while holding a
339 * database record lock. It also provides an initialised database thang
340 * for the key and record. */
341 #ifdef HAVE_DB_H
with_db_lock(t_locked_fn fn,struct tags_cache * c,const char * file,int tags_sel,int client_id)342 static void *with_db_lock (t_locked_fn fn, struct tags_cache *c,
343 const char *file, int tags_sel, int client_id)
344 {
345 int rc;
346 void *result;
347 DB_LOCK lock;
348 DBT key, record;
349
350 assert (c->db_env != NULL);
351
352 memset (&key, 0, sizeof (key));
353 key.data = (void *) file;
354 key.size = strlen (file);
355
356 memset (&record, 0, sizeof (record));
357 record.flags = DB_DBT_MALLOC;
358
359 rc = c->db_env->lock_get (c->db_env, c->locker, 0,
360 &key, DB_LOCK_WRITE, &lock);
361 if (rc)
362 fatal ("Can't get DB lock: %s", db_strerror (rc));
363
364 result = fn (c, file, tags_sel, client_id, &key, &record);
365
366 rc = c->db_env->lock_put (c->db_env, &lock);
367 if (rc)
368 fatal ("Can't release DB lock: %s", db_strerror (rc));
369
370 if (record.data)
371 free (record.data);
372
373 return result;
374 }
375 #endif
376
377 #ifdef HAVE_DB_H
tags_cache_remove_rec(struct tags_cache * c,const char * fname)378 static void tags_cache_remove_rec (struct tags_cache *c, const char *fname)
379 {
380 DBT key;
381 int ret;
382
383 assert (fname != NULL);
384
385 debug ("Removing %s from the cache...", fname);
386
387 memset (&key, 0, sizeof(key));
388 key.data = (void *)fname;
389 key.size = strlen (fname);
390
391 ret = c->db->del (c->db, NULL, &key, 0);
392 if (ret)
393 logit ("Can't remove item for %s from the cache: %s", fname,
394 db_strerror (ret));
395 }
396 #endif
397
398 /* Remove the one element of the cache based on it's access time. */
399 #ifdef HAVE_DB_H
tags_cache_gc(struct tags_cache * c)400 static void tags_cache_gc (struct tags_cache *c)
401 {
402 DBC *cur;
403 DBT key;
404 DBT serialized_cache_rec;
405 int ret;
406 char *last_referenced = NULL;
407 time_t last_referenced_atime = time (NULL) + 1;
408 int nitems = 0;
409
410 c->db->cursor (c->db, NULL, &cur, 0);
411
412 memset (&key, 0, sizeof(key));
413 memset (&serialized_cache_rec, 0, sizeof(serialized_cache_rec));
414
415 key.flags = DB_DBT_MALLOC;
416 serialized_cache_rec.flags = DB_DBT_MALLOC;
417
418 while (true) {
419 struct cache_record rec;
420
421 #if DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR < 6
422 ret = cur->c_get (cur, &key, &serialized_cache_rec, DB_NEXT);
423 #else
424 ret = cur->get (cur, &key, &serialized_cache_rec, DB_NEXT);
425 #endif
426
427 if (ret != 0)
428 break;
429
430 if (cache_record_deserialize (&rec, serialized_cache_rec.data,
431 serialized_cache_rec.size, 1)
432 && rec.atime < last_referenced_atime) {
433 last_referenced_atime = rec.atime;
434
435 if (last_referenced)
436 free (last_referenced);
437 last_referenced = (char *)xmalloc (key.size + 1);
438 memcpy (last_referenced, key.data, key.size);
439 last_referenced[key.size] = '\0';
440 }
441
442 // TODO: remove objects with serialization error.
443
444 nitems++;
445
446 free (key.data);
447 free (serialized_cache_rec.data);
448 }
449
450 if (ret != DB_NOTFOUND)
451 logit ("Searching for element to remove failed (cursor): %s",
452 db_strerror (ret));
453
454 #if DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR < 6
455 cur->c_close (cur);
456 #else
457 cur->close (cur);
458 #endif
459
460 debug ("Elements in cache: %d (limit %d)", nitems, c->max_items);
461
462 if (last_referenced) {
463 if (nitems >= c->max_items)
464 tags_cache_remove_rec (c, last_referenced);
465 free (last_referenced);
466 }
467 else
468 debug ("Cache empty");
469 }
470 #endif
471
472 /* Synchronize cache every DB_SYNC_COUNT updates. */
473 #ifdef HAVE_DB_H
tags_cache_sync(struct tags_cache * c)474 static void tags_cache_sync (struct tags_cache *c)
475 {
476 static int sync_count = 0;
477
478 if (DB_SYNC_COUNT == 0)
479 return;
480
481 sync_count += 1;
482 if (sync_count >= DB_SYNC_COUNT) {
483 sync_count = 0;
484 c->db->sync (c->db, 0);
485 }
486 }
487 #endif
488
489 /* Add this tags object for the file to the cache. */
490 #ifdef HAVE_DB_H
tags_cache_add(struct tags_cache * c,const char * file,DBT * key,struct file_tags * tags)491 static void tags_cache_add (struct tags_cache *c, const char *file,
492 DBT *key, struct file_tags *tags)
493 {
494 char *serialized_cache_rec;
495 int serial_len;
496 struct cache_record rec;
497 DBT data;
498 int ret;
499
500 assert (tags != NULL);
501
502 debug ("Adding/updating cache object");
503
504 rec.mod_time = get_mtime (file);
505 rec.atime = time (NULL);
506 rec.tags = tags;
507
508 serialized_cache_rec = cache_record_serialize (&rec, &serial_len);
509 if (!serialized_cache_rec)
510 return;
511
512 memset (&data, 0, sizeof(data));
513 data.data = serialized_cache_rec;
514 data.size = serial_len;
515
516 tags_cache_gc (c);
517
518 ret = c->db->put (c->db, NULL, key, &data, 0);
519 if (ret)
520 error ("DB put error: %s", db_strerror (ret));
521
522 tags_cache_sync (c);
523
524 free (serialized_cache_rec);
525 }
526 #endif
527
528 /* Read time tags for a file into tags structure (or create it if NULL). */
read_missing_tags(const char * file,struct file_tags * tags,int tags_sel)529 struct file_tags *read_missing_tags (const char *file,
530 struct file_tags *tags, int tags_sel)
531 {
532 if (tags == NULL)
533 tags = tags_new ();
534
535 if (tags_sel & TAGS_TIME) {
536 int time;
537
538 /* Try to get it from the server's playlist first. */
539 time = audio_get_ftime (file);
540
541 if (time != -1) {
542 tags->time = time;
543 tags->filled |= TAGS_TIME;
544 tags_sel &= ~TAGS_TIME;
545 }
546 }
547
548 tags = read_file_tags (file, tags, tags_sel);
549
550 return tags;
551 }
552
553 /* Read the selected tags for this file and add it to the cache. */
554 #ifdef HAVE_DB_H
locked_read_add(struct tags_cache * c,const char * file,const int tags_sel,const int client_id,DBT * key,DBT * serialized_cache_rec)555 static void *locked_read_add (struct tags_cache *c, const char *file,
556 const int tags_sel, const int client_id,
557 DBT *key, DBT *serialized_cache_rec)
558 {
559 int ret;
560 struct file_tags *tags = NULL;
561
562 assert (c->db != NULL);
563
564 ret = c->db->get (c->db, NULL, key, serialized_cache_rec, 0);
565 if (ret && ret != DB_NOTFOUND)
566 logit ("Cache DB get error: %s", db_strerror (ret));
567
568 /* If this entry is already present in the cache, we have 3 options:
569 * we must read different tags (TAGS_*) or the tags are outdated
570 * or this is an immediate tags read (client_id == -1) */
571 if (ret == 0) {
572 struct cache_record rec;
573
574 if (cache_record_deserialize (&rec, serialized_cache_rec->data,
575 serialized_cache_rec->size, 0)) {
576 time_t curr_mtime = get_mtime (file);
577
578 if (rec.mod_time != curr_mtime) {
579 debug ("Tags in the cache are outdated");
580 tags_free (rec.tags); /* remove them and reread tags */
581 }
582 else if ((rec.tags->filled & tags_sel) == tags_sel
583 && client_id == -1) {
584 debug ("Tags are in the cache.");
585 return rec.tags;
586 }
587 else {
588 debug ("Tags in the cache are not what we want");
589 tags = rec.tags; /* read additional tags */
590 }
591 }
592 }
593
594 tags = read_missing_tags (file, tags, tags_sel);
595 tags_cache_add (c, file, key, tags);
596
597 return tags;
598 }
599 #endif
600
601 /* Read the selected tags for this file and add it to the cache.
602 * If client_id != -1, the server is notified using tags_response().
603 * If client_id == -1, copy of file_tags is returned. */
tags_cache_read_add(struct tags_cache * c ATTR_UNUSED,const char * file,int tags_sel,int client_id)604 static struct file_tags *tags_cache_read_add (struct tags_cache *c ATTR_UNUSED,
605 const char *file, int tags_sel, int client_id)
606 {
607 struct file_tags *tags = NULL;
608
609 assert (file != NULL);
610
611 debug ("Getting tags for %s", file);
612
613 #ifdef HAVE_DB_H
614 if (c->max_items)
615 tags = (struct file_tags *)with_db_lock (locked_read_add, c, file,
616 tags_sel, client_id);
617 else
618 #endif
619 tags = read_missing_tags (file, tags, tags_sel);
620
621 if (client_id != -1) {
622 tags_response (client_id, file, tags);
623 tags_free (tags);
624 tags = NULL;
625 }
626
627 /* TODO: Remove the oldest items from the cache if we exceeded the maximum
628 * cache size */
629
630 return tags;
631 }
632
reader_thread(void * cache_ptr)633 static void *reader_thread (void *cache_ptr)
634 {
635 struct tags_cache *c;
636 int curr_queue = 0; /* index of the queue from where
637 we will get the next request */
638
639 logit ("Tags reader thread started");
640
641 assert (cache_ptr != NULL);
642
643 c = (struct tags_cache *)cache_ptr;
644
645 LOCK (c->mutex);
646
647 while (!c->stop_reader_thread) {
648 int i;
649 char *request_file;
650 int tags_sel = 0;
651
652 /* Find the queue with a request waiting. Begin searching at
653 * curr_queue: we want to get one request from each queue,
654 * and then move to the next non-empty queue. */
655 i = curr_queue;
656 while (i < CLIENTS_MAX && request_queue_empty (&c->queues[i]))
657 i++;
658 if (i == CLIENTS_MAX) {
659 i = 0;
660 while (i < curr_queue && request_queue_empty (&c->queues[i]))
661 i++;
662
663 if (i == curr_queue) {
664 debug ("All queues empty, waiting");
665 pthread_cond_wait (&c->request_cond, &c->mutex);
666 continue;
667 }
668 }
669 curr_queue = i;
670
671 request_file = request_queue_pop (&c->queues[curr_queue], &tags_sel);
672 UNLOCK (c->mutex);
673
674 tags_cache_read_add (c, request_file, tags_sel, curr_queue);
675 free (request_file);
676
677 LOCK (c->mutex);
678 curr_queue = (curr_queue + 1) % CLIENTS_MAX;
679 }
680
681 UNLOCK (c->mutex);
682
683 logit ("Exiting tags reader thread");
684
685 return NULL;
686 }
687
tags_cache_init(struct tags_cache * c,size_t max_size)688 void tags_cache_init (struct tags_cache *c, size_t max_size)
689 {
690 int i, rc;
691
692 assert (c != NULL);
693
694 #ifdef HAVE_DB_H
695 c->db_env = NULL;
696 c->db = NULL;
697 #endif
698
699 for (i = 0; i < CLIENTS_MAX; i++)
700 request_queue_init (&c->queues[i]);
701
702 #if CACHE_DB_FORMAT_VERSION
703 c->max_items = max_size;
704 #else
705 c->max_items = 0;
706 #endif
707 c->stop_reader_thread = 0;
708 pthread_mutex_init (&c->mutex, NULL);
709
710 rc = pthread_cond_init (&c->request_cond, NULL);
711 if (rc != 0)
712 fatal ("Can't create request_cond: %s", strerror (rc));
713
714 rc = pthread_create (&c->reader_thread, NULL, reader_thread, c);
715 if (rc != 0)
716 fatal ("Can't create tags cache thread: %s", strerror (rc));
717 }
718
tags_cache_destroy(struct tags_cache * c)719 void tags_cache_destroy (struct tags_cache *c)
720 {
721 int i, rc;
722
723 assert (c != NULL);
724
725 LOCK (c->mutex);
726 c->stop_reader_thread = 1;
727 pthread_cond_signal (&c->request_cond);
728 UNLOCK (c->mutex);
729
730 #ifdef HAVE_DB_H
731 if (c->db) {
732 c->db->set_errcall (c->db, NULL);
733 c->db->set_msgcall (c->db, NULL);
734 c->db->set_paniccall (c->db, NULL);
735 c->db->close (c->db, 0);
736 c->db = NULL;
737 }
738 #endif
739
740 #ifdef HAVE_DB_H
741 if (c->db_env) {
742 c->db_env->lock_id_free (c->db_env, c->locker);
743 c->db_env->set_errcall (c->db_env, NULL);
744 c->db_env->set_msgcall (c->db_env, NULL);
745 c->db_env->set_paniccall (c->db_env, NULL);
746 c->db_env->close (c->db_env, 0);
747 c->db_env = NULL;
748 }
749 #endif
750
751 rc = pthread_join (c->reader_thread, NULL);
752 if (rc != 0)
753 fatal ("pthread_join() on cache reader thread failed: %s",
754 strerror (rc));
755
756 for (i = 0; i < CLIENTS_MAX; i++)
757 request_queue_clear (&c->queues[i]);
758
759 rc = pthread_mutex_destroy (&c->mutex);
760 if (rc != 0)
761 logit ("Can't destroy mutex: %s", strerror (rc));
762 rc = pthread_cond_destroy (&c->request_cond);
763 if (rc != 0)
764 logit ("Can't destroy request_cond: %s", strerror (rc));
765 }
766
767 #ifdef HAVE_DB_H
locked_add_request(struct tags_cache * c,const char * file,int tags_sel,int client_id,DBT * key,DBT * serialized_cache_rec)768 static void *locked_add_request (struct tags_cache *c, const char *file,
769 int tags_sel, int client_id,
770 DBT *key, DBT *serialized_cache_rec)
771 {
772 int db_ret;
773 struct cache_record rec;
774
775 assert (c->db);
776
777 db_ret = c->db->get (c->db, NULL, key, serialized_cache_rec, 0);
778
779 if (db_ret == DB_NOTFOUND)
780 return NULL;
781
782 if (db_ret) {
783 error ("Cache DB search error: %s", db_strerror (db_ret));
784 return NULL;
785 }
786
787 if (cache_record_deserialize (&rec, serialized_cache_rec->data,
788 serialized_cache_rec->size, 0)) {
789 if (rec.mod_time == get_mtime (file)
790 && (rec.tags->filled & tags_sel) == tags_sel) {
791 tags_response (client_id, file, rec.tags);
792 tags_free (rec.tags);
793 debug ("Tags are present in the cache");
794 return (void *)1;
795 }
796
797 tags_free (rec.tags);
798 debug ("Found outdated or incomplete tags in the cache");
799 }
800
801 return NULL;
802 }
803 #endif
804
tags_cache_add_request(struct tags_cache * c,const char * file,int tags_sel,int client_id)805 void tags_cache_add_request (struct tags_cache *c, const char *file,
806 int tags_sel, int client_id)
807 {
808 void *rc = NULL;
809
810 assert (c != NULL);
811 assert (file != NULL);
812 assert (LIMIT(client_id, CLIENTS_MAX));
813
814 debug ("Request for tags for '%s' from client %d", file, client_id);
815
816 #ifdef HAVE_DB_H
817 if (c->max_items)
818 rc = with_db_lock (locked_add_request, c, file, tags_sel, client_id);
819 #endif
820
821 if (!rc) {
822 LOCK (c->mutex);
823 request_queue_add (&c->queues[client_id], file, tags_sel);
824 pthread_cond_signal (&c->request_cond);
825 UNLOCK (c->mutex);
826 }
827 }
828
tags_cache_clear_queue(struct tags_cache * c,int client_id)829 void tags_cache_clear_queue (struct tags_cache *c, int client_id)
830 {
831 assert (c != NULL);
832 assert (LIMIT(client_id, CLIENTS_MAX));
833
834 LOCK (c->mutex);
835 request_queue_clear (&c->queues[client_id]);
836 debug ("Cleared requests queue for client %d", client_id);
837 UNLOCK (c->mutex);
838 }
839
840 /* Remove all pending requests from the queue for the given client up to
841 * the request associated with the given file. */
tags_cache_clear_up_to(struct tags_cache * c,const char * file,int client_id)842 void tags_cache_clear_up_to (struct tags_cache *c, const char *file,
843 int client_id)
844 {
845 assert (c != NULL);
846 assert (LIMIT(client_id, CLIENTS_MAX));
847 assert (file != NULL);
848
849 LOCK (c->mutex);
850 debug ("Removing requests for client %d up to file %s", client_id,
851 file);
852 request_queue_clear_up_to (&c->queues[client_id], file);
853 UNLOCK (c->mutex);
854 }
855
tags_cache_save(struct tags_cache * c ATTR_UNUSED,const char * cache_dir ATTR_UNUSED)856 void tags_cache_save (struct tags_cache *c ATTR_UNUSED,
857 const char *cache_dir ATTR_UNUSED)
858 {
859 //TODO: to remove
860
861 assert (c != NULL);
862 assert (cache_dir != NULL);
863 }
864
865 #ifdef HAVE_DB_H
db_err_cb(const DB_ENV * dbenv ATTR_UNUSED,const char * errpfx,const char * msg)866 static void db_err_cb (const DB_ENV *dbenv ATTR_UNUSED, const char *errpfx,
867 const char *msg)
868 {
869 assert (msg);
870
871 if (errpfx && errpfx[0])
872 logit ("BDB said: %s: %s", errpfx, msg);
873 else
874 logit ("BDB said: %s", msg);
875 }
876 #endif
877
878 #ifdef HAVE_DB_H
db_msg_cb(const DB_ENV * dbenv ATTR_UNUSED,const char * msg)879 static void db_msg_cb (const DB_ENV *dbenv ATTR_UNUSED, const char *msg)
880 {
881 assert (msg);
882
883 logit ("BDB said: %s", msg);
884 }
885 #endif
886
887 #ifdef HAVE_DB_H
db_panic_cb(DB_ENV * dbenv ATTR_UNUSED,int errval)888 static void db_panic_cb (DB_ENV *dbenv ATTR_UNUSED, int errval)
889 {
890 logit ("BDB said: %s", db_strerror (errval));
891 }
892 #endif
893
894 /* Purge content of a directory. */
895 #ifdef HAVE_DB_H
purge_directory(const char * dir_path)896 static int purge_directory (const char *dir_path)
897 {
898 DIR *dir;
899 struct dirent *d;
900
901 logit ("Purging %s...", dir_path);
902
903 dir = opendir (dir_path);
904 if (!dir) {
905 logit ("Can't open directory %s: %s", dir_path, strerror (errno));
906 return 0;
907 }
908
909 while ((d = readdir (dir))) {
910 struct stat st;
911 char *fpath;
912 int len;
913
914 if (!strcmp (d->d_name, ".") || !strcmp (d->d_name, ".."))
915 continue;
916
917 len = strlen (dir_path) + strlen (d->d_name) + 2;
918 fpath = (char *)xmalloc (len);
919 snprintf (fpath, len, "%s/%s", dir_path, d->d_name);
920
921 if (stat (fpath, &st) < 0) {
922 logit ("Can't stat %s: %s", fpath, strerror (errno));
923 free (fpath);
924 closedir (dir);
925 return 0;
926 }
927
928 if (S_ISDIR(st.st_mode)) {
929 if (!purge_directory (fpath)) {
930 free (fpath);
931 closedir (dir);
932 return 0;
933 }
934
935 logit ("Removing directory %s...", fpath);
936 if (rmdir (fpath) < 0) {
937 logit ("Can't remove %s: %s", fpath, strerror (errno));
938 free (fpath);
939 closedir (dir);
940 return 0;
941 }
942 }
943 else {
944 logit ("Removing file %s...", fpath);
945
946 if (unlink (fpath) < 0) {
947 logit ("Can't remove %s: %s", fpath, strerror (errno));
948 free (fpath);
949 closedir (dir);
950 return 0;
951 }
952 }
953
954 free (fpath);
955 }
956
957 closedir (dir);
958 return 1;
959 }
960 #endif
961
962 /* Remove the old Berkley DB backing files from the cache directory. */
963 #ifdef HAVE_DB_H
vacuum_old_db_files(const char * dir_path)964 static void vacuum_old_db_files (const char *dir_path)
965 {
966 DIR *dir;
967 struct dirent *d;
968
969 dir = opendir (dir_path);
970 if (!dir) {
971 logit ("Can't open directory %s: %s", dir_path, strerror (errno));
972 return;
973 }
974
975 while ((d = readdir (dir))) {
976 if (!strncmp (d->d_name, "__db.", 5)) {
977 char *fpath;
978 int len;
979
980 len = strlen (dir_path) + strlen (d->d_name) + 2;
981 fpath = (char *)xmalloc (len);
982 snprintf (fpath, len, "%s/%s", dir_path, d->d_name);
983
984 logit ("Vacuuming file: %s", fpath);
985
986 if (unlink (fpath) < 0)
987 logit ("Can't remove %s: %s", fpath, strerror (errno));
988
989 free (fpath);
990 }
991 }
992
993 closedir (dir);
994 }
995 #endif
996
997 /* Create a MOC/db version string.
998 *
999 * @param buf Output buffer (at least VERSION_TAG_MAX chars long)
1000 */
1001 #ifdef HAVE_DB_H
create_version_tag(char * buf)1002 static const char *create_version_tag (char *buf)
1003 {
1004 int db_major;
1005 int db_minor;
1006
1007 db_version (&db_major, &db_minor, NULL);
1008
1009 #ifdef PACKAGE_REVISION
1010 snprintf (buf, VERSION_TAG_MAX, "%d %d %d r%s",
1011 CACHE_DB_FORMAT_VERSION, db_major, db_minor, PACKAGE_REVISION);
1012 #else
1013 snprintf (buf, VERSION_TAG_MAX, "%d %d %d",
1014 CACHE_DB_FORMAT_VERSION, db_major, db_minor);
1015 #endif
1016
1017 return buf;
1018 }
1019 #endif
1020
1021 /* Check version of the cache directory. If it was created
1022 * using format not handled by this version of MOC, return 0. */
1023 #ifdef HAVE_DB_H
cache_version_matches(const char * cache_dir)1024 static int cache_version_matches (const char *cache_dir)
1025 {
1026 char *fname = NULL;
1027 char disk_version_tag[VERSION_TAG_MAX];
1028 ssize_t rres;
1029 FILE *f;
1030 int compare_result = 0;
1031
1032 fname = (char *)xmalloc (strlen (cache_dir) + sizeof (MOC_VERSION_TAG) + 1);
1033 sprintf (fname, "%s/%s", cache_dir, MOC_VERSION_TAG);
1034
1035 f = fopen (fname, "r");
1036 if (!f) {
1037 logit ("No %s in cache directory", MOC_VERSION_TAG);
1038 free (fname);
1039 return 0;
1040 }
1041
1042 rres = fread (disk_version_tag, 1, sizeof (disk_version_tag) - 1, f);
1043 if (rres == sizeof (disk_version_tag) - 1) {
1044 logit ("On-disk version tag too long");
1045 }
1046 else {
1047 char *ptr, cur_version_tag[VERSION_TAG_MAX];
1048
1049 disk_version_tag[rres] = '\0';
1050 ptr = strrchr (disk_version_tag, '\n');
1051 if (ptr)
1052 *ptr = '\0';
1053 ptr = strrchr (disk_version_tag, ' ');
1054 if (ptr && ptr[1] == 'r')
1055 *ptr = '\0';
1056
1057 create_version_tag (cur_version_tag);
1058 ptr = strrchr (cur_version_tag, '\n');
1059 if (ptr)
1060 *ptr = '\0';
1061 ptr = strrchr (cur_version_tag, ' ');
1062 if (ptr && ptr[1] == 'r')
1063 *ptr = '\0';
1064
1065 compare_result = !strcmp (disk_version_tag, cur_version_tag);
1066 }
1067
1068 fclose (f);
1069 free (fname);
1070
1071 return compare_result;
1072 }
1073 #endif
1074
1075 #ifdef HAVE_DB_H
write_cache_version(const char * cache_dir)1076 static void write_cache_version (const char *cache_dir)
1077 {
1078 char cur_version_tag[VERSION_TAG_MAX];
1079 char *fname = NULL;
1080 FILE *f;
1081 int rc;
1082
1083 fname = (char *)xmalloc (strlen (cache_dir) + sizeof (MOC_VERSION_TAG) + 1);
1084 sprintf (fname, "%s/%s", cache_dir, MOC_VERSION_TAG);
1085
1086 f = fopen (fname, "w");
1087 if (!f) {
1088 logit ("Error opening cache: %s", strerror (errno));
1089 free (fname);
1090 return;
1091 }
1092
1093 create_version_tag (cur_version_tag);
1094 rc = fwrite (cur_version_tag, strlen (cur_version_tag), 1, f);
1095 if (rc != 1)
1096 logit ("Error writing cache version tag: %d", rc);
1097
1098 free (fname);
1099 fclose (f);
1100 }
1101 #endif
1102
1103 /* Make sure that the cache directory exists and clear it if necessary. */
1104 #ifdef HAVE_DB_H
prepare_cache_dir(const char * cache_dir)1105 static int prepare_cache_dir (const char *cache_dir)
1106 {
1107 if (mkdir (cache_dir, 0700) == 0) {
1108 write_cache_version (cache_dir);
1109 return 1;
1110 }
1111
1112 if (errno != EEXIST) {
1113 error ("Failed to create directory for tags cache: %s",
1114 strerror (errno));
1115 return 0;
1116 }
1117
1118 if (!cache_version_matches (cache_dir)) {
1119 logit ("Tags cache directory is the wrong version, purging....");
1120
1121 if (!purge_directory (cache_dir))
1122 return 0;
1123 write_cache_version (cache_dir);
1124 }
1125 else
1126 vacuum_old_db_files (cache_dir);
1127
1128 return 1;
1129 }
1130 #endif
1131
tags_cache_load(struct tags_cache * c,const char * cache_dir)1132 void tags_cache_load (struct tags_cache *c, const char *cache_dir)
1133 {
1134 assert (c != NULL);
1135 assert (cache_dir != NULL);
1136
1137 #ifdef HAVE_DB_H
1138 int ret;
1139
1140 if (!c->max_items)
1141 return;
1142
1143 if (!prepare_cache_dir (cache_dir)) {
1144 error ("Can't prepare cache directory!");
1145 goto err;
1146 }
1147
1148 ret = db_env_create (&c->db_env, 0);
1149 if (ret) {
1150 error ("Can't create DB environment: %s", db_strerror (ret));
1151 goto err;
1152 }
1153
1154 c->db_env->set_errcall (c->db_env, db_err_cb);
1155 c->db_env->set_msgcall (c->db_env, db_msg_cb);
1156 ret = c->db_env->set_paniccall (c->db_env, db_panic_cb);
1157 if (ret)
1158 logit ("Could not set DB panic callback");
1159
1160 ret = c->db_env->open (c->db_env, cache_dir,
1161 DB_CREATE | DB_PRIVATE | DB_INIT_MPOOL |
1162 DB_THREAD | DB_INIT_LOCK, 0);
1163 if (ret) {
1164 error ("Can't open DB environment (%s): %s",
1165 cache_dir, db_strerror (ret));
1166 goto err;
1167 }
1168
1169 ret = c->db_env->lock_id (c->db_env, &c->locker);
1170 if (ret) {
1171 error ("Failed to get DB locker: %s", db_strerror (ret));
1172 goto err;
1173 }
1174
1175 ret = db_create (&c->db, c->db_env, 0);
1176 if (ret) {
1177 error ("Failed to create cache db: %s", db_strerror (ret));
1178 goto err;
1179 }
1180
1181 c->db->set_errcall (c->db, db_err_cb);
1182 c->db->set_msgcall (c->db, db_msg_cb);
1183 ret = c->db->set_paniccall (c->db, db_panic_cb);
1184 if (ret)
1185 logit ("Could not set DB panic callback");
1186
1187 ret = c->db->open (c->db, NULL, TAGS_DB, NULL, DB_BTREE,
1188 DB_CREATE | DB_THREAD, 0);
1189 if (ret) {
1190 error ("Failed to open (or create) tags cache db: %s",
1191 db_strerror (ret));
1192 goto err;
1193 }
1194
1195 return;
1196
1197 err:
1198 if (c->db) {
1199 c->db->set_errcall (c->db, NULL);
1200 c->db->set_msgcall (c->db, NULL);
1201 c->db->set_paniccall (c->db, NULL);
1202 c->db->close (c->db, 0);
1203 c->db = NULL;
1204 }
1205 if (c->db_env) {
1206 c->db_env->set_errcall (c->db_env, NULL);
1207 c->db_env->set_msgcall (c->db_env, NULL);
1208 c->db_env->set_paniccall (c->db_env, NULL);
1209 c->db_env->close (c->db_env, 0);
1210 c->db_env = NULL;
1211 }
1212 c->max_items = 0;
1213 error ("Failed to initialise tags cache: caching disabled");
1214 #endif
1215 }
1216
1217 /* Immediately read tags for a file bypassing the request queue. */
tags_cache_get_immediate(struct tags_cache * c,const char * file,int tags_sel)1218 struct file_tags *tags_cache_get_immediate (struct tags_cache *c,
1219 const char *file, int tags_sel)
1220 {
1221 struct file_tags *tags;
1222
1223 assert (c != NULL);
1224 assert (file != NULL);
1225
1226 debug ("Immediate tags read for %s", file);
1227
1228 if (!is_url (file))
1229 tags = tags_cache_read_add (c, file, tags_sel, -1);
1230 else
1231 tags = tags_new ();
1232
1233 return tags;
1234 }
1235