1 /*
2  * gamin.c: glue to the gamin library for access from Python
3  *
4  * See Copyright for the status of this software.
5  *
6  * veillard@redhat.com
7  */
8 
9 #include <Python.h>
10 #include <fam.h>
11 #include "config.h"
12 
13 #ifdef GAMIN_DEBUG_API
14 int FAMDebug(FAMConnection *fc, const char *filename, FAMRequest * fr,
15              void *userData);
16 #endif
17 
18 void init_gamin(void);
19 
20 static FAMConnection **connections = NULL;
21 static int nb_connections = 0;
22 static int max_connections = 0;
23 
24 static FAMRequest **requests = NULL;
25 static int nb_requests = 0;
26 static int max_requests = 0;
27 
28 static int
get_connection(void)29 get_connection(void) {
30     int i;
31 
32     if (connections == NULL) {
33         max_connections = 10;
34         connections = (FAMConnection **) malloc(max_connections *
35 	                                        sizeof(FAMConnection *));
36 	if (connections == NULL) {
37 	    max_connections = 0;
38 	    return(-1);
39 	}
40 	memset(connections, 0, max_connections * sizeof(FAMConnection *));
41     }
42     for (i = 0;i < max_connections;i++)
43         if (connections[i] == NULL)
44 	    break;
45     if (i >= max_connections) {
46         FAMConnection **tmp;
47 
48 	tmp = (FAMConnection **) realloc(connections, max_connections * 2 *
49 	                                 sizeof(FAMConnection *));
50 	if (tmp == NULL)
51 	    return(-1);
52 	memset(&tmp[max_connections], 0,
53 	       max_connections * sizeof(FAMConnection *));
54 	max_connections *= 2;
55 	connections = tmp;
56     }
57     connections[i] = (FAMConnection *) malloc(sizeof(FAMConnection));
58     if (connections[i] == NULL)
59         return(-1);
60     nb_connections++;
61     return(i);
62 }
63 
64 static int
release_connection(int no)65 release_connection(int no) {
66     if ((no < 0) || (no >= max_connections))
67         return(-1);
68     if (connections[no] == NULL)
69         return(-1);
70     free(connections[no]);
71     connections[no] = NULL;
72     nb_connections--;
73     return(0);
74 }
75 
76 static FAMConnection *
check_connection(int no)77 check_connection(int no) {
78     if ((no < 0) || (no >= max_connections))
79         return(NULL);
80     return(connections[no]);
81 }
82 
83 static int
get_request(void)84 get_request(void) {
85     int i;
86 
87     if (requests == NULL) {
88         max_requests = 10;
89         requests = (FAMRequest **) malloc(max_requests *
90 	                                        sizeof(FAMRequest *));
91 	if (requests == NULL) {
92 	    max_requests = 0;
93 	    return(-1);
94 	}
95 	memset(requests, 0, max_requests * sizeof(FAMRequest *));
96     }
97     for (i = 0;i < max_requests;i++)
98         if (requests[i] == NULL)
99 	    break;
100     if (i >= max_requests) {
101         FAMRequest **tmp;
102 
103 	tmp = (FAMRequest **) realloc(requests, max_requests * 2 *
104 	                                 sizeof(FAMRequest *));
105 	if (tmp == NULL)
106 	    return(-1);
107 	memset(&tmp[max_requests], 0,
108 	       max_requests * sizeof(FAMRequest *));
109 	max_requests *= 2;
110 	requests = tmp;
111     }
112     requests[i] = (FAMRequest *) malloc(sizeof(FAMRequest));
113     if (requests[i] == NULL)
114         return(-1);
115     nb_requests++;
116     return(i);
117 }
118 
119 static int
release_request(int no)120 release_request(int no) {
121     if ((no < 0) || (no >= max_requests))
122         return(-1);
123     if (requests[no] == NULL)
124         return(-1);
125     free(requests[no]);
126     requests[no] = NULL;
127     nb_requests--;
128     return(0);
129 }
130 
131 static FAMRequest *
check_request(int no)132 check_request(int no) {
133     if ((no < 0) || (no >= max_requests))
134         return(NULL);
135     return(requests[no]);
136 }
137 
fam_connect(void)138 static int fam_connect(void) {
139     int ret;
140     int no = get_connection();
141     FAMConnection *conn;
142 
143     if (no < 0)
144         return(-1);
145     conn = connections[no];
146     if (conn == NULL)
147         return(-1);
148     ret = FAMOpen2(conn, "gamin-python");
149     if (ret < 0) {
150         release_connection(no);
151         return(ret);
152     }
153     return(no);
154 }
155 
156 static int
call_internal_callback(PyObject * self,const char * filename,FAMCodes event)157 call_internal_callback(PyObject *self, const char *filename, FAMCodes event) {
158     PyObject *ret;
159 
160     if ((self == NULL) || (filename == NULL))
161         return(-1);
162 /*    fprintf(stderr, "call_internal_callback(%p)\n", self); */
163     ret = PyEval_CallMethod(self, (char *) "_internal_callback",
164                             (char *) "(zi)", filename, (int) event);
165     if (ret != NULL) {
166         Py_DECREF(ret);
167 #if 0
168     } else {
169 	fprintf(stderr, "call_internal_callback() failed\n");
170 #endif
171     }
172     return(0);
173 }
174 
175 static PyObject *
gamin_Errno(PyObject * self,PyObject * args)176 gamin_Errno(PyObject *self, PyObject * args) {
177     return(PyInt_FromLong(FAMErrno));
178 }
179 
180 static PyObject *
gamin_MonitorConnect(PyObject * self,PyObject * args)181 gamin_MonitorConnect(PyObject *self, PyObject * args) {
182     int ret;
183 
184     ret = fam_connect();
185     if (ret < 0) {
186 	return(PyInt_FromLong(-1));
187     }
188     return(PyInt_FromLong(ret));
189 }
190 
191 static PyObject *
gamin_GetFd(PyObject * self,PyObject * args)192 gamin_GetFd(PyObject *self, PyObject * args) {
193     int no;
194     FAMConnection *conn;
195 
196     if (!PyArg_ParseTuple(args, (char *)"i:GetFd", &no))
197 	return(NULL);
198 
199     conn = check_connection(no);
200     if (conn == NULL) {
201 	return(PyInt_FromLong(-1));
202     }
203     return(PyInt_FromLong(FAMCONNECTION_GETFD(conn)));
204 }
205 
206 static PyObject *
gamin_MonitorClose(PyObject * self,PyObject * args)207 gamin_MonitorClose(PyObject *self, PyObject * args) {
208     int no;
209     int ret;
210 
211     if (!PyArg_ParseTuple(args, (char *)"i:MonitorClose", &no))
212 	return(NULL);
213 
214     ret = release_connection(no);
215     return(PyInt_FromLong(ret));
216 }
217 
218 static PyObject *
gamin_ProcessOneEvent(PyObject * self,PyObject * args)219 gamin_ProcessOneEvent(PyObject *self, PyObject * args) {
220     int ret;
221     FAMEvent fe;
222     int no;
223     FAMConnection *conn;
224 
225     if (!PyArg_ParseTuple(args, (char *)"i:ProcessOneEvent", &no))
226 	return(NULL);
227 
228     conn = check_connection(no);
229     if (conn == NULL) {
230 	return(PyInt_FromLong(-1));
231     }
232 
233     ret = FAMNextEvent(conn, &fe);
234     if (ret < 0) {
235 	return(PyInt_FromLong(-1));
236     }
237     call_internal_callback(fe.userdata, &fe.filename[0], fe.code);
238 
239     return(PyInt_FromLong(ret));
240 }
241 
242 static PyObject *
gamin_ProcessEvents(PyObject * self,PyObject * args)243 gamin_ProcessEvents(PyObject *self, PyObject * args) {
244     int ret;
245     int nb = 0;
246     FAMEvent fe;
247     int no;
248     FAMConnection *conn;
249 
250     if (!PyArg_ParseTuple(args, (char *)"i:ProcessOneEvent", &no))
251 	return(NULL);
252 
253     conn = check_connection(no);
254     if (conn == NULL) {
255 	return(PyInt_FromLong(-1));
256     }
257 
258     do {
259 	ret = FAMPending(conn);
260 	if (ret < 0)
261 	    return(PyInt_FromLong(-1));
262 	if (ret == 0)
263 	    break;
264 	ret = FAMNextEvent(conn, &fe);
265 	if (ret < 0)
266 	    return(PyInt_FromLong(-1));
267 	call_internal_callback(fe.userdata, &fe.filename[0], fe.code);
268 	nb++;
269     } while (ret >= 0);
270 
271     return(PyInt_FromLong(nb));
272 }
273 
274 static PyObject *
gamin_EventPending(PyObject * self,PyObject * args)275 gamin_EventPending(PyObject *self, PyObject * args) {
276     int no;
277     FAMConnection *conn;
278 
279     if (!PyArg_ParseTuple(args, (char *)"i:ProcessOneEvent", &no))
280 	return(NULL);
281 
282     conn = check_connection(no);
283     if (conn == NULL) {
284 	return(PyInt_FromLong(-1));
285     }
286     return(PyInt_FromLong(FAMPending(conn)));
287 }
288 
289 static PyObject *
gamin_MonitorNoExists(PyObject * self,PyObject * args)290 gamin_MonitorNoExists(PyObject *self, PyObject * args) {
291     int no;
292     FAMConnection *conn;
293 
294     if (!PyArg_ParseTuple(args, (char *)"i:MonitorNoExists", &no))
295 	return(NULL);
296 
297     conn = check_connection(no);
298     if (conn == NULL) {
299 	return(PyInt_FromLong(-1));
300     }
301     return(PyInt_FromLong(FAMNoExists(conn)));
302 }
303 
304 static PyObject *
gamin_MonitorDirectory(PyObject * self,PyObject * args)305 gamin_MonitorDirectory(PyObject *self, PyObject * args) {
306     PyObject *userdata;
307     char * filename;
308     int ret;
309     int noreq;
310     int no;
311     FAMConnection *conn;
312     FAMRequest *request;
313 
314     if (!PyArg_ParseTuple(args, (char *)"izO:MonitorDirectory",
315         &no, &filename, &userdata))
316 	return(NULL);
317 
318     conn = check_connection(no);
319     if (conn == NULL) {
320 	return(PyInt_FromLong(-1));
321     }
322     noreq = get_request();
323     if (noreq < 0) {
324 	return(PyInt_FromLong(-1));
325     }
326     request = check_request(noreq);
327 
328     ret = FAMMonitorDirectory(conn, filename, request, userdata);
329     if (ret < 0) {
330         release_request(noreq);
331 	return(PyInt_FromLong(-1));
332     }
333     return(PyInt_FromLong(noreq));
334 }
335 
336 static PyObject *
gamin_MonitorFile(PyObject * self,PyObject * args)337 gamin_MonitorFile(PyObject *self, PyObject * args) {
338     PyObject *userdata;
339     char * filename;
340     int ret;
341     int noreq;
342     int no;
343     FAMConnection *conn;
344     FAMRequest *request;
345 
346     if (!PyArg_ParseTuple(args, (char *)"izO:MonitorFile",
347         &no, &filename, &userdata))
348 	return(NULL);
349 
350     conn = check_connection(no);
351     if (conn == NULL) {
352 	return(PyInt_FromLong(-1));
353     }
354     noreq = get_request();
355     if (noreq < 0) {
356 	return(PyInt_FromLong(-1));
357     }
358     request = check_request(noreq);
359 
360     ret = FAMMonitorFile(conn, filename, request, userdata);
361     if (ret < 0) {
362         release_request(noreq);
363 	return(PyInt_FromLong(-1));
364     }
365     return(PyInt_FromLong(noreq));
366 }
367 
368 static PyObject *
gamin_MonitorCancel(PyObject * self,PyObject * args)369 gamin_MonitorCancel(PyObject *self, PyObject * args) {
370     int ret;
371     int noreq;
372     int no;
373     FAMConnection *conn;
374     FAMRequest *request;
375 
376     if (!PyArg_ParseTuple(args, (char *)"ii:MonitorCancel",
377         &no, &noreq))
378 	return(NULL);
379 
380     conn = check_connection(no);
381     if (conn == NULL) {
382 	return(PyInt_FromLong(-1));
383     }
384     request = check_request(noreq);
385     if (request == NULL) {
386 	return(PyInt_FromLong(-1));
387     }
388 
389     ret = FAMCancelMonitor(conn, request);
390     if (ret < 0) {
391         release_request(noreq);
392 	return(PyInt_FromLong(-1));
393     }
394     return(PyInt_FromLong(ret));
395 }
396 
397 #ifdef GAMIN_DEBUG_API
398 static PyObject *
gamin_MonitorDebug(PyObject * self,PyObject * args)399 gamin_MonitorDebug(PyObject *self, PyObject * args) {
400     PyObject *userdata;
401     char * filename;
402     int ret;
403     int noreq;
404     int no;
405     FAMConnection *conn;
406     FAMRequest *request;
407 
408     if (!PyArg_ParseTuple(args, (char *)"izO:MonitorDebug",
409         &no, &filename, &userdata))
410 	return(NULL);
411 
412     conn = check_connection(no);
413     if (conn == NULL) {
414 	return(PyInt_FromLong(-1));
415     }
416     noreq = get_request();
417     if (noreq < 0) {
418 	return(PyInt_FromLong(-1));
419     }
420     request = check_request(noreq);
421 
422     ret = FAMDebug(conn, filename, request, userdata);
423     if (ret < 0) {
424         release_request(noreq);
425 	return(PyInt_FromLong(-1));
426     }
427     return(PyInt_FromLong(noreq));
428 }
429 #endif
430 
431 static PyMethodDef gaminMethods[] = {
432     {(char *)"MonitorConnect", gamin_MonitorConnect, METH_VARARGS, NULL},
433     {(char *)"MonitorDirectory", gamin_MonitorDirectory, METH_VARARGS, NULL},
434     {(char *)"MonitorFile", gamin_MonitorFile, METH_VARARGS, NULL},
435     {(char *)"MonitorCancel", gamin_MonitorCancel, METH_VARARGS, NULL},
436     {(char *)"MonitorNoExists", gamin_MonitorNoExists, METH_VARARGS, NULL},
437     {(char *)"EventPending", gamin_EventPending, METH_VARARGS, NULL},
438     {(char *)"ProcessOneEvent", gamin_ProcessOneEvent, METH_VARARGS, NULL},
439     {(char *)"ProcessEvents", gamin_ProcessEvents, METH_VARARGS, NULL},
440     {(char *)"MonitorClose", gamin_MonitorClose, METH_VARARGS, NULL},
441     {(char *)"GetFd", gamin_GetFd, METH_VARARGS, NULL},
442     {(char *)"Errno", gamin_Errno, METH_VARARGS, NULL},
443 #ifdef GAMIN_DEBUG_API
444     {(char *)"MonitorDebug", gamin_MonitorDebug, METH_VARARGS, NULL},
445 #endif
446     {NULL, NULL, 0, NULL}
447 };
448 
449 void
init_gamin(void)450 init_gamin(void)
451 {
452     static int initialized = 0;
453 
454     if (initialized != 0)
455         return;
456 
457     /* intialize the python extension module */
458     Py_InitModule((char *) "_gamin", gaminMethods);
459 
460     initialized = 1;
461 }
462 
463