777c89bcdb7882e12b4aaf171269fd9b5c422865
[fuse-music.git] / C / fuse-music.c
1 #define _GNU_SOURCE
2 #include <stdio.h>
3 #include <string.h>
4 #include <errno.h>
5 #include <fcntl.h>
6 #include <sys/types.h>
7 #include <stdbool.h>
8 #include <ctype.h>
9 #include <sys/stat.h>
10 #include <unistd.h>
11 #include <stdlib.h>
12 #include <dirent.h>
13 #include <pthread.h>
14 #include <stddef.h>
15 #include <libgen.h>
16 #include "fmdb.h"
17 #include "fmcache.h"
18 #include "misc.h"
19
20 #define FUSE_USE_VERSION 26 /* Latest version */
21 #include <fuse.h>
22 #include <fuse_opt.h>
23
24 FILE *logfile = NULL;
25
26 #define MAIN_DEBUG 0
27
28 #if MAIN_DEBUG >= 0
29 #   define MAINLOG0(x) LOGPRINT(x)
30 #else
31 #   define MAINLOG0(x)
32 #endif
33
34 #if MAIN_DEBUG >= 1
35 #   define MAINLOG1(x) LOGPRINT(x)
36 #else
37 #   define MAINLOG1(x)
38 #endif
39
40 #if MAIN_DEBUG >= 2
41 #   define MAINLOG2(x) LOGPRINT(x)
42 #else
43 #   define MAINLOG2(x)
44 #endif
45
46 typedef enum
47 {
48     OUTFMT_OGG = 0,
49     OUTFMT_MP3,
50     OUTFMT_INVALID
51 } fmt_e;
52
53 typedef struct
54 {
55     char *extension;
56     int   extension_len;
57     char *default_quality;
58     char *encode_command;
59 } format_t;
60
61 format_t formats[] =
62 {
63     { "ogg", 3, "7", "encode_ogg" },
64     { "mp3", 3, "4", "encode_mp3" },
65 };
66
67 #define KiB 1024
68 #define MiB (KiB * KiB)
69 #define GiB (KiB * KiB * KiB)
70
71 #define CACHE_MIN_SIZE (100ULL * MiB)
72 #define CACHE_DEFAULT_SIZE (10ULL * GiB)
73 #define DIR_TRIGGER 2
74
75 static fmt_e format_index = OUTFMT_OGG;
76 static long long cachesize_value = CACHE_DEFAULT_SIZE;
77 static long num_encoder_threads_value = 1;
78 char *quality = NULL;
79 FMDB *db_state = NULL;
80
81 typedef enum
82 {
83     ENCODE_WAITING = 0,
84     ENCODE_IN_PROGRESS,
85     ENCODE_DONE,
86     ENCODE_INVALID
87 } encode_state_e;
88
89 typedef enum
90 {
91     ENCODE_DONT_KEEP = 0,
92     ENCODE_KEEP,
93 } encode_keep_e;
94
95 /* Linked list of things to work on / have been worked on */
96 typedef struct _work_list_t
97 {
98     struct _work_list_t *next;
99     int                  error;
100     char                 flac_path[PATH_MAX];
101     pthread_mutex_t      lock;
102     encode_state_e       state;
103     pthread_cond_t       cv;
104     int                  refcount;
105     int                  keep;
106 } work_list_t;
107
108 pthread_t *bg_thread;
109 struct thread_work
110 {
111     pthread_mutex_t lock;
112     int             num_todo;
113     pthread_cond_t  cv;
114     work_list_t    *work_list;
115 };
116 struct thread_work global_work;
117
118 #define MOUNT_OPT(n)      char *n
119 #define FUSE_MOUNT_OPT(n) {"--"#n"=%s", offsetof(struct mount_opts, n), 1}
120 #define CONFIG_OPT(n)     {#n, offsetof(struct mount_opts, n)}
121
122 struct mount_opts
123 {
124     MOUNT_OPT(config_file);    
125     MOUNT_OPT(basedir);    
126     MOUNT_OPT(cachedir);
127     MOUNT_OPT(db_file);
128     MOUNT_OPT(format);
129     MOUNT_OPT(quality);
130     MOUNT_OPT(logfile);
131     MOUNT_OPT(cachesize);
132     MOUNT_OPT(num_threads);
133 };
134 struct mount_opts mo;
135
136 static const struct fuse_opt fm_mount_opts[] = {    
137     FUSE_MOUNT_OPT(config_file),
138     FUSE_MOUNT_OPT(basedir),
139     FUSE_MOUNT_OPT(cachedir),
140     FUSE_MOUNT_OPT(db_file),
141     FUSE_MOUNT_OPT(format),
142     FUSE_MOUNT_OPT(quality),
143     FUSE_MOUNT_OPT(logfile),
144     FUSE_MOUNT_OPT(cachesize),
145     FUSE_MOUNT_OPT(num_threads),
146     FUSE_OPT_END
147 };
148
149 struct config_opt
150 {
151     char *match;
152     unsigned long offset;
153 };
154 static const struct config_opt config_opts[] = {
155     CONFIG_OPT(config_file),
156     CONFIG_OPT(basedir),
157     CONFIG_OPT(cachedir),
158     CONFIG_OPT(db_file),
159     CONFIG_OPT(format),
160     CONFIG_OPT(quality),
161     CONFIG_OPT(logfile),
162     CONFIG_OPT(cachesize),
163     CONFIG_OPT(num_threads),
164     {NULL, 0}
165 };
166
167 struct
168 {
169     pthread_mutex_t lock;
170     char last[PATH_MAX];
171     int  num;
172 } dir_state;
173 pthread_once_t once_control = PTHREAD_ONCE_INIT;
174 void *bg_handler(void *arg);
175 void *bg_db_cleanup(void *arg);
176
177 static void init_threads(void)
178 {
179     intptr_t i = 0;
180     int error = 0;
181     int *threadcount;
182     pthread_mutexattr_t attr;
183     pthread_mutexattr_init(&attr);
184     pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK_NP);    
185
186     pthread_mutex_init(&dir_state.lock, &attr);
187     pthread_mutex_init(&global_work.lock, &attr);
188     pthread_cond_init(&global_work.cv, NULL);
189     /* +1 for the DB cleanup thread */
190     bg_thread = malloc((1 + num_encoder_threads_value) * sizeof(pthread_t));
191     if (!bg_thread)
192     {
193         MAINLOG0((logfile, "%s: Failed to malloc thread space for thread descriptors\n", __func__));
194         db_close(db_state);
195     }
196     threadcount = malloc((1 + num_encoder_threads_value) * sizeof(int));
197     if (!threadcount)
198     {
199         MAINLOG0((logfile, "%s: Failed to malloc thread space for thread numbers\n", __func__));
200         db_close(db_state);
201     }
202     for (i = 0; i < num_encoder_threads_value; i++)
203     {
204         threadcount[i] = i;
205         error = pthread_create(&bg_thread[i], NULL, bg_handler, &threadcount[i]);
206     }
207     error = pthread_create(&bg_thread[i], NULL, bg_db_cleanup, &threadcount[i]);
208     i++;
209 }
210
211 static char *str_encode_state(encode_state_e state)
212 {
213     switch (state)
214     {
215         case ENCODE_WAITING:
216             return "WAITING";
217         case ENCODE_IN_PROGRESS:
218             return "IN_PROGRESS";
219         case ENCODE_DONE:
220             return "DONE";
221         default:
222             return "BOGUS";
223     }
224 }
225
226 static void dump_global_queue(void)
227 {
228     work_list_t *entry = global_work.work_list;
229     int i = 0;
230     int states[ENCODE_INVALID] = {0};
231
232     log_lock();
233     fprintf(logfile, "%s:\n", __func__);
234     fprintf(logfile, " #  enc_state   keep ref flac\n");
235     while (entry)
236     {
237         fprintf(logfile, "% 3d %11s % 2d   % 2d  %s\n",
238                 i++, str_encode_state(entry->state),
239                 entry->keep, entry->refcount, entry->flac_path);
240         states[entry->state]++;
241         entry = entry->next;
242     }
243     fprintf(logfile, "found %d entries total:\n", i);
244     for (i = 0; i < ENCODE_INVALID; i++)
245         fprintf(logfile, "  %s: %d ", str_encode_state(i), states[i]);
246     fprintf(logfile, "\n");
247     log_unlock();
248 }
249
250 static char *convert_to_base_path(const char *path)
251 {
252     char *basename = strdup(path);
253     int length;
254     char *out = NULL;
255     int ret = 0;
256
257 //    fprintf(logfile, "%s: path %s, mo.basedir %s\n", __func__, path, mo.basedir);
258     
259     if (!basename)
260         return NULL;
261
262     length = strlen(basename);
263     length -= (formats[format_index].extension_len);
264     if ((length >= 1) &&
265         (basename[length-1] == '.') &&
266         (!strcmp(&basename[length], formats[format_index].extension)))
267     {
268         basename[length] = 0;
269         ret = asprintf(&out, "%s%sflac", mo.basedir, basename);
270     }
271     else
272     {
273         ret = asprintf(&out, "%s%s", mo.basedir, basename);
274     }
275
276     if (-1 == ret)
277     {
278         errno = ENOMEM;
279         out = NULL;
280     }
281     
282     free(basename);
283     return out;
284 }
285
286 static bool is_flac(const char *path)
287 {
288     int length = strlen(path);
289     if ( (length >= 5) && /* strlen(".flac") */
290          (!strcmp(&path[length-5], ".flac")))
291         return true;
292     /* else */
293     return false;
294 }
295
296 static size_t encoded_size(const char *flac_path, long long *size)
297 {
298     db_size_entry_t in, out;
299     int error = 0;
300     time_t mtime = file_mtime(flac_path);
301
302     strncpy(in.flac_path, strip_leading_path(flac_path, mo.basedir), sizeof(in.flac_path));
303     strncpy(in.format_choice, formats[format_index].extension, sizeof(in.format_choice));
304     strncpy(in.format_quality, quality, sizeof(in.format_quality));
305
306     error = db_lookup_size_entry(db_state, &in, &out);
307     if (error)
308     {
309         MAINLOG1((logfile, "%s: returned error %d for %s\n", __func__, error, flac_path));
310         return error;
311     }
312     else if (mtime > out.mtime)
313     {
314         MAINLOG1((logfile, "%s: file is newer than cached size entry (%ld > %ld) for %s; re-encode\n",
315                   __func__, mtime, out.mtime, flac_path));
316         return EAGAIN;
317     }
318     
319     *size = out.size;
320     return 0;
321 }
322
323 static char *convert_from_flac_name(const char *path)
324 {
325     int length = strlen(path);
326
327     /* length -5(.flac) +1(".") + 1(NULL) */
328     char *out = malloc(length - 3 + formats[format_index].extension_len);
329     if (!out)
330     {
331         errno = ENOMEM;
332         return NULL;
333     }
334     memcpy(out, path, length - 5);
335     sprintf(&out[length - 5], ".%s", formats[format_index].extension);
336     return out;
337 }
338
339 static int call_encoder(int threadnum, const char *flac_path, const char *cache_path)
340 {
341     int error = 0;
342     char *command = NULL;
343
344     error = asprintf(&command, "%s -t %d -q %s \"%s\" \"%s\"",
345                      formats[format_index].encode_command,
346                      threadnum,
347                      quality,
348                      flac_path,
349                      cache_path);
350     if (-1 == error)
351         return ENOMEM;
352
353     MAINLOG1((logfile, "%s: Ready to run command %s\n", __func__, command));
354     error = system(command);
355     MAINLOG1((logfile, "%s: system() returned %d\n", __func__, error));
356     return error;
357 }
358
359 static int encode_file(int threadnum, const char *flac_path, int keep)
360 {
361     db_size_entry_t size_entry;
362     int error = 0;
363     long long needed_size = file_size(flac_path);
364     time_t flac_mtime = file_mtime(flac_path);
365     char *cache_file_name = NULL;
366     char cache_path[PATH_MAX];
367
368     /* If we don't have a db entry for the file already, then the only
369      * guess that we can make about the encoded size is that it will
370      * be smaller than the flac size. Use that as a guide for the
371      * space we need. */
372     if (0 != encoded_size(flac_path, &needed_size))
373     {
374         needed_size = file_size(flac_path);
375         MAINLOG2((logfile, "%s: No DB entry yet for %s, so we'll have to guess at the flac size (%lld) for cache space\n",
376                   __func__, flac_path, needed_size));
377     }
378
379     cache_file_name = cache_allocate(mo.basedir, flac_path, needed_size);
380     if (!cache_file_name)
381     {
382         MAINLOG0((logfile, "%s: unable to allocate %lld bytes for a file in the cache, error %d\n",
383                   __func__, needed_size, errno));
384         return errno;
385     }
386
387     sprintf(cache_path, "%s/%s", mo.cachedir, cache_file_name);
388     error = call_encoder(threadnum, flac_path, cache_path);
389     if (error)
390     {
391         MAINLOG0((logfile, "%s: encoding failed, error %d\n", __func__, error));
392         cache_entry_failed(cache_file_name);
393         free(cache_file_name);
394         return error;
395     }
396     
397     strncpy(size_entry.flac_path, strip_leading_path(flac_path, mo.basedir), sizeof(size_entry.flac_path));
398     strncpy(size_entry.format_choice, formats[format_index].extension, sizeof(size_entry.format_choice));
399     strncpy(size_entry.format_quality, quality, sizeof(size_entry.format_quality));
400     size_entry.size = file_size(cache_path);
401     size_entry.mtime = flac_mtime;
402
403     error = db_store_size_entry(db_state, &size_entry);
404     if (error)
405     {
406         MAINLOG0((logfile, "%s: unable to store size entry for %s, error %d\n",
407                   __func__, flac_path, error));
408         cache_entry_failed(cache_file_name);
409         free(cache_file_name);
410         return error;
411     }
412
413     error = cache_entry_encode_complete(cache_file_name,
414                                         file_size(cache_path),
415                                         keep);
416     if (error)
417     {
418         MAINLOG0((logfile, "%s: unable to update cache DB entry for %s, error %d\n",
419                   __func__, flac_path, error));
420         cache_entry_failed(cache_file_name);
421         free(cache_file_name);
422         return error;
423     }
424
425     free(cache_file_name);
426     return 0;
427 }
428
429 static int add_todo_entry_file(const char *flac_path,
430                                work_list_t **enqueued,
431                                encode_keep_e keep)
432 {
433     work_list_t *entry;
434     char dbgname[64];
435     pthread_mutexattr_t attr;
436     pthread_mutexattr_init(&attr);
437     pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK_NP);
438
439     sprintf(dbgname, "%s(%ld):", __func__, (unsigned long)pthread_self());
440     
441     /* Check if we already have an entry for this file; if so, just
442      * add ourselves to the ref count. If not, add a new entry. */
443     entry = global_work.work_list;
444     while (entry)
445     {
446         pthread_mutex_lock(&entry->lock);
447         if (!strcmp(entry->flac_path, flac_path))
448         {
449             entry->refcount++;
450             *enqueued = entry;
451             if (keep)
452                 entry->keep++;
453             pthread_mutex_unlock(&entry->lock);
454             MAINLOG1((logfile, "%s: refcount++ on %s\n", dbgname, flac_path));
455             return 0;
456         }
457         pthread_mutex_unlock(&entry->lock);
458         entry = entry->next;
459     }
460
461     /* If we get here, we didn't find an existing entry */
462     entry = calloc(1, sizeof *entry);
463     if (!entry)
464         return ENOMEM;
465
466     entry->next = NULL;
467     entry->error = 0;
468     strcpy(entry->flac_path, flac_path);
469     pthread_mutex_init(&entry->lock, &attr);
470     pthread_cond_init(&entry->cv, NULL);    
471     entry->state = ENCODE_WAITING;
472     entry->refcount = 1;
473
474     MAINLOG1((logfile, "%s: adding file %s to encode list\n", dbgname, flac_path));
475
476     /* Empty list */
477     if (global_work.work_list == NULL)
478     {
479         global_work.work_list = entry;
480     }
481     else
482     {
483         /* Append to list */
484         work_list_t *current = NULL;
485         current = global_work.work_list;
486         while (current->next)
487             current = current->next;
488         current->next = entry;
489     }
490     global_work.num_todo++;
491     *enqueued = entry;
492     return 0;
493 }
494
495 static void free_todo_entry_file(work_list_t *entry)
496 {
497     char dbgname[64];
498     sprintf(dbgname, "%s(%ld):", __func__, (unsigned long)pthread_self());
499
500     entry->refcount--;
501     if (0 == entry->refcount)
502     {
503         MAINLOG1((logfile, "%s: entry %s is finished, free it now\n", dbgname, entry->flac_path));
504         pthread_cond_destroy(&entry->cv);
505         pthread_mutex_unlock(&entry->lock);
506         pthread_mutex_destroy(&entry->lock);
507         free(entry);
508     }
509     else
510     {
511         MAINLOG1((logfile, "%s: entry %s still in use %d time(s)\n", dbgname, entry->flac_path, entry->refcount));
512         pthread_mutex_unlock(&entry->lock);
513     }        
514 }
515
516 static void *entry_file_thread(void *arg, encode_keep_e keep)
517 {
518     char *flac_path = arg;
519     int error = 0;
520     work_list_t *entry;
521     char dbgname[64];
522     sprintf(dbgname, "%s(%ld):", __func__, (unsigned long)pthread_self());
523     
524     pthread_mutex_lock(&global_work.lock);
525     error = add_todo_entry_file(flac_path, &entry, keep);
526     free(flac_path);
527     if (error)
528     {
529         pthread_mutex_unlock(&global_work.lock);        
530         return NULL;
531     }
532     MAINLOG1((logfile, "%s: signal, %d files in queue\n", dbgname, global_work.num_todo));
533     /* Tell the background thread that we have work to do */
534     pthread_cond_broadcast(&global_work.cv);
535     pthread_mutex_unlock(&global_work.lock);    
536
537     pthread_mutex_lock(&entry->lock);
538     while (entry->state != ENCODE_DONE)
539     {
540         pthread_cond_wait(&entry->cv, &entry->lock);
541     }
542     MAINLOG1((logfile, "%s: wakeup, %s done, error %d\n",
543               dbgname, entry->flac_path, entry->error));
544     error = entry->error;
545     free_todo_entry_file(entry);
546     return NULL;
547 }
548
549 static void *entry_file_thread_keep(void *arg)
550 {
551     return entry_file_thread(arg, ENCODE_KEEP);
552 }
553
554 static void *entry_file_thread_nokeep(void *arg)
555 {
556     return entry_file_thread(arg, ENCODE_DONT_KEEP);
557 }
558
559 static int add_todo_entry_dir(const char *flac_dir, int keep)
560 {
561     char *flac_path;
562     struct dirent *de;        
563     int error = 0;
564     DIR *dp = opendir(flac_dir);
565     long long enc_size = 0;
566     if (dp == NULL)
567         return errno;
568     int should_encode;
569
570     MAINLOG1((logfile, "%s: adding contents of dir %s to encode list\n", __func__, flac_dir));
571
572     /* Start a new thread for each file we want to encode, to deal
573      * with cleanup etc. */
574     while ((de = readdir(dp)) != NULL)
575     {
576         struct stat st;
577         should_encode = 1;
578         
579         if (!asprintf(&flac_path, "%s/%s", flac_dir, de->d_name))
580         {
581             MAINLOG0((logfile, "%s: asprintf failure\n", __func__));
582             return -ENOMEM;
583         }
584         error = lstat(flac_path, &st);
585         if (error < 0)
586         {
587             MAINLOG0((logfile, "%s: lstat %s returned error %d\n", __func__, flac_path, error));
588             free(flac_path);
589             closedir(dp);
590             return errno;
591         }
592
593         if (should_encode && !S_ISREG(st.st_mode))
594         {
595             /* Not a file - don't encode it */
596             should_encode = 0;
597             MAINLOG1((logfile, "%s: dirent %s (%s) is not a file, ignore it\n", __func__, de->d_name, flac_path));
598         }
599         
600         if (should_encode && !is_flac(de->d_name))
601         {
602             /* Not a flac file - don't encode it */
603             should_encode = 0;
604             MAINLOG1((logfile, "%s: dirent %s (%s) is not a flac, ignore it\n", __func__, de->d_name, flac_path));
605         }
606         
607         if (should_encode && !keep && (0 == encoded_size(flac_path, &enc_size)))
608         {
609             /* We've been called for readdir(), so we have nokeep. The
610              * file already exists in the size cache validly at this
611              * point, so there's no need to re-encode it *again* just
612              * for a readdir lookahead at this point. */
613             should_encode = 0;
614             MAINLOG1((logfile, "%s: dirent %s (%s) does not need re-encoding for readdir, ignore it\n",
615                       __func__, de->d_name, flac_path));
616         }
617         
618         if (should_encode)
619         {
620             pthread_t thread;
621             pthread_attr_t attr;
622             pthread_attr_init(&attr);
623             pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
624             pthread_attr_setstacksize(&attr, 4 * 1024 * 1024);
625             
626             if (keep)
627                 error = pthread_create(&thread, &attr, entry_file_thread_keep, flac_path);
628             else
629                 error = pthread_create(&thread, &attr, entry_file_thread_nokeep, flac_path);
630             
631             pthread_attr_destroy(&attr);
632             if (error)
633             {
634                 MAINLOG0((logfile, "%s: pthread_create returned error %d\n", __func__, error));
635                 closedir(dp);
636                 free(flac_path);
637                 return error;
638             }
639         }
640         else
641             free(flac_path);
642     }
643     closedir(dp);
644     return 0;
645 }
646
647 static int enqueue_encode_file(const char *flac_path, encode_keep_e keep)
648 {
649     int error = 0;
650     char *tmp_path = NULL;
651     char *current_dir;
652     work_list_t *entry;
653     char dbgname[64];
654     sprintf(dbgname, "%s(%ld):", __func__, (unsigned long)pthread_self());
655
656     pthread_mutex_lock(&global_work.lock);
657     error = add_todo_entry_file(flac_path, &entry, keep);
658     if (error)
659     {
660         pthread_mutex_unlock(&global_work.lock);
661         return error;
662     }
663
664     pthread_mutex_unlock(&global_work.lock);
665
666     if (num_encoder_threads_value > 1)
667     {
668         tmp_path = strdup(flac_path);
669         current_dir = dirname(tmp_path);
670         pthread_mutex_lock(&dir_state.lock);
671         if (!strcmp(current_dir, dir_state.last))
672         {
673             dir_state.num++;
674             MAINLOG1((logfile, "%s: triggered again on dir %s, now called %d times in succession\n",
675                       dbgname, dir_state.last, dir_state.num));
676         }
677         else
678         {
679             strcpy(dir_state.last, current_dir);
680             dir_state.num = 1;
681             MAINLOG1((logfile, "%s: new dir %s\n", dbgname, dir_state.last));
682         }
683
684         if (dir_state.num >= DIR_TRIGGER)
685         {
686             error = add_todo_entry_dir(tmp_path, keep);
687             if (error)
688             {
689                 pthread_mutex_unlock(&dir_state.lock);
690                 free(tmp_path);
691                 pthread_mutex_unlock(&global_work.lock);
692                 MAINLOG0((logfile, "%s: add_todo_entry_dir failed, error %d\n",
693                           dbgname, error));
694                 return error;
695             }
696             dir_state.num = 0;
697         }
698         pthread_mutex_unlock(&dir_state.lock);
699         free(tmp_path);
700     } /* num_encoder_threads_value > 1 */
701  
702     pthread_mutex_lock(&global_work.lock);
703     MAINLOG1((logfile, "%s: signal, %d files in queue\n", dbgname, global_work.num_todo));
704     /* Tell the background thread that we have work to do */
705     pthread_cond_broadcast(&global_work.cv);
706     pthread_mutex_unlock(&global_work.lock);
707
708     pthread_mutex_lock(&entry->lock);
709     while (entry->state != ENCODE_DONE)
710     {
711         pthread_cond_wait(&entry->cv, &entry->lock);
712     }
713     MAINLOG1((logfile, "%s: wakeup, %s done, error %d\n",
714               dbgname, entry->flac_path, entry->error));
715     error = entry->error;
716     free_todo_entry_file(entry);
717     return error;
718 }
719
720 static int fm_getattr(const char *path, struct stat *stbuf)
721 {
722     int res = 0;
723     char *flac_path = NULL;
724     int error = 0;
725
726     pthread_once(&once_control, init_threads);
727     MAINLOG1((logfile, "%s: %s\n", __func__, path));
728     memset(stbuf, 0, sizeof(struct stat));
729     if (strcmp(path, "/") == 0)
730     {
731         stbuf->st_mode = S_IFDIR | 0755;
732         stbuf->st_nlink = 2;
733         return 0;
734     }
735
736     flac_path = convert_to_base_path(path);
737     if (!flac_path)
738         return -errno;
739
740     res = lstat(flac_path, stbuf);
741     if (res < 0)
742     {
743         free(flac_path);
744         return -errno;
745     }
746
747     if (S_ISDIR(stbuf->st_mode))
748     {
749         free(flac_path);
750         return 0;
751     }
752
753     if (S_ISREG(stbuf->st_mode) && is_flac(flac_path))
754     {
755         long long size;
756         error = encoded_size(flac_path, &size);
757         if (error)
758         {
759             /* Need to (re-)encode the file. We're only looking for
760              * the size right now, so don't mark the file for
761              * keeping. */
762             error = enqueue_encode_file(flac_path, ENCODE_DONT_KEEP);
763             if (error)
764             {
765                 MAINLOG0((logfile, "%s: encoding %s failed with error %d\n",
766                           __func__, flac_path, error));
767                 free(flac_path);
768                 return -EIO;
769             }
770
771             /* Encoding should have updated the database, so look again */
772             error = encoded_size(flac_path, &size);
773             if (error)
774                 return -error;
775         }
776         stbuf->st_mode = S_IFREG | 0444;
777         stbuf->st_nlink = 1;
778         stbuf->st_size = size;
779         stbuf->st_blocks = (size+511)/512;
780     }
781     
782     free(flac_path);
783     return 0;
784 }
785
786 static int fm_readlink(const char *path, char *buf, size_t size)
787 {
788     int res = 0;
789
790     pthread_once(&once_control, init_threads);
791     MAINLOG1((logfile, "%s: %s\n", __func__, path));
792     res = readlink(path, buf, size - 1);
793     if (res == -1)
794         return -errno;
795
796     buf[res] = '\0';
797     return 0;
798 }
799
800 static int fm_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
801                       off_t offset, struct fuse_file_info *fi)
802 {
803     DIR *dp;
804     struct dirent *de;
805     char *flac_path = NULL;
806
807     pthread_once(&once_control, init_threads);
808
809     (void) offset;
810     (void) fi;
811
812     MAINLOG1((logfile, "%s: %s\n", __func__, path));
813
814     flac_path = convert_to_base_path(path);
815     if (!flac_path)
816         return -errno;
817
818     dp = opendir(flac_path);
819     if (dp == NULL)
820     {
821         free(flac_path);
822         return -errno;
823     }
824
825     while ((de = readdir(dp)) != NULL)
826     {
827         struct stat st;        
828         memset(&st, 0, sizeof(st));
829         st.st_ino = de->d_ino;
830         st.st_mode = de->d_type << 12;
831         
832         /* Do we have a file, and does it end in .flac? */
833         if (S_ISREG(st.st_mode) && is_flac(de->d_name))
834         {
835             /* We'll have to convert the name */
836             int ret = 0;
837             char *out = convert_from_flac_name(de->d_name);
838             if (!out)
839             {
840                 free(flac_path);
841                 return -ENOMEM;
842             }
843             ret = filler(buf, out, &st, 0);
844             free(out);
845             if (ret)
846                 break;
847         }
848         else
849         {
850             /* Something else, just pass the name through directly */
851             if (filler(buf, de->d_name, &st, 0))
852                 break;
853         }
854     }
855     closedir(dp);
856     return 0;
857 }
858
859 static int fm_open(const char *path, struct fuse_file_info *fi)
860 {
861     int fd;
862     char *flac_path = NULL;
863     int error = 0;
864
865     pthread_once(&once_control, init_threads);
866
867     MAINLOG1((logfile, "%s: %s\n", __func__, path));
868
869     if (fi->flags & (O_RDWR|O_WRONLY))
870         return -EROFS;
871
872     flac_path = convert_to_base_path(path);
873     if (!flac_path)
874         return -errno;
875
876     /* if flac, do something special, otherwise... */
877     if (is_flac(flac_path))
878     {
879         char *cache_file_name = cache_lookup_for_read(mo.basedir, flac_path);
880         char cache_full_path[PATH_MAX];
881         if (NULL == cache_file_name)
882         {
883             /* Need to encode the file. We're looking to open the file
884              * straight afterwards, so mark the file for keeping in
885              * the cache. */
886             error = enqueue_encode_file(flac_path, ENCODE_KEEP);
887             if (error)
888             {
889                 MAINLOG0((logfile, "%s: encoding %s failed with error %d\n",
890                           __func__, flac_path, error));
891                 free(flac_path);
892                 return -EIO;
893             }
894             cache_file_name = cache_lookup_for_read(mo.basedir, flac_path);
895             if (NULL == cache_file_name)
896             {
897                 free(flac_path);
898                 return -EIO; /* Should never get here! */
899             }
900         }
901         sprintf(cache_full_path, "%s/%s", mo.cachedir, cache_file_name);
902         fd = open(cache_full_path, fi->flags);
903         if (fd == -1)
904         {
905             cache_read_finished(mo.basedir, cache_file_name);
906             free(cache_file_name);
907             free(flac_path);
908             return -errno;
909         }
910         free(cache_file_name);
911     }
912     else
913     {
914         fd = open(flac_path, fi->flags);
915         if (fd == -1)
916         {
917             free(flac_path);
918             return -errno;
919         }
920     }
921     fi->fh = fd;
922     free(flac_path);
923     return 0;
924 }
925
926 static int fm_release(const char *path, struct fuse_file_info *fi)
927 {
928     char *flac_path = NULL;
929
930     pthread_once(&once_control, init_threads);
931     MAINLOG1((logfile, "%s: %s\n", __func__, path));
932
933     close(fi->fh);
934
935     flac_path = convert_to_base_path(path);
936     if (!flac_path)
937         return -errno;
938
939     /* if flac, do something special, otherwise... */
940     if (is_flac(flac_path))
941         (void)cache_read_finished(mo.basedir, flac_path);
942
943     free(flac_path);
944     return 0;
945 }
946
947 static int fm_read(const char *path, char *buf, size_t size, off_t offset,
948                    struct fuse_file_info *fi)
949 {
950     int res;
951
952     pthread_once(&once_control, init_threads);
953     MAINLOG1((logfile, "%s: %s\n", __func__, path));
954
955     (void) path;
956     res = pread(fi->fh, buf, size, offset);
957     if (res == -1)
958     {
959         MAINLOG0((logfile, "%s: failed, error %d\n", __func__, errno));
960         res = -errno;
961     }
962     return res;
963 }
964
965 static int fm_statfs(const char *path, struct statvfs *stbuf)
966 {
967     int res;
968
969     pthread_once(&once_control, init_threads);
970     MAINLOG1((logfile, "%s: %s\n", __func__, path));
971
972     res = statvfs(mo.basedir, stbuf);
973     if (res == -1)
974         return -errno;
975
976     stbuf->f_bfree = 0;
977     stbuf->f_bavail = 0;
978     stbuf->f_ffree = 0;
979     stbuf->f_favail = 0;
980
981     return 0;
982 }
983
984 static struct fuse_operations fm_oper = {
985     .getattr        = fm_getattr,
986     .readlink       = fm_readlink,
987     .readdir        = fm_readdir,
988     .open           = fm_open,
989     .release        = fm_release,
990     .read           = fm_read,
991     .statfs         = fm_statfs,
992 };
993
994 void *bg_handler(void *arg)
995 {
996     int *tmp = arg;
997     int threadnum = *tmp;
998     char dbgname[64];
999     
1000     sprintf(dbgname, "%s:%d (%ld)", __func__, threadnum, (unsigned long)pthread_self());
1001     MAINLOG1((logfile, "%s: startup\n", dbgname));
1002     
1003     while(1)
1004     {
1005         work_list_t *current = NULL;
1006         int error = 0;
1007
1008         /* wait to be given some work to do */
1009         pthread_mutex_lock(&global_work.lock);
1010         while (0 == global_work.num_todo) {
1011             pthread_cond_wait(&global_work.cv, &global_work.lock);
1012         }
1013         
1014         MAINLOG1((logfile, "%s: wakeup, %d in queue\n", dbgname, global_work.num_todo));
1015         if (MAIN_DEBUG > 0)
1016             dump_global_queue();
1017         /* Yay! We've got something. Work on the first item on the
1018          * list */
1019         current = global_work.work_list;
1020         while (current)
1021         {
1022             pthread_mutex_lock(&current->lock);
1023             if (current->state == ENCODE_WAITING)
1024             {
1025                 global_work.num_todo--;
1026                 current->refcount++;
1027                 current->state = ENCODE_IN_PROGRESS;
1028                 pthread_mutex_unlock(&current->lock);
1029                 break;
1030             }
1031             pthread_mutex_unlock(&current->lock);
1032             current = current->next;
1033         }
1034         pthread_mutex_unlock(&global_work.lock);
1035
1036         if (!current)
1037         {
1038             MAINLOG0((logfile, "%s: WTF? didn't find an entry to work on\n", dbgname));
1039             continue;
1040         }
1041
1042         /* Drop the list lock and work directly on the entry we picked
1043          * up. */
1044         pthread_mutex_lock(&current->lock);
1045         MAINLOG1((logfile, "%s: asked to encode %s\n", dbgname, current->flac_path));
1046         pthread_mutex_unlock(&current->lock);
1047         
1048         error = encode_file(threadnum, current->flac_path, current->keep);
1049
1050         /* Time passes ... */
1051
1052         /* Finished. Now run through the global_work list again to
1053          * remove the current entry */
1054         pthread_mutex_lock(&global_work.lock);
1055         if (current == global_work.work_list)
1056         {
1057             global_work.work_list = current->next;
1058         }
1059         else
1060         {
1061             work_list_t *remove = global_work.work_list;
1062             work_list_t *prev = remove;
1063             while (remove)
1064             {
1065                 if (remove == current)
1066                 {
1067                     prev->next = current->next;
1068                     remove = global_work.work_list;
1069                     break;
1070                 }
1071                 prev = remove;
1072                 remove = remove->next;
1073             }
1074             if (!remove)
1075                 MAINLOG0((logfile, "%s:, failed to find global_work list entry for %s!\n",
1076                           dbgname, current->flac_path));
1077         }
1078         pthread_mutex_unlock(&global_work.lock);
1079
1080         /* Now report what happened back to any/all who are waiting on
1081          * this entry */
1082         pthread_mutex_lock(&current->lock);
1083         MAINLOG1((logfile, "%s: signal %s is done\n", dbgname, current->flac_path));
1084         current->error = error;
1085         current->state = ENCODE_DONE;
1086         pthread_cond_broadcast(&current->cv);
1087         free_todo_entry_file(current);
1088         pthread_mutex_unlock(&current->lock);
1089     }
1090 }
1091
1092 /* Background thread to periodically clean up old size database entries */
1093 void *bg_db_cleanup(void *arg)
1094 {
1095     int *tmp = arg;
1096     int threadnum = *tmp;
1097     char dbgname[64];
1098     
1099     sprintf(dbgname, "%s:%d (%ld)", __func__, threadnum, (unsigned long)pthread_self());
1100     MAINLOG1((logfile, "%s: startup\n", dbgname));
1101     
1102     while(1)
1103     {
1104         db_cleanup_sizes(db_state, mo.basedir);
1105         sleep(3600); /* Wait an hour */
1106     }
1107 }
1108
1109 /* Deal with:
1110  *
1111  * ( )*CONFIG_VAR( )*=( )*value( )*
1112  * 
1113  * and ignore 
1114  *
1115  * ( )*#( )*
1116  */
1117 static int parse_config_line(int lineno, char *ptr)
1118 {
1119     char *endptr;
1120     int len = strlen(ptr);
1121     int i = 0;
1122
1123     endptr = &ptr[len-1];
1124     /* strip leading whitespace, and dump comments */
1125     while(1)
1126     {
1127         if (isspace(*ptr))
1128             ptr++;
1129         else if ('#' == *ptr)
1130             return 0; /* ignore the rest of the line */
1131         else
1132             break;
1133     }
1134
1135     /* strip trailing whitespace */
1136     while(1)
1137     {
1138         if (endptr <= ptr)
1139             return 0; /* blank line */
1140         if (*endptr && !isspace(*endptr))
1141             break;
1142         *endptr-- = 0;
1143     }
1144
1145     for (i = 0; config_opts[i].match != NULL; i++)
1146     {
1147         char **valueptr = NULL;
1148
1149         if (strncmp(ptr, config_opts[i].match, strlen(config_opts[i].match)))
1150             continue;
1151         ptr += strlen(config_opts[i].match);
1152         while(isspace(*ptr))
1153             ptr++;
1154         if (*ptr != '=')
1155             return EINVAL;
1156         ptr++;
1157         while(isspace(*ptr))
1158             ptr++;
1159         valueptr = (void *)&mo + config_opts[i].offset;
1160         *valueptr = strdup(ptr);
1161         return 0;
1162     }
1163     return EINVAL; /* Didn't match anything */
1164 }
1165
1166 static int parse_config_file(void)
1167 {
1168     FILE *config = NULL;
1169     char *ptr = NULL;
1170     ssize_t size = 0;
1171     size_t linelen = 0;
1172     int error = 0;
1173     int lineno = 1;
1174
1175     if (!mo.config_file)
1176         return 0;
1177     
1178     config = fopen(mo.config_file, "rb");
1179     if (NULL == config)
1180     {
1181         fprintf(stderr, "Failed to open config file %s, error %d\n",
1182                 mo.config_file, errno);
1183         exit(errno);
1184     }
1185     
1186     while (1)
1187     {
1188         errno = 0;
1189         size = getline(&ptr, &linelen, config);
1190         if (-1 == size)
1191         {
1192             error = errno;            
1193             break;
1194         }
1195         error = parse_config_line(lineno++, ptr);
1196         if (error)
1197             break;
1198     }
1199     free (ptr);
1200     fclose(config);
1201     return error;
1202 }
1203
1204 int main(int argc, char *argv[])
1205 {
1206     intptr_t i = 0;
1207     int error = 0;
1208     struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
1209     umask(0);
1210
1211     logfile = stderr;
1212
1213     fuse_opt_parse(&args, &mo, fm_mount_opts, NULL);
1214
1215     error = parse_config_file();
1216     if (error)
1217     {
1218         printf("Error in config file %s: %d (%s)\n",
1219                mo.config_file, error, strerror(error));
1220         return error;
1221     }
1222
1223     if (mo.logfile)
1224     {
1225         FILE *newlog;
1226         fclose(stderr);
1227         newlog = fopen(mo.logfile, "wb");
1228         if (!newlog)
1229         {
1230             printf("Can't open specified logfile %s for writing, error %d\n",
1231                     mo.logfile, errno);
1232             return errno;
1233         }
1234         logfile = newlog;
1235         setvbuf(logfile, (char *) NULL, _IONBF, 0);
1236     }
1237
1238     /* Validate options */
1239     if (!mo.basedir)
1240     {
1241         MAINLOG0((logfile, "Need to specify a basedir\n"));
1242         return EINVAL;
1243     }
1244     MAINLOG1((logfile, "mo.basedir %s\n", mo.basedir));
1245
1246     if (!mo.cachedir)
1247     {
1248         MAINLOG0((logfile, "Need to specify a cachedir\n"));
1249         return EINVAL;
1250     }
1251
1252     /* Convert strings into useful integers */
1253     if (mo.cachesize)
1254     {
1255         error = parse_string_to_long_long(mo.cachesize, &cachesize_value);
1256         if (error)
1257         {
1258             MAINLOG0((logfile, "Unable to parse cachesize value: %s\n", mo.cachesize));
1259             return EINVAL;
1260         }
1261     }
1262     
1263     if (mo.num_threads)
1264     {
1265         error = parse_string_to_long(mo.num_threads, &num_encoder_threads_value);
1266         if (error)
1267         {
1268             MAINLOG0((logfile, "Unable to parse num_threads value: %s\n", mo.num_threads));
1269             return EINVAL;
1270         }
1271     }
1272
1273     if (cachesize_value < CACHE_MIN_SIZE)
1274     {
1275         MAINLOG0((logfile, "Cache size %lld is too small; minimum allowed is %lld\n",
1276                   cachesize_value, CACHE_MIN_SIZE));
1277         return EINVAL;
1278     }
1279
1280     if (!mo.db_file)
1281     {
1282         MAINLOG0((logfile, "Need to specify a db file\n"));
1283         return EINVAL;
1284     }
1285
1286     if (mo.format)
1287     {
1288         for (i = 0; i < OUTFMT_INVALID; i++)
1289         {
1290             if (!strcmp(mo.format, formats[i].extension))
1291             {
1292                 format_index = i;
1293                 break;
1294             }
1295             if (i == OUTFMT_INVALID)
1296             {
1297                 MAINLOG0((logfile, "%s not recognised as a valid output format\n", mo.format));
1298                 return EINVAL;
1299             }
1300         }
1301     }
1302     
1303     if (mo.quality)
1304         quality = mo.quality;
1305     else
1306         quality = strdup(formats[format_index].default_quality);
1307
1308     db_state = db_open(mo.db_file);
1309     if (!db_state)
1310     {
1311         MAINLOG0((logfile, "Failed to open database %s\n", mo.db_file));
1312         return 1;
1313     }
1314
1315     error = cache_init(db_state, mo.cachedir, cachesize_value);
1316     if (error)
1317     {
1318         MAINLOG0((logfile, "Failed to init cache %s, error %d\n", mo.cachedir, error));
1319         db_close(db_state);
1320         return 1;
1321     }
1322
1323     strcpy(dir_state.last, "");
1324     dir_state.num = 0;
1325
1326     error = fuse_main(args.argc, args.argv, &fm_oper, NULL);
1327     if (error)
1328         MAINLOG0((logfile, "fuse_main failed, error %d\n", error));
1329
1330     error = cache_shutdown();
1331     if (error)
1332     {
1333         MAINLOG0((logfile, "Failed to shutdown cache %s, error %d\n", mo.cachedir, error));
1334         db_close(db_state);
1335     }
1336
1337     db_close(db_state);
1338     return error;
1339 }