transmission 2.83
[tomato.git] / release / src / router / transmission / daemon / watch.c
bloba1d2edb03983f2de695323f499818231e6b337e9
1 /*
2 * This file Copyright (C) 2009-2014 Mnemosyne LLC
4 * It may be used under the GNU GPL versions 2 or 3
5 * or any future license endorsed by Mnemosyne LLC.
7 * $Id: watch.c 14241 2014-01-21 03:10:30Z jordan $
8 */
10 #ifdef WITH_INOTIFY
11 #include <sys/inotify.h>
12 #include <sys/select.h>
13 #include <unistd.h> /* close */
14 #else
15 #include <sys/types.h> /* stat */
16 #include <sys/stat.h> /* stat */
17 #include <event2/buffer.h> /* evbuffer */
18 #endif
20 #include <errno.h>
21 #include <string.h> /* strlen () */
22 #include <stdio.h> /* perror () */
24 #include <dirent.h> /* readdir */
26 #include <libtransmission/transmission.h>
27 #include <libtransmission/log.h>
28 #include <libtransmission/utils.h> /* tr_buildPath (), tr_logAddInfo () */
29 #include "watch.h"
31 struct dtr_watchdir
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
44 /***
45 **** INOTIFY IMPLEMENTATION
46 ***/
48 #if defined (WITH_INOTIFY)
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)
57 #define DTR_INOTIFY_MASK (IN_CLOSE_WRITE|IN_MOVED_TO|IN_CREATE|IN_ONLYDIR)
59 static void
60 watchdir_new_impl (dtr_watchdir * w)
62 int i;
63 DIR * odir;
64 w->inotify_fd = inotify_init ();
66 if (w->inotify_fd < 0)
68 i = -1;
70 else
72 tr_logAddInfo ("Using inotify to watch directory \"%s\"", w->dir);
73 i = inotify_add_watch (w->inotify_fd, w->dir, DTR_INOTIFY_MASK);
76 if (i < 0)
78 tr_logAddError ("Unable to watch \"%s\": %s", w->dir, tr_strerror (errno));
80 else if ((odir = opendir (w->dir)))
82 struct dirent * d;
84 while ((d = readdir (odir)))
86 const char * name = d->d_name;
88 if (!tr_str_has_suffix (name, ".torrent")) /* skip non-torrents */
89 continue;
91 tr_logAddInfo ("Found new .torrent file \"%s\" in watchdir \"%s\"", name, w->dir);
92 w->callback (w->session, w->dir, name);
95 closedir (odir);
99 static void
100 watchdir_free_impl (dtr_watchdir * w)
102 if (w->inotify_fd >= 0)
104 inotify_rm_watch (w->inotify_fd, DTR_INOTIFY_MASK);
106 close (w->inotify_fd);
109 static void
110 watchdir_update_impl (dtr_watchdir * w)
112 int ret;
113 fd_set rfds;
114 struct timeval time;
115 const int fd = w->inotify_fd;
117 /* timeout after one second */
118 time.tv_sec = 1;
119 time.tv_usec = 0;
121 /* make the fd_set hold the inotify fd */
122 FD_ZERO (&rfds);
123 FD_SET (fd, &rfds);
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"))
140 tr_logAddInfo ("Found new .torrent file \"%s\" in watchdir \"%s\"", name, w->dir);
141 w->callback (w->session, w->dir, name);
143 i += EVENT_SIZE + event->len;
148 #else /* WITH_INOTIFY */
150 /***
151 **** READDIR IMPLEMENTATION
152 ***/
154 #define WATCHDIR_POLL_INTERVAL_SECS 10
156 #define FILE_DELIMITER '\t'
158 static void
159 watchdir_new_impl (dtr_watchdir * w UNUSED)
161 tr_logAddInfo ("Using readdir to watch directory \"%s\"", w->dir);
162 w->lastFiles = evbuffer_new ();
164 static void
165 watchdir_free_impl (dtr_watchdir * w)
167 evbuffer_free (w->lastFiles);
170 static char*
171 get_key_from_file (const char * filename, const size_t len)
173 return tr_strdup_printf ("%c%*.*s%d", FILE_DELIMITER, (int)len, (int)len, filename, FILE_DELIMITER);
176 static void
177 add_file_to_list (struct evbuffer * buf, const char * filename, size_t len)
179 char * key = get_key_from_file (filename, len);
180 evbuffer_add (buf, key, strlen (key));
181 tr_free (key);
183 static bool
184 is_file_in_list (struct evbuffer * buf, const char * filename, size_t len)
186 bool in_list;
187 struct evbuffer_ptr ptr;
188 char * key = get_key_from_file (filename, len);
190 ptr = evbuffer_search (buf, key, strlen (key), NULL);
191 in_list = ptr.pos != -1;
193 tr_free (key);
194 return in_list;
196 static void
197 watchdir_update_impl (dtr_watchdir * w)
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 ();
205 if ((oldTime + WATCHDIR_POLL_INTERVAL_SECS < time (NULL))
206 && !stat (dirname, &sb)
207 && S_ISDIR (sb.st_mode)
208 && ((odir = opendir (dirname))))
210 struct dirent * d;
212 for (d = readdir (odir); d != NULL; d = readdir (odir))
214 size_t len;
215 const char * name = d->d_name;
217 if (!name || *name=='.') /* skip dotfiles */
218 continue;
219 if (!tr_str_has_suffix (name, ".torrent")) /* skip non-torrents */
220 continue;
222 len = strlen (name);
223 add_file_to_list (curFiles, name, len);
225 /* if this file wasn't here last time, try adding it */
226 if (!is_file_in_list (w->lastFiles, name, len)) {
227 tr_logAddInfo ("Found new .torrent file \"%s\" in watchdir \"%s\"", name, w->dir);
228 w->callback (w->session, w->dir, name);
232 closedir (odir);
233 w->lastTimeChecked = time (NULL);
234 evbuffer_free (w->lastFiles);
235 w->lastFiles = curFiles;
239 #endif
241 /***
242 ****
243 ***/
245 dtr_watchdir*
246 dtr_watchdir_new (tr_session * session, const char * dir, dtr_watchdir_callback * callback)
248 dtr_watchdir * w = tr_new0 (dtr_watchdir, 1);
250 w->session = session;
251 w->dir = tr_strdup (dir);
252 w->callback = callback;
254 watchdir_new_impl (w);
256 return w;
259 void
260 dtr_watchdir_update (dtr_watchdir * w)
262 if (w != NULL)
263 watchdir_update_impl (w);
266 void
267 dtr_watchdir_free (dtr_watchdir * w)
269 if (w != NULL)
271 watchdir_free_impl (w);
272 tr_free (w->dir);
273 tr_free (w);