Transmission: update to 2.76
[tomato.git] / release / src / router / transmission / daemon / watch.c
blob657a688b9be59a026e11acb3503c021f10d3705d
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 13748 2013-01-03 23:54:25Z 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_CREATE|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);