source: trunk/daemon/watch.c @ 11594

Last change on this file since 11594 was 11594, checked in by charles, 11 years ago

(trunk daemon) get the non-inotify chunks of code working with libevent2

  • Property svn:keywords set to Date Rev Author Id
File size: 6.5 KB
Line 
1/*
2 * This file Copyright (C) 2009-2010 Mnemosyne LLC
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2
6 * as published by the Free Software Foundation.
7 *
8 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
9 *
10 * $Id: watch.c 11594 2010-12-24 15:41:10Z charles $
11 */
12#ifdef WITH_INOTIFY
13  #include <sys/inotify.h>
14  #include <sys/select.h>
15  #include <unistd.h> /* close */
16#else
17  #include <sys/types.h> /* stat */
18  #include <sys/stat.h> /* stat */
19  #include <event2/buffer.h> /* evbuffer */
20#endif
21
22#include <errno.h>
23#include <string.h> /* strstr */
24
25#include <dirent.h> /* readdir */
26
27#include <libtransmission/transmission.h>
28#include <libtransmission/utils.h> /* tr_buildPath(), tr_inf() */
29#include "watch.h"
30
31struct dtr_watchdir
32{
33    tr_session * session;
34    char * dir;
35    dtr_watchdir_callback * callback;
36#ifdef WITH_INOTIFY
37    int inotify_fd;
38#else /* readdir implementation */
39    time_t lastTimeChecked;
40    struct evbuffer * lastFiles;
41#endif
42};
43
44/***
45****  INOTIFY IMPLEMENTATION
46***/
47
48#if defined(WITH_INOTIFY)
49
50/* how many inotify events to try to batch into a single read */
51#define EVENT_BATCH_COUNT 50
52/* size of the event structure, not counting name */
53#define EVENT_SIZE  (sizeof (struct inotify_event))
54/* reasonable guess as to size of 50 events */
55#define BUF_LEN (EVENT_BATCH_COUNT * (EVENT_SIZE + 16) + 2048)
56
57#define DTR_INOTIFY_MASK (IN_CLOSE_WRITE|IN_MOVED_TO|IN_ONLYDIR)
58
59static void
60watchdir_new_impl( dtr_watchdir * w )
61{
62    int i;
63    DIR * odir;
64    w->inotify_fd = inotify_init( );
65
66    if( w->inotify_fd < 0 )
67    {
68        i = -1;
69    }
70    else
71    {
72        tr_inf( "Using inotify to watch directory \"%s\"", w->dir );
73        i = inotify_add_watch( w->inotify_fd, w->dir, DTR_INOTIFY_MASK );
74    }
75
76    if( i < 0 )
77    {
78        tr_err( "Unable to watch \"%s\": %s", w->dir, strerror( errno ) );
79    }
80    else if(( odir = opendir( w->dir )))
81    {
82        struct dirent * d;
83
84        while(( d = readdir( odir )))
85        {
86            const char * name = d->d_name;
87
88            if( !tr_str_has_suffix( name, ".torrent" ) ) /* skip non-torrents */
89                continue;
90
91            tr_inf( "Found new .torrent file \"%s\" in watchdir \"%s\"", name, w->dir );
92            w->callback( w->session, w->dir, name );
93        }
94
95        closedir( odir );
96    }
97
98}
99static void
100watchdir_free_impl( dtr_watchdir * w )
101{
102    if( w->inotify_fd >= 0 )
103    {
104        inotify_rm_watch( w->inotify_fd, DTR_INOTIFY_MASK );
105
106        close( w->inotify_fd );
107    }
108}
109static void
110watchdir_update_impl( dtr_watchdir * w )
111{
112    int ret;
113    fd_set rfds;
114    struct timeval time;
115    const int fd = w->inotify_fd;
116
117    /* timeout after one second */
118    time.tv_sec = 1;
119    time.tv_usec = 0;
120
121    /* make the fd_set hold the inotify fd */
122    FD_ZERO( &rfds );
123    FD_SET( fd, &rfds );
124
125    /* check for added files */
126    ret = select( fd+1, &rfds, NULL, NULL, &time );
127    if( ret < 0 ) {
128        perror( "select" );
129    } else if( !ret ) {
130        /* timed out! */
131    } else if( FD_ISSET( fd, &rfds ) ) {
132        int i = 0;
133        char buf[BUF_LEN];
134        int len = read( fd, buf, sizeof( buf ) );
135        while (i < len) {
136            struct inotify_event * event = (struct inotify_event *) &buf[i];
137            const char * name = event->name;
138            if( tr_str_has_suffix( name, ".torrent" ) )
139            {
140                tr_inf( "Found new .torrent file \"%s\" in watchdir \"%s\"", name, w->dir );
141                w->callback( w->session, w->dir, name );
142            }
143            i += EVENT_SIZE +  event->len;
144        }
145    }
146}
147
148#else /* WITH_INOTIFY */
149
150/***
151****  READDIR IMPLEMENTATION
152***/
153
154#define WATCHDIR_POLL_INTERVAL_SECS 10
155
156#define FILE_DELIMITER '\0'
157
158static void
159watchdir_new_impl( dtr_watchdir * w UNUSED )
160{
161    tr_inf( "Using readdir to watch directory \"%s\"", w->dir );
162    w->lastFiles = evbuffer_new( );
163}
164static void
165watchdir_free_impl( dtr_watchdir * w )
166{
167    evbuffer_free( w->lastFiles );
168}
169
170static char*
171get_key_from_file( const char * filename, const size_t len )
172{
173    return tr_strdup_printf( "%c%*.*s%d", FILE_DELIMITER, (int)len, (int)len, filename, FILE_DELIMITER );
174}
175
176static void
177add_file_to_list( struct evbuffer * buf, const char * filename, size_t len )
178{
179    char * key = get_key_from_file( filename, len );
180    evbuffer_add( buf, key, strlen( key ) );
181    tr_free( key );
182}
183static tr_bool
184is_file_in_list( struct evbuffer * buf, const char * filename, size_t len )
185{
186    tr_bool in_list;
187    struct evbuffer_ptr ptr;
188    char * key = get_key_from_file( filename, len );
189
190    ptr = evbuffer_search( buf, key, strlen( key ), NULL );
191    in_list = ptr.pos != -1;
192
193    tr_free( key );
194    return in_list;
195}
196static void
197watchdir_update_impl( dtr_watchdir * w )
198{
199    struct stat sb;
200    DIR * odir;
201    const time_t oldTime = w->lastTimeChecked;
202    const char * dirname = w->dir;
203    struct evbuffer * curFiles = evbuffer_new( );
204
205    if ( ( oldTime + WATCHDIR_POLL_INTERVAL_SECS < time( NULL ) )
206         && !stat( dirname, &sb )
207         && S_ISDIR( sb.st_mode )
208         && (( odir = opendir( dirname ))) )
209    {
210        struct dirent * d;
211
212        for( d = readdir( odir ); d != NULL; d = readdir( odir ) )
213        {
214            size_t len;
215            const char * name = d->d_name;
216
217            if( !name || *name=='.' ) /* skip dotfiles */
218                continue;
219            if( !tr_str_has_suffix( name, ".torrent" ) ) /* skip non-torrents */
220                continue;
221
222            len = strlen( name );
223            add_file_to_list( curFiles, name, len );
224
225            /* if this file wasn't here last time, try adding it */
226            if( !is_file_in_list( w->lastFiles, name, len ) ) {
227                tr_inf( "Found new .torrent file \"%s\" in watchdir \"%s\"", name, w->dir );
228                w->callback( w->session, w->dir, name );
229            }
230        }
231
232        closedir( odir );
233        w->lastTimeChecked = time( NULL );
234        evbuffer_free( w->lastFiles );
235        w->lastFiles = curFiles;
236    }
237}
238
239#endif
240
241/***
242****
243***/
244
245dtr_watchdir*
246dtr_watchdir_new( tr_session * session, const char * dir, dtr_watchdir_callback * callback )
247{
248    dtr_watchdir * w = tr_new0( dtr_watchdir, 1 );
249
250    w->session = session;
251    w->dir = tr_strdup( dir );
252    w->callback = callback;
253
254    watchdir_new_impl( w );
255
256    return w;
257}
258
259void
260dtr_watchdir_update( dtr_watchdir * w )
261{
262    if( w != NULL )
263        watchdir_update_impl( w );
264}
265
266void
267dtr_watchdir_free( dtr_watchdir * w )
268{
269    if( w != NULL )
270    {
271        watchdir_free_impl( w );
272        tr_free( w->dir );
273        tr_free( w );
274    }
275}
Note: See TracBrowser for help on using the repository browser.