comparison hgext/inotify/linux/_inotify.c @ 6239:39cfcef4f463

Add inotify extension
author Bryan O'Sullivan <bos@serpentine.com>
date Wed, 12 Mar 2008 15:30:11 -0700
parents
children 7016f7fb8fe3
comparison
equal deleted inserted replaced
6236:ad6b123de1c7 6239:39cfcef4f463
1 /*
2 * _inotify.c - Python extension interfacing to the Linux inotify subsystem
3 *
4 * Copyright 2006 Bryan O'Sullivan <bos@serpentine.com>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of version 2.1 of the GNU Lesser General
8 * Public License, incorporated herein by reference.
9 */
10
11 #include <Python.h>
12 #include <alloca.h>
13 #include <sys/inotify.h>
14 #include <stdint.h>
15 #include <sys/ioctl.h>
16 #include <unistd.h>
17
18 static PyObject *init(PyObject *self, PyObject *args)
19 {
20 PyObject *ret = NULL;
21 int fd = -1;
22
23 if (!PyArg_ParseTuple(args, ":init"))
24 goto bail;
25
26 Py_BEGIN_ALLOW_THREADS
27 fd = inotify_init();
28 Py_END_ALLOW_THREADS
29
30 if (fd == -1) {
31 PyErr_SetFromErrno(PyExc_OSError);
32 goto bail;
33 }
34
35 ret = PyInt_FromLong(fd);
36 if (ret == NULL)
37 goto bail;
38
39 goto done;
40
41 bail:
42 if (fd != -1)
43 close(fd);
44
45 Py_CLEAR(ret);
46
47 done:
48 return ret;
49 }
50
51 PyDoc_STRVAR(
52 init_doc,
53 "init() -> fd\n"
54 "\n"
55 "Initialise an inotify instance.\n"
56 "Return a file descriptor associated with a new inotify event queue.");
57
58 static PyObject *add_watch(PyObject *self, PyObject *args)
59 {
60 PyObject *ret = NULL;
61 uint32_t mask;
62 int wd = -1;
63 char *path;
64 int fd;
65
66 if (!PyArg_ParseTuple(args, "isI:add_watch", &fd, &path, &mask))
67 goto bail;
68
69 Py_BEGIN_ALLOW_THREADS
70 wd = inotify_add_watch(fd, path, mask);
71 Py_END_ALLOW_THREADS
72
73 if (wd == -1) {
74 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
75 goto bail;
76 }
77
78 ret = PyInt_FromLong(wd);
79 if (ret == NULL)
80 goto bail;
81
82 goto done;
83
84 bail:
85 if (wd != -1)
86 inotify_rm_watch(fd, wd);
87
88 Py_CLEAR(ret);
89
90 done:
91 return ret;
92 }
93
94 PyDoc_STRVAR(
95 add_watch_doc,
96 "add_watch(fd, path, mask) -> wd\n"
97 "\n"
98 "Add a watch to an inotify instance, or modify an existing watch.\n"
99 "\n"
100 " fd: file descriptor returned by init()\n"
101 " path: path to watch\n"
102 " mask: mask of events to watch for\n"
103 "\n"
104 "Return a unique numeric watch descriptor for the inotify instance\n"
105 "mapped by the file descriptor.");
106
107 static PyObject *remove_watch(PyObject *self, PyObject *args)
108 {
109 PyObject *ret = NULL;
110 uint32_t wd;
111 int fd;
112 int r;
113
114 if (!PyArg_ParseTuple(args, "iI:remove_watch", &fd, &wd))
115 goto bail;
116
117 Py_BEGIN_ALLOW_THREADS
118 r = inotify_rm_watch(fd, wd);
119 Py_END_ALLOW_THREADS
120
121 if (r == -1) {
122 PyErr_SetFromErrno(PyExc_OSError);
123 goto bail;
124 }
125
126 Py_INCREF(Py_None);
127
128 goto done;
129
130 bail:
131 Py_CLEAR(ret);
132
133 done:
134 return ret;
135 }
136
137 PyDoc_STRVAR(
138 remove_watch_doc,
139 "remove_watch(fd, wd)\n"
140 "\n"
141 " fd: file descriptor returned by init()\n"
142 " wd: watch descriptor returned by add_watch()\n"
143 "\n"
144 "Remove a watch associated with the watch descriptor wd from the\n"
145 "inotify instance associated with the file descriptor fd.\n"
146 "\n"
147 "Removing a watch causes an IN_IGNORED event to be generated for this\n"
148 "watch descriptor.");
149
150 #define bit_name(x) {x, #x}
151
152 static struct {
153 int bit;
154 const char *name;
155 PyObject *pyname;
156 } bit_names[] = {
157 bit_name(IN_ACCESS),
158 bit_name(IN_MODIFY),
159 bit_name(IN_ATTRIB),
160 bit_name(IN_CLOSE_WRITE),
161 bit_name(IN_CLOSE_NOWRITE),
162 bit_name(IN_OPEN),
163 bit_name(IN_MOVED_FROM),
164 bit_name(IN_MOVED_TO),
165 bit_name(IN_CREATE),
166 bit_name(IN_DELETE),
167 bit_name(IN_DELETE_SELF),
168 bit_name(IN_MOVE_SELF),
169 bit_name(IN_UNMOUNT),
170 bit_name(IN_Q_OVERFLOW),
171 bit_name(IN_IGNORED),
172 bit_name(IN_ONLYDIR),
173 bit_name(IN_DONT_FOLLOW),
174 bit_name(IN_MASK_ADD),
175 bit_name(IN_ISDIR),
176 bit_name(IN_ONESHOT),
177 {0}
178 };
179
180 static PyObject *decode_mask(int mask)
181 {
182 PyObject *ret = PyList_New(0);
183 int i;
184
185 if (ret == NULL)
186 goto bail;
187
188 for (i = 0; bit_names[i].bit; i++) {
189 if (mask & bit_names[i].bit) {
190 if (bit_names[i].pyname == NULL) {
191 bit_names[i].pyname = PyString_FromString(bit_names[i].name);
192 if (bit_names[i].pyname == NULL)
193 goto bail;
194 }
195 Py_INCREF(bit_names[i].pyname);
196 if (PyList_Append(ret, bit_names[i].pyname) == -1)
197 goto bail;
198 }
199 }
200
201 goto done;
202
203 bail:
204 Py_CLEAR(ret);
205
206 done:
207 return ret;
208 }
209
210 static PyObject *pydecode_mask(PyObject *self, PyObject *args)
211 {
212 int mask;
213
214 if (!PyArg_ParseTuple(args, "i:decode_mask", &mask))
215 return NULL;
216
217 return decode_mask(mask);
218 }
219
220 PyDoc_STRVAR(
221 decode_mask_doc,
222 "decode_mask(mask) -> list_of_strings\n"
223 "\n"
224 "Decode an inotify mask value into a list of strings that give the\n"
225 "name of each bit set in the mask.");
226
227 static char doc[] = "Low-level inotify interface wrappers.";
228
229 static void define_const(PyObject *dict, const char *name, uint32_t val)
230 {
231 PyObject *pyval = PyInt_FromLong(val);
232 PyObject *pyname = PyString_FromString(name);
233
234 if (!pyname || !pyval)
235 goto bail;
236
237 PyDict_SetItem(dict, pyname, pyval);
238
239 bail:
240 Py_XDECREF(pyname);
241 Py_XDECREF(pyval);
242 }
243
244 static void define_consts(PyObject *dict)
245 {
246 define_const(dict, "IN_ACCESS", IN_ACCESS);
247 define_const(dict, "IN_MODIFY", IN_MODIFY);
248 define_const(dict, "IN_ATTRIB", IN_ATTRIB);
249 define_const(dict, "IN_CLOSE_WRITE", IN_CLOSE_WRITE);
250 define_const(dict, "IN_CLOSE_NOWRITE", IN_CLOSE_NOWRITE);
251 define_const(dict, "IN_OPEN", IN_OPEN);
252 define_const(dict, "IN_MOVED_FROM", IN_MOVED_FROM);
253 define_const(dict, "IN_MOVED_TO", IN_MOVED_TO);
254
255 define_const(dict, "IN_CLOSE", IN_CLOSE);
256 define_const(dict, "IN_MOVE", IN_MOVE);
257
258 define_const(dict, "IN_CREATE", IN_CREATE);
259 define_const(dict, "IN_DELETE", IN_DELETE);
260 define_const(dict, "IN_DELETE_SELF", IN_DELETE_SELF);
261 define_const(dict, "IN_MOVE_SELF", IN_MOVE_SELF);
262 define_const(dict, "IN_UNMOUNT", IN_UNMOUNT);
263 define_const(dict, "IN_Q_OVERFLOW", IN_Q_OVERFLOW);
264 define_const(dict, "IN_IGNORED", IN_IGNORED);
265
266 define_const(dict, "IN_ONLYDIR", IN_ONLYDIR);
267 define_const(dict, "IN_DONT_FOLLOW", IN_DONT_FOLLOW);
268 define_const(dict, "IN_MASK_ADD", IN_MASK_ADD);
269 define_const(dict, "IN_ISDIR", IN_ISDIR);
270 define_const(dict, "IN_ONESHOT", IN_ONESHOT);
271 define_const(dict, "IN_ALL_EVENTS", IN_ALL_EVENTS);
272 }
273
274 struct event {
275 PyObject_HEAD
276 PyObject *wd;
277 PyObject *mask;
278 PyObject *cookie;
279 PyObject *name;
280 };
281
282 static PyObject *event_wd(PyObject *self, void *x)
283 {
284 struct event *evt = (struct event *) self;
285 Py_INCREF(evt->wd);
286 return evt->wd;
287 }
288
289 static PyObject *event_mask(PyObject *self, void *x)
290 {
291 struct event *evt = (struct event *) self;
292 Py_INCREF(evt->mask);
293 return evt->mask;
294 }
295
296 static PyObject *event_cookie(PyObject *self, void *x)
297 {
298 struct event *evt = (struct event *) self;
299 Py_INCREF(evt->cookie);
300 return evt->cookie;
301 }
302
303 static PyObject *event_name(PyObject *self, void *x)
304 {
305 struct event *evt = (struct event *) self;
306 Py_INCREF(evt->name);
307 return evt->name;
308 }
309
310 static struct PyGetSetDef event_getsets[] = {
311 {"wd", event_wd, NULL,
312 "watch descriptor"},
313 {"mask", event_mask, NULL,
314 "event mask"},
315 {"cookie", event_cookie, NULL,
316 "rename cookie, if rename-related event"},
317 {"name", event_name, NULL,
318 "file name"},
319 {NULL}
320 };
321
322 PyDoc_STRVAR(
323 event_doc,
324 "event: Structure describing an inotify event.");
325
326 static PyObject *event_new(PyTypeObject *t, PyObject *a, PyObject *k)
327 {
328 return (*t->tp_alloc)(t, 0);
329 }
330
331 static void event_dealloc(struct event *evt)
332 {
333 Py_XDECREF(evt->wd);
334 Py_XDECREF(evt->mask);
335 Py_XDECREF(evt->cookie);
336 Py_XDECREF(evt->name);
337
338 (*evt->ob_type->tp_free)(evt);
339 }
340
341 static PyObject *event_repr(struct event *evt)
342 {
343 int wd = PyInt_AsLong(evt->wd);
344 int cookie = evt->cookie == Py_None ? -1 : PyInt_AsLong(evt->cookie);
345 PyObject *ret = NULL, *pymasks = NULL, *pymask = NULL;
346 PyObject *join = NULL;
347 char *maskstr;
348
349 join = PyString_FromString("|");
350 if (join == NULL)
351 goto bail;
352
353 pymasks = decode_mask(PyInt_AsLong(evt->mask));
354 if (pymasks == NULL)
355 goto bail;
356
357 pymask = _PyString_Join(join, pymasks);
358 if (pymask == NULL)
359 goto bail;
360
361 maskstr = PyString_AsString(pymask);
362
363 if (evt->name != Py_None) {
364 PyObject *pyname = PyString_Repr(evt->name, 1);
365 char *name = pyname ? PyString_AsString(pyname) : "???";
366
367 if (cookie == -1)
368 ret = PyString_FromFormat("event(wd=%d, mask=%s, name=%s)",
369 wd, maskstr, name);
370 else
371 ret = PyString_FromFormat("event(wd=%d, mask=%s, "
372 "cookie=0x%x, name=%s)",
373 wd, maskstr, cookie, name);
374
375 Py_XDECREF(pyname);
376 } else {
377 if (cookie == -1)
378 ret = PyString_FromFormat("event(wd=%d, mask=%s)",
379 wd, maskstr);
380 else {
381 ret = PyString_FromFormat("event(wd=%d, mask=%s, cookie=0x%x)",
382 wd, maskstr, cookie);
383 }
384 }
385
386 goto done;
387 bail:
388 Py_CLEAR(ret);
389
390 done:
391 Py_XDECREF(pymask);
392 Py_XDECREF(pymasks);
393 Py_XDECREF(join);
394
395 return ret;
396 }
397
398 static PyTypeObject event_type = {
399 PyObject_HEAD_INIT(NULL)
400 0, /*ob_size*/
401 "_inotify.event", /*tp_name*/
402 sizeof(struct event), /*tp_basicsize*/
403 0, /*tp_itemsize*/
404 (destructor)event_dealloc, /*tp_dealloc*/
405 0, /*tp_print*/
406 0, /*tp_getattr*/
407 0, /*tp_setattr*/
408 0, /*tp_compare*/
409 (reprfunc)event_repr, /*tp_repr*/
410 0, /*tp_as_number*/
411 0, /*tp_as_sequence*/
412 0, /*tp_as_mapping*/
413 0, /*tp_hash */
414 0, /*tp_call*/
415 0, /*tp_str*/
416 0, /*tp_getattro*/
417 0, /*tp_setattro*/
418 0, /*tp_as_buffer*/
419 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
420 event_doc, /* tp_doc */
421 0, /* tp_traverse */
422 0, /* tp_clear */
423 0, /* tp_richcompare */
424 0, /* tp_weaklistoffset */
425 0, /* tp_iter */
426 0, /* tp_iternext */
427 0, /* tp_methods */
428 0, /* tp_members */
429 event_getsets, /* tp_getset */
430 0, /* tp_base */
431 0, /* tp_dict */
432 0, /* tp_descr_get */
433 0, /* tp_descr_set */
434 0, /* tp_dictoffset */
435 0, /* tp_init */
436 0, /* tp_alloc */
437 event_new, /* tp_new */
438 };
439
440 PyObject *read_events(PyObject *self, PyObject *args)
441 {
442 PyObject *ctor_args = NULL;
443 PyObject *pybufsize = NULL;
444 PyObject *ret = NULL;
445 int bufsize = 65536;
446 char *buf = NULL;
447 int nread, pos;
448 int fd;
449
450 if (!PyArg_ParseTuple(args, "i|O:read", &fd, &pybufsize))
451 goto bail;
452
453 if (pybufsize && pybufsize != Py_None)
454 bufsize = PyInt_AsLong(pybufsize);
455
456 ret = PyList_New(0);
457 if (ret == NULL)
458 goto bail;
459
460 if (bufsize <= 0) {
461 int r;
462
463 Py_BEGIN_ALLOW_THREADS
464 r = ioctl(fd, FIONREAD, &bufsize);
465 Py_END_ALLOW_THREADS
466
467 if (r == -1) {
468 PyErr_SetFromErrno(PyExc_OSError);
469 goto bail;
470 }
471 if (bufsize == 0)
472 goto done;
473 }
474 else {
475 static long name_max;
476 static long name_fd = -1;
477 long min;
478
479 if (name_fd != fd) {
480 name_fd = fd;
481 Py_BEGIN_ALLOW_THREADS
482 name_max = fpathconf(fd, _PC_NAME_MAX);
483 Py_END_ALLOW_THREADS
484 }
485
486 min = sizeof(struct inotify_event) + name_max + 1;
487
488 if (bufsize < min) {
489 PyErr_Format(PyExc_ValueError, "bufsize must be at least %d",
490 (int) min);
491 goto bail;
492 }
493 }
494
495 buf = alloca(bufsize);
496
497 Py_BEGIN_ALLOW_THREADS
498 nread = read(fd, buf, bufsize);
499 Py_END_ALLOW_THREADS
500
501 if (nread == -1) {
502 PyErr_SetFromErrno(PyExc_OSError);
503 goto bail;
504 }
505
506 ctor_args = PyTuple_New(0);
507
508 if (ctor_args == NULL)
509 goto bail;
510
511 pos = 0;
512
513 while (pos < nread) {
514 struct inotify_event *in = (struct inotify_event *) (buf + pos);
515 struct event *evt;
516 PyObject *obj;
517
518 obj = PyObject_CallObject((PyObject *) &event_type, ctor_args);
519
520 if (obj == NULL)
521 goto bail;
522
523 evt = (struct event *) obj;
524
525 evt->wd = PyInt_FromLong(in->wd);
526 evt->mask = PyInt_FromLong(in->mask);
527 if (in->mask & IN_MOVE)
528 evt->cookie = PyInt_FromLong(in->cookie);
529 else {
530 Py_INCREF(Py_None);
531 evt->cookie = Py_None;
532 }
533 if (in->len)
534 evt->name = PyString_FromString(in->name);
535 else {
536 Py_INCREF(Py_None);
537 evt->name = Py_None;
538 }
539
540 if (!evt->wd || !evt->mask || !evt->cookie || !evt->name)
541 goto mybail;
542
543 if (PyList_Append(ret, obj) == -1)
544 goto mybail;
545
546 pos += sizeof(struct inotify_event) + in->len;
547 continue;
548
549 mybail:
550 Py_CLEAR(evt->wd);
551 Py_CLEAR(evt->mask);
552 Py_CLEAR(evt->cookie);
553 Py_CLEAR(evt->name);
554 Py_DECREF(obj);
555
556 goto bail;
557 }
558
559 goto done;
560
561 bail:
562 Py_CLEAR(ret);
563
564 done:
565 Py_XDECREF(ctor_args);
566
567 return ret;
568 }
569
570 PyDoc_STRVAR(
571 read_doc,
572 "read(fd, bufsize[=65536]) -> list_of_events\n"
573 "\n"
574 "\nRead inotify events from a file descriptor.\n"
575 "\n"
576 " fd: file descriptor returned by init()\n"
577 " bufsize: size of buffer to read into, in bytes\n"
578 "\n"
579 "Return a list of event objects.\n"
580 "\n"
581 "If bufsize is > 0, block until events are available to be read.\n"
582 "Otherwise, immediately return all events that can be read without\n"
583 "blocking.");
584
585
586 static PyMethodDef methods[] = {
587 {"init", init, METH_VARARGS, init_doc},
588 {"add_watch", add_watch, METH_VARARGS, add_watch_doc},
589 {"remove_watch", remove_watch, METH_VARARGS, remove_watch_doc},
590 {"read", read_events, METH_VARARGS, read_doc},
591 {"decode_mask", pydecode_mask, METH_VARARGS, decode_mask_doc},
592 {NULL},
593 };
594
595 void init_inotify(void)
596 {
597 PyObject *mod, *dict;
598
599 if (PyType_Ready(&event_type) == -1)
600 return;
601
602 mod = Py_InitModule3("_inotify", methods, doc);
603
604 dict = PyModule_GetDict(mod);
605
606 if (dict)
607 define_consts(dict);
608 }