3 * Bacula interface to Python for the Director
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.
29 #undef _POSIX_C_SOURCE
32 extern char *configfile;
33 extern JCR *get_jcr_from_PyObject(PyObject *self);
34 extern PyObject *find_method(PyObject *eventsObject, PyObject *method,
38 static PyObject *set_job_events(PyObject *self, PyObject *arg);
39 static PyObject *job_run(PyObject *self, PyObject *arg);
40 static PyObject *job_write(PyObject *self, PyObject *arg);
41 static PyObject *job_cancel(PyObject *self, PyObject *arg);
42 static PyObject *job_does_vol_exist(PyObject *self, PyObject *arg);
44 PyMethodDef JobMethods[] = {
45 {"set_events", set_job_events, METH_VARARGS, "Set Job events"},
46 {"run", job_run, METH_VARARGS, "Run a Job"},
47 {"write", job_write, METH_VARARGS, "Write to output"},
48 {"cancel", job_cancel, METH_VARARGS, "Cancel a Job"},
49 {"DoesVolumeExist", job_does_vol_exist, METH_VARARGS, "Does Volume Exist"},
50 {NULL, NULL, 0, NULL} /* last item */
59 /* Read-only variables */
60 static struct s_vars getvars[] = {
66 { N_("NumVols"), "i"},
68 { N_("Storage"), "s"},
69 { N_("Catalog"), "s"},
70 { N_("MediaType"), "s"},
71 { N_("JobName"), "s"},
72 { N_("JobStatus"), "s"},
73 { N_("Priority"), "i"},
74 { N_("CatalogRes"), "(sssssis)"},
79 /* Writable variables */
80 static struct s_vars setvars[] = {
81 { N_("JobReport"), "s"},
82 { N_("VolumeName"), "s"},
83 { N_("Priority"), "i"},
89 /* Return Job variables */
90 /* Returns: NULL if error
91 * PyObject * return value if OK
93 PyObject *job_getattr(PyObject *self, char *attrname)
101 Dmsg0(100, "In job_getattr.\n");
102 jcr = get_jcr_from_PyObject(self);
104 bstrncpy(errmsg, _("Job pointer not found."), sizeof(errmsg));
107 for (i=0; getvars[i].name; i++) {
108 if (strcmp(getvars[i].name, attrname) == 0) {
114 /* Try our methods */
115 return Py_FindMethod(JobMethods, self, attrname);
119 return Py_BuildValue(getvars[i].fmt, jcr->job->hdr.name);
121 return Py_BuildValue(getvars[i].fmt, job_level_to_str(jcr->JobLevel));
123 return Py_BuildValue(getvars[i].fmt, job_type_to_str(jcr->JobType));
125 return Py_BuildValue(getvars[i].fmt, jcr->JobId);
127 return Py_BuildValue(getvars[i].fmt, jcr->client->hdr.name);
128 case 5: /* NumVols */
130 memset(&pr, 0, sizeof(pr));
131 bstrncpy(pr.Name, jcr->pool->hdr.name, sizeof(pr.Name));
132 if (db_get_pool_record(jcr, jcr->db, &pr)) {
133 jcr->NumVols = pr.NumVols;
134 return Py_BuildValue(getvars[i].fmt, jcr->NumVols);
136 bsnprintf(errmsg, sizeof(errmsg), _("Pool record not found."));
140 return Py_BuildValue(getvars[i].fmt, jcr->pool->hdr.name);
141 case 7: /* Storage */
142 return Py_BuildValue(getvars[i].fmt, jcr->store->hdr.name);
144 return Py_BuildValue(getvars[i].fmt, jcr->catalog->hdr.name);
145 case 9: /* MediaType */
146 return Py_BuildValue(getvars[i].fmt, jcr->store->media_type);
147 case 10: /* JobName */
148 return Py_BuildValue(getvars[i].fmt, jcr->Job);
149 case 11: /* JobStatus */
151 buf[0] = jcr->JobStatus;
152 return Py_BuildValue(getvars[i].fmt, buf);
153 case 12: /* Priority */
154 return Py_BuildValue(getvars[i].fmt, jcr->JobPriority);
155 case 13: /* CatalogRes */
156 return Py_BuildValue(getvars[i].fmt,
157 jcr->catalog->db_name, jcr->catalog->db_address,
158 jcr->catalog->db_user, jcr->catalog->db_password,
159 jcr->catalog->db_socket, jcr->catalog->db_port,
163 bsnprintf(errmsg, sizeof(errmsg), _("Attribute %s not found."), attrname);
165 PyErr_SetString(PyExc_AttributeError, errmsg);
170 /* Set Job variables */
174 int job_setattr(PyObject *self, char *attrname, PyObject *value)
182 Dmsg2(100, "In job_setattr=%s val=%p.\n", attrname, value);
183 if (value == NULL) { /* Cannot delete variables */
186 jcr = get_jcr_from_PyObject(self);
191 /* Find attribute name in list */
192 for (i=0; setvars[i].name; i++) {
193 if (strcmp(setvars[i].name, attrname) == 0) {
201 /* Get argument value */
202 if (setvars[i].fmt != NULL) {
203 switch (setvars[i].fmt[0]) {
205 if (!PyArg_Parse(value, setvars[i].fmt, &strval)) {
206 PyErr_SetString(PyExc_TypeError, _("Read-only attribute"));
211 if (!PyArg_Parse(value, setvars[i].fmt, &intval)) {
212 PyErr_SetString(PyExc_TypeError, _("Read-only attribute"));
219 case 0: /* JobReport */
220 Jmsg(jcr, M_INFO, 0, "%s", strval);
222 case 1: /* VolumeName */
223 /* Make sure VolumeName is valid and we are in VolumeName event */
224 if (strcmp("NewVolume", jcr->event) == 0 &&
225 is_volume_name_legal(NULL, strval)) {
226 pm_strcpy(jcr->VolumeName, strval);
227 Dmsg1(100, "Set Vol=%s\n", strval);
230 jcr->VolumeName[0] = 0;
233 case 2: /* Priority */
234 Dmsg1(000, "Set priority=%d\n", intval);
238 PyErr_SetString(PyExc_AttributeError, attrname);
243 static PyObject *set_job_events(PyObject *self, PyObject *arg)
248 Dmsg0(100, "In set_job_events.\n");
249 if (!PyArg_ParseTuple(arg, "O:set_events", &eObject)) {
250 Dmsg0(000, "Error in ParseTuple\n");
253 jcr = get_jcr_from_PyObject(self);
254 Py_XDECREF((PyObject *)jcr->Python_events);
256 jcr->Python_events = (void *)eObject;
261 /* Run a Bacula command */
262 static PyObject *job_run(PyObject *self, PyObject *arg)
268 if (!PyArg_ParseTuple(arg, "s:run", &item)) {
269 Dmsg0(000, "Error in ParseTuple\n");
272 /* Release lock due to recursion */
273 PyEval_ReleaseLock();
274 jcr = get_jcr_from_PyObject(self);
275 UAContext *ua = new_ua_context(jcr);
277 pm_strcpy(ua->cmd, item); /* copy command */
278 parse_ua_args(ua); /* parse command */
279 stat = run_cmd(ua, ua->cmd);
281 PyEval_AcquireLock();
282 return PyInt_FromLong((long)stat);
285 static PyObject *job_write(PyObject *self, PyObject *args)
289 if (!PyArg_ParseTuple(args, "s:write", &text)) {
290 Dmsg0(000, "Parse tuple error in job_write\n");
294 JCR *jcr = get_jcr_from_PyObject(self);
295 Jmsg(jcr, M_INFO, 0, "%s", text);
301 static PyObject *job_does_vol_exist(PyObject *self, PyObject *args)
303 char *VolName = NULL;
305 if (!PyArg_ParseTuple(args, "s:does_volume_exist", &VolName)) {
306 Dmsg0(000, "Parse tuple error in job_does_vol_exist\n");
312 JCR *jcr = get_jcr_from_PyObject(self);
313 memset(&mr, 0, sizeof(mr));
314 bstrncpy(mr.VolumeName, VolName, sizeof(mr.VolumeName));
315 ok = db_get_media_record(jcr, jcr->db, &mr);
316 return Py_BuildValue("i", ok);
323 static PyObject *job_cancel(PyObject *self, PyObject *args)
329 if (!PyArg_ParseTuple(args, "i:cancel", &JobId)) {
330 Dmsg0(000, "Parse tuple error in job_write\n");
334 if (jcr->JobId == 0) {
338 if (jcr->JobId == JobId) {
344 /* ***FIXME*** raise exception */
347 PyEval_ReleaseLock();
348 UAContext *ua = new_ua_context(jcr);
350 if (!cancel_job(ua, jcr)) {
351 /* ***FIXME*** raise exception */
356 PyEval_AcquireLock();
362 * Generate a Job event, which means look up the event
363 * method defined by the user, and if it exists,
366 int generate_job_event(JCR *jcr, const char *event)
368 PyObject *method = NULL;
369 PyObject *Job = (PyObject *)jcr->Python_job;
370 PyObject *events = (PyObject *)jcr->Python_events;
371 PyObject *result = NULL;
374 if (!Job || !events) {
378 PyEval_AcquireLock();
380 method = find_method(events, method, event);
385 bstrncpy(jcr->event, event, sizeof(jcr->event));
386 result = PyObject_CallFunction(method, "O", Job);
387 jcr->event[0] = 0; /* no event in progress */
388 if (result == NULL) {
389 if (PyErr_Occurred()) {
391 Dmsg1(000, "Error in Python method %s\n", event);
399 PyEval_ReleaseLock();
403 bool python_set_prog(JCR*, char const*) { return false; }
407 /* Dummy if Python not configured */
408 int generate_job_event(JCR *jcr, const char *event) { return 1; }
411 #endif /* HAVE_PYTHON */