]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/pythondir.c
Switch from GPLv2 to AGPLv3
[bacula/bacula] / bacula / src / dird / pythondir.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2004-2008 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 interface to Python for the Director
31  *
32  * Kern Sibbald, November MMIV
33  *
34  *   Version $Id$
35  *
36  */
37
38 #include "bacula.h"
39 #include "dird.h"
40
41 #ifdef HAVE_PYTHON
42 #undef _POSIX_C_SOURCE
43 #include <Python.h>
44
45 #include <lib/pythonlib.h>
46
47 extern struct s_jl joblevels[];
48
49 static PyObject *set_job_events(PyObject *self, PyObject *arg);
50 static PyObject *job_run(PyObject *self, PyObject *arg);
51 static PyObject *job_write(PyObject *self, PyObject *arg);
52 static PyObject *job_cancel(PyObject *self, PyObject *arg);
53 static PyObject *job_does_vol_exist(PyObject *self, PyObject *arg);
54
55 PyMethodDef JobMethods[] = {
56     {"set_events", set_job_events, METH_VARARGS, "Set Job events"},
57     {"run", job_run, METH_VARARGS, "Run a Job"},
58     {"write", job_write, METH_VARARGS, "Write to output"},
59     {"cancel", job_cancel, METH_VARARGS, "Cancel a Job"},
60     {"DoesVolumeExist", job_does_vol_exist, METH_VARARGS, "Does Volume Exist"},
61     {NULL, NULL, 0, NULL}             /* last item */
62 };
63  
64 struct s_vars {
65    const char *name;
66    const char *fmt;
67 };
68
69 /* Read-only variables */
70 static struct s_vars getvars[] = {
71    { "Job",        "s"},
72    { "Level",      "s"},
73    { "Type",       "s"},
74    { "JobId",      "i"},
75    { "Client",     "s"},
76    { "NumVols",    "i"},
77    { "Pool",       "s"},
78    { "Storage",    "s"},
79    { "Catalog",    "s"},
80    { "MediaType",  "s"},
81    { "JobName",    "s"},
82    { "JobStatus",  "s"},
83    { "Priority",   "i"},
84    { "VolumeName", "s"},
85    { "CatalogRes", "(sssssis)"},
86    { "JobErrors",  "i"},
87    { "JobFiles",   "i"},
88    { "SDJobFiles", "i"},
89    { "SDErrors",   "i"},
90    { "FDJobStatus","s"},
91    { "SDJobStatus","s"},
92
93    { NULL,             NULL}
94 };
95
96 /* Writable variables */
97 static struct s_vars setvars[] = {
98    { "JobReport",   "s"},
99    { "VolumeName",  "s"},
100    { "Priority",    "i"},
101    { "JobLevel",    "s"},
102
103    { NULL,             NULL}
104 };
105
106
107 /* Return Job variables */
108 /* Returns:  NULL if error
109  *           PyObject * return value if OK
110  */
111 PyObject *job_getattr(PyObject *self, char *attrname)
112 {
113    JCR *jcr;
114    bool found = false;
115    int i;
116    char buf[10];
117    char errmsg[200];
118
119    Dmsg0(100, "In job_getattr.\n");
120    jcr = get_jcr_from_PyObject(self);
121    if (!jcr) {
122       bstrncpy(errmsg, _("Job pointer not found."), sizeof(errmsg));
123       goto bail_out;
124    }
125    for (i=0; getvars[i].name; i++) {
126       if (strcmp(getvars[i].name, attrname) == 0) {
127          found = true;
128          break;
129       }
130    }
131    if (!found) {
132       /* Try our methods */
133       return Py_FindMethod(JobMethods, self, attrname);
134    }
135    switch (i) {
136    case 0:                            /* Job */
137       return Py_BuildValue((char *)getvars[i].fmt, jcr->job->hdr.name);
138    case 1:                            /* level */
139       return Py_BuildValue((char *)getvars[i].fmt, job_level_to_str(jcr->getJobLevel()));
140    case 2:                            /* type */
141       return Py_BuildValue((char *)getvars[i].fmt, job_type_to_str(jcr->getJobType()));
142    case 3:                            /* JobId */
143       return Py_BuildValue((char *)getvars[i].fmt, jcr->JobId);
144    case 4:                            /* Client */
145       return Py_BuildValue((char *)getvars[i].fmt, jcr->client->hdr.name);
146    case 5:                            /* NumVols */
147       POOL_DBR pr;
148       memset(&pr, 0, sizeof(pr));
149       bstrncpy(pr.Name, jcr->pool->hdr.name, sizeof(pr.Name));
150       if (db_get_pool_record(jcr, jcr->db, &pr)) {
151          jcr->NumVols = pr.NumVols;
152          return Py_BuildValue((char *)getvars[i].fmt, jcr->NumVols);
153       } else {
154          bsnprintf(errmsg, sizeof(errmsg), _("Pool record not found."));
155          goto bail_out;
156       }
157    case 6:                            /* Pool */
158       return Py_BuildValue((char *)getvars[i].fmt, jcr->pool->name());
159    case 7:                            /* Storage */
160       if (jcr->wstore) {
161          return Py_BuildValue((char *)getvars[i].fmt, jcr->wstore->name());
162       } else if (jcr->rstore) {
163          return Py_BuildValue((char *)getvars[i].fmt, jcr->rstore->name());
164       } else {
165          goto bail_out;
166       }
167    case 8:
168       return Py_BuildValue((char *)getvars[i].fmt, jcr->catalog->name());
169    case  9:                           /* MediaType */
170       if (jcr->wstore) {
171          return Py_BuildValue((char *)getvars[i].fmt, jcr->wstore->media_type);
172       } else if (jcr->rstore) {
173          return Py_BuildValue((char *)getvars[i].fmt, jcr->rstore->media_type);
174       } else {
175          goto bail_out;
176       }
177    case 10:                           /* JobName */
178       return Py_BuildValue((char *)getvars[i].fmt, jcr->Job);
179    case 11:                           /* JobStatus */
180       buf[1] = 0;
181       buf[0] = jcr->JobStatus;
182       return Py_BuildValue((char *)getvars[i].fmt, buf);
183    case 12:                           /* Priority */
184       return Py_BuildValue((char *)getvars[i].fmt, jcr->JobPriority);
185    case 13:
186       return Py_BuildValue((char *)getvars[i].fmt, jcr->VolumeName);
187    case 14:                           /* CatalogRes */
188       return Py_BuildValue((char *)getvars[i].fmt,
189          jcr->catalog->db_name, jcr->catalog->db_address, 
190          jcr->catalog->db_user, jcr->catalog->db_password,
191          jcr->catalog->db_socket, jcr->catalog->db_port,
192          db_get_type());
193    case 15:                           /* JobErrors */
194       return Py_BuildValue((char *)getvars[i].fmt, jcr->JobErrors);
195    case 16:                           /* JobFiles */
196       return Py_BuildValue((char *)getvars[i].fmt, jcr->JobFiles);
197    case 17:                           /* SDJobFiles */
198       return Py_BuildValue((char *)getvars[i].fmt, jcr->SDJobFiles);
199    case 18:                           /* SDErrors */
200       return Py_BuildValue((char *)getvars[i].fmt, jcr->SDErrors);
201    case 19:                           /* FDJobStatus */
202       buf[1] = 0;
203       buf[0] = jcr->FDJobStatus;
204       return Py_BuildValue((char *)getvars[i].fmt, buf);
205    case 20:                           /* SDJobStatus */
206       buf[1] = 0;
207       buf[0] = jcr->SDJobStatus;
208       return Py_BuildValue((char *)getvars[i].fmt, buf);
209    }
210    bsnprintf(errmsg, sizeof(errmsg), _("Attribute %s not found."), attrname);
211 bail_out:
212    PyErr_SetString(PyExc_AttributeError, errmsg);
213    return NULL;
214 }
215
216
217 /* Set Job variables */
218 /*  Returns:   0 for OK
219  *            -1 for error
220  */
221 int job_setattr(PyObject *self, char *attrname, PyObject *value)
222 {
223    JCR *jcr;
224    bool found = false;
225    char *strval = NULL;
226    int intval = 0;
227    int i;
228
229    Dmsg2(100, "In job_setattr=%s val=%p.\n", attrname, value);
230    if (value == NULL) {                /* Cannot delete variables */
231        goto bail_out;
232    }
233    jcr = get_jcr_from_PyObject(self);
234    if (!jcr) {
235       goto bail_out;
236    }
237
238    /* Find attribute name in list */
239    for (i=0; setvars[i].name; i++) {
240       if (strcmp(setvars[i].name, attrname) == 0) {
241          found = true;
242          break;
243       }
244    }
245    if (!found) {
246       goto bail_out;
247    }
248    /* Get argument value */
249    if (setvars[i].fmt != NULL) {
250       switch (setvars[i].fmt[0]) {
251       case 's':
252          if (!PyArg_Parse(value, (char *)setvars[i].fmt, &strval)) {
253             PyErr_SetString(PyExc_TypeError, _("Read-only attribute"));
254             return -1;
255          }
256          break;
257       case 'i':
258          if (!PyArg_Parse(value, (char *)setvars[i].fmt, &intval)) {
259             PyErr_SetString(PyExc_TypeError, _("Read-only attribute"));
260             return -1;
261          }
262          break;
263       }
264    }   
265    switch (i) {
266    case 0:                            /* JobReport */
267       Jmsg(jcr, M_INFO, 0, "%s", strval);
268       return 0;
269    case 1:                            /* VolumeName */
270       /* Make sure VolumeName is valid and we are in VolumeName event */
271       if (strcmp("NewVolume", jcr->event) == 0 &&
272           is_volume_name_legal(NULL, strval)) {
273          pm_strcpy(jcr->VolumeName, strval);
274          Dmsg1(100, "Set Vol=%s\n", strval);
275          return 0;
276       } else {
277          jcr->VolumeName[0] = 0;
278       }
279       break;
280    case 2:                            /* Priority */
281       Dmsg1(000, "Set priority=%d\n", intval);
282       if (intval >= 1 && intval <= 100) {
283          jcr->JobPriority = intval;
284       } else {
285          PyErr_SetString(PyExc_ValueError, _("Priority must be 1-100"));
286          return -1;
287       }
288    case 3:                            /* Job Level */
289       if (strcmp("JobInit", jcr->event) != 0) {
290          PyErr_SetString(PyExc_RuntimeError, _("Job Level can be set only during JobInit"));
291          return -1;
292       }
293       if (strval != NULL) {
294          for (i=0; joblevels[i].level_name; i++) {
295             if (strcmp(strval, joblevels[i].level_name) == 0) {
296                if (joblevels[i].job_type == jcr->getJobType()) {
297                   jcr->set_JobLevel(joblevels[i].level);
298                   jcr->jr.JobLevel = jcr->getJobLevel();
299                   return 0;
300                }
301             }
302          }
303       }
304       PyErr_SetString(PyExc_ValueError, _("Bad JobLevel string"));
305       return -1;
306    }
307 bail_out:
308    PyErr_SetString(PyExc_AttributeError, attrname);
309    return -1;
310 }
311
312 /*
313  * Set pointer to instantiated events class
314  */
315 static PyObject *set_job_events(PyObject *self, PyObject *arg)
316 {
317    PyObject *eObject;
318    JCR *jcr;
319
320    Dmsg0(100, "In set_job_events.\n");
321    if (!PyArg_ParseTuple(arg, "O:set_events", &eObject)) {
322       Dmsg0(000, "Error in ParseTuple\n");
323       return NULL;
324    }
325    jcr = get_jcr_from_PyObject(self);
326    Py_XDECREF((PyObject *)jcr->Python_events);
327    Py_INCREF(eObject);
328    jcr->Python_events = (void *)eObject;
329    Py_INCREF(Py_None);
330    return Py_None;
331 }
332
333 /* Run a Bacula job */
334 static PyObject *job_run(PyObject *self, PyObject *arg)
335 {
336    JCR *jcr;
337    char *item;
338    int stat;
339
340    if (!PyArg_ParseTuple(arg, "s:run", &item)) {
341       Dmsg0(000, "Error in ParseTuple\n");
342       return NULL;
343    }
344    /* Release lock due to recursion */
345 // PyEval_ReleaseLock();
346    jcr = get_jcr_from_PyObject(self);
347    UAContext *ua = new_ua_context(jcr);
348    ua->batch = true;
349    pm_strcpy(ua->cmd, item);          /* copy command */
350    parse_ua_args(ua);                 /* parse command */
351    stat = run_cmd(ua, ua->cmd);
352    free_ua_context(ua);
353 // PyEval_AcquireLock();
354    return PyInt_FromLong((long)stat);
355 }
356
357 static PyObject *job_write(PyObject *self, PyObject *args)
358 {
359    char *text = NULL;
360
361    if (!PyArg_ParseTuple(args, "s:write", &text)) {
362       Dmsg0(000, "Parse tuple error in job_write\n");
363       return NULL;
364    }
365    if (text) {
366       JCR *jcr = get_jcr_from_PyObject(self);
367       Jmsg(jcr, M_INFO, 0, "%s", text);
368    }
369    Py_INCREF(Py_None);
370    return Py_None;
371 }
372
373 static PyObject *job_does_vol_exist(PyObject *self, PyObject *args)
374 {
375    char *VolName = NULL;
376
377    if (!PyArg_ParseTuple(args, "s:does_volume_exist", &VolName)) {
378       Dmsg0(000, "Parse tuple error in job_does_vol_exist\n");
379       return NULL;
380    }
381    if (VolName) {
382       MEDIA_DBR mr;
383       int ok;
384       JCR *jcr = get_jcr_from_PyObject(self);
385       memset(&mr, 0, sizeof(mr));
386       bstrncpy(mr.VolumeName, VolName, sizeof(mr.VolumeName));
387       ok = db_get_media_record(jcr, jcr->db, &mr);
388       return Py_BuildValue("i", ok);
389    }
390    Py_INCREF(Py_None);
391    return Py_None;
392 }
393
394
395 static PyObject *job_cancel(PyObject *self, PyObject *args)
396 {
397    JobId_t JobId = 0;
398    JCR *jcr;
399    bool found = false;
400
401    if (!PyArg_ParseTuple(args, "i:cancel", &JobId)) {
402       Dmsg0(000, "Parse tuple error in job_write\n");
403       return NULL;
404    }
405    foreach_jcr(jcr) {
406       if (jcr->JobId == 0) {
407          continue;
408       }
409       if (jcr->JobId == JobId) {
410          found = true;
411          break;
412       }
413    }
414    /* endeach_jcr(jcr) not needed because freed below */
415
416    if (!found) {
417       /* ***FIXME*** raise exception */
418       return NULL;
419    }
420 // PyEval_ReleaseLock();
421    UAContext *ua = new_ua_context(jcr);
422    ua->batch = true;
423    if (!cancel_job(ua, jcr)) {
424       /* ***FIXME*** raise exception */
425       return NULL;
426    }
427    free_ua_context(ua);
428    free_jcr(jcr);
429 // PyEval_AcquireLock();   
430    Py_INCREF(Py_None);
431    return Py_None;
432 }
433
434 /*
435  * Generate a Job event, which means look up the event
436  *  method defined by the user, and if it exists, 
437  *  call it.
438  */
439 int generate_job_event(JCR *jcr, const char *event)
440 {
441    PyObject *method = NULL;
442    PyObject *Job = (PyObject *)jcr->Python_job;
443    PyObject *events = (PyObject *)jcr->Python_events;
444    PyObject *result = NULL;
445    int stat = 0;
446
447    if (!Job || !events) {
448       return 0;
449    }
450
451    lock_python();
452 // PyEval_AcquireLock();
453
454    method = find_method(events, method, event);
455    if (!method) {
456       goto bail_out;
457    }
458
459    bstrncpy(jcr->event, event, sizeof(jcr->event));
460    result = PyObject_CallFunction(method, (char *)"O", Job);
461    jcr->event[0] = 0;             /* no event in progress */
462    if (result == NULL) {
463       if (PyErr_Occurred()) {
464          PyErr_Print();
465          Dmsg1(000, "Error in Python method %s\n", event);
466       }
467    } else {
468       stat = 1;
469    }
470    Py_XDECREF(result);
471
472 bail_out:
473    unlock_python();
474 // PyEval_ReleaseLock();
475    return stat;
476 }
477
478 bool python_set_prog(JCR*, char const*) { return false; }
479
480 #else
481
482 /* Dummy if Python not configured */
483 int generate_job_event(JCR *jcr, const char *event) { return 1; }
484    
485
486 #endif /* HAVE_PYTHON */