-
-
Save hetsch/2917841 to your computer and use it in GitHub Desktop.
/* | |
* Copyright (c) 2004 Novell, Inc. | |
* Copyright (c) 2005 Manuel Amador <[email protected]> | |
* Copyright (c) 2009-2011 Forest Bond <[email protected]> | |
* | |
* Permission is hereby granted, free of charge, to any person obtaining a | |
* copy of this software and associated documentation files (the "Software"), | |
* to deal in the Software without restriction, including without limitation | |
* the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
* and/or sell copies of the Software, and to permit persons to whom the | |
* Software is furnished to do so, subject to the following conditions: | |
* | |
* The above copyright notice and this permission notice shall be included in | |
* all copies or substantial portions of the Software. | |
* | |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
* DEALINGS IN THE SOFTWARE. | |
*/ | |
#include <Python.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <fcntl.h> | |
#include <unistd.h> | |
#include <errno.h> | |
#include <sys/ioctl.h> | |
#include <sys/select.h> | |
#include <sys/inotify.h> | |
#include <linux/types.h> | |
#include <linux/limits.h> | |
#define MAX_PENDING_PAUSE_COUNT 5 | |
#define PENDING_PAUSE_MICROSECONDS 2000 | |
#define PENDING_THRESHOLD(qsize) ((qsize) >> 1) | |
#define EVENT_SIZE (sizeof (struct inotify_event)) | |
#define BUF_LEN (1024 * (EVENT_SIZE + 16)) | |
static PyObject * inotifyx_init(PyObject *self, PyObject *args) { | |
int fd; | |
Py_BEGIN_ALLOW_THREADS; | |
fd = inotify_init(); | |
Py_END_ALLOW_THREADS; | |
if(fd < 0) { | |
PyErr_SetFromErrno(PyExc_IOError); | |
return NULL; | |
} | |
return Py_BuildValue("i", fd); | |
} | |
static PyObject * inotifyx_add_watch(PyObject *self, PyObject *args) { | |
int fd; | |
char *path; | |
int watch_descriptor; | |
uint32_t mask; | |
mask = IN_ALL_EVENTS; | |
// changed from python2 (s) to python3 (u) | |
if(! PyArg_ParseTuple(args, "is|i", &fd, &path, &mask)) { | |
return NULL; | |
} | |
Py_BEGIN_ALLOW_THREADS; | |
watch_descriptor = inotify_add_watch(fd, (const char *)path, mask); | |
Py_END_ALLOW_THREADS; | |
if(watch_descriptor < 0) { | |
PyErr_SetFromErrno(PyExc_IOError); | |
return NULL; | |
} | |
return Py_BuildValue("i", watch_descriptor); | |
} | |
static PyObject * inotifyx_rm_watch(PyObject *self, PyObject *args) { | |
int fd; | |
int watch_descriptor; | |
int retvalue; | |
if(! PyArg_ParseTuple(args, "ii", &fd, &watch_descriptor)) { | |
return NULL; | |
} | |
Py_BEGIN_ALLOW_THREADS; | |
retvalue = inotify_rm_watch(fd, watch_descriptor); | |
Py_END_ALLOW_THREADS; | |
if(retvalue < 0) { | |
PyErr_SetFromErrno(PyExc_IOError); | |
return NULL; | |
} | |
return Py_BuildValue("i", retvalue); | |
} | |
static PyObject * inotifyx_get_events(PyObject *self, PyObject *args) { | |
int fd; | |
static char buf[BUF_LEN]; | |
int i; | |
int len; | |
float timeout_arg; | |
struct timeval timeout; | |
void *ptimeout; | |
struct inotify_event *event; | |
fd_set read_fds; | |
int select_retval; | |
timeout_arg = -1.0; | |
if(! PyArg_ParseTuple(args, "i|f", &fd, &timeout_arg)) { | |
return NULL; | |
} | |
if(timeout_arg < 0.0) { | |
ptimeout = NULL; | |
} else { | |
timeout.tv_sec = (int)timeout_arg; | |
timeout.tv_usec = (int)(1000000.0 * (timeout_arg - (float)((int) timeout_arg))); | |
ptimeout = &timeout; | |
} | |
FD_ZERO(&read_fds); | |
FD_SET(fd, &read_fds); | |
Py_BEGIN_ALLOW_THREADS; | |
select_retval = select(fd + 1, &read_fds, NULL, NULL, ptimeout); | |
Py_END_ALLOW_THREADS; | |
if(select_retval == 0) { | |
// Timed out. | |
return PyList_New(0); | |
} else if(select_retval < 0) { | |
PyErr_SetFromErrno(PyExc_IOError); | |
return NULL; | |
} | |
PyObject* retvalue = PyList_New(0); | |
timeout.tv_sec = 0; | |
timeout.tv_usec = 0; | |
ptimeout = &timeout; | |
while (1) { | |
Py_BEGIN_ALLOW_THREADS; | |
len = read(fd, buf, BUF_LEN); | |
Py_END_ALLOW_THREADS; | |
if(len < 0) { | |
PyErr_SetFromErrno(PyExc_IOError); | |
return NULL; | |
} else if(len == 0) { | |
PyErr_SetString(PyExc_IOError, "event buffer too small"); | |
return NULL; | |
} | |
i = 0; | |
while(i < len) { | |
event = (struct inotify_event *)(& buf[i]); | |
PyObject* value = NULL; | |
if(event->name[0] != '\0') { | |
value = Py_BuildValue( | |
// changed from python2 (s) to python3 (u) | |
"iiis", | |
event->wd, | |
event->mask, | |
event->cookie, | |
event->name | |
); | |
} else { | |
value = Py_BuildValue( | |
"iiiO", | |
event->wd, | |
event->mask, | |
event->cookie, | |
Py_None | |
); | |
} | |
if(PyList_Append(retvalue, value) == -1) { | |
return NULL; | |
} | |
i += EVENT_SIZE + event->len; | |
} | |
FD_ZERO(&read_fds); | |
FD_SET(fd, &read_fds); | |
Py_BEGIN_ALLOW_THREADS; | |
select_retval = select(fd + 1, &read_fds, NULL, NULL, ptimeout); | |
Py_END_ALLOW_THREADS; | |
if (select_retval <= 0) | |
break; | |
} | |
return retvalue; | |
} | |
static PyMethodDef InotifyMethods[] = { | |
{ | |
"init", | |
inotifyx_init, | |
METH_VARARGS, | |
( | |
"init()\n\n" | |
"Initialize an inotify instance and return the associated file\n" | |
"descriptor. The file descriptor should be closed via os.close\n" | |
"after it is no longer needed." | |
) | |
}, | |
{ | |
"add_watch", | |
inotifyx_add_watch, | |
METH_VARARGS, | |
( | |
"add_watch(fd, path[, mask])\n\n" | |
"Add a watch for path and return the watch descriptor.\n" | |
"fd should be the file descriptor returned by init.\n" | |
"If left unspecified, mask defaults to IN_ALL_EVENTS.\n" | |
"See the inotify documentation for details." | |
) | |
}, | |
{ | |
"rm_watch", | |
inotifyx_rm_watch, | |
METH_VARARGS, | |
( | |
"rm_watch(fd, wd)\n\n" | |
"Remove the watch associated with watch descriptor wd.\n" | |
"fd should be the file descriptor returned by init.\n" | |
) | |
}, | |
{ | |
"get_events", | |
inotifyx_get_events, | |
METH_VARARGS, | |
"get_events(fd[, timeout])\n\n" | |
"Read events from inotify and return a list of tuples " | |
"(wd, mask, cookie, name).\n" | |
"The name field is None if no name is associated with the inotify event.\n" | |
"Timeout specifies a timeout in seconds (as an integer or float).\n" | |
"If left unspecified, there is no timeout and get_events will block\n" | |
"indefinitely. If timeout is zero, get_events will not block." | |
}, | |
{NULL, NULL, 0, NULL} | |
}; | |
/* | |
http://python3porting.com/cextensions.html | |
http://www.tutorialspoint.com/python/python_further_extensions.htm | |
*/ | |
static struct PyModuleDef moduledef = { | |
PyModuleDef_HEAD_INIT, | |
"inotifyx.binding", | |
( | |
"Low-level interface to inotify. Do not use this module directly.\n" | |
"Instead, use the inotifyx module." | |
), | |
-1, | |
InotifyMethods, | |
NULL, | |
NULL, | |
NULL, | |
NULL | |
}; | |
static PyObject * moduleinit(void) { | |
PyObject *module = PyModule_Create(&moduledef); | |
/* | |
PyObject* module = Py_InitModule3( | |
"inotifyx.binding", | |
InotifyMethods, | |
( | |
"Low-level interface to inotify. Do not use this module directly.\n" | |
"Instead, use the inotifyx module." | |
) | |
); | |
*/ | |
if (module == NULL) | |
return NULL; | |
PyModule_AddIntConstant(module, "IN_ACCESS", IN_ACCESS); | |
PyModule_AddIntConstant(module, "IN_MODIFY", IN_MODIFY); | |
PyModule_AddIntConstant(module, "IN_ATTRIB", IN_ATTRIB); | |
PyModule_AddIntConstant(module, "IN_CLOSE_WRITE", IN_CLOSE_WRITE); | |
PyModule_AddIntConstant(module, "IN_CLOSE_NOWRITE", IN_CLOSE_NOWRITE); | |
PyModule_AddIntConstant(module, "IN_CLOSE", IN_CLOSE); | |
PyModule_AddIntConstant(module, "IN_OPEN", IN_OPEN); | |
PyModule_AddIntConstant(module, "IN_MOVED_FROM", IN_MOVED_FROM); | |
PyModule_AddIntConstant(module, "IN_MOVED_TO", IN_MOVED_TO); | |
PyModule_AddIntConstant(module, "IN_MOVE", IN_MOVE); | |
PyModule_AddIntConstant(module, "IN_CREATE", IN_CREATE); | |
PyModule_AddIntConstant(module, "IN_DELETE", IN_DELETE); | |
PyModule_AddIntConstant(module, "IN_DELETE_SELF", IN_DELETE_SELF); | |
PyModule_AddIntConstant(module, "IN_MOVE_SELF", IN_MOVE_SELF); | |
PyModule_AddIntConstant(module, "IN_UNMOUNT", IN_UNMOUNT); | |
PyModule_AddIntConstant(module, "IN_Q_OVERFLOW", IN_Q_OVERFLOW); | |
PyModule_AddIntConstant(module, "IN_IGNORED", IN_IGNORED); | |
PyModule_AddIntConstant(module, "IN_ONLYDIR", IN_ONLYDIR); | |
PyModule_AddIntConstant(module, "IN_DONT_FOLLOW", IN_DONT_FOLLOW); | |
PyModule_AddIntConstant(module, "IN_MASK_ADD", IN_MASK_ADD); | |
PyModule_AddIntConstant(module, "IN_ISDIR", IN_ISDIR); | |
PyModule_AddIntConstant(module, "IN_ONESHOT", IN_ONESHOT); | |
PyModule_AddIntConstant(module, "IN_ALL_EVENTS", IN_ALL_EVENTS); | |
return module; | |
} | |
PyMODINIT_FUNC | |
PyInit_binding(void) { | |
return moduleinit(); | |
} |
Always got that kind of error:
2012-06-15 15:07:37,246 BACKEND.FILESYSTEM.SCANNER.LINUX Unknown error while handling filesystem change
2012-06-15 15:07:37,246 BACKEND.FILESYSTEM.SCANNER.LINUX ../Objects/listobject.c:290: bad argument to internal function
Traceback (most recent call last):
File "linux.py", line 397, in run
for event in inotifyx.get_events(self.inotify_fd, 0):
File "/home/hetsch/.virtualenvs/medialibrary-python-3/lib/python3.2/site-packages/inotifyx/init.py", line 118, in get_events
for wd, mask, cookie, name in binding.get_events(fd, *args)
SystemError: ../Objects/listobject.c:290: bad argument to internal function
Has something to to with .app (MacOsX fileextensions) and inotify. In Ubuntu .app extensions show up as directory, so inotify tries to add a watch on the inner files of the .app file and crashes
Since then, there are more recent patches for python3 compatibility on launchpad: https://bugs.launchpad.net/inotifyx/+bug/1006053
Also had to change all s to u (PyBuildValue) to respect python3s new string/byte types
-- reverted back to (s) - u is broken