3 * Bacula common code library interface to Python
5 * Kern Sibbald, November MMIV
11 Copyright (C) 2004-2005 Kern Sibbald
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.
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.
30 #undef _POSIX_C_SOURCE
33 extern char *configfile;
35 /* Imported subroutines */
36 //extern PyMethodDef JobMethods[];
37 extern PyObject *job_getattr(PyObject *self, char *attrname);
38 extern int job_setattr(PyObject *self, char *attrname, PyObject *value);
40 static PyObject *bacula_module = NULL; /* We create this */
41 static PyObject *StartUp_module = NULL; /* We import this */
43 /* These are the daemon events or methods that are defined */
44 static PyObject *JobStart_method = NULL;
45 static PyObject *JobEnd_method = NULL;
46 static PyObject *Exit_method = NULL;
48 static PyObject *set_bacula_events(PyObject *self, PyObject *args);
49 static PyObject *bacula_write(PyObject *self, PyObject *args);
51 PyObject *find_method(PyObject *eventsObject, PyObject *method, const char *name);
53 /* Define Bacula daemon method entry points */
54 static PyMethodDef BaculaMethods[] = {
55 {"set_events", set_bacula_events, METH_VARARGS, "Define Bacula events."},
56 {"write", bacula_write, METH_VARARGS, "Write output."},
57 {NULL, NULL, 0, NULL} /* last item */
60 static char my_version[] = VERSION " " BDATE;
63 * This is a Bacula Job type as defined in Python. We store a pointer
64 * to the jcr. That is all we need, but the user's script may keep
65 * local data attached to this.
67 typedef struct s_JobObject {
72 static PyTypeObject JobType = {
73 PyObject_HEAD_INIT(NULL)
74 /* Other items filled in in code below */
77 /* Return the JCR pointer from the JobObject */
78 JCR *get_jcr_from_PyObject(PyObject *self)
83 return ((JobObject *)self)->jcr;
87 /* Start the interpreter */
88 void init_python_interpreter(const char *progname, const char *scripts,
93 if (!scripts || scripts[0] == 0) {
94 Dmsg1(100, "No script dir. prog=%s\n", module);
97 Dmsg2(100, "Script dir=%s prog=%s\n", scripts, module);
99 Py_SetProgramName((char *)progname);
101 PyEval_InitThreads();
102 bacula_module = Py_InitModule("bacula", BaculaMethods);
103 PyModule_AddStringConstant(bacula_module, "Name", my_name);
104 PyModule_AddStringConstant(bacula_module, "Version", my_version);
105 PyModule_AddStringConstant(bacula_module, "ConfigFile", configfile);
106 PyModule_AddStringConstant(bacula_module, "WorkingDir", (char *)working_directory);
107 if (!bacula_module) {
108 Jmsg0(NULL, M_ERROR_TERM, 0, "Could not initialize Python\n");
110 bsnprintf(buf, sizeof(buf), "import sys\n"
111 "sys.path.append('%s')\n", scripts);
112 if (PyRun_SimpleString(buf) != 0) {
113 Jmsg1(NULL, M_ERROR_TERM, 0, "Could not Run Python string %s\n", buf);
116 /* Explicitly set values we want */
117 JobType.tp_name = "Bacula.Job";
118 JobType.tp_basicsize = sizeof(JobObject);
119 JobType.tp_flags = Py_TPFLAGS_DEFAULT;
120 JobType.tp_doc = "Bacula Job object";
121 JobType.tp_getattr = job_getattr;
122 JobType.tp_setattr = job_setattr;
124 if (PyType_Ready(&JobType) != 0) {
125 Jmsg0(NULL, M_ERROR_TERM, 0, "Could not initialize Python Job type.\n");
128 StartUp_module = PyImport_ImportModule((char *)module);
129 if (!StartUp_module) {
130 Emsg2(M_ERROR, 0, "Could not import Python script %s/%s. Python disabled.\n",
132 if (PyErr_Occurred()) {
134 Dmsg0(000, "Python Import error.\n");
137 PyEval_ReleaseLock();
141 void term_python_interpreter()
143 if (StartUp_module) {
144 Py_XDECREF(StartUp_module);
149 static PyObject *set_bacula_events(PyObject *self, PyObject *args)
153 Dmsg0(100, "In set_bacula_events.\n");
154 if (!PyArg_ParseTuple(args, "O:set_bacula_events", &eObject)) {
157 JobStart_method = find_method(eObject, JobStart_method, "JobStart");
158 JobEnd_method = find_method(eObject, JobEnd_method, "JobEnd");
159 Exit_method = find_method(eObject, Exit_method, "Exit");
166 /* Write text to daemon output */
167 static PyObject *bacula_write(PyObject *self, PyObject *args)
170 if (!PyArg_ParseTuple(args, "s:write", &text)) {
174 Jmsg(NULL, M_INFO, 0, "%s", text);
182 * Check that a method exists and is callable.
184 PyObject *find_method(PyObject *eventsObject, PyObject *method, const char *name)
186 Py_XDECREF(method); /* release old method if any */
187 method = PyObject_GetAttrString(eventsObject, (char *)name);
188 if (method == NULL) {
189 Dmsg1(000, "Python method %s not found\n", name);
190 } else if (PyCallable_Check(method) == 0) {
191 Dmsg1(000, "Python object %s found but not a method.\n", name);
195 Dmsg1(100, "Got method %s\n", name);
202 * Generate and process a Bacula event by importing a Python
203 * module and running it.
205 * Returns: 0 if Python not configured or module not found
209 int generate_daemon_event(JCR *jcr, const char *event)
213 PyObject *result = NULL;
215 if (!StartUp_module) {
216 Dmsg0(100, "No startup module.\n");
220 Dmsg1(100, "event=%s\n", event);
221 PyEval_AcquireLock();
222 if (strcmp(event, "JobStart") == 0) {
223 if (!JobStart_method) {
227 /* Create JCR argument to send to function */
228 pJob = (PyObject *)PyObject_New(JobObject, &JobType);
230 Jmsg(jcr, M_ERROR, 0, "Could not create Python Job Object.\n");
233 ((JobObject *)pJob)->jcr = jcr;
234 bstrncpy(jcr->event, event, sizeof(jcr->event));
235 result = PyObject_CallFunction(JobStart_method, "O", pJob);
236 jcr->event[0] = 0; /* no event in progress */
237 if (result == NULL) {
238 JobStart_method = NULL;
239 if (PyErr_Occurred()) {
241 Dmsg0(000, "Python JobStart error.\n");
243 Jmsg(jcr, M_ERROR, 0, "Python function \"%s\" not found.\n", event);
247 jcr->Python_job = (void *)pJob;
251 } else if (strcmp(event, "JobEnd") == 0) {
252 if (!JobEnd_method || !jcr->Python_job) {
253 stat = 0; /* probably already here */
256 bstrncpy(jcr->event, event, sizeof(jcr->event));
257 Dmsg1(100, "Call daemon event=%s\n", event);
258 result = PyObject_CallFunction(JobEnd_method, "O", jcr->Python_job);
259 jcr->event[0] = 0; /* no event in progress */
260 if (result == NULL) {
261 if (PyErr_Occurred()) {
263 Dmsg2(000, "Python JobEnd error. job=%p JobId=%d\n", jcr->Python_job,
265 JobEnd_method = NULL;
267 Jmsg(jcr, M_ERROR, 0, "Python function \"%s\" not found.\n", event);
271 } else if (strcmp(event, "Exit") == 0) {
276 result = PyObject_CallFunction(Exit_method, NULL);
277 if (result == NULL) {
282 Jmsg1(jcr, M_ABORT, 0, "Unknown Python daemon event %s\n", event);
287 Py_XDECREF((PyObject *)jcr->Python_job);
288 jcr->Python_job = NULL;
289 Py_XDECREF((PyObject *)jcr->Python_events);
290 jcr->Python_events = NULL;
295 PyEval_ReleaseLock();
302 * No Python configured -- create external entry points and
303 * dummy routines so that library code can call this without
304 * problems even if it is not configured.
306 int generate_daemon_event(JCR *jcr, const char *event) { return 0; }
307 void init_python_interpreter(const char *progname, const char *scripts,
308 const char *module) { }
309 void term_python_interpreter() { }
311 #endif /* HAVE_PYTHON */