]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/pythonlib.c
04Jul05
[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-2005 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 ammended 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 /* Imported subroutines */
34 //extern PyMethodDef JobMethods[];
35 extern PyObject *job_getattr(PyObject *self, char *attrname);
36 extern int job_setattr(PyObject *self, char *attrname, PyObject *value);
37
38 static PyObject *bacula_module = NULL;    /* We create this */
39 static PyObject *StartUp_module = NULL;   /* We import this */
40
41 /* These are the daemon events or methods that are defined */
42 static PyObject *JobStart_method = NULL;
43 static PyObject *JobEnd_method = NULL;
44 static PyObject *Exit_method = NULL;
45
46 static PyObject *set_bacula_events(PyObject *self, PyObject *args);
47 static PyObject *bacula_write(PyObject *self, PyObject *args);
48
49 PyObject *find_method(PyObject *eventsObject, PyObject *method, const char *name);
50
51 /* Define Bacula daemon method entry points */
52 static PyMethodDef BaculaMethods[] = {
53     {"set_events", set_bacula_events, METH_VARARGS, "Define Bacula events."},
54     {"write", bacula_write, METH_VARARGS, "Write output."},
55     {NULL, NULL, 0, NULL}             /* last item */
56 };
57
58
59 /*
60  * This is a Bacula Job type as defined in Python. We store a pointer
61  *   to the jcr. That is all we need, but the user's script may keep
62  *   local data attached to this. 
63  */
64 typedef struct s_JobObject {
65     PyObject_HEAD
66     JCR *jcr;
67 } JobObject;
68
69 static PyTypeObject JobType = {
70     PyObject_HEAD_INIT(NULL)
71     /* Other items filled in in code below */
72 };
73
74 /* Return the JCR pointer from the JobObject */
75 JCR *get_jcr_from_PyObject(PyObject *self)
76 {
77    if (!self) {
78       return NULL;
79    }
80    return ((JobObject *)self)->jcr;
81 }
82
83
84 /* Start the interpreter */
85 void init_python_interpreter(const char *progname, const char *scripts,
86     const char *module)
87 {
88    char buf[MAXSTRING];
89
90    if (!scripts || scripts[0] == 0) {
91       Dmsg1(100, "No script dir. prog=%s\n", module);
92       return;
93    }
94    Dmsg2(100, "Script dir=%s prog=%s\n", scripts, module);
95
96    Py_SetProgramName((char *)progname);
97    Py_Initialize();
98    PyEval_InitThreads();
99    bacula_module = Py_InitModule("bacula", BaculaMethods);
100    PyModule_AddStringConstant(bacula_module, "name", my_name);
101    if (!bacula_module) {
102       Jmsg0(NULL, M_ERROR_TERM, 0, "Could not initialize Python\n");
103    }
104    bsnprintf(buf, sizeof(buf), "import sys\n"
105             "sys.path.append('%s')\n", scripts);
106    if (PyRun_SimpleString(buf) != 0) {
107       Jmsg1(NULL, M_ERROR_TERM, 0, "Could not Run Python string %s\n", buf);
108    }   
109
110    /* Explicitly set values we want */
111    JobType.tp_name = "Bacula.Job";
112    JobType.tp_basicsize = sizeof(JobObject);
113    JobType.tp_flags = Py_TPFLAGS_DEFAULT;
114    JobType.tp_doc = "Bacula Job object";
115    JobType.tp_getattr = job_getattr;
116    JobType.tp_setattr = job_setattr;
117
118    if (PyType_Ready(&JobType) != 0) {
119       Jmsg0(NULL, M_ERROR_TERM, 0, "Could not initialize Python Job type.\n");
120       PyErr_Print();
121    }   
122    StartUp_module = PyImport_ImportModule((char *)module);
123    if (!StartUp_module) {
124       Emsg2(M_ERROR, 0, "Could not import Python script %s/%s. Python disabled.\n",
125            scripts, module);
126       if (PyErr_Occurred()) {
127          PyErr_Print();
128          Dmsg0(000, "Python Import error.\n");
129       }
130    }
131    PyEval_ReleaseLock();
132 }
133
134
135 void term_python_interpreter()
136 {
137    if (StartUp_module) {
138       Py_XDECREF(StartUp_module);
139       Py_Finalize();
140    }
141 }
142
143 static PyObject *set_bacula_events(PyObject *self, PyObject *args)
144 {
145    PyObject *eObject;
146
147    Dmsg0(100, "In set_bacula_events.\n");
148    if (!PyArg_ParseTuple(args, "O:set_bacula_events", &eObject)) {
149       return NULL;
150    }
151    JobStart_method = find_method(eObject, JobStart_method, "JobStart");
152    JobEnd_method = find_method(eObject, JobEnd_method, "JobEnd");
153    Exit_method = find_method(eObject, Exit_method, "Exit");
154
155    Py_XINCREF(eObject);
156    Py_INCREF(Py_None);
157    return Py_None;
158 }
159
160 /* Write text to daemon output */
161 static PyObject *bacula_write(PyObject *self, PyObject *args)
162 {
163    char *text;
164    if (!PyArg_ParseTuple(args, "s:write", &text)) {
165       return NULL;
166    }
167    if (text) {
168       Jmsg(NULL, M_INFO, 0, "%s", text);
169    }
170    Py_INCREF(Py_None);
171    return Py_None;
172 }
173
174
175 /*
176  * Check that a method exists and is callable.
177  */
178 PyObject *find_method(PyObject *eventsObject, PyObject *method, const char *name)
179 {
180    Py_XDECREF(method);             /* release old method if any */
181    method = PyObject_GetAttrString(eventsObject, (char *)name);
182    if (method == NULL) {
183        Dmsg1(000, "Python method %s not found\n", name);
184    } else if (PyCallable_Check(method) == 0) {
185        Dmsg1(000, "Python object %s found but not a method.\n", name);
186        Py_XDECREF(method);
187        method = NULL;
188    } else {
189        Dmsg1(100, "Got method %s\n", name);
190    }
191    return method; 
192 }
193
194
195 /*
196  * Generate and process a Bacula event by importing a Python
197  *  module and running it.
198  *
199  *  Returns: 0 if Python not configured or module not found
200  *          -1 on Python error
201  *           1 OK
202  */
203 int generate_daemon_event(JCR *jcr, const char *event)
204 {
205    PyObject *pJob;
206    int stat = -1;
207    PyObject *result = NULL;
208
209    if (!StartUp_module) {
210       Dmsg0(100, "No startup module.\n");
211       return 0;
212    }
213
214    Dmsg1(100, "event=%s\n", event);
215    PyEval_AcquireLock();
216    if (strcmp(event, "JobStart") == 0) {
217       if (!JobStart_method) {
218          stat = 0;
219          goto bail_out;
220       }
221       /* Create JCR argument to send to function */
222       pJob = (PyObject *)PyObject_New(JobObject, &JobType);
223       if (!pJob) {
224          Jmsg(jcr, M_ERROR, 0, "Could not create Python Job Object.\n");
225          goto bail_out;
226       }
227       ((JobObject *)pJob)->jcr = jcr;
228       bstrncpy(jcr->event, event, sizeof(jcr->event));
229       result = PyObject_CallFunction(JobStart_method, "O", pJob);
230       jcr->event[0] = 0;             /* no event in progress */
231       if (result == NULL) {
232          JobStart_method = NULL;
233          if (PyErr_Occurred()) {
234             PyErr_Print();
235             Dmsg0(000, "Python JobStart error.\n");
236          }
237          Jmsg(jcr, M_ERROR, 0, "Python function \"%s\" not found.\n", event);
238          Py_XDECREF(pJob);
239          goto bail_out;
240       }
241       jcr->Python_job = (void *)pJob;
242       stat = 1;                       /* OK */
243       goto jobstart_ok;
244
245    } else if (strcmp(event, "JobEnd") == 0) {
246       if (!JobEnd_method || !jcr->Python_job) {
247          stat = 0;                    /* probably already here */
248          goto bail_out;
249       }
250       bstrncpy(jcr->event, event, sizeof(jcr->event));
251       Dmsg1(100, "Call daemon event=%s\n", event);
252       result = PyObject_CallFunction(JobEnd_method, "O", jcr->Python_job);
253       jcr->event[0] = 0;             /* no event in progress */
254       if (result == NULL) {
255          if (PyErr_Occurred()) {
256             PyErr_Print();
257             Dmsg2(000, "Python JobEnd error. job=%p JobId=%d\n", jcr->Python_job,
258                jcr->JobId);
259             JobEnd_method = NULL;
260          }
261          Jmsg(jcr, M_ERROR, 0, "Python function \"%s\" not found.\n", event);
262          goto bail_out;
263       }
264       stat = 1;                    /* OK */
265    } else if (strcmp(event, "Exit") == 0) {
266       if (!Exit_method) {
267          stat = 0;
268          goto bail_out;
269       }
270       result = PyObject_CallFunction(Exit_method, NULL);
271       if (result == NULL) {
272          goto bail_out;
273       }
274       stat = 1;                    /* OK */
275    } else {
276       Jmsg1(jcr, M_ABORT, 0, "Unknown Python daemon event %s\n", event);
277    }
278
279 bail_out:
280    if (jcr) {
281       Py_XDECREF((PyObject *)jcr->Python_job);
282       jcr->Python_job = NULL;
283       Py_XDECREF((PyObject *)jcr->Python_events);
284       jcr->Python_events = NULL;
285    }
286    /* Fall through */
287 jobstart_ok:
288    Py_XDECREF(result);
289    PyEval_ReleaseLock();
290    return stat; 
291 }
292
293 #else
294
295 /*
296  *  No Python configured -- create external entry points and
297  *    dummy routines so that library code can call this without
298  *    problems even if it is not configured.
299  */
300 int generate_daemon_event(JCR *jcr, const char *event) { return 0; }
301 void init_python_interpreter(const char *progname, const char *scripts,
302          const char *module) { }
303 void term_python_interpreter() { }
304
305 #endif /* HAVE_PYTHON */