]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/pythonlib.c
- Remove the -f option from the chown in Makefile.in for more
[bacula/bacula] / bacula / src / lib / pythonlib.c
1 /*
2  *
3  * Bacula common code library interface to Python
4  *
5  * Kern Sibbald, November MMIV
6  *
7  *   Version $Id$
8  *
9  */
10 /*
11    Copyright (C) 2004-2006 Kern Sibbald
12
13    This program is free software; you can redistribute it and/or
14    modify it under the terms of the GNU General Public License
15    version 2 as amended with additional clauses defined in the
16    file LICENSE in the main source directory.
17
18    This program is distributed in the hope that it will be useful,
19    but WITHOUT ANY WARRANTY; without even the implied warranty of
20    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
21    the file LICENSE for additional details.
22
23  */
24
25 #include "bacula.h"
26 #include "jcr.h"
27
28 #ifdef HAVE_PYTHON
29
30 #undef _POSIX_C_SOURCE
31 #include <Python.h>
32
33 /* Forward referenced subroutines */
34 static void init_python_lock();
35 static void term_python_lock();
36 void lock_python();
37 void unlock_python();
38
39 extern char *configfile;
40
41 /* Imported subroutines */
42 //extern PyMethodDef JobMethods[];
43 extern PyObject *job_getattr(PyObject *self, char *attrname);
44 extern int job_setattr(PyObject *self, char *attrname, PyObject *value);
45
46 static PyObject *bacula_module = NULL;    /* We create this */
47 static PyObject *StartUp_module = NULL;   /* We import this */
48
49 /* These are the daemon events or methods that are defined */
50 static PyObject *JobStart_method = NULL;
51 static PyObject *JobEnd_method = NULL;
52 static PyObject *Exit_method = NULL;
53
54 static PyObject *set_bacula_events(PyObject *self, PyObject *args);
55 static PyObject *bacula_write(PyObject *self, PyObject *args);
56
57 PyObject *find_method(PyObject *eventsObject, PyObject *method, const char *name);
58
59 /* Define Bacula daemon method entry points */
60 static PyMethodDef BaculaMethods[] = {
61     {"set_events", set_bacula_events, METH_VARARGS, "Define Bacula events."},
62     {"write", bacula_write, METH_VARARGS, "Write output."},
63     {NULL, NULL, 0, NULL}             /* last item */
64 };
65
66 static char my_version[] = VERSION " " BDATE;
67
68 /*
69  * This is a Bacula Job type as defined in Python. We store a pointer
70  *   to the jcr. That is all we need, but the user's script may keep
71  *   local data attached to this. 
72  */
73 typedef struct s_JobObject {
74     PyObject_HEAD
75     JCR *jcr;
76 } JobObject;
77
78 static PyTypeObject JobType = {
79     PyObject_HEAD_INIT(NULL)
80     /* Other items filled in in code below */
81 };
82
83 /* Return the JCR pointer from the JobObject */
84 JCR *get_jcr_from_PyObject(PyObject *self)
85 {
86    if (!self) {
87       return NULL;
88    }
89    return ((JobObject *)self)->jcr;
90 }
91
92
93 /* Start the interpreter */
94 void init_python_interpreter(const char *progname, const char *scripts,
95     const char *module)
96 {
97    char buf[MAXSTRING];
98
99    if (!scripts || scripts[0] == 0) {
100       Dmsg1(100, "No script dir. prog=%s\n", module);
101       return;
102    }
103    Dmsg2(100, "Script dir=%s prog=%s\n", scripts, module);
104
105    Py_SetProgramName((char *)progname);
106    Py_Initialize();
107    PyEval_InitThreads();
108    bacula_module = Py_InitModule("bacula", BaculaMethods);
109    PyModule_AddStringConstant(bacula_module, "Name", my_name);
110    PyModule_AddStringConstant(bacula_module, "Version", my_version);
111    PyModule_AddStringConstant(bacula_module, "ConfigFile", configfile);
112    PyModule_AddStringConstant(bacula_module, "WorkingDir", (char *)working_directory);
113    if (!bacula_module) {
114       Jmsg0(NULL, M_ERROR_TERM, 0, _("Could not initialize Python\n"));
115    }
116    bsnprintf(buf, sizeof(buf), "import sys\n"
117             "sys.path.append('%s')\n", scripts);
118    if (PyRun_SimpleString(buf) != 0) {
119       Jmsg1(NULL, M_ERROR_TERM, 0, _("Could not Run Python string %s\n"), buf);
120    }   
121
122    /* Explicitly set values we want */
123    JobType.tp_name = "Bacula.Job";
124    JobType.tp_basicsize = sizeof(JobObject);
125    JobType.tp_flags = Py_TPFLAGS_DEFAULT;
126    JobType.tp_doc = "Bacula Job object";
127    JobType.tp_getattr = job_getattr;
128    JobType.tp_setattr = job_setattr;
129
130    if (PyType_Ready(&JobType) != 0) {
131       Jmsg0(NULL, M_ERROR_TERM, 0, _("Could not initialize Python Job type.\n"));
132       PyErr_Print();
133    }   
134    StartUp_module = PyImport_ImportModule((char *)module);
135    if (!StartUp_module) {
136       Emsg2(M_ERROR, 0, _("Could not import Python script %s/%s. Python disabled.\n"),
137            scripts, module);
138       if (PyErr_Occurred()) {
139          PyErr_Print();
140          Dmsg0(000, "Python Import error.\n");
141       }
142    }
143    PyEval_ReleaseLock();
144    init_python_lock();
145 }
146
147
148 void term_python_interpreter()
149 {
150    if (StartUp_module) {
151       Py_XDECREF(StartUp_module);
152       Py_Finalize();
153    }
154    term_python_lock();
155 }
156
157 static PyObject *set_bacula_events(PyObject *self, PyObject *args)
158 {
159    PyObject *eObject;
160
161    Dmsg0(100, "In set_bacula_events.\n");
162    if (!PyArg_ParseTuple(args, "O:set_bacula_events", &eObject)) {
163       return NULL;
164    }
165    JobStart_method = find_method(eObject, JobStart_method, "JobStart");
166    JobEnd_method = find_method(eObject, JobEnd_method, "JobEnd");
167    Exit_method = find_method(eObject, Exit_method, "Exit");
168
169    Py_XINCREF(eObject);
170    Py_INCREF(Py_None);
171    return Py_None;
172 }
173
174 /* Write text to daemon output */
175 static PyObject *bacula_write(PyObject *self, PyObject *args)
176 {
177    char *text;
178    if (!PyArg_ParseTuple(args, "s:write", &text)) {
179       return NULL;
180    }
181    if (text) {
182       Jmsg(NULL, M_INFO, 0, "%s", text);
183    }
184    Py_INCREF(Py_None);
185    return Py_None;
186 }
187
188
189 /*
190  * Check that a method exists and is callable.
191  */
192 PyObject *find_method(PyObject *eventsObject, PyObject *method, const char *name)
193 {
194    Py_XDECREF(method);             /* release old method if any */
195    method = PyObject_GetAttrString(eventsObject, (char *)name);
196    if (method == NULL) {
197        Dmsg1(000, "Python method %s not found\n", name);
198    } else if (PyCallable_Check(method) == 0) {
199        Dmsg1(000, "Python object %s found but not a method.\n", name);
200        Py_XDECREF(method);
201        method = NULL;
202    } else {
203        Dmsg1(100, "Got method %s\n", name);
204    }
205    return method; 
206 }
207
208
209 /*
210  * Generate and process a Bacula event by importing a Python
211  *  module and running it.
212  *
213  *  Returns: 0 if Python not configured or module not found
214  *          -1 on Python error
215  *           1 OK
216  */
217 int generate_daemon_event(JCR *jcr, const char *event)
218 {
219    PyObject *pJob;
220    int stat = -1;
221    PyObject *result = NULL;
222
223    if (!StartUp_module) {
224       Dmsg0(100, "No startup module.\n");
225       return 0;
226    }
227
228    Dmsg1(100, "event=%s\n", event);
229    lock_python();
230 // PyEval_AcquireLock();
231    if (strcmp(event, "JobStart") == 0) {
232       if (!JobStart_method) {
233          stat = 0;
234          goto bail_out;
235       }
236       /* Create JCR argument to send to function */
237       pJob = (PyObject *)PyObject_New(JobObject, &JobType);
238       if (!pJob) {
239          Jmsg(jcr, M_ERROR, 0, _("Could not create Python Job Object.\n"));
240          goto bail_out;
241       }
242       ((JobObject *)pJob)->jcr = jcr;
243       bstrncpy(jcr->event, event, sizeof(jcr->event));
244       result = PyObject_CallFunction(JobStart_method, "O", pJob);
245       jcr->event[0] = 0;             /* no event in progress */
246       if (result == NULL) {
247          JobStart_method = NULL;
248          if (PyErr_Occurred()) {
249             PyErr_Print();
250             Dmsg0(000, "Python JobStart error.\n");
251          }
252          Jmsg(jcr, M_ERROR, 0, _("Python function \"%s\" not found.\n"), event);
253          Py_XDECREF(pJob);
254          goto bail_out;
255       }
256       jcr->Python_job = (void *)pJob;
257       stat = 1;                       /* OK */
258       goto jobstart_ok;
259
260    } else if (strcmp(event, "JobEnd") == 0) {
261       if (!JobEnd_method || !jcr->Python_job) {
262          stat = 0;                    /* probably already here */
263          goto bail_out;
264       }
265       bstrncpy(jcr->event, event, sizeof(jcr->event));
266       Dmsg1(100, "Call daemon event=%s\n", event);
267       result = PyObject_CallFunction(JobEnd_method, "O", jcr->Python_job);
268       jcr->event[0] = 0;             /* no event in progress */
269       if (result == NULL) {
270          if (PyErr_Occurred()) {
271             PyErr_Print();
272             Dmsg2(000, "Python JobEnd error. job=%p JobId=%d\n", jcr->Python_job,
273                jcr->JobId);
274             JobEnd_method = NULL;
275          }
276          Jmsg(jcr, M_ERROR, 0, _("Python function \"%s\" not found.\n"), event);
277          goto bail_out;
278       }
279       stat = 1;                    /* OK */
280    } else if (strcmp(event, "Exit") == 0) {
281       if (!Exit_method) {
282          stat = 0;
283          goto bail_out;
284       }
285       result = PyObject_CallFunction(Exit_method, NULL);
286       if (result == NULL) {
287          goto bail_out;
288       }
289       stat = 1;                    /* OK */
290    } else {
291       Jmsg1(jcr, M_ABORT, 0, _("Unknown Python daemon event %s\n"), event);
292    }
293
294 bail_out:
295    if (jcr) {
296       Py_XDECREF((PyObject *)jcr->Python_job);
297       jcr->Python_job = NULL;
298       Py_XDECREF((PyObject *)jcr->Python_events);
299       jcr->Python_events = NULL;
300    }
301    /* Fall through */
302 jobstart_ok:
303    Py_XDECREF(result);
304    unlock_python();
305 // PyEval_ReleaseLock();
306    return stat; 
307 }
308
309 static brwlock_t python_rwlock;
310
311 static void init_python_lock()
312 {
313    int errstat;
314    if ((errstat=rwl_init(&python_rwlock)) != 0) {
315       berrno be;
316       Emsg1(M_ABORT, 0, _("Unable to initialize the Python lock. ERR=%s\n"),
317             be.strerror(errstat));
318    }
319
320 }
321
322 static void term_python_lock()
323 {
324    rwl_destroy(&python_rwlock);
325 }
326
327 /* This applies to a drive and to Volumes */
328 void lock_python()
329 {
330    int errstat;
331    if ((errstat=rwl_writelock(&python_rwlock)) != 0) {
332       berrno be;
333       Emsg2(M_ABORT, 0, "Python rwl_writelock failure. stat=%d: ERR=%s\n",
334            errstat, be.strerror(errstat));
335    }
336 }
337
338 void unlock_python()
339 {
340    int errstat;
341    if ((errstat=rwl_writeunlock(&python_rwlock)) != 0) {
342       berrno be;
343       Emsg2(M_ABORT, 0, "Python rwl_writeunlock failure. stat=%d: ERR=%s\n",
344            errstat, be.strerror(errstat));
345    }
346 }
347
348
349 #else
350
351 /*
352  *  No Python configured -- create external entry points and
353  *    dummy routines so that library code can call this without
354  *    problems even if it is not configured.
355  */
356 int generate_daemon_event(JCR *jcr, const char *event) { return 0; }
357 void init_python_interpreter(const char *progname, const char *scripts,
358          const char *module) { }
359 void term_python_interpreter() { }
360
361 #endif /* HAVE_PYTHON */