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