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