On v, jan 18, 2015 at 19:57:44 +0000, Stuart Henderson wrote: > On 2015/01/18 19:10, David Coppa wrote: > > On Sun, Jan 18, 2015 at 6:38 PM, Stuart Henderson <st...@openbsd.org> wrote: > > > On 2015/01/18 17:42, LÉVAI Dániel wrote: > > >> Hi! > > >> > > >> Has anyone ever had a problem with minidlna not refreshing its media > > >> library automatically (on -current)? > > > > > > That's expected. > > > > > >> Does this only work on Linux w/ inotify? > > > > > > yep. > > > > Daniel, maybe you could investigate if devel/libinotify is good enough for > > it.
Yeah, I got it compiled, but the first thing was a segfault, and I didn't even bother to dig deeper after I read sthen@'s mail about a patch to support kqueue/kevent in FreeBSD's ports tree :-) Also, it seems with libinotify's lib installed in an unusual place (lib/inotify), one would had to start minidlnad with LD_LIBRARY_FLAGS set -- but of course, I may be incorrect. > It might be better to look at the kqueue patch that FreeBSD are using. Now this seems to work for me on i386, at least. It applied almost clean, but I've taken the liberty and removed the whitespace-only changes from FreeBSD's diff(s). I'll be honest, it got a bit slower, but this could very well be because of my rusty hard drives. Here is my diff: Index: Makefile =================================================================== RCS file: /cvs/ports/multimedia/minidlna/Makefile,v retrieving revision 1.10 diff -p -u -r1.10 Makefile --- Makefile 3 Sep 2014 21:15:43 -0000 1.10 +++ Makefile 18 Jan 2015 22:45:10 -0000 @@ -3,6 +3,7 @@ COMMENT= lightweight DLNA/UPnP-AV media server V= 1.1.4 +REVISION= 0 DISTNAME= minidlna-$V PKGNAME= minidlna-$V @@ -34,9 +35,15 @@ LIB_DEPENDS= audio/flac \ graphics/jpeg \ graphics/libexif +AUTOCONF_VERSION= 2.69 +AUTOMAKE_VERSION= 1.14 + NO_TEST= Yes E= ${PREFIX}/share/examples/minidlna +post-patch: + @cd ${WRKSRC} && env AUTOCONF_VERSION=${AUTOCONF_VERSION} \ + AUTOMAKE_VERSION=${AUTOMAKE_VERSION} ./autogen.sh post-install: ${INSTALL_DATA_DIR} $E ${INSTALL_DATA} ${WRKSRC}/minidlna.conf $E Index: patches/patch-configure_ac =================================================================== RCS file: patches/patch-configure_ac diff -N patches/patch-configure_ac --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-configure_ac 18 Jan 2015 22:45:10 -0000 @@ -0,0 +1,12 @@ +$OpenBSD$ +--- configure.ac.orig Tue Aug 26 23:09:22 2014 ++++ configure.ac Sun Jan 18 22:45:07 2015 +@@ -481,7 +481,7 @@ AC_CHECK_LIB(vorbisfile, vorbis_comment_query, + ################################################################################################################ + ### Header checks + +-AC_CHECK_HEADERS([arpa/inet.h asm/unistd.h endian.h machine/endian.h fcntl.h libintl.h locale.h netdb.h netinet/in.h stddef.h stdlib.h string.h sys/file.h sys/inotify.h sys/ioctl.h sys/param.h sys/socket.h sys/time.h unistd.h]) ++AC_CHECK_HEADERS([arpa/inet.h asm/unistd.h endian.h machine/endian.h fcntl.h libintl.h locale.h netdb.h netinet/in.h stddef.h stdlib.h string.h sys/file.h sys/inotify.h sys/ioctl.h sys/param.h sys/socket.h sys/time.h unistd.h sys/event.h]) + + AC_CHECK_FUNCS(inotify_init, AC_DEFINE(HAVE_INOTIFY,1,[Whether kernel has inotify support]), [ + AC_MSG_CHECKING([for __NR_inotify_init syscall]) Index: patches/patch-inotify_c =================================================================== RCS file: patches/patch-inotify_c diff -N patches/patch-inotify_c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-inotify_c 18 Jan 2015 22:45:10 -0000 @@ -0,0 +1,569 @@ +$OpenBSD$ +--- inotify.c.orig Sun Jan 18 22:45:24 2015 ++++ inotify.c Sun Jan 18 22:45:27 2015 +@@ -15,9 +15,9 @@ + * You should have received a copy of the GNU General Public License + * along with MiniDLNA. If not, see <http://www.gnu.org/licenses/>. + */ + #include "config.h" + +-#ifdef HAVE_INOTIFY ++#if defined(HAVE_INOTIFY) || defined(HAVE_SYS_EVENT_H) + #include <stdio.h> + #include <string.h> + #include <stdlib.h> +@@ -31,11 +31,16 @@ + #include <sys/time.h> + #include <sys/resource.h> + #include <poll.h> ++#ifdef HAVE_INOTIFY + #ifdef HAVE_SYS_INOTIFY_H + #include <sys/inotify.h> +-#else ++#else /*HAVE_SYS_INOTIFY_H*/ + #include "linux/inotify.h" + #include "linux/inotify-syscalls.h" ++#endif /*HAVE_SYS_INOTIFY_H*/ ++#else ++#include <sys/event.h> ++#include <fcntl.h> + #endif + #include "libav.h" + +@@ -49,11 +54,13 @@ + #include "playlist.h" + #include "log.h" + ++#ifdef HAVE_INOTIFY + #define EVENT_SIZE ( sizeof (struct inotify_event) ) + #define BUF_LEN ( 1024 * ( EVENT_SIZE + 16 ) ) + #define DESIRED_WATCH_LIMIT 65536 + + #define PATH_BUF_SIZE PATH_MAX ++#endif + + struct watch + { +@@ -86,13 +93,35 @@ add_watch(int fd, const char * path) + struct watch *nw; + int wd; + ++#ifdef HAVE_INOTIFY + wd = inotify_add_watch(fd, path, IN_CREATE|IN_CLOSE_WRITE|IN_DELETE|IN_MOVE); + if( wd < 0 ) + { + DPRINTF(E_ERROR, L_INOTIFY, "inotify_add_watch(%s) [%s]\n", path, strerror(errno)); + return -1; + } ++#else /*HAVE_INOTIFY*/ ++ wd = open(path, O_RDONLY); ++ if (wd == -1) ++ { ++ DPRINTF(E_ERROR, L_INOTIFY, "inotify_add_watch[kqueue,open](%s) [%s]\n", path, strerror(errno)); ++ return -1; ++ } + ++ struct kevent ke; ++ EV_SET(&ke, wd, ++ EVFILT_VNODE, ++ EV_ADD | EV_ENABLE | EV_CLEAR, ++ NOTE_DELETE | NOTE_WRITE | NOTE_EXTEND /*| NOTE_ATTRB*/, ++ 0, NULL); ++ ++ if( kevent(fd, &ke, 1, NULL, 0, NULL) == -1 ) ++ { ++ DPRINTF(E_ERROR, L_INOTIFY, "inotify_add_watch[kqueue](%s) [%s]\n", path, strerror(errno)); ++ return -1; ++ } ++#endif ++ + nw = malloc(sizeof(struct watch)); + if( nw == NULL ) + { +@@ -125,7 +154,12 @@ remove_watch(int fd, const char * path) + for( w = watches; w; w = w->next ) + { + if( strcmp(path, w->path) == 0 ) ++#ifdef HAVE_INOTIFY + return(inotify_rm_watch(fd, w->wd)); ++#else ++ close(w->wd); /* kqueue cleans up events when handle dies*/ ++ return(0); ++#endif + } + + return 1; +@@ -145,15 +179,14 @@ next_highest(unsigned int num) + int + inotify_create_watches(int fd) + { +- FILE * max_watches; +- unsigned int num_watches = 0, watch_limit; ++ unsigned int num_watches = 0; + char **result; + int i, rows = 0; + struct media_dir_s * media_path; + + for( media_path = media_dirs; media_path != NULL; media_path = media_path->next ) + { +- DPRINTF(E_DEBUG, L_INOTIFY, "Add watch to %s\n", media_path->path); ++ DPRINTF(E_DEBUG, L_INOTIFY, "Setting up monitoring on %s\n", media_path->path); + add_watch(fd, media_path->path); + num_watches++; + } +@@ -165,7 +198,11 @@ inotify_create_watches(int fd) + num_watches++; + } + sqlite3_free_table(result); + ++#ifdef HAVE_INOTIFY ++ FILE * max_watches; ++ unsigned int watch_limit; ++ + max_watches = fopen("/proc/sys/fs/inotify/max_user_watches", "r"); + if( max_watches ) + { +@@ -194,16 +231,17 @@ inotify_create_watches(int fd) + else + { + DPRINTF(E_WARN, L_INOTIFY, "WARNING: Inotify max_user_watches [%u] is low or close to the number of used watches [%u] " + "and I do not have permission to increase this limit. Please do so manually by " + "writing a higher value into /proc/sys/fs/inotify/max_user_watches.\n", watch_limit, num_watches); + } + } + } + else + { + DPRINTF(E_WARN, L_INOTIFY, "WARNING: Could not read inotify max_user_watches! " + "Hopefully it is enough to cover %u current directories plus any new ones added.\n", num_watches); + } ++#endif + + return rows; + } +@@ -218,7 +256,11 @@ inotify_remove_watches(int fd) + while( w ) + { + last_w = w; ++#ifdef HAVE_INOTIFY + inotify_rm_watch(fd, w->wd); ++#else ++ close(w->wd); /*kqueue cleans up after fhandle dies*/ ++#endif + free(w->path); + rm_watches++; + w = w->next; +@@ -261,10 +303,10 @@ int add_dir_watch(int fd, char * path, char * filename + while( (e = readdir(ds)) ) + { + if( strcmp(e->d_name, ".") == 0 || + strcmp(e->d_name, "..") == 0 ) + continue; + if( (e->d_type == DT_DIR) || + (e->d_type == DT_UNKNOWN && resolve_unknown_type(dir, NO_MEDIA) == TYPE_DIR) ) + i += add_dir_watch(fd, dir, e->d_name); + } + } +@@ -293,7 +335,10 @@ inotify_insert_file(char * name, const char * path) + media_types types = ALL_MEDIA; + struct media_dir_s * media_path = media_dirs; + struct stat st; ++ struct timeval now; + ++ DPRINTF(E_DEBUG, L_INOTIFY, "inotify_insert_file: %s @ %s\n", name, path); ++ + /* Is it cover art for another file? */ + if( is_image(path) ) + update_if_album_art(path); +@@ -357,6 +402,16 @@ inotify_insert_file(char * name, const char * path) + if( stat(path, &st) != 0 ) + return -1; + ++ (void)gettimeofday(&now, NULL); ++ while (now.tv_sec < st.st_mtime + 3) ++ { ++ DPRINTF(E_DEBUG, L_INOTIFY, "Sleeping until %s is stable for a few seconds ...\n", path); ++ sleep(1); ++ (void)gettimeofday(&now, NULL); ++ if (stat(path, &st) != 0) ++ return -1; ++ } ++ + ts = sql_get_int_field(db, "SELECT TIMESTAMP from DETAILS where PATH = '%q'", path); + if( !ts && is_playlist(path) && (sql_get_int_field(db, "SELECT ID from PLAYLISTS where PATH = '%q'", path) > 0) ) + { +@@ -386,9 +441,9 @@ inotify_insert_file(char * name, const char * path) + + do + { +- //DEBUG DPRINTF(E_DEBUG, L_INOTIFY, "Checking %s\n", parent_buf); ++ /*DEBUG DPRINTF(E_DEBUG, L_INOTIFY, "Checking %s\n", parent_buf);*/ + id = sql_get_text_field(db, "SELECT OBJECT_ID from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID)" + " where d.PATH = '%q' and REF_ID is NULL", parent_buf); + if( id ) + { + if( !depth ) +@@ -446,6 +501,8 @@ inotify_insert_directory(int fd, char *name, const cha + struct media_dir_s* media_path; + struct stat st; + ++ DPRINTF(E_DEBUG, L_INOTIFY, "inotify_insert_directory: %s @ %s\n", name, path); ++ + if( access(path, R_OK|X_OK) != 0 ) + { + DPRINTF(E_WARN, L_INOTIFY, "Could not access %s [%s]\n", path, strerror(errno)); +@@ -459,7 +516,7 @@ inotify_insert_directory(int fd, char *name, const cha + + parent_buf = strdup(path); + id = sql_get_text_field(db, "SELECT OBJECT_ID from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID)" + " where d.PATH = '%q' and REF_ID is NULL", dirname(parent_buf)); + if( !id ) + id = sqlite3_mprintf("%s", BROWSEDIR_ID); + insert_directory(name, path, BROWSEDIR_ID, id+2, get_next_available_id("OBJECTS", id)); +@@ -554,27 +611,25 @@ inotify_remove_file(const char * path) + { + sql_exec(db, "DELETE from PLAYLISTS where ID = %lld", detailID); + sql_exec(db, "DELETE from DETAILS where ID =" + " (SELECT DETAIL_ID from OBJECTS where OBJECT_ID = '%s$%llX')", + MUSIC_PLIST_ID, detailID); + sql_exec(db, "DELETE from OBJECTS where OBJECT_ID = '%s$%llX' or PARENT_ID = '%s$%llX'", + MUSIC_PLIST_ID, detailID, MUSIC_PLIST_ID, detailID); + } + else + { + /* Delete the parent containers if we are about to empty them. */ +- snprintf(sql, sizeof(sql), "SELECT PARENT_ID from OBJECTS where DETAIL_ID = %lld" +- " and PARENT_ID not like '64$%%'", +- (long long int)detailID); ++ snprintf(sql, sizeof(sql), "SELECT PARENT_ID from OBJECTS where DETAIL_ID = %lld", (long long int)detailID); + if( (sql_get_table(db, sql, &result, &rows, NULL) == SQLITE_OK) ) + { + int i, children; + for( i = 1; i <= rows; i++ ) + { + /* If it's a playlist item, adjust the item count of the playlist */ + if( strncmp(result[i], MUSIC_PLIST_ID, strlen(MUSIC_PLIST_ID)) == 0 ) + { + sql_exec(db, "UPDATE PLAYLISTS set FOUND = (FOUND-1) where ID = %d", + atoi(strrchr(result[i], '$') + 1)); + } + + children = sql_get_int_field(db, "SELECT count(*) from OBJECTS where PARENT_ID = '%s'", result[i]); +@@ -582,6 +637,8 @@ inotify_remove_file(const char * path) + continue; + if( children < 2 ) + { ++ sql_exec(db, "DELETE from DETAILS where ID =" ++ " (SELECT DETAIL_ID from OBJECTS where OBJECT_ID = '%s')", result[i]); + sql_exec(db, "DELETE from OBJECTS where OBJECT_ID = '%s'", result[i]); + + ptr = strrchr(result[i], '$'); +@@ -589,6 +646,8 @@ inotify_remove_file(const char * path) + *ptr = '\0'; + if( sql_get_int_field(db, "SELECT count(*) from OBJECTS where PARENT_ID = '%s'", result[i]) == 0 ) + { ++ sql_exec(db, "DELETE from DETAILS where ID =" ++ " (SELECT DETAIL_ID from OBJECTS where OBJECT_ID = '%s')", result[i]); + sql_exec(db, "DELETE from OBJECTS where OBJECT_ID = '%s'", result[i]); + } + } +@@ -613,11 +672,13 @@ inotify_remove_directory(int fd, const char * path) + int64_t detailID = 0; + int rows, i, ret = 1; + ++ DPRINTF(E_DEBUG, L_INOTIFY, "inotify_remove_directory: %s\n", path); ++ + /* Invalidate the scanner cache so we don't insert files into non-existent containers */ + valid_cache = 0; + remove_watch(fd, path); + sql = sqlite3_mprintf("SELECT ID from DETAILS where (PATH > '%q/' and PATH <= '%q/%c')" + " or PATH = '%q'", path, path, 0xFF, path); + if( (sql_get_table(db, sql, &result, &rows, NULL) == SQLITE_OK) ) + { + if( rows ) +@@ -639,6 +700,7 @@ inotify_remove_directory(int fd, const char * path) + return ret; + } + ++#ifdef HAVE_INOTIFY + void * + start_inotify() + { +@@ -649,7 +711,7 @@ start_inotify() + int length, i = 0; + char * esc_name = NULL; + struct stat st; + + pollfds[0].fd = inotify_init(); + pollfds[0].events = POLLIN; + +@@ -664,13 +726,13 @@ start_inotify() + } + inotify_create_watches(pollfds[0].fd); + if (setpriority(PRIO_PROCESS, 0, 19) == -1) + DPRINTF(E_WARN, L_INOTIFY, "Failed to reduce inotify thread priority\n"); + sqlite3_release_memory(1<<31); + av_register_all(); + + while( !quitting ) + { + length = poll(pollfds, 1, timeout); + if( !length ) + { + if( next_pl_fill && (time(NULL) >= next_pl_fill) ) +@@ -708,12 +770,12 @@ start_inotify() + snprintf(path_buf, sizeof(path_buf), "%s/%s", get_path_from_wd(event->wd), event->name); + if ( event->mask & IN_ISDIR && (event->mask & (IN_CREATE|IN_MOVED_TO)) ) + { + DPRINTF(E_DEBUG, L_INOTIFY, "The directory %s was %s.\n", + path_buf, (event->mask & IN_MOVED_TO ? "moved here" : "created")); + inotify_insert_directory(pollfds[0].fd, esc_name, path_buf); + } + else if ( (event->mask & (IN_CLOSE_WRITE|IN_MOVED_TO|IN_CREATE)) && + (lstat(path_buf, &st) == 0) ) + { + if( S_ISLNK(st.st_mode) ) + { +@@ -727,7 +789,7 @@ start_inotify() + else if( event->mask & (IN_CLOSE_WRITE|IN_MOVED_TO) && st.st_size > 0 ) + { + if( (event->mask & IN_MOVED_TO) || + (sql_get_int_field(db, "SELECT TIMESTAMP from DETAILS where PATH = '%q'", path_buf) != st.st_mtime) ) + { + DPRINTF(E_DEBUG, L_INOTIFY, "The file %s was %s.\n", + path_buf, (event->mask & IN_MOVED_TO ? "moved here" : "changed")); +@@ -756,4 +818,234 @@ quitting: + + return 0; + } ++#else ++void * ++start_kqueue() ++{ ++ int global_kqueue_handle = -1; ++ ++ global_kqueue_handle = kqueue(); ++ if ( global_kqueue_handle < 0 ) ++ { ++ DPRINTF(E_ERROR, L_INOTIFY, "kqueue() failed: %s\n", strerror(errno)); ++ return 0; ++ } ++ ++ while( scanning ) ++ { ++ if( quitting ) ++ goto quitting; ++ ++ DPRINTF(E_DEBUG, L_INOTIFY, "..waiting for scanning to complete...\n"); ++ sleep(1); ++ } ++ ++ DPRINTF(E_DEBUG, L_INOTIFY, "GOING WILD!\n"); ++ ++ inotify_create_watches(global_kqueue_handle); ++ if (setpriority(PRIO_PROCESS, 0, 19) == -1) ++ DPRINTF(E_WARN, L_INOTIFY, "Failed to reduce kqueue thread priority\n"); ++ sqlite3_release_memory(1<<31); ++ av_register_all(); ++ ++ while( !quitting ) ++ { ++ struct kevent ke; ++ if ( kevent(global_kqueue_handle, NULL, 0, &ke, 1, NULL) == -1 ) ++ { ++ DPRINTF(E_WARN, L_INOTIFY, "kevent polling failure: %s\n", strerror(errno)); ++ continue; ++ } ++ ++ /*DPRINTF(E_DEBUG, L_INOTIFY, "GOT KEVENT:\n" ++ "ident=0x%X, filter=0x%X, flags=0x%X, fflags=0x%X, data=0x%X, udata=0x%X\n", ++ ke.ident, ke.filter, ke.flags, ke.fflags, ke.data, ke.udata);*/ ++ ++ char* dir_path = get_path_from_wd(ke.ident); ++ if (dir_path == NULL) ++ { ++ DPRINTF(E_ERROR, L_INOTIFY, "Path with FD=0x%X can't be resolved.\n", ke.ident); ++ continue; ++ } ++ ++ if (ke.fflags & NOTE_DELETE) ++ { ++ DPRINTF(E_DEBUG, L_INOTIFY, "Path [%s] deleted.\n", dir_path); ++ inotify_remove_directory(ke.ident, dir_path); ++ } ++ else if ((ke.fflags & (NOTE_WRITE | NOTE_LINK)) == (NOTE_WRITE | NOTE_LINK)) ++ { ++ DPRINTF(E_DEBUG, L_INOTIFY, "Path [%s] content updated (directory).\n", dir_path); ++ ++ char * sql; ++ char **result; ++ int i, rows; ++ sql = sqlite3_mprintf("SELECT PATH from DETAILS where (PATH > '%q/' and PATH <= '%q/%c')" ++ " and SIZE = ''", dir_path, dir_path, 0xFF); ++ if( (sql_get_table(db, sql, &result, &rows, NULL) != SQLITE_OK) ) ++ { ++ DPRINTF(E_WARN, L_INOTIFY, "Read state [%s]: Query failed, not updating\n", dir_path); ++ sqlite3_free(sql); ++ continue; ++ } ++ ++ for( i=1; i <= rows; i++ ) ++ { ++ DPRINTF(E_DEBUG, L_INOTIFY, "Indexed content: %s\n", result[i]); ++ if (access(result[i], R_OK) == -1) ++ { ++ /* actually, global_kqueue_handle is not used here */ ++ inotify_remove_directory(global_kqueue_handle, result[i]); ++ } ++ } ++ ++ DIR* d; ++ struct dirent * entry; ++ d = opendir(dir_path); ++ if (!d) ++ { ++ DPRINTF(E_ERROR, L_INOTIFY, "Can't list [%s] (%s)\n", dir_path, strerror(errno)); ++ continue; ++ } ++ ++ for ( entry = readdir(d); entry != NULL; entry = readdir(d) ) ++ { ++ if ( (entry->d_type == DT_DIR) && ++ (strcmp(entry->d_name, "..") != 0) && ++ (strcmp(entry->d_name, ".") != 0) ) ++ { ++ char tmp_path[PATH_MAX]; ++ int result_path_len; ++ ++ result_path_len = snprintf(tmp_path, PATH_MAX, ++ "%s/%s", dir_path, entry->d_name); ++ if (result_path_len >= PATH_MAX) ++ { ++ DPRINTF(E_ERROR, L_INOTIFY, "File path too long for %s!", entry->d_name); ++ continue; ++ } ++ ++ DPRINTF(E_DEBUG, L_INOTIFY, "Walking %s\n", tmp_path); ++ char found_flag = 0; ++ for( i=1; i <= rows; i++ ) ++ { ++ if (strcmp(result[i], tmp_path) == 0) ++ { ++ found_flag = 1; ++ break; ++ } ++ } ++ ++ if ( !found_flag ) ++ { ++ char * esc_name = NULL; ++ esc_name = modifyString(strdup(entry->d_name), "&", "&amp;", 0); ++ inotify_insert_directory(global_kqueue_handle, esc_name, tmp_path); ++ free(esc_name); ++ } ++ } ++ } ++ ++ closedir(d); ++ ++ sqlite3_free_table(result); ++ sqlite3_free(sql); ++ } ++ else if (ke.fflags & NOTE_WRITE) ++ { ++ DPRINTF(E_DEBUG, L_INOTIFY, "Path [%s] content updated (file).\n", dir_path); ++ ++ char * sql; ++ char **result; ++ int i, rows; ++ sql = sqlite3_mprintf("SELECT PATH from DETAILS where (PATH > '%q/' and PATH <= '%q/%c')" ++ " and SIZE <> ''", dir_path, dir_path, 0xFF); ++ if( (sql_get_table(db, sql, &result, &rows, NULL) != SQLITE_OK) ) ++ { ++ DPRINTF(E_WARN, L_INOTIFY, "Read state [%s]: Query failed, not updating\n", dir_path); ++ sqlite3_free(sql); ++ continue; ++ } ++ ++ for( i=1; i <= rows; i++ ) ++ { ++ DPRINTF(E_DEBUG, L_INOTIFY, "Indexed content: %s\n", result[i]); ++ if (access(result[i], R_OK) == -1) /*oops, our file is gone*/ ++ { ++ inotify_remove_file(result[i]); ++ } ++ } ++ ++ DIR* d; ++ struct dirent * entry; ++ d = opendir(dir_path); ++ if (!d) ++ { ++ DPRINTF(E_ERROR, L_INOTIFY, "Can't list [%s] (%s)\n", dir_path, strerror(errno)); ++ continue; ++ } ++ ++ for ( entry = readdir(d); entry != NULL; entry = readdir(d) ) ++ { ++ if ( (entry->d_type == DT_REG) || ++ (entry->d_type == DT_LNK) ) ++ { ++ char tmp_path[PATH_MAX]; ++ int result_path_len; ++ ++ result_path_len = snprintf(tmp_path, PATH_MAX, ++ "%s/%s", dir_path, entry->d_name); ++ if (result_path_len >= PATH_MAX) ++ { ++ DPRINTF(E_ERROR, L_INOTIFY, "File path too long for %s!", entry->d_name); ++ continue; ++ } ++ ++ DPRINTF(E_DEBUG, L_INOTIFY, "Walking %s\n", tmp_path); ++ ++ char found_flag = 0; ++ for( i=1; i <= rows; i++ ) ++ { ++ if (strcmp(result[i], tmp_path) == 0) ++ { ++ found_flag = 1; ++ break; ++ } ++ } ++ ++ if ( !found_flag ) ++ { ++ char * esc_name = NULL; ++ struct stat st; ++ ++ if( stat(tmp_path, &st) != 0 ) ++ { ++ DPRINTF(E_ERROR, L_INOTIFY, "'%s' disappeared!", tmp_path); ++ continue; ++ } ++ ++ esc_name = modifyString(strdup(entry->d_name), "&", "&amp;", 0); ++ if ( S_ISDIR(st.st_mode) ) ++ inotify_insert_directory(global_kqueue_handle, esc_name, tmp_path); ++ else ++ inotify_insert_file(esc_name, tmp_path); ++ free(esc_name); ++ } ++ } ++ } ++ ++ closedir(d); ++ ++ sqlite3_free_table(result); ++ sqlite3_free(sql); ++ } ++ } ++ inotify_remove_watches(global_kqueue_handle); ++quitting: ++ ++ return 0; ++} + #endif ++ ++#endif // defined(HAVE_INOTIFY) || defined(HAVE_SYS_EVENT_H) ++ Index: patches/patch-inotify_h =================================================================== RCS file: patches/patch-inotify_h diff -N patches/patch-inotify_h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-inotify_h 18 Jan 2015 22:45:10 -0000 @@ -0,0 +1,14 @@ +$OpenBSD$ +--- inotify.h.orig Tue Aug 26 23:09:22 2014 ++++ inotify.h Sun Jan 18 22:45:07 2015 +@@ -4,4 +4,10 @@ inotify_remove_file(const char * path); + + void * + start_inotify(); ++#elif defined(HAVE_SYS_EVENT_H) ++int ++inotify_remove_file(const char* path); ++ ++void * ++start_kqueue(); + #endif Index: patches/patch-metadata_c =================================================================== RCS file: patches/patch-metadata_c diff -N patches/patch-metadata_c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-metadata_c 18 Jan 2015 22:45:10 -0000 @@ -0,0 +1,12 @@ +$OpenBSD$ +--- metadata.c.orig Tue Aug 26 23:09:22 2014 ++++ metadata.c Sun Jan 18 22:45:07 2015 +@@ -149,7 +149,7 @@ check_for_captions(const char *path, int64_t detailID) + + if (ret == 0) + { +- sql_exec(db, "INSERT into CAPTIONS" ++ sql_exec(db, "INSERT OR REPLACE into CAPTIONS" + " (ID, PATH) " + "VALUES" + " (%lld, %Q)", detailID, file); Index: patches/patch-minidlna_c =================================================================== RCS file: patches/patch-minidlna_c diff -N patches/patch-minidlna_c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-minidlna_c 18 Jan 2015 22:45:10 -0000 @@ -0,0 +1,43 @@ +$OpenBSD$ +--- minidlna.c.orig Tue Aug 26 23:09:22 2014 ++++ minidlna.c Sun Jan 18 22:45:07 2015 +@@ -46,6 +46,7 @@ + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ ++#define FD_SETSIZE 8192 + #include <stdlib.h> + #include <unistd.h> + #include <string.h> +@@ -376,6 +377,7 @@ rescan: + open_db(&db); + if (*scanner_pid == 0) /* child (scanner) process */ + { ++ DPRINTF(E_DEBUG, L_GENERAL, "Starting scanner in forked child\n"); + start_scanner(); + sqlite3_close(db); + log_close(); +@@ -384,6 +386,7 @@ rescan: + } + else if (*scanner_pid < 0) + { ++ DPRINTF(E_DEBUG, L_GENERAL, "Starting scanner in parent\n"); + start_scanner(); + } + #else +@@ -1047,6 +1050,15 @@ main(int argc, char **argv) + "Inotify will be disabled.\n"); + else if (pthread_create(&inotify_thread, NULL, start_inotify, NULL) != 0) + DPRINTF(E_FATAL, L_GENERAL, "ERROR: pthread_create() failed for start_inotify. EXITING\n"); ++ } ++#elif defined(HAVE_SYS_EVENT_H) ++ if( GETFLAG(INOTIFY_MASK) ) ++ { ++ if (!sqlite3_threadsafe() || sqlite3_libversion_number() < 3005001) ++ DPRINTF(E_ERROR, L_GENERAL, "SQLite library is not threadsafe! " ++ "Kqueue will be disabled.\n"); ++ else if (pthread_create(&inotify_thread, NULL, start_kqueue, NULL) != 0) ++ DPRINTF(E_FATAL, L_GENERAL, "ERROR: pthread_create() failed for start_kqueue. EXITING\n"); + } + #endif + smonitor = OpenAndConfMonitorSocket(); Index: patches/patch-minidlna_conf =================================================================== RCS file: /cvs/ports/multimedia/minidlna/patches/patch-minidlna_conf,v retrieving revision 1.4 diff -p -u -r1.4 patch-minidlna_conf --- patches/patch-minidlna_conf 11 Jun 2014 00:26:59 -0000 1.4 +++ patches/patch-minidlna_conf 18 Jan 2015 22:45:10 -0000 @@ -28,7 +28,7 @@ default directories, plus we don't use i # set this to merge all media_dir base contents into the root container # note: the default is no -@@ -25,22 +26,18 @@ media_dir=/opt +@@ -25,22 +26,22 @@ media_dir=/opt #friendly_name=My DLNA Server # set this if you would like to specify the directory where you want MiniDLNA to store its database and album art cache @@ -47,10 +47,10 @@ default directories, plus we don't use i # this should be a list of file names to check for when searching for album art # note: names should be delimited with a forward slash ("/") album_art_names=Cover.jpg/cover.jpg/AlbumArtSmall.jpg/albumartsmall.jpg/AlbumArt.jpg/albumart.jpg/Album.jpg/album.jpg/Folder.jpg/folder.jpg/Thumb.jpg/thumb.jpg -- --# set this to no to disable inotify monitoring to automatically discover new files --# note: the default is yes --inotify=yes + + # set this to no to disable inotify monitoring to automatically discover new files + # note: the default is yes + inotify=yes # set this to yes to enable support for streaming .jpg and .mp3 files to a TiVo supporting HMO enable_tivo=no Index: patches/patch-upnpevents_c =================================================================== RCS file: patches/patch-upnpevents_c diff -N patches/patch-upnpevents_c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-upnpevents_c 18 Jan 2015 22:45:10 -0000 @@ -0,0 +1,14 @@ +$OpenBSD$ +--- upnpevents.c.orig Tue Aug 26 23:09:22 2014 ++++ upnpevents.c Sun Jan 18 22:45:07 2015 +@@ -417,6 +417,10 @@ void upnpevents_selectfds(fd_set *readset, fd_set *wri + { + struct upnp_event_notify * obj; + for(obj = notifylist.lh_first; obj != NULL; obj = obj->entries.le_next) { ++ if (obj->s > FD_SETSIZE) ++ DPRINTF(E_FATAL, L_HTTP, ++ "upnpevents_selectfds: file descriptor %d too big for select, limit is %d\n", ++ obj->s, FD_SETSIZE); + DPRINTF(E_DEBUG, L_HTTP, "upnpevents_selectfds: %p %d %d\n", + obj, obj->state, obj->s); + if(obj->s >= 0) { Comments, tests? Daniel -- LÉVAI Dániel PGP key ID = 0x83B63A8F Key fingerprint = DBEC C66B A47A DFA2 792D 650C C69B BE4C 83B6 3A8F