1 /* MiniDLNA media server
2 * Copyright (C) 2008-2010 Justin Maggard
4 * This file is part of MiniDLNA.
6 * MiniDLNA is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
10 * MiniDLNA is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with MiniDLNA. If not, see <http://www.gnu.org/licenses/>.
27 #include <sys/types.h>
30 #include <sys/resource.h>
33 #include <sys/inotify.h>
35 #include "linux/inotify.h"
36 #include "linux/inotify-syscalls.h"
39 #include "upnpglobalvars.h"
49 #define EVENT_SIZE ( sizeof (struct inotify_event) )
50 #define BUF_LEN ( 1024 * ( EVENT_SIZE + 16 ) )
51 #define DESIRED_WATCH_LIMIT 65536
53 #define PATH_BUF_SIZE PATH_MAX
57 int wd
; /* watch descriptor */
58 char *path
; /* watched path */
62 static struct watch
*watches
;
63 static struct watch
*lastwatch
= NULL
;
64 static time_t next_pl_fill
= 0;
66 char *get_path_from_wd(int wd
)
68 struct watch
*w
= watches
;
81 add_watch(int fd
, const char * path
)
86 wd
= inotify_add_watch(fd
, path
, IN_CREATE
|IN_CLOSE_WRITE
|IN_DELETE
|IN_MOVE
);
89 DPRINTF(E_ERROR
, L_INOTIFY
, "inotify_add_watch(%s) [%s]\n", path
, strerror(errno
));
93 nw
= malloc(sizeof(struct watch
));
96 DPRINTF(E_ERROR
, L_INOTIFY
, "malloc() error\n");
101 nw
->path
= strdup(path
);
103 if( watches
== NULL
)
108 if( lastwatch
!= NULL
)
110 lastwatch
->next
= nw
;
118 remove_watch(int fd
, const char * path
)
122 for( w
= watches
; w
; w
= w
->next
)
124 if( strcmp(path
, w
->path
) == 0 )
125 return(inotify_rm_watch(fd
, w
->wd
));
132 next_highest(unsigned int num
)
143 inotify_create_watches(int fd
)
146 unsigned int num_watches
= 0, watch_limit
;
149 struct media_dir_s
* media_path
;
151 for( media_path
= media_dirs
; media_path
!= NULL
; media_path
= media_path
->next
)
153 DPRINTF(E_DEBUG
, L_INOTIFY
, "Add watch to %s\n", media_path
->path
);
154 add_watch(fd
, media_path
->path
);
157 sql_get_table(db
, "SELECT PATH from DETAILS where MIME is NULL and PATH is not NULL", &result
, &rows
, NULL
);
158 for( i
=1; i
<= rows
; i
++ )
160 DPRINTF(E_DEBUG
, L_INOTIFY
, "Add watch to %s\n", result
[i
]);
161 add_watch(fd
, result
[i
]);
164 sqlite3_free_table(result
);
166 max_watches
= fopen("/proc/sys/fs/inotify/max_user_watches", "r");
169 if( fscanf(max_watches
, "%10u", &watch_limit
) < 1 )
172 if( (watch_limit
< DESIRED_WATCH_LIMIT
) || (watch_limit
< (num_watches
*4/3)) )
174 max_watches
= fopen("/proc/sys/fs/inotify/max_user_watches", "w");
177 if( DESIRED_WATCH_LIMIT
>= (num_watches
*3/4) )
179 fprintf(max_watches
, "%u", DESIRED_WATCH_LIMIT
);
181 else if( next_highest(num_watches
) >= (num_watches
*3/4) )
183 fprintf(max_watches
, "%u", next_highest(num_watches
));
187 fprintf(max_watches
, "%u", next_highest(next_highest(num_watches
)));
193 DPRINTF(E_WARN
, L_INOTIFY
, "WARNING: Inotify max_user_watches [%u] is low or close to the number of used watches [%u] "
194 "and I do not have permission to increase this limit. Please do so manually by "
195 "writing a higher value into /proc/sys/fs/inotify/max_user_watches.\n", watch_limit
, num_watches
);
201 DPRINTF(E_WARN
, L_INOTIFY
, "WARNING: Could not read inotify max_user_watches! "
202 "Hopefully it is enough to cover %u current directories plus any new ones added.\n", num_watches
);
209 inotify_remove_watches(int fd
)
211 struct watch
*w
= watches
;
212 struct watch
*last_w
;
218 inotify_rm_watch(fd
, w
->wd
);
229 int add_dir_watch(int fd
, char * path
, char * filename
)
240 snprintf(buf
, sizeof(buf
), "%s/%s", path
, filename
);
246 wd
= add_watch(fd
, dir
);
249 DPRINTF(E_ERROR
, L_INOTIFY
, "add_watch() [%s]\n", strerror(errno
));
253 DPRINTF(E_INFO
, L_INOTIFY
, "Added watch to %s [%d]\n", dir
, wd
);
259 while( (e
= readdir(ds
)) )
261 if( strcmp(e
->d_name
, ".") == 0 ||
262 strcmp(e
->d_name
, "..") == 0 )
264 if( (e
->d_type
== DT_DIR
) ||
265 (e
->d_type
== DT_UNKNOWN
&& resolve_unknown_type(dir
, NO_MEDIA
) == TYPE_DIR
) )
266 i
+= add_dir_watch(fd
, dir
, e
->d_name
);
271 DPRINTF(E_ERROR
, L_INOTIFY
, "Opendir error! [%s]\n", strerror(errno
));
280 inotify_insert_file(char * name
, const char * path
)
287 char * parent_buf
= NULL
;
291 enum media_types type
= ALL_MEDIA
;
292 struct media_dir_s
* media_path
= media_dirs
;
295 /* Is it cover art for another file? */
297 update_if_album_art(path
);
298 else if( ends_with(path
, ".srt") )
299 check_for_captions(path
, 0);
301 /* Check if we're supposed to be scanning for this file type in this directory */
304 if( strncmp(path
, media_path
->path
, strlen(media_path
->path
)) == 0 )
306 type
= media_path
->type
;
309 media_path
= media_path
->next
;
314 if( !is_image(path
) &&
321 if( !is_audio(path
) &&
326 if( !is_video(path
) )
330 if( !is_image(path
) )
338 /* If it's already in the database and hasn't been modified, skip it. */
339 if( stat(path
, &st
) != 0 )
342 ts
= sql_get_int_field(db
, "SELECT TIMESTAMP from DETAILS where PATH = '%q'", path
);
343 if( !ts
&& is_playlist(path
) && (sql_get_int_field(db
, "SELECT ID from PLAYLISTS where PATH = '%q'", path
) > 0) )
345 DPRINTF(E_DEBUG
, L_INOTIFY
, "Re-reading modified playlist.\n", path
);
346 inotify_remove_file(path
);
349 else if( ts
< st
.st_mtime
)
352 DPRINTF(E_DEBUG
, L_INOTIFY
, "%s is newer than the last db entry.\n", path
);
353 inotify_remove_file(path
);
356 /* Find the parentID. If it's not found, create all necessary parents. */
357 len
= strlen(path
)+1;
358 if( !(path_buf
= malloc(len
)) ||
359 !(last_dir
= malloc(len
)) ||
360 !(base_name
= malloc(len
)) )
362 base_copy
= base_name
;
366 strcpy(path_buf
, path
);
367 parent_buf
= dirname(path_buf
);
371 //DEBUG DPRINTF(E_DEBUG, L_INOTIFY, "Checking %s\n", parent_buf);
372 id
= sql_get_text_field(db
, "SELECT OBJECT_ID from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID)"
373 " where d.PATH = '%q' and REF_ID is NULL", parent_buf
);
378 DPRINTF(E_DEBUG
, L_INOTIFY
, "Found first known parentID: %s [%s]\n", id
, parent_buf
);
379 /* Insert newly-found directory */
380 strcpy(base_name
, last_dir
);
381 base_copy
= basename(base_name
);
382 insert_directory(base_copy
, last_dir
, BROWSEDIR_ID
, id
+2, get_next_available_id("OBJECTS", id
));
387 strcpy(last_dir
, parent_buf
);
388 parent_buf
= dirname(parent_buf
);
390 while( strcmp(parent_buf
, "/") != 0 );
392 if( strcmp(parent_buf
, "/") == 0 )
394 id
= sqlite3_mprintf("%s", BROWSEDIR_ID
);
398 strcpy(path_buf
, path
);
406 //DEBUG DPRINTF(E_DEBUG, L_INOTIFY, "Inserting %s\n", name);
407 insert_file(name
, path
, id
+2, get_next_available_id("OBJECTS", id
));
409 if( (is_audio(path
) || is_playlist(path
)) && next_pl_fill
!= 1 )
411 next_pl_fill
= time(NULL
) + 120; // Schedule a playlist scan for 2 minutes from now.
412 //DEBUG DPRINTF(E_WARN, L_INOTIFY, "Playlist scan scheduled for %s", ctime(&next_pl_fill));
419 inotify_insert_directory(int fd
, char *name
, const char * path
)
423 char *id
, *parent_buf
, *esc_name
;
424 char path_buf
[PATH_MAX
];
426 enum file_types type
= TYPE_UNKNOWN
;
427 enum media_types dir_type
= ALL_MEDIA
;
428 struct media_dir_s
* media_path
;
431 if( access(path
, R_OK
|X_OK
) != 0 )
433 DPRINTF(E_WARN
, L_INOTIFY
, "Could not access %s [%s]\n", path
, strerror(errno
));
436 if( sql_get_int_field(db
, "SELECT ID from DETAILS where PATH = '%q'", path
) > 0 )
438 DPRINTF(E_DEBUG
, L_INOTIFY
, "%s already exists\n", path
);
442 parent_buf
= strdup(path
);
443 id
= sql_get_text_field(db
, "SELECT OBJECT_ID from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID)"
444 " where d.PATH = '%q' and REF_ID is NULL", dirname(parent_buf
));
446 id
= sqlite3_mprintf("%s", BROWSEDIR_ID
);
447 insert_directory(name
, path
, BROWSEDIR_ID
, id
+2, get_next_available_id("OBJECTS", id
));
451 wd
= add_watch(fd
, path
);
454 DPRINTF(E_ERROR
, L_INOTIFY
, "add_watch() failed\n");
458 DPRINTF(E_INFO
, L_INOTIFY
, "Added watch to %s [%d]\n", path
, wd
);
461 media_path
= media_dirs
;
464 if( strncmp(path
, media_path
->path
, strlen(media_path
->path
)) == 0 )
466 dir_type
= media_path
->type
;
469 media_path
= media_path
->next
;
475 DPRINTF(E_ERROR
, L_INOTIFY
, "opendir failed! [%s]\n", strerror(errno
));
478 while( (e
= readdir(ds
)) )
480 if( e
->d_name
[0] == '.' )
482 esc_name
= escape_tag(e
->d_name
, 1);
483 snprintf(path_buf
, sizeof(path_buf
), "%s/%s", path
, e
->d_name
);
490 type
= resolve_unknown_type(path_buf
, dir_type
);
494 if( type
== TYPE_DIR
)
496 inotify_insert_directory(fd
, esc_name
, path_buf
);
498 else if( type
== TYPE_FILE
)
500 if( (stat(path_buf
, &st
) == 0) && (st
.st_blocks
<<9 >= st
.st_size
) )
502 inotify_insert_file(esc_name
, path_buf
);
513 inotify_remove_file(const char * path
)
516 char art_cache
[PATH_MAX
];
520 sqlite_int64 detailID
;
523 if( ends_with(path
, ".srt") )
525 rows
= sql_exec(db
, "DELETE from CAPTIONS where PATH = '%q'", path
);
528 /* Invalidate the scanner cache so we don't insert files into non-existent containers */
530 playlist
= is_playlist(path
);
531 id
= sql_get_text_field(db
, "SELECT ID from %s where PATH = '%q'", playlist
?"PLAYLISTS":"DETAILS", path
);
534 detailID
= strtoll(id
, NULL
, 10);
538 sql_exec(db
, "DELETE from PLAYLISTS where ID = %lld", detailID
);
539 sql_exec(db
, "DELETE from DETAILS where ID ="
540 " (SELECT DETAIL_ID from OBJECTS where OBJECT_ID = '%s$%llX')",
541 MUSIC_PLIST_ID
, detailID
);
542 sql_exec(db
, "DELETE from OBJECTS where OBJECT_ID = '%s$%llX' or PARENT_ID = '%s$%llX'",
543 MUSIC_PLIST_ID
, detailID
, MUSIC_PLIST_ID
, detailID
);
547 /* Delete the parent containers if we are about to empty them. */
548 snprintf(sql
, sizeof(sql
), "SELECT PARENT_ID from OBJECTS where DETAIL_ID = %lld", detailID
);
549 if( (sql_get_table(db
, sql
, &result
, &rows
, NULL
) == SQLITE_OK
) )
552 for( i
=1; i
<= rows
; i
++ )
554 /* If it's a playlist item, adjust the item count of the playlist */
555 if( strncmp(result
[i
], MUSIC_PLIST_ID
, strlen(MUSIC_PLIST_ID
)) == 0 )
557 sql_exec(db
, "UPDATE PLAYLISTS set FOUND = (FOUND-1) where ID = %d",
558 atoi(strrchr(result
[i
], '$') + 1));
561 children
= sql_get_int_field(db
, "SELECT count(*) from OBJECTS where PARENT_ID = '%s'", result
[i
]);
566 sql_exec(db
, "DELETE from DETAILS where ID ="
567 " (SELECT DETAIL_ID from OBJECTS where OBJECT_ID = '%s')", result
[i
]);
568 sql_exec(db
, "DELETE from OBJECTS where OBJECT_ID = '%s'", result
[i
]);
570 ptr
= strrchr(result
[i
], '$');
573 if( sql_get_int_field(db
, "SELECT count(*) from OBJECTS where PARENT_ID = '%s'", result
[i
]) == 0 )
575 sql_exec(db
, "DELETE from DETAILS where ID ="
576 " (SELECT DETAIL_ID from OBJECTS where OBJECT_ID = '%s')", result
[i
]);
577 sql_exec(db
, "DELETE from OBJECTS where OBJECT_ID = '%s'", result
[i
]);
581 sqlite3_free_table(result
);
583 /* Now delete the actual objects */
584 sql_exec(db
, "DELETE from DETAILS where ID = %lld", detailID
);
585 sql_exec(db
, "DELETE from OBJECTS where DETAIL_ID = %lld", detailID
);
587 snprintf(art_cache
, sizeof(art_cache
), "%s/art_cache%s", db_path
, path
);
594 inotify_remove_directory(int fd
, const char * path
)
598 sqlite_int64 detailID
= 0;
599 int rows
, i
, ret
= 1;
601 /* Invalidate the scanner cache so we don't insert files into non-existent containers */
603 remove_watch(fd
, path
);
604 sql
= sqlite3_mprintf("SELECT ID from DETAILS where PATH glob '%q/*'"
605 " UNION ALL SELECT ID from DETAILS where PATH = '%q'", path
, path
);
606 if( (sql_get_table(db
, sql
, &result
, &rows
, NULL
) == SQLITE_OK
) )
610 for( i
=1; i
<= rows
; i
++ )
612 detailID
= strtoll(result
[i
], NULL
, 10);
613 sql_exec(db
, "DELETE from DETAILS where ID = %lld", detailID
);
614 sql_exec(db
, "DELETE from OBJECTS where DETAIL_ID = %lld", detailID
);
618 sqlite3_free_table(result
);
621 /* Clean up any album art entries in the deleted directory */
622 sql_exec(db
, "DELETE from ALBUM_ART where PATH glob '%q/*'", path
);
630 struct pollfd pollfds
[1];
632 char buffer
[BUF_LEN
];
633 char path_buf
[PATH_MAX
];
635 char * esc_name
= NULL
;
638 pollfds
[0].fd
= inotify_init();
639 pollfds
[0].events
= POLLIN
;
641 if ( pollfds
[0].fd
< 0 )
642 DPRINTF(E_ERROR
, L_INOTIFY
, "inotify_init() failed!\n");
650 inotify_create_watches(pollfds
[0].fd
);
651 if (setpriority(PRIO_PROCESS
, 0, 19) == -1)
652 DPRINTF(E_WARN
, L_INOTIFY
, "Failed to reduce inotify thread priority\n");
653 sqlite3_release_memory(1<<31);
657 length
= poll(pollfds
, 1, timeout
);
660 if( next_pl_fill
&& (time(NULL
) >= next_pl_fill
) )
667 else if( length
< 0 )
669 if( (errno
== EINTR
) || (errno
== EAGAIN
) )
672 DPRINTF(E_ERROR
, L_INOTIFY
, "read failed!\n");
676 length
= read(pollfds
[0].fd
, buffer
, BUF_LEN
);
682 struct inotify_event
* event
= (struct inotify_event
*) &buffer
[i
];
685 if( *(event
->name
) == '.' )
687 i
+= EVENT_SIZE
+ event
->len
;
690 esc_name
= modifyString(strdup(event
->name
), "&", "&amp;", 0);
691 sprintf(path_buf
, "%s/%s", get_path_from_wd(event
->wd
), event
->name
);
692 if ( event
->mask
& IN_ISDIR
&& (event
->mask
& (IN_CREATE
|IN_MOVED_TO
)) )
694 DPRINTF(E_DEBUG
, L_INOTIFY
, "The directory %s was %s.\n",
695 path_buf
, (event
->mask
& IN_MOVED_TO
? "moved here" : "created"));
697 /* This could be a directory created by auto-mount.
698 * It will be empty until the drive is mounted to this directory.
699 * So let's wait a few seconds to allow mount to complete.
701 if ( wait_for_mount(path_buf
) >= 0 )
702 inotify_insert_directory(pollfds
[0].fd
, esc_name
, path_buf
);
705 else if ( (event
->mask
& (IN_CLOSE_WRITE
|IN_MOVED_TO
|IN_CREATE
)) &&
706 (lstat(path_buf
, &st
) == 0) )
708 if( S_ISLNK(st
.st_mode
) )
710 DPRINTF(E_DEBUG
, L_INOTIFY
, "The symbolic link %s was %s.\n",
711 path_buf
, (event
->mask
& IN_MOVED_TO
? "moved here" : "created"));
712 if( stat(path_buf
, &st
) == 0 && S_ISDIR(st
.st_mode
) )
713 inotify_insert_directory(pollfds
[0].fd
, esc_name
, path_buf
);
715 inotify_insert_file(esc_name
, path_buf
);
717 else if( event
->mask
& (IN_CLOSE_WRITE
|IN_MOVED_TO
) && st
.st_size
> 0 )
719 if( (event
->mask
& IN_MOVED_TO
) ||
720 (sql_get_int_field(db
, "SELECT TIMESTAMP from DETAILS where PATH = '%q'", path_buf
) != st
.st_mtime
) )
722 DPRINTF(E_DEBUG
, L_INOTIFY
, "The file %s was %s.\n",
723 path_buf
, (event
->mask
& IN_MOVED_TO
? "moved here" : "changed"));
724 inotify_insert_file(esc_name
, path_buf
);
728 else if ( event
->mask
& (IN_DELETE
|IN_MOVED_FROM
) )
730 DPRINTF(E_DEBUG
, L_INOTIFY
, "The %s %s was %s.\n",
731 (event
->mask
& IN_ISDIR
? "directory" : "file"),
732 path_buf
, (event
->mask
& IN_MOVED_FROM
? "moved away" : "deleted"));
733 if ( event
->mask
& IN_ISDIR
)
734 inotify_remove_directory(pollfds
[0].fd
, path_buf
);
736 inotify_remove_file(path_buf
);
740 i
+= EVENT_SIZE
+ event
->len
;
743 inotify_remove_watches(pollfds
[0].fd
);
745 close(pollfds
[0].fd
);