/* * This file is part of Python FAM. * Copyright (C) 2002 Robert Sander * Copyright (C) 2005 Martin v. Loewis * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * $Id: _fam.c,v 1.14 2005/04/10 07:48:45 loewis Exp $ * */ #include #include #include #include "_fam.h" #define PY22 0x020200F0 // #define _DEBUG 1 #define add_int(d, name) \ { \ PyObject *i = PyInt_FromLong(FAM##name); \ PyDict_SetItemString( d, #name, i ); \ Py_DECREF(i); \ } char* code2str(int code) { char* rv; switch (code) { case FAMExists: rv = "exists"; break; case FAMEndExist: rv = "endExist"; break; case FAMChanged: rv = "changed"; break; case FAMDeleted: rv = "deleted"; break; case FAMStartExecuting: rv = "started executing"; break; case FAMStopExecuting: rv = "stopped executing"; break; case FAMCreated: rv = "created"; break; case FAMMoved: rv = "moved"; break; case FAMAcknowledge: rv = "acknowleged"; break; default: rv = NULL; } return rv; } /* * FAMConnection object definitions * * The Connection object is returned by the open() method of the module. * It implements FAMClose(), FAMMonitorDirectory(), FAMMonitorFile(), * FAMNextEvent() and FAMPending(). * */ typedef struct { PyObject_HEAD FAMConnection *fc; } _fam_connection_object; static PyMethodDef _fam_connection_methods[] = { { "close", _fam_close, 1, _fam_close__doc__}, { "monitorDirectory", _fam_monitor_directory, 1, _fam_monitor_directory__doc__}, { "monitorFile", _fam_monitor_file, 1, _fam_monitor_file__doc__}, { "nextEvent", _fam_next_event, 1, _fam_next_event__doc__}, { "pending", _fam_pending, 1, _fam_pending__doc__}, { "fileno", _fam_fileno, 1, _fam_fileno__doc__}, { NULL, NULL, 0, NULL} }; #if PY_VERSION_HEX < PY22 static PyObject* _fam_connection_getattr(PyObject* self, char* name) { #ifdef _DEBUG fprintf(stderr, "_fam: getting attribute %s from connection object.\n", name); #endif return Py_FindMethod(_fam_connection_methods, self, name); } #endif void _fam_connection_del(PyObject* self) { #ifdef _DEBUG fprintf(stderr, "_fam: deleting connection object.\n"); #endif _fam_close(self, NULL); PyMem_DEL(self); } static PyTypeObject _fam_connection_type = { #if defined(WIN32) || defined(__CYGWIN__) PyObject_HEAD_INIT(NULL) #else PyObject_HEAD_INIT(&PyType_Type) #endif 0, /* ob_size */ "_fam.FAMConnection", /* tp_name */ sizeof(_fam_connection_object), /* tp_basicsize */ 0, /* tp_itemsize */ _fam_connection_del, /* tp_dealloc */ 0, /* tp_print */ #if PY_VERSION_HEX < PY22 _fam_connection_getattr, /* tp_getattr */ #else 0, /* tp_getattr */ #endif 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* call */ 0, /* str */ #if PY_VERSION_HEX >= PY22 PyObject_GenericGetAttr, /* tp_getattro */ #else 0, /* tp_getattro */ #endif 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ _fam_connection_type__doc__, /* doc string */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ #if PY_VERSION_HEX >= PY22 0, /* tp_iter */ 0, /* tp_iternext */ _fam_connection_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ 0, /* tp_free */ 0, /* tp_is_gc */ 0, /* tp_bases */ 0, /* tp_mro */ 0, /* tp_cache */ 0, /* tp_subclasses */ 0, /* tp_weaklist */ #endif }; /* * FAMRequest object definitions * * A Request object is returned by one of the two monitor methods. * It implements FAMSuspendMonitor(), FAMResumeMonitor() and * FAMCancelMonitor() * */ typedef struct { PyObject_HEAD _fam_connection_object *fc_obj; FAMRequest *fr; } _fam_request_object; static PyMethodDef _fam_request_methods[] = { { "suspendMonitor", _fam_suspend_monitor, 1, _fam_suspend_monitor__doc__}, { "resumeMonitor", _fam_resume_monitor, 1, _fam_resume_monitor__doc__}, { "cancelMonitor", _fam_cancel_monitor, 1, _fam_cancel_monitor__doc__}, { "requestID", _fam_request_id, 1, _fam_request_id__doc__}, { NULL, NULL, 0, NULL} }; #if PY_VERSION_HEX < PY22 static PyObject* _fam_request_getattr(PyObject* self, char* name) { #ifdef _DEBUG fprintf(stderr, "_fam: getting attribute %s from request object.\n", name); #endif return Py_FindMethod(_fam_request_methods, self, name); } #endif void _fam_request_del(PyObject* self) { #ifdef _DEBUG fprintf(stderr, "_fam: deleting request object.\n"); #endif _fam_cancel_monitor(self, NULL); PyMem_DEL(self); } static PyTypeObject _fam_request_type = { #if defined(WIN32) || defined(__CYGWIN__) PyObject_HEAD_INIT(NULL) #else PyObject_HEAD_INIT(&PyType_Type) #endif 0, /* ob_size */ "_fam.FAMRequest", /* tp_name */ sizeof(_fam_request_object), /* tp_basicsize */ 0, /* tp_itemsize */ _fam_request_del, /* tp_dealloc */ 0, /* tp_print */ #if PY_VERSION_HEX < PY22 _fam_request_getattr, /* tp_getattr */ #else 0, /* tp_getattr */ #endif 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* call */ 0, /* str */ #if PY_VERSION_HEX >= PY22 PyObject_GenericGetAttr, /* tp_getattro */ #else 0, /* tp_getattro */ #endif 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ _fam_request_type__doc__, /* doc string */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ #if PY_VERSION_HEX >= PY22 0, /* tp_iter */ 0, /* tp_iternext */ _fam_request_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ 0, /* tp_free */ 0, /* tp_is_gc */ 0, /* tp_bases */ 0, /* tp_mro */ 0, /* tp_cache */ 0, /* tp_subclasses */ 0, /* tp_weaklist */ #endif }; /* * FAMEvent object definitions * * An Event object is returned by the nextEvent method. * It implements the code2str() method and has * the attributes connection, requestID, hostname, * filename, userData and code. * */ typedef struct { PyObject_HEAD PyObject *attr; _fam_connection_object *fc_obj; enum FAMCodes code; } _fam_event_object; static PyMethodDef _fam_event_methods[] = { { "code2str", _fam_code2str, 1, _fam_code2str__doc__}, { NULL, NULL, 0, NULL} }; #if PY_VERSION_HEX >= PY22 static PyMemberDef _fam_event_members[] = { { "__dict__", T_OBJECT, offsetof(_fam_event_object, attr), READONLY, 0}, { NULL, 0, 0, 0, NULL} }; #endif #if PY_VERSION_HEX < PY22 static PyObject* _fam_event_getattr(PyObject* self, char* name) { _fam_event_object *fe_obj; PyObject *v; fe_obj = (_fam_event_object *)self; if (fe_obj->attr) { v = PyDict_GetItemString(fe_obj->attr, name); if (v) { Py_INCREF(v); return v; } } return Py_FindMethod(_fam_event_methods, self, name); } static int _fam_event_setattr(PyObject* self, char* name, PyObject* v) { _fam_event_object *fe_obj = (_fam_event_object *)self; if (! fe_obj->attr) { fe_obj->attr = PyDict_New(); if (! fe_obj->attr) return -1; } if (! v) { int rv = PyDict_DelItemString(fe_obj->attr, name); if (rv < 0) PyErr_SetString(PyExc_AttributeError, "delete non-existing FAMEvent attribute"); return rv; } else return PyDict_SetItemString(fe_obj->attr, name, v); } #endif void _fam_event_del(PyObject* self) { _fam_event_object *fe_obj = (_fam_event_object *)self; #ifdef _DEBUG fprintf(stderr, "_fam: deleting event object.\n"); #endif if (fe_obj) { if (fe_obj->fc_obj) { Py_DECREF(fe_obj->fc_obj); fe_obj->fc_obj = NULL; } Py_XDECREF(fe_obj->attr); PyMem_DEL(fe_obj); } } static PyTypeObject _fam_event_type = { #if defined(WIN32) || defined(__CYGWIN__) PyObject_HEAD_INIT(NULL) #else PyObject_HEAD_INIT(&PyType_Type) #endif 0, /* ob_size */ "_fam.FAMEvent", /* tp_name */ sizeof(_fam_event_object), /* tp_basicsize */ 0, /* tp_itemsize */ _fam_event_del, /* tp_dealloc */ 0, /* tp_print */ #if PY_VERSION_HEX < PY22 _fam_event_getattr, /* tp_getattr */ _fam_event_setattr, /* tp_setattr */ #else 0, /* tp_getattr */ 0, /* tp_setattr */ #endif 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* call */ 0, /* str */ #if PY_VERSION_HEX >= PY22 PyObject_GenericGetAttr, /* tp_getattro */ PyObject_GenericSetAttr, /* tp_setattro */ #else 0, /* tp_getattro */ 0, /* tp_setattro */ #endif 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT, /* tp_flags */ _fam_event_type__doc__, /* doc string */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ #if PY_VERSION_HEX >= PY22 0, /* tp_iter */ 0, /* tp_iternext */ _fam_event_methods, /* tp_methods */ _fam_event_members, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ offsetof(_fam_event_object, attr), /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ 0, /* tp_free */ 0, /* tp_is_gc */ 0, /* tp_bases */ 0, /* tp_mro */ 0, /* tp_cache */ 0, /* tp_subclasses */ 0, /* tp_weaklist */ #endif }; /* * The method definitions. * */ static PyObject* _fam_open(PyObject* self, PyObject* args) { int rc; _fam_connection_object *fc_obj = PyObject_NEW(_fam_connection_object, &_fam_connection_type); if (!fc_obj) return NULL; fc_obj->fc = (FAMConnection *)malloc(sizeof(FAMConnection)); if (!fc_obj->fc) { PyErr_SetString(PyExc_MemoryError, "_fam: unable to malloc for connection"); return NULL; } rc = FAMOpen(fc_obj->fc); if (!rc) { return (PyObject*)fc_obj; } else { PyErr_SetString(PyExc_IOError, "_fam: unable to open connection"); return NULL; } }; static PyObject* _fam_close(PyObject* self, PyObject* args) { int rc; _fam_connection_object *fc_obj = (_fam_connection_object *)self; if (fc_obj->fc) { rc = FAMClose(fc_obj->fc); free(fc_obj->fc); fc_obj->fc = NULL; if (!rc) { #ifdef _DEBUG fprintf(stderr, "_fam: closed connection.\n"); #endif } else { PyErr_SetString(PyExc_IOError, "_fam: unable to close connection"); return NULL; } } else { #ifdef _DEBUG fprintf(stderr, "_fam: no connection to close.\n"); #endif } Py_INCREF(Py_None); return Py_None; }; static PyObject* _fam_monitor_directory(PyObject* self, PyObject* args) { int rc; char *filename; PyObject *userData; _fam_connection_object *fc_obj; _fam_request_object *fr_obj; fc_obj = (_fam_connection_object *)self; if (fc_obj->fc) { if (!PyArg_ParseTuple(args, "sO", &filename, &userData)) return NULL; fr_obj = PyObject_NEW(_fam_request_object, &_fam_request_type); if (!fr_obj) return NULL; fr_obj->fc_obj = fc_obj; Py_INCREF(fc_obj); fr_obj->fr = (FAMRequest *)malloc(sizeof(FAMRequest)); if (!fr_obj->fr) { PyErr_SetString(PyExc_MemoryError, "_fam: unable to malloc for request"); return NULL; } rc = FAMMonitorDirectory(fc_obj->fc, filename, fr_obj->fr, (void *)userData); if (!rc) { Py_INCREF(userData); return (PyObject*)fr_obj; } else { PyErr_SetString(PyExc_IOError, "_fam: unable to monitor directory"); return NULL; } } else { PyErr_SetString(PyExc_RuntimeError, "_fam: no connection to monitor"); return NULL; } }; static PyObject* _fam_monitor_file(PyObject* self, PyObject* args) { int rc; char *filename; PyObject *userData; _fam_connection_object *fc_obj; _fam_request_object *fr_obj; fc_obj = (_fam_connection_object *)self; if (fc_obj->fc) { if (!PyArg_ParseTuple(args, "sO", &filename, &userData)) return NULL; fr_obj = PyObject_NEW(_fam_request_object, &_fam_request_type); if (!fr_obj) return NULL; fr_obj->fc_obj = fc_obj; Py_INCREF(fc_obj); fr_obj->fr = (FAMRequest *)malloc(sizeof(FAMRequest)); if (!fr_obj->fr) { PyErr_SetString(PyExc_MemoryError, "_fam: unable to malloc for request"); return NULL; } rc = FAMMonitorFile(fc_obj->fc, filename, fr_obj->fr, (void *)userData); if (!rc) { Py_INCREF(userData); return (PyObject*)fr_obj; } else { PyErr_SetString(PyExc_IOError, "_fam: unable to monitor file"); return NULL; } } else { PyErr_SetString(PyExc_RuntimeError, "_fam: no connection to monitor"); return NULL; } }; static PyObject* _fam_suspend_monitor(PyObject* self, PyObject* args) { int rc; _fam_request_object *fr_obj; fr_obj = (_fam_request_object *)self; if (fr_obj->fc_obj && fr_obj->fc_obj->fc && fr_obj->fr) { rc = FAMSuspendMonitor(fr_obj->fc_obj->fc, fr_obj->fr); if (rc) { PyErr_SetString(PyExc_RuntimeError, "_fam: unable to suspend monitor"); return NULL; } } Py_INCREF(Py_None); return Py_None; }; static PyObject* _fam_resume_monitor(PyObject* self, PyObject* args) { int rc; _fam_request_object *fr_obj; fr_obj = (_fam_request_object *)self; if (fr_obj->fc_obj && fr_obj->fc_obj->fc && fr_obj->fr) { rc = FAMResumeMonitor(fr_obj->fc_obj->fc, fr_obj->fr); if (rc) { PyErr_SetString(PyExc_RuntimeError, "_fam: unable to resume monitor"); return NULL; } } Py_INCREF(Py_None); return Py_None; }; static PyObject* _fam_cancel_monitor(PyObject* self, PyObject* args) { int rc; _fam_request_object *fr_obj; fr_obj = (_fam_request_object *)self; if (fr_obj->fc_obj && fr_obj->fc_obj->fc && fr_obj->fr) { rc = FAMCancelMonitor(fr_obj->fc_obj->fc, fr_obj->fr); free(fr_obj->fr); fr_obj->fr = NULL; Py_DECREF(fr_obj->fc_obj); fr_obj->fc_obj = NULL; } else { #ifdef _DEBUG fprintf(stderr, "_fam: no monitor to cancel.\n"); #endif } Py_INCREF(Py_None); return Py_None; }; static PyObject* _fam_next_event(PyObject* self, PyObject* args) { int rc; _fam_connection_object *fc_obj; _fam_event_object *fe_obj; FAMEvent fe; PyObject *reqid, *hostname, *filename, *userData, *code; fe.fc = NULL; fe.hostname = NULL; fe.userdata = NULL; fc_obj = (_fam_connection_object *)self; if (fc_obj->fc) { fe_obj = PyObject_NEW(_fam_event_object, &_fam_event_type); if (!fe_obj) return NULL; #ifdef _DEBUG fprintf(stderr, "_fam: got fe_obj.\n"); #endif fe_obj->attr = NULL; fe_obj->fc_obj = fc_obj; Py_INCREF(fc_obj); rc = FAMNextEvent(fc_obj->fc, &fe); if (rc == 1) { if (PyObject_SetAttrString((PyObject*)fe_obj, "connection", (PyObject*)fc_obj) < 0) return NULL; #ifdef _DEBUG fprintf(stderr, "_fam: got next event from queue.\n"); #endif reqid = PyInt_FromLong((long)fe.fr.reqnum); if (PyObject_SetAttrString((PyObject*)fe_obj, "requestID", reqid) < 0) return NULL; #ifdef _DEBUG fprintf(stderr, "_fam: got request ID.\n"); #endif filename = PyString_FromString(fe.filename); if (PyObject_SetAttrString((PyObject*)fe_obj, "filename", filename) < 0) return NULL; #ifdef _DEBUG fprintf(stderr, "_fam: got filename %s.\n", fe.filename); #endif userData = (PyObject*)fe.userdata; if (PyObject_SetAttrString((PyObject*)fe_obj, "userData", userData) < 0) return NULL; #ifdef _DEBUG fprintf(stderr, "_fam: got userdata.\n"); #endif code = PyInt_FromLong(fe.code); if (PyObject_SetAttrString((PyObject*)fe_obj, "code", code) < 0) return NULL; fe_obj->code = fe.code; #ifdef _DEBUG fprintf(stderr, "_fam: got code %d: '%s'.\n", fe.code, code2str(fe.code)); #endif if (! fe.hostname) { #ifdef _DEBUG fprintf(stderr, "_fam: null pointer to hostname.\n"); #endif fe.hostname = (char*)malloc(255*sizeof(char)); bzero(fe.hostname,255); #ifdef _DEBUG } else { fprintf(stderr, "_fam: valid pointer to hostname %s.\n", fe.hostname); #endif } #ifdef _DEBUG fprintf(stderr, "_fam: casting hostname to PyString.\n"); #endif hostname = PyString_FromString(fe.hostname); #ifdef _DEBUG fprintf(stderr, "_fam: casted hostname to PyString.\n"); #endif if (PyObject_SetAttrString((PyObject*)fe_obj, "hostname", hostname) < 0) return NULL; #ifdef _DEBUG fprintf(stderr, "_fam: got hostname.\n"); #endif return (PyObject*)fe_obj; } else { PyErr_SetString(PyExc_IOError, "_fam: unable to get next event"); return NULL; } } else { PyErr_SetString(PyExc_RuntimeError, "_fam: no connection for next event"); return NULL; } }; static PyObject* _fam_pending(PyObject* self, PyObject* args) { PyObject *pyval = Py_False; _fam_connection_object *fc_obj; fc_obj = (_fam_connection_object *)self; if (fc_obj->fc) pyval = FAMPending(fc_obj->fc) ? Py_True : Py_False; Py_INCREF(pyval); return pyval; }; static PyObject* _fam_code2str(PyObject* self, PyObject* args) { _fam_event_object *fe_obj; PyObject *rv; char *str; fe_obj = (_fam_event_object *)self; str = code2str(fe_obj->code); if (str) { rv = PyString_FromString(str); } else { PyErr_SetString(PyExc_RuntimeError, "_fam: unknown event code"); rv = NULL; } return rv; } static PyObject* _fam_fileno(PyObject* self, PyObject* args) { _fam_connection_object *fc_obj; PyObject *rv; fc_obj = (_fam_connection_object *)self; rv = PyInt_FromLong(FAMCONNECTION_GETFD(fc_obj->fc)); if (!rv) { PyErr_SetString(PyExc_RuntimeError, "_fam: unable to get file descriptor from connection"); } return rv; } static PyObject* _fam_request_id(PyObject* self, PyObject* args) { _fam_request_object *fr_obj; PyObject *rv; fr_obj = (_fam_request_object *)self; rv = PyInt_FromLong(FAMREQUEST_GETREQNUM(fr_obj->fr)); if (!rv) { PyErr_SetString(PyExc_RuntimeError, "_fam: unable to get ID from request"); } return rv; } void _fam_cleanup(void) { #ifdef _DEBUG fprintf(stderr, "cleaning up _fam.\n"); #endif } static PyMethodDef moduleMethods[] = { { "open", _fam_open, 1, _fam_open__doc__}, { NULL, NULL, 0, NULL} }; static char _fam__doc__[] = "File Alteration Monitor library routines"; void init_fam(void) { PyObject *m, *d; m = Py_InitModule3("_fam", moduleMethods, _fam__doc__); d = PyModule_GetDict(m); add_int(d, Changed); add_int(d, Deleted); add_int(d, StartExecuting); add_int(d, StopExecuting); add_int(d, Created); add_int(d, Moved); add_int(d, Acknowledge); add_int(d, Exists); add_int(d, EndExist); Py_INCREF(&_fam_connection_type); PyModule_AddObject(m, "FAMConnection", (PyObject*)&_fam_connection_type); Py_INCREF(&_fam_request_type); PyModule_AddObject(m, "FAMRequest", (PyObject*)&_fam_request_type); Py_INCREF(&_fam_event_type); PyModule_AddObject(m, "FAMEvent", (PyObject*)&_fam_event_type); #if defined(WIN32) || defined(__CYGWIN__) _fam_connection_type.ob_type = &PyType_Type; _fam_request_type.ob_type = &PyType_Type; #endif if (Py_AtExit(_fam_cleanup)) { fprintf(stderr, "_fam: warining: cleanup procedure not registered.\n"); } };