(trunk daemon) #1882: take KyleK's suggestion of using inotify when possible. As he writes, "the opendir() approach will basically prevent my NAS drives to go to standby."
This commit is contained in:
parent
7400adb70d
commit
05bc8472dc
35
configure.ac
35
configure.ac
|
@ -112,6 +112,41 @@ AC_CHECK_DECLS(posix_fadvise, [], [], [
|
|||
AC_CHECK_FUNCS([posix_fadvise])
|
||||
|
||||
|
||||
dnl ----------------------------------------------------------------------------
|
||||
dnl
|
||||
dnl file monitoring for the daemon
|
||||
|
||||
AC_CHECK_HEADER([sys/inotify.h],
|
||||
[AC_CHECK_FUNC([inotify_init],[have_inotify="yes"],[have_inotify="no"])],
|
||||
[have_inotify="no"])
|
||||
AC_ARG_WITH([inotify],
|
||||
[AS_HELP_STRING([--with-inotify],[Enable inotify support (default=auto)])],
|
||||
[want_inotify=${enableval}],
|
||||
[want_inotify=${have_inotify}])
|
||||
if test "x$want_inotify" = "xyes" ; then
|
||||
if test "x$have_inotify" = "xyes"; then
|
||||
AC_DEFINE([WITH_INOTIFY],[1])
|
||||
else
|
||||
AC_MSG_ERROR("inotify not found!")
|
||||
fi
|
||||
fi
|
||||
|
||||
AC_CHECK_HEADER([sys/event.h],
|
||||
[AC_CHECK_FUNC([kqueue],[have_kqueue="yes"],[have_kqueue="no"])],
|
||||
[have_kqueue="no"])
|
||||
AC_ARG_WITH([kqueue],
|
||||
[AS_HELP_STRING([--with-kqueue],[Enable kqueue support (default=auto)])],
|
||||
[want_kqueue=${enableval}],
|
||||
[want_kqueue=${have_kqueue}])
|
||||
if test "x$want_kqueue" = "xyes" ; then
|
||||
if test "x$have_kqueue" = "xyes"; then
|
||||
AC_DEFINE([WITH_KQUEUE],[1])
|
||||
else
|
||||
AC_MSG_ERROR("kqueue not found!")
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
dnl ----------------------------------------------------------------------------
|
||||
dnl
|
||||
dnl va_copy
|
||||
|
|
|
@ -28,5 +28,8 @@ LDADD = \
|
|||
$(OPENSSL_LIBS) \
|
||||
$(PTHREAD_LIBS) -lm
|
||||
|
||||
transmission_daemon_SOURCES = daemon.c
|
||||
noinst_HEADERS = \
|
||||
watch.h
|
||||
|
||||
transmission_daemon_SOURCES = daemon.c watch.c
|
||||
transmission_remote_SOURCES = remote.c
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
|
||||
#include <sys/types.h> /* umask*/
|
||||
#include <sys/stat.h> /* umask*/
|
||||
#include <dirent.h> /* readdir */
|
||||
|
||||
#include <fcntl.h> /* open */
|
||||
#include <signal.h>
|
||||
|
@ -32,16 +31,16 @@
|
|||
#include <libtransmission/utils.h>
|
||||
#include <libtransmission/version.h>
|
||||
|
||||
#include "watch.h"
|
||||
|
||||
#define MY_NAME "transmission-daemon"
|
||||
|
||||
#define PREF_KEY_DIR_WATCH "watch-dir"
|
||||
#define PREF_KEY_DIR_WATCH_ENABLED "watch-dir-enabled"
|
||||
|
||||
#define WATCHDIR_POLL_INTERVAL_SECS 15
|
||||
|
||||
|
||||
static int closing = FALSE;
|
||||
static tr_session * mySession = NULL;
|
||||
static tr_bool closing = FALSE;
|
||||
static tr_session * mySession = NULL;
|
||||
|
||||
/***
|
||||
**** Config File
|
||||
|
@ -181,46 +180,20 @@ getConfigDir( int argc, const char ** argv )
|
|||
return configDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is crude compared to using kqueue/inotify/etc, but has the advantage
|
||||
* of working portably on whatever random embedded platform we throw at it.
|
||||
* Since we're only walking a single directory, nonrecursively, a few times
|
||||
* per minute, let's go with this unless users complain about the load
|
||||
*/
|
||||
static void
|
||||
checkForNewFiles( tr_session * session, const char * dirname, time_t oldTime )
|
||||
onFileAdded( tr_session * session, const char * dir, const char * file )
|
||||
{
|
||||
struct stat sb;
|
||||
DIR * odir;
|
||||
|
||||
if( !stat( dirname, &sb ) && S_ISDIR( sb.st_mode ) && (( odir = opendir( dirname ))) )
|
||||
if( strstr( file, ".torrent" ) != NULL )
|
||||
{
|
||||
struct dirent * d;
|
||||
char * filename = tr_buildPath( dir, file, NULL );
|
||||
tr_ctor * ctor = tr_ctorNew( session );
|
||||
|
||||
for( d = readdir( odir ); d != NULL; d = readdir( odir ) )
|
||||
{
|
||||
char * filename;
|
||||
int err = tr_ctorSetMetainfoFromFile( ctor, filename );
|
||||
if( !err )
|
||||
tr_torrentNew( session, ctor, &err );
|
||||
|
||||
if( !d->d_name || *d->d_name=='.' ) /* skip dotfiles */
|
||||
continue;
|
||||
if( !strstr( d->d_name, ".torrent" ) ) /* skip non-torrents */
|
||||
continue;
|
||||
|
||||
/* if the file's changed since our last pass, try adding it */
|
||||
filename = tr_buildPath( dirname, d->d_name, NULL );
|
||||
if( !stat( filename, &sb ) && sb.st_mtime >= oldTime )
|
||||
{
|
||||
tr_ctor * ctor = tr_ctorNew( session );
|
||||
int err = tr_ctorSetMetainfoFromFile( ctor, filename );
|
||||
if( !err )
|
||||
tr_torrentNew( session, ctor, &err );
|
||||
tr_ctorFree( ctor );
|
||||
}
|
||||
|
||||
tr_free( filename );
|
||||
}
|
||||
|
||||
closedir( odir );
|
||||
tr_ctorFree( ctor );
|
||||
tr_free( filename );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -234,8 +207,7 @@ main( int argc, char ** argv )
|
|||
tr_bool foreground = FALSE;
|
||||
tr_bool dumpSettings = FALSE;
|
||||
const char * configDir = NULL;
|
||||
const char * watchDir = NULL;
|
||||
time_t lastCheckTime = 0;
|
||||
dtr_watchdir * watchdir = NULL;
|
||||
|
||||
signal( SIGINT, gotsig );
|
||||
signal( SIGTERM, gotsig );
|
||||
|
@ -335,13 +307,16 @@ main( int argc, char ** argv )
|
|||
/* maybe add a watchdir */
|
||||
{
|
||||
int64_t doWatch;
|
||||
const char * dir;
|
||||
|
||||
if( tr_bencDictFindInt( &settings, PREF_KEY_DIR_WATCH_ENABLED, &doWatch )
|
||||
&& doWatch
|
||||
&& tr_bencDictFindStr( &settings, PREF_KEY_DIR_WATCH, &watchDir )
|
||||
&& watchDir
|
||||
&& *watchDir )
|
||||
&& tr_bencDictFindStr( &settings, PREF_KEY_DIR_WATCH, &dir )
|
||||
&& dir
|
||||
&& *dir )
|
||||
{
|
||||
tr_ninf( MY_NAME, "watching \"%s\" for added .torrent files", watchDir );
|
||||
tr_inf( "Watching \"%s\" for new .torrent files", dir );
|
||||
watchdir = dtr_watchdir_new( mySession, dir, onFileAdded );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -356,17 +331,13 @@ main( int argc, char ** argv )
|
|||
while( !closing )
|
||||
{
|
||||
tr_wait( 1000 ); /* sleep one second */
|
||||
|
||||
if( watchDir && ( lastCheckTime + WATCHDIR_POLL_INTERVAL_SECS < time( NULL ) ) )
|
||||
{
|
||||
checkForNewFiles( mySession, watchDir, lastCheckTime );
|
||||
lastCheckTime = time( NULL );
|
||||
}
|
||||
dtr_watchdir_update( watchdir );
|
||||
}
|
||||
|
||||
/* shutdown */
|
||||
printf( "Closing transmission session..." );
|
||||
tr_sessionSaveSettings( mySession, configDir, &settings );
|
||||
dtr_watchdir_free( watchdir );
|
||||
tr_sessionClose( mySession );
|
||||
printf( " done.\n" );
|
||||
|
||||
|
|
|
@ -0,0 +1,200 @@
|
|||
/*
|
||||
* This file Copyright (C) 2009 Charles Kerr <charles@transmissionbt.com>
|
||||
*
|
||||
* This file is licensed by the GPL version 2. Works owned by the
|
||||
* Transmission project are granted a special exemption to clause 2(b)
|
||||
* so that the bulk of its code can remain under the MIT license.
|
||||
* This exemption does not extend to derived works not owned by
|
||||
* the Transmission project.
|
||||
*
|
||||
* $Id:$
|
||||
*/
|
||||
|
||||
#undef WITH_INOTIFY
|
||||
|
||||
#ifdef WITH_INOTIFY
|
||||
#include <sys/inotify.h>
|
||||
#include <sys/select.h>
|
||||
#include <unistd.h> /* close */
|
||||
#else
|
||||
#include <sys/types.h> /* stat */
|
||||
#include <sys/stat.h> /* stat */
|
||||
#include <dirent.h> /* readdir */
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h> /* strstr */
|
||||
|
||||
#include <libtransmission/transmission.h>
|
||||
#include <libtransmission/utils.h> /* tr_buildPath */
|
||||
#include "watch.h"
|
||||
|
||||
struct dtr_watchdir
|
||||
{
|
||||
tr_session * session;
|
||||
char * dir;
|
||||
dtr_watchdir_callback * callback;
|
||||
#ifdef WITH_INOTIFY
|
||||
int inotify_fd;
|
||||
#else /* readdir implementation */
|
||||
time_t lastTimeChecked;
|
||||
#endif
|
||||
};
|
||||
|
||||
/***
|
||||
**** INOTIFY IMPLEMENTATION
|
||||
***/
|
||||
|
||||
#if defined(WITH_INOTIFY)
|
||||
|
||||
/* how many inotify events to try to batch into a single read */
|
||||
#define EVENT_BATCH_COUNT 50
|
||||
/* size of the event structure, not counting name */
|
||||
#define EVENT_SIZE (sizeof (struct inotify_event))
|
||||
/* reasonable guess as to size of 50 events */
|
||||
#define BUF_LEN (EVENT_BATCH_COUNT * (EVENT_SIZE + 16) + 2048)
|
||||
|
||||
#define DTR_INOTIFY_MASK (IN_CREATE|IN_CLOSE_WRITE|IN_MOVED_TO)
|
||||
|
||||
static void
|
||||
watchdir_new_impl( dtr_watchdir * w )
|
||||
{
|
||||
int i;
|
||||
w->inotify_fd = inotify_init( );
|
||||
tr_inf( "Using inotify to watch directory \"%s\"", w->dir );
|
||||
i = inotify_add_watch( w->inotify_fd, w->dir, DTR_INOTIFY_MASK );
|
||||
if( i < 0 )
|
||||
tr_err( "Unable to watch \"%s\": %s", w->dir, strerror (errno) );
|
||||
}
|
||||
static void
|
||||
watchdir_free_impl( dtr_watchdir * w )
|
||||
{
|
||||
inotify_rm_watch( w->inotify_fd, DTR_INOTIFY_MASK );
|
||||
close( w->inotify_fd );
|
||||
}
|
||||
static void
|
||||
watchdir_update_impl( dtr_watchdir * w )
|
||||
{
|
||||
int ret;
|
||||
fd_set rfds;
|
||||
struct timeval time;
|
||||
const int fd = w->inotify_fd;
|
||||
|
||||
/* timeout after one second */
|
||||
time.tv_sec = 1;
|
||||
time.tv_usec = 0;
|
||||
|
||||
/* make the fd_set hold the inotify fd */
|
||||
FD_ZERO( &rfds );
|
||||
FD_SET( fd, &rfds );
|
||||
|
||||
/* check for added files */
|
||||
ret = select( fd+1, &rfds, NULL, NULL, &time );
|
||||
if( ret < 0 ) {
|
||||
perror( "select" );
|
||||
} else if( !ret ) {
|
||||
/* timed out! */
|
||||
} else if( FD_ISSET( fd, &rfds ) ) {
|
||||
int i = 0;
|
||||
char buf[BUF_LEN];
|
||||
int len = read( fd, buf, sizeof( buf ) );
|
||||
while (i < len) {
|
||||
struct inotify_event * event = (struct inotify_event *) &buf[i];
|
||||
w->callback( w->session, w->dir, event->name );
|
||||
i += EVENT_SIZE + event->len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#else /* WITH_INOTIFY */
|
||||
|
||||
/***
|
||||
**** READDIR IMPLEMENTATION
|
||||
***/
|
||||
|
||||
#define WATCHDIR_POLL_INTERVAL_SECS 10
|
||||
|
||||
static void
|
||||
watchdir_new_impl( dtr_watchdir * w UNUSED )
|
||||
{
|
||||
tr_inf( "Using readdir to watch directory \"%s\"", w->dir );
|
||||
}
|
||||
static void
|
||||
watchdir_free_impl( dtr_watchdir * w UNUSED )
|
||||
{
|
||||
/* NOOP */
|
||||
}
|
||||
static void
|
||||
watchdir_update_impl( dtr_watchdir * w )
|
||||
{
|
||||
struct stat sb;
|
||||
DIR * odir;
|
||||
const time_t oldTime = w->lastTimeChecked;
|
||||
const char * dirname = w->dir;
|
||||
|
||||
if ( ( oldTime + WATCHDIR_POLL_INTERVAL_SECS < time( NULL ) )
|
||||
&& !stat( dirname, &sb )
|
||||
&& S_ISDIR( sb.st_mode )
|
||||
&& (( odir = opendir( dirname ))) )
|
||||
{
|
||||
struct dirent * d;
|
||||
|
||||
for( d = readdir( odir ); d != NULL; d = readdir( odir ) )
|
||||
{
|
||||
char * filename;
|
||||
|
||||
if( !d->d_name || *d->d_name=='.' ) /* skip dotfiles */
|
||||
continue;
|
||||
if( !strstr( d->d_name, ".torrent" ) ) /* skip non-torrents */
|
||||
continue;
|
||||
|
||||
/* if the file's changed since our last pass, try adding it */
|
||||
filename = tr_buildPath( dirname, d->d_name, NULL );
|
||||
if( !stat( filename, &sb ) && sb.st_mtime >= oldTime )
|
||||
w->callback( w->session, w->dir, d->d_name );
|
||||
tr_free( filename );
|
||||
}
|
||||
|
||||
closedir( odir );
|
||||
|
||||
w->lastTimeChecked = time( NULL );
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
dtr_watchdir*
|
||||
dtr_watchdir_new( tr_session * session, const char * dir, dtr_watchdir_callback * callback )
|
||||
{
|
||||
dtr_watchdir * w = tr_new0( dtr_watchdir, 1 );
|
||||
|
||||
w->session = session;
|
||||
w->dir = tr_strdup( dir );
|
||||
w->callback = callback;
|
||||
|
||||
watchdir_new_impl( w );
|
||||
|
||||
return w;
|
||||
}
|
||||
|
||||
void
|
||||
dtr_watchdir_update( dtr_watchdir * w )
|
||||
{
|
||||
if( w != NULL )
|
||||
watchdir_update_impl( w );
|
||||
}
|
||||
|
||||
void
|
||||
dtr_watchdir_free( dtr_watchdir * w )
|
||||
{
|
||||
if( w != NULL )
|
||||
{
|
||||
watchdir_free_impl( w );
|
||||
tr_free( w->dir );
|
||||
tr_free( w );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
#ifndef DTR_WATCH_H
|
||||
#define DTR_WATCH_H
|
||||
|
||||
/*
|
||||
* This file Copyright (C) 2009 Charles Kerr <charles@transmissionbt.com>
|
||||
*
|
||||
* This file is licensed by the GPL version 2. Works owned by the
|
||||
* Transmission project are granted a special exemption to clause 2(b)
|
||||
* so that the bulk of its code can remain under the MIT license.
|
||||
* This exemption does not extend to derived works not owned by
|
||||
* the Transmission project.
|
||||
*
|
||||
* $Id:$
|
||||
*/
|
||||
|
||||
typedef struct dtr_watchdir dtr_watchdir;
|
||||
|
||||
typedef void( dtr_watchdir_callback )( tr_session * session, const char * dir, const char * file );
|
||||
|
||||
dtr_watchdir* dtr_watchdir_new( tr_session * session, const char * dir, dtr_watchdir_callback cb );
|
||||
|
||||
void dtr_watchdir_update( dtr_watchdir * w );
|
||||
|
||||
void dtr_watchdir_free( dtr_watchdir * w );
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue