fix syslog service
[tomato.git] / release / src / router / transmission / daemon / watch.c
bloba7153385c9b55c1bf07ce0c108db67ff22e9d571
1 /*
2 * This file Copyright (C) Mnemosyne LLC
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.
8 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
10 * $Id: watch.c 12476 2011-05-30 15:28:55Z jordan $
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
22 #include <errno.h>
23 #include <string.h> /* strlen() */
24 #include <stdio.h> /* perror() */
26 #include <dirent.h> /* readdir */
28 #include <libtransmission/transmission.h>
29 #include <libtransmission/utils.h> /* tr_buildPath(), tr_inf() */
30 #include "watch.h"
32 struct dtr_watchdir
34 tr_session * session;
35 char * dir;
36 dtr_watchdir_callback * callback;
37 #ifdef WITH_INOTIFY
38 int inotify_fd;
39 #else /* readdir implementation */
40 time_t lastTimeChecked;
41 struct evbuffer * lastFiles;
42 #endif
45 /***
46 **** INOTIFY IMPLEMENTATION
47 ***/
49 #if defined(WITH_INOTIFY)
51 /* how many inotify events to try to batch into a single read */
52 #define EVENT_BATCH_COUNT 50
53 /* size of the event structure, not counting name */
54 #define EVENT_SIZE (sizeof (struct inotify_event))
55 /* reasonable guess as to size of 50 events */
56 #define BUF_LEN (EVENT_BATCH_COUNT * (EVENT_SIZE + 16) + 2048)
58 #define DTR_INOTIFY_MASK (IN_CLOSE_WRITE|IN_MOVED_TO|IN_ONLYDIR)
60 static void
61 watchdir_new_impl( dtr_watchdir * w )
63 int i;
64 DIR * odir;
65 w->inotify_fd = inotify_init( );
67 if( w->inotify_fd < 0 )
69 i = -1;
71 else
73 tr_inf( "Using inotify to watch directory \"%s\"", w->dir );
74 i = inotify_add_watch( w->inotify_fd, w->dir, DTR_INOTIFY_MASK );
77 if( i < 0 )
79 tr_err( "Unable to watch \"%s\": %s", w->dir, tr_strerror( errno ) );
81 else if(( odir = opendir( w->dir )))
83 struct dirent * d;
85 while(( d = readdir( odir )))
87 const char * name = d->d_name;
89 if( !tr_str_has_suffix( name, ".torrent" ) ) /* skip non-torrents */
90 continue;
92 tr_inf( "Found new .torrent file \"%s\" in watchdir \"%s\"", name, w->dir );
93 w->callback( w->session, w->dir, name );
96 closedir( odir );
100 static void
101 watchdir_free_impl( dtr_watchdir * w )
103 if( w->inotify_fd >= 0 )
105 inotify_rm_watch( w->inotify_fd, DTR_INOTIFY_MASK );
107 close( w->inotify_fd );
110 static void
111 watchdir_update_impl( dtr_watchdir * w )
113 int ret;
114 fd_set rfds;
115 struct timeval time;
116 const int fd = w->inotify_fd;
118 /* timeout after one second */
119 time.tv_sec = 1;
120 time.tv_usec = 0;
122 /* make the fd_set hold the inotify fd */
123 FD_ZERO( &rfds );
124 FD_SET( fd, &rfds );
126 /* check for added files */
127 ret = select( fd+1, &rfds, NULL, NULL, &time );
128 if( ret < 0 ) {
129 perror( "select" );
130 } else if( !ret ) {
131 /* timed out! */
132 } else if( FD_ISSET( fd, &rfds ) ) {
133 int i = 0;
134 char buf[BUF_LEN];
135 int len = read( fd, buf, sizeof( buf ) );
136 while (i < len) {
137 struct inotify_event * event = (struct inotify_event *) &buf[i];
138 const char * name = event->name;
139 if( tr_str_has_suffix( name, ".torrent" ) )
141 tr_inf( "Found new .torrent file \"%s\" in watchdir \"%s\"", name, w->dir );
142 w->callback( w->session, w->dir, name );
144 i += EVENT_SIZE + event->len;
149 #else /* WITH_INOTIFY */
151 /***
152 **** READDIR IMPLEMENTATION
153 ***/
155 #define WATCHDIR_POLL_INTERVAL_SECS 10
157 #define FILE_DELIMITER '\t'
159 static void
160 watchdir_new_impl( dtr_watchdir * w UNUSED )
162 tr_inf( "Using readdir to watch directory \"%s\"", w->dir );
163 w->lastFiles = evbuffer_new( );
165 static void
166 watchdir_free_impl( dtr_watchdir * w )
168 evbuffer_free( w->lastFiles );
171 static char*
172 get_key_from_file( const char * filename, const size_t len )
174 return tr_strdup_printf( "%c%*.*s%d", FILE_DELIMITER, (int)len, (int)len, filename, FILE_DELIMITER );
177 static void
178 add_file_to_list( struct evbuffer * buf, const char * filename, size_t len )
180 char * key = get_key_from_file( filename, len );
181 evbuffer_add( buf, key, strlen( key ) );
182 tr_free( key );
184 static bool
185 is_file_in_list( struct evbuffer * buf, const char * filename, size_t len )
187 bool in_list;
188 struct evbuffer_ptr ptr;
189 char * key = get_key_from_file( filename, len );
191 ptr = evbuffer_search( buf, key, strlen( key ), NULL );
192 in_list = ptr.pos != -1;
194 tr_free( key );
195 return in_list;
197 static void
198 watchdir_update_impl( dtr_watchdir * w )
200 struct stat sb;
201 DIR * odir;
202 const time_t oldTime = w->lastTimeChecked;
203 const char * dirname = w->dir;
204 struct evbuffer * curFiles = evbuffer_new( );
206 if ( ( oldTime + WATCHDIR_POLL_INTERVAL_SECS < time( NULL ) )
207 && !stat( dirname, &sb )
208 && S_ISDIR( sb.st_mode )
209 && (( odir = opendir( dirname ))) )
211 struct dirent * d;
213 for( d = readdir( odir ); d != NULL; d = readdir( odir ) )
215 size_t len;
216 const char * name = d->d_name;
218 if( !name || *name=='.' ) /* skip dotfiles */
219 continue;
220 if( !tr_str_has_suffix( name, ".torrent" ) ) /* skip non-torrents */
221 continue;
223 len = strlen( name );
224 add_file_to_list( curFiles, name, len );
226 /* if this file wasn't here last time, try adding it */
227 if( !is_file_in_list( w->lastFiles, name, len ) ) {
228 tr_inf( "Found new .torrent file \"%s\" in watchdir \"%s\"", name, w->dir );
229 w->callback( w->session, w->dir, name );
233 closedir( odir );
234 w->lastTimeChecked = time( NULL );
235 evbuffer_free( w->lastFiles );
236 w->lastFiles = curFiles;
240 #endif
242 /***
243 ****
244 ***/
246 dtr_watchdir*
247 dtr_watchdir_new( tr_session * session, const char * dir, dtr_watchdir_callback * callback )
249 dtr_watchdir * w = tr_new0( dtr_watchdir, 1 );
251 w->session = session;
252 w->dir = tr_strdup( dir );
253 w->callback = callback;
255 watchdir_new_impl( w );
257 return w;
260 void
261 dtr_watchdir_update( dtr_watchdir * w )
263 if( w != NULL )
264 watchdir_update_impl( w );
267 void
268 dtr_watchdir_free( dtr_watchdir * w )
270 if( w != NULL )
272 watchdir_free_impl( w );
273 tr_free( w->dir );
274 tr_free( w );