source: trunk/daemon/watch.c @ 7980

Last change on this file since 7980 was 7980, checked in by charles, 13 years ago

oops, I left in the #undef WITH_INOTIFY that I was using to test the readdir() implementation...

File size: 4.6 KB
Line 
1/*
2 * This file Copyright (C) 2009 Charles Kerr <charles@transmissionbt.com>
3 *
4 * This file is licensed by the GPL version 2.  Works owned by the
5 * Transmission project are granted a special exemption to clause 2(b)
6 * so that the bulk of its code can remain under the MIT license.
7 * This exemption does not extend to derived works not owned by
8 * the Transmission project.
9 *
10 * $Id:$
11 */
12
13#ifdef WITH_INOTIFY
14  #include <sys/inotify.h>
15  #include <sys/select.h>
16  #include <unistd.h> /* close */
17#else
18  #include <sys/types.h> /* stat */
19  #include <sys/stat.h> /* stat */
20  #include <dirent.h> /* readdir */
21#endif
22
23#include <errno.h>
24#include <string.h> /* strstr */
25
26#include <libtransmission/transmission.h>
27#include <libtransmission/utils.h> /* tr_buildPath */
28#include "watch.h"
29
30struct dtr_watchdir
31{
32    tr_session * session;
33    char * dir;
34    dtr_watchdir_callback * callback;
35#ifdef WITH_INOTIFY
36    int inotify_fd;
37#else /* readdir implementation */
38    time_t lastTimeChecked;
39#endif
40};
41
42/***
43****  INOTIFY IMPLEMENTATION
44***/
45
46#if defined(WITH_INOTIFY)
47
48/* how many inotify events to try to batch into a single read */
49#define EVENT_BATCH_COUNT 50
50/* size of the event structure, not counting name */
51#define EVENT_SIZE  (sizeof (struct inotify_event))
52/* reasonable guess as to size of 50 events */
53#define BUF_LEN (EVENT_BATCH_COUNT * (EVENT_SIZE + 16) + 2048)
54
55#define DTR_INOTIFY_MASK (IN_CREATE|IN_CLOSE_WRITE|IN_MOVED_TO)
56
57static void
58watchdir_new_impl( dtr_watchdir * w )
59{
60    int i;
61    w->inotify_fd = inotify_init( );
62    tr_inf( "Using inotify to watch directory \"%s\"", w->dir );
63    i = inotify_add_watch( w->inotify_fd, w->dir, DTR_INOTIFY_MASK );
64    if( i < 0 )
65        tr_err( "Unable to watch \"%s\": %s", w->dir, strerror (errno) );
66}
67static void
68watchdir_free_impl( dtr_watchdir * w )
69{
70    inotify_rm_watch( w->inotify_fd, DTR_INOTIFY_MASK );
71    close( w->inotify_fd );
72}
73static void
74watchdir_update_impl( dtr_watchdir * w )
75{
76    int ret;
77    fd_set rfds;
78    struct timeval time;
79    const int fd = w->inotify_fd;
80
81    /* timeout after one second */
82    time.tv_sec = 1;
83    time.tv_usec = 0;
84
85    /* make the fd_set hold the inotify fd */
86    FD_ZERO( &rfds );
87    FD_SET( fd, &rfds );
88
89    /* check for added files */
90    ret = select( fd+1, &rfds, NULL, NULL, &time );
91    if( ret < 0 ) {
92        perror( "select" );
93    } else if( !ret ) {
94        /* timed out! */
95    } else if( FD_ISSET( fd, &rfds ) ) {
96        int i = 0;
97        char buf[BUF_LEN];
98        int len = read( fd, buf, sizeof( buf ) );
99        while (i < len) {
100            struct inotify_event * event = (struct inotify_event *) &buf[i];
101            w->callback( w->session, w->dir, event->name );
102            i += EVENT_SIZE +  event->len;
103        }
104    }
105}
106
107#else /* WITH_INOTIFY */
108
109/***
110****  READDIR IMPLEMENTATION
111***/
112
113#define WATCHDIR_POLL_INTERVAL_SECS 10
114
115static void
116watchdir_new_impl( dtr_watchdir * w UNUSED )
117{
118    tr_inf( "Using readdir to watch directory \"%s\"", w->dir );
119}
120static void
121watchdir_free_impl( dtr_watchdir * w UNUSED )
122{
123    /* NOOP */
124}
125static void
126watchdir_update_impl( dtr_watchdir * w )
127{
128    struct stat sb;
129    DIR * odir;
130    const time_t oldTime = w->lastTimeChecked;
131    const char * dirname = w->dir;
132
133    if ( ( oldTime + WATCHDIR_POLL_INTERVAL_SECS < time( NULL ) )
134         && !stat( dirname, &sb )
135         && S_ISDIR( sb.st_mode )
136         && (( odir = opendir( dirname ))) )
137    {
138        struct dirent * d;
139
140        for( d = readdir( odir ); d != NULL; d = readdir( odir ) )
141        {
142            char * filename;
143
144            if( !d->d_name || *d->d_name=='.' ) /* skip dotfiles */
145                continue;
146            if( !strstr( d->d_name, ".torrent" ) ) /* skip non-torrents */
147                continue;
148
149            /* if the file's changed since our last pass, try adding it */
150            filename = tr_buildPath( dirname, d->d_name, NULL );
151            if( !stat( filename, &sb ) && sb.st_mtime >= oldTime )
152                w->callback( w->session, w->dir, d->d_name );
153            tr_free( filename );
154        }
155
156        closedir( odir );
157
158        w->lastTimeChecked = time( NULL );
159    }
160}
161
162#endif
163
164/***
165****
166***/
167
168dtr_watchdir*
169dtr_watchdir_new( tr_session * session, const char * dir, dtr_watchdir_callback * callback )
170{
171    dtr_watchdir * w = tr_new0( dtr_watchdir, 1 );
172
173    w->session = session;
174    w->dir = tr_strdup( dir );
175    w->callback = callback;
176
177    watchdir_new_impl( w );
178
179    return w;
180}
181
182void
183dtr_watchdir_update( dtr_watchdir * w )
184{
185    if( w != NULL )
186        watchdir_update_impl( w );
187}
188
189void
190dtr_watchdir_free( dtr_watchdir * w )
191{
192    if( w != NULL )
193    {
194        watchdir_free_impl( w );
195        tr_free( w->dir );
196        tr_free( w );
197    }
198}
Note: See TracBrowser for help on using the repository browser.