00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00038
00039 #ifdef HAVE_CONFIG_H
00040 #include "autoconfig.h"
00041 #endif
00042
00043 #ifdef HAVE_INOTIFY
00044
00045 #include <sys/ioctl.h>
00046 #include <unistd.h>
00047 #include <errno.h>
00048 #include <assert.h>
00049
00050 #ifdef HAVE_SYS_UTSNAME_H
00051 #include <sys/utsname.h>
00052 #endif
00053
00054 #include "mt_inotify.h"
00055 #include "tools.h"
00056
00057 #define MAX_EVENTS 4096
00058 #define MAX_STRLEN 4096
00059
00060 using namespace zmm;
00061
00062 Inotify::Inotify()
00063 {
00064 inotify_fd = inotify_init();
00065 if (inotify_fd < 0)
00066 throw _Exception(_("Unable to initialize inotify!\n"));
00067
00068 if (pipe(stop_fds_pipe) < 0)
00069 throw _Exception(_("Unable to create pipe!\n"));
00070
00071 stop_fd_read = stop_fds_pipe[0];
00072 stop_fd_write = stop_fds_pipe[1];
00073 }
00074
00075 Inotify::~Inotify()
00076 {
00077 if (inotify_fd >= 0)
00078 close(inotify_fd);
00079 }
00080
00081 bool Inotify::supported()
00082 {
00083 #if defined(HAVE_UNAME) && defined(HAVE_SYS_UTSNAME_H)
00084 struct utsname info;
00085
00086 if (uname(&info) == 0)
00087 {
00088
00089 Ref<Array<StringBase> > kversion = split_string(_(info.release), '.');
00090 if (kversion->size() >= 3)
00091 {
00092 int major = _(kversion->get(0)->data).toInt();
00093 int minor = _(kversion->get(1)->data).toInt();
00094 int patch = _(kversion->get(2)->data).toInt();
00095
00096 if ((major < 2) && (minor < 6) && (patch < 13))
00097 return false;
00098 }
00099 }
00100 #endif
00101 int test_fd = inotify_init();
00102 if (test_fd < 0)
00103 return false;
00104 else
00105 {
00106 close(test_fd);
00107 return true;
00108 }
00109 }
00110
00111 int Inotify::addWatch(String path, int events)
00112 {
00113 int wd = inotify_add_watch(inotify_fd, path.c_str(), events);
00114 if (wd < 0 && errno != ENOENT)
00115 {
00116 if (errno == ENOSPC)
00117 throw _Exception(_("The user limit on the total number of inotify watches was reached or the kernel failed to allocate a needed resource."));
00118 else if (errno == EACCES)
00119 {
00120 log_warning("Cannot add inotify watch for %s: %s\n", path.c_str(), strerror(errno));
00121 return -1;
00122 }
00123 else
00124 throw _Exception(mt_strerror(errno));
00125 }
00126 return wd;
00127 }
00128
00129 void Inotify::removeWatch(int wd)
00130 {
00131 if (inotify_rm_watch(inotify_fd, wd) < 0)
00132 {
00133 log_debug("Error removing watch: %s\n", strerror(errno));
00134 }
00135 }
00136
00137 struct inotify_event *Inotify::nextEvent()
00138 {
00139 static struct inotify_event event[MAX_EVENTS];
00140 static struct inotify_event * ret;
00141 static int first_byte = 0;
00142 static ssize_t bytes;
00143
00144 int fd_max;
00145
00146
00147 if ( first_byte != 0
00148 && first_byte <= (int)(bytes - sizeof(struct inotify_event)) ) {
00149
00150 ret = (struct inotify_event *)((char *)&event[0] + first_byte);
00151 first_byte += sizeof(struct inotify_event) + ret->len;
00152
00153
00154
00155 if ( first_byte == bytes ) {
00156 first_byte = 0;
00157 }
00158 else if ( first_byte > bytes ) {
00159
00160
00161
00162
00163
00164
00165 assert( (long)((char *)&event[0] +
00166 sizeof(struct inotify_event) +
00167 event[0].len) <= (long)ret);
00168
00169
00170 bytes = (char *)&event[0] + bytes - (char *)ret;
00171 memcpy( &event[0], ret, bytes );
00172 return nextEvent();
00173 }
00174 return ret;
00175
00176 }
00177
00178 else if ( first_byte == 0 ) {
00179 bytes = 0;
00180 }
00181
00182 static ssize_t this_bytes;
00183 static unsigned int bytes_to_read;
00184 static int rc;
00185 static fd_set read_fds;
00186
00187 FD_ZERO(&read_fds);
00188
00189 FD_SET(inotify_fd, &read_fds);
00190
00191 fd_max = inotify_fd;
00192
00193 FD_SET(stop_fd_read, &read_fds);
00194
00195 if (stop_fd_read >fd_max)
00196 fd_max = stop_fd_read;
00197
00198 rc = select(fd_max + 1, &read_fds,
00199 NULL, NULL, NULL);
00200 if ( rc < 0 ) {
00201 return NULL;
00202 }
00203 else if ( rc == 0 ) {
00204
00205 return NULL;
00206 }
00207
00208 if (FD_ISSET(stop_fd_read, &read_fds))
00209 {
00210 char buf;
00211 read(stop_fd_read, &buf, 1);
00212 }
00213
00214 if (FD_ISSET(inotify_fd, &read_fds))
00215 {
00216
00217
00218 do {
00219 rc = ioctl( inotify_fd, FIONREAD, &bytes_to_read );
00220 } while ( !rc &&
00221 bytes_to_read < sizeof(struct inotify_event));
00222
00223 if ( rc == -1 ) {
00224 return NULL;
00225 }
00226
00227 this_bytes = read(inotify_fd, &event[0] + bytes,
00228 sizeof(struct inotify_event)*MAX_EVENTS - bytes);
00229 if ( this_bytes < 0 ) {
00230 return NULL;
00231 }
00232 if ( this_bytes == 0 ) {
00233 log_error("Inotify reported end-of-file. Possibly too many "
00234 "events occurred at once.\n");
00235 return NULL;
00236 }
00237 bytes += this_bytes;
00238
00239 ret = &event[0];
00240 first_byte = sizeof(struct inotify_event) + ret->len;
00241 assert( first_byte <= bytes);
00242
00243 if ( first_byte == bytes ) {
00244 first_byte = 0;
00245 }
00246
00247 return ret;
00248 }
00249
00250 return NULL;
00251 }
00252
00253
00254 void Inotify::stop()
00255 {
00256 char stop = 's';
00257 write(stop_fd_write, &stop, 1);
00258 }
00259
00260 #endif//HAVE_INOTIFY