3 * Bacula interface to Python for the Director
5 * Kern Sibbald, November MMIV
11 Bacula® - The Network Backup Solution
13 Copyright (C) 2004-2006 Free Software Foundation Europe e.V.
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 and included
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.
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
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.
42 #undef _POSIX_C_SOURCE
45 extern char *configfile;
46 extern struct s_jl joblevels[];
47 extern JCR *get_jcr_from_PyObject(PyObject *self);
48 extern PyObject *find_method(PyObject *eventsObject, PyObject *method,
52 static PyObject *set_job_events(PyObject *self, PyObject *arg);
53 static PyObject *job_run(PyObject *self, PyObject *arg);
54 static PyObject *job_write(PyObject *self, PyObject *arg);
55 static PyObject *job_cancel(PyObject *self, PyObject *arg);
56 static PyObject *job_does_vol_exist(PyObject *self, PyObject *arg);
58 PyMethodDef JobMethods[] = {
59 {"set_events", set_job_events, METH_VARARGS, "Set Job events"},
60 {"run", job_run, METH_VARARGS, "Run a Job"},
61 {"write", job_write, METH_VARARGS, "Write to output"},
62 {"cancel", job_cancel, METH_VARARGS, "Cancel a Job"},
63 {"DoesVolumeExist", job_does_vol_exist, METH_VARARGS, "Does Volume Exist"},
64 {NULL, NULL, 0, NULL} /* last item */
73 /* Read-only variables */
74 static struct s_vars getvars[] = {
89 { "CatalogRes", "(sssssis)"},
100 /* Writable variables */
101 static struct s_vars setvars[] = {
103 { "VolumeName", "s"},
111 /* Return Job variables */
112 /* Returns: NULL if error
113 * PyObject * return value if OK
115 PyObject *job_getattr(PyObject *self, char *attrname)
123 Dmsg0(100, "In job_getattr.\n");
124 jcr = get_jcr_from_PyObject(self);
126 bstrncpy(errmsg, _("Job pointer not found."), sizeof(errmsg));
129 for (i=0; getvars[i].name; i++) {
130 if (strcmp(getvars[i].name, attrname) == 0) {
136 /* Try our methods */
137 return Py_FindMethod(JobMethods, self, attrname);
141 return Py_BuildValue(getvars[i].fmt, jcr->job->hdr.name);
143 return Py_BuildValue(getvars[i].fmt, job_level_to_str(jcr->JobLevel));
145 return Py_BuildValue(getvars[i].fmt, job_type_to_str(jcr->JobType));
147 return Py_BuildValue(getvars[i].fmt, jcr->JobId);
149 return Py_BuildValue(getvars[i].fmt, jcr->client->hdr.name);
150 case 5: /* NumVols */
152 memset(&pr, 0, sizeof(pr));
153 bstrncpy(pr.Name, jcr->pool->hdr.name, sizeof(pr.Name));
154 if (db_get_pool_record(jcr, jcr->db, &pr)) {
155 jcr->NumVols = pr.NumVols;
156 return Py_BuildValue(getvars[i].fmt, jcr->NumVols);
158 bsnprintf(errmsg, sizeof(errmsg), _("Pool record not found."));
162 return Py_BuildValue(getvars[i].fmt, jcr->pool->name());
163 case 7: /* Storage */
165 return Py_BuildValue(getvars[i].fmt, jcr->wstore->name());
166 } else if (jcr->rstore) {
167 return Py_BuildValue(getvars[i].fmt, jcr->rstore->name());
172 return Py_BuildValue(getvars[i].fmt, jcr->catalog->name());
173 case 9: /* MediaType */
175 return Py_BuildValue(getvars[i].fmt, jcr->wstore->media_type);
176 } else if (jcr->rstore) {
177 return Py_BuildValue(getvars[i].fmt, jcr->rstore->media_type);
181 case 10: /* JobName */
182 return Py_BuildValue(getvars[i].fmt, jcr->Job);
183 case 11: /* JobStatus */
185 buf[0] = jcr->JobStatus;
186 return Py_BuildValue(getvars[i].fmt, buf);
187 case 12: /* Priority */
188 return Py_BuildValue(getvars[i].fmt, jcr->JobPriority);
190 return Py_BuildValue(getvars[i].fmt, jcr->VolumeName);
191 case 14: /* CatalogRes */
192 return Py_BuildValue(getvars[i].fmt,
193 jcr->catalog->db_name, jcr->catalog->db_address,
194 jcr->catalog->db_user, jcr->catalog->db_password,
195 jcr->catalog->db_socket, jcr->catalog->db_port,
197 case 15: /* JobErrors */
198 return Py_BuildValue(getvars[i].fmt, jcr->JobErrors);
199 case 16: /* JobFiles */
200 return Py_BuildValue(getvars[i].fmt, jcr->JobFiles);
201 case 17: /* SDJobFiles */
202 return Py_BuildValue(getvars[i].fmt, jcr->SDJobFiles);
203 case 18: /* SDErrors */
204 return Py_BuildValue(getvars[i].fmt, jcr->SDErrors);
205 case 19: /* FDJobStatus */
207 buf[0] = jcr->FDJobStatus;
208 return Py_BuildValue(getvars[i].fmt, buf);
209 case 29: /* SDJobStatus */
211 buf[0] = jcr->SDJobStatus;
212 return Py_BuildValue(getvars[i].fmt, buf);
214 bsnprintf(errmsg, sizeof(errmsg), _("Attribute %s not found."), attrname);
216 PyErr_SetString(PyExc_AttributeError, errmsg);
221 /* Set Job variables */
225 int job_setattr(PyObject *self, char *attrname, PyObject *value)
233 Dmsg2(100, "In job_setattr=%s val=%p.\n", attrname, value);
234 if (value == NULL) { /* Cannot delete variables */
237 jcr = get_jcr_from_PyObject(self);
242 /* Find attribute name in list */
243 for (i=0; setvars[i].name; i++) {
244 if (strcmp(setvars[i].name, attrname) == 0) {
252 /* Get argument value */
253 if (setvars[i].fmt != NULL) {
254 switch (setvars[i].fmt[0]) {
256 if (!PyArg_Parse(value, setvars[i].fmt, &strval)) {
257 PyErr_SetString(PyExc_TypeError, _("Read-only attribute"));
262 if (!PyArg_Parse(value, setvars[i].fmt, &intval)) {
263 PyErr_SetString(PyExc_TypeError, _("Read-only attribute"));
270 case 0: /* JobReport */
271 Jmsg(jcr, M_INFO, 0, "%s", strval);
273 case 1: /* VolumeName */
274 /* Make sure VolumeName is valid and we are in VolumeName event */
275 if (strcmp("NewVolume", jcr->event) == 0 &&
276 is_volume_name_legal(NULL, strval)) {
277 pm_strcpy(jcr->VolumeName, strval);
278 Dmsg1(100, "Set Vol=%s\n", strval);
281 jcr->VolumeName[0] = 0;
284 case 2: /* Priority */
285 Dmsg1(000, "Set priority=%d\n", intval);
286 if (intval >= 1 && intval <= 100) {
287 jcr->JobPriority = intval;
289 PyErr_SetString(PyExc_ValueError, _("Priority must be 1-100"));
292 case 3: /* Job Level */
293 if (strcmp("JobInit", jcr->event) != 0) {
294 PyErr_SetString(PyExc_RuntimeError, _("Job Level can be set only during JobInit"));
297 for (i=0; joblevels[i].level_name; i++) {
298 if (strcmp(strval, joblevels[i].level_name) == 0) {
299 if (joblevels[i].job_type == jcr->JobType) {
300 jcr->JobLevel = joblevels[i].level;
301 jcr->jr.JobLevel = jcr->JobLevel;
306 PyErr_SetString(PyExc_ValueError, _("Bad JobLevel string"));
310 PyErr_SetString(PyExc_AttributeError, attrname);
315 * Set pointer to instantiated events class
317 static PyObject *set_job_events(PyObject *self, PyObject *arg)
322 Dmsg0(100, "In set_job_events.\n");
323 if (!PyArg_ParseTuple(arg, "O:set_events", &eObject)) {
324 Dmsg0(000, "Error in ParseTuple\n");
327 jcr = get_jcr_from_PyObject(self);
328 Py_XDECREF((PyObject *)jcr->Python_events);
330 jcr->Python_events = (void *)eObject;
335 /* Run a Bacula job */
336 static PyObject *job_run(PyObject *self, PyObject *arg)
342 if (!PyArg_ParseTuple(arg, "s:run", &item)) {
343 Dmsg0(000, "Error in ParseTuple\n");
346 /* Release lock due to recursion */
347 // PyEval_ReleaseLock();
348 jcr = get_jcr_from_PyObject(self);
349 UAContext *ua = new_ua_context(jcr);
351 pm_strcpy(ua->cmd, item); /* copy command */
352 parse_ua_args(ua); /* parse command */
353 stat = run_cmd(ua, ua->cmd);
355 // PyEval_AcquireLock();
356 return PyInt_FromLong((long)stat);
359 static PyObject *job_write(PyObject *self, PyObject *args)
363 if (!PyArg_ParseTuple(args, "s:write", &text)) {
364 Dmsg0(000, "Parse tuple error in job_write\n");
368 JCR *jcr = get_jcr_from_PyObject(self);
369 Jmsg(jcr, M_INFO, 0, "%s", text);
375 static PyObject *job_does_vol_exist(PyObject *self, PyObject *args)
377 char *VolName = NULL;
379 if (!PyArg_ParseTuple(args, "s:does_volume_exist", &VolName)) {
380 Dmsg0(000, "Parse tuple error in job_does_vol_exist\n");
386 JCR *jcr = get_jcr_from_PyObject(self);
387 memset(&mr, 0, sizeof(mr));
388 bstrncpy(mr.VolumeName, VolName, sizeof(mr.VolumeName));
389 ok = db_get_media_record(jcr, jcr->db, &mr);
390 return Py_BuildValue("i", ok);
397 static PyObject *job_cancel(PyObject *self, PyObject *args)
403 if (!PyArg_ParseTuple(args, "i:cancel", &JobId)) {
404 Dmsg0(000, "Parse tuple error in job_write\n");
408 if (jcr->JobId == 0) {
411 if (jcr->JobId == JobId) {
416 /* endeach_jcr(jcr) not needed because freed below */
419 /* ***FIXME*** raise exception */
422 // PyEval_ReleaseLock();
423 UAContext *ua = new_ua_context(jcr);
425 if (!cancel_job(ua, jcr)) {
426 /* ***FIXME*** raise exception */
431 // PyEval_AcquireLock();
437 * Generate a Job event, which means look up the event
438 * method defined by the user, and if it exists,
441 int generate_job_event(JCR *jcr, const char *event)
443 PyObject *method = NULL;
444 PyObject *Job = (PyObject *)jcr->Python_job;
445 PyObject *events = (PyObject *)jcr->Python_events;
446 PyObject *result = NULL;
449 if (!Job || !events) {
454 // PyEval_AcquireLock();
456 method = find_method(events, method, event);
461 bstrncpy(jcr->event, event, sizeof(jcr->event));
462 result = PyObject_CallFunction(method, "O", Job);
463 jcr->event[0] = 0; /* no event in progress */
464 if (result == NULL) {
465 if (PyErr_Occurred()) {
467 Dmsg1(000, "Error in Python method %s\n", event);
476 // PyEval_ReleaseLock();
480 bool python_set_prog(JCR*, char const*) { return false; }
484 /* Dummy if Python not configured */
485 int generate_job_event(JCR *jcr, const char *event) { return 1; }
488 #endif /* HAVE_PYTHON */