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