]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/pythondir.c
- Make Version a tuple (version, build-date)
[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-2005 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 ammended 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 JCR *get_jcr_from_PyObject(PyObject *self);
34 extern PyObject *find_method(PyObject *eventsObject, PyObject *method, 
35          const char *name);
36
37
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
43 PyMethodDef JobMethods[] = {
44     {"set_events", set_job_events, METH_VARARGS, "Set Job events"},
45     {"run", job_run, METH_VARARGS, "Run a Job"},
46     {"write", job_write, METH_VARARGS, "Write to output"},
47     {"cancel", job_cancel, METH_VARARGS, "Cancel a Job"},
48     {NULL, NULL, 0, NULL}             /* last item */
49 };
50  
51
52 struct s_vars {
53    const char *name;
54    char *fmt;
55 };
56
57 /* Read-only variables */
58 static struct s_vars getvars[] = {
59    { N_("Job"),        "s"},
60    { N_("DirName"),    "s"},
61    { N_("Level"),      "s"},
62    { N_("Type"),       "s"},
63    { N_("JobId"),      "i"},
64    { N_("Client"),     "s"},
65    { N_("NumVols"),    "i"},
66    { N_("Pool"),       "s"},
67    { N_("Storage"),    "s"},
68    { N_("Catalog"),    "s"},
69    { N_("MediaType"),  "s"},
70    { N_("JobName"),    "s"},
71    { N_("JobStatus"),  "s"},
72    { N_("Priority"),   "i"},
73    { N_("Version"),    "(ss)"},
74    { N_("ConfigFile"), "s"},
75    { N_("WorkingDir"), "s"},
76    { N_("CatalogRes"), "(sssssi)"},
77
78    { NULL,             NULL}
79 };
80
81 /* Writable variables */
82 static struct s_vars setvars[] = {
83    { N_("JobReport"),   "s"},
84    { N_("VolumeName"),  "s"},
85    { N_("Priority"),    "i"},
86
87    { NULL,             NULL}
88 };
89
90
91 /* Return Job variables */
92 /* Returns:  NULL if error
93  *           PyObject * return value if OK
94  */
95 PyObject *job_getattr(PyObject *self, char *attrname)
96 {
97    JCR *jcr;
98    bool found = false;
99    int i;
100    char buf[10];
101    char errmsg[200];
102
103    Dmsg0(100, "In job_getattr.\n");
104    jcr = get_jcr_from_PyObject(self);
105    if (!jcr) {
106       bstrncpy(errmsg, "Job pointer not found.", sizeof(errmsg));
107       goto bail_out;
108    }
109    for (i=0; getvars[i].name; i++) {
110       if (strcmp(getvars[i].name, attrname) == 0) {
111          found = true;
112          break;
113       }
114    }
115    if (!found) {
116       /* Try our methods */
117       return Py_FindMethod(JobMethods, self, attrname);
118    }
119    switch (i) {
120    case 0:                            /* Job */
121       return Py_BuildValue(getvars[i].fmt, jcr->job->hdr.name);
122    case 1:                            /* Director's name */
123       return Py_BuildValue(getvars[i].fmt, my_name);
124    case 2:                            /* level */
125       return Py_BuildValue(getvars[i].fmt, job_level_to_str(jcr->JobLevel));
126    case 3:                            /* type */
127       return Py_BuildValue(getvars[i].fmt, job_type_to_str(jcr->JobType));
128    case 4:                            /* JobId */
129       return Py_BuildValue(getvars[i].fmt, jcr->JobId);
130    case 5:                            /* Client */
131       return Py_BuildValue(getvars[i].fmt, jcr->client->hdr.name);
132    case 6:                            /* NumVols */
133       return Py_BuildValue(getvars[i].fmt, jcr->NumVols);
134    case 7:                            /* Pool */
135       return Py_BuildValue(getvars[i].fmt, jcr->pool->hdr.name);
136    case 8:                            /* Storage */
137       return Py_BuildValue(getvars[i].fmt, jcr->store->hdr.name);
138    case 9:
139       return Py_BuildValue(getvars[i].fmt, jcr->catalog->hdr.name);
140    case 10:                           /* MediaType */
141       return Py_BuildValue(getvars[i].fmt, jcr->store->media_type);
142    case 11:                           /* JobName */
143       return Py_BuildValue(getvars[i].fmt, jcr->Job);
144    case 12:                           /* JobStatus */
145       buf[1] = 0;
146       buf[0] = jcr->JobStatus;
147       return Py_BuildValue(getvars[i].fmt, buf);
148    case 13:                           /* Priority */
149       return Py_BuildValue(getvars[i].fmt, jcr->JobPriority);
150    case 14:                           /* Version */
151       return Py_BuildValue(getvars[i].fmt, VERSION, BDATE);
152    case 15:                           /* Config Dir */
153       return Py_BuildValue(getvars[i].fmt, configfile);
154    case 16:                           /* Working Dir */
155       return Py_BuildValue(getvars[i].fmt, director->working_directory);
156    case 17:                           /* CatalogRes */
157       return Py_BuildValue(getvars[i].fmt,
158          jcr->catalog->db_name, jcr->catalog->db_address, 
159          jcr->catalog->db_user, jcr->catalog->db_password,
160          jcr->catalog->db_socket, jcr->catalog->db_port);
161
162    }
163    bsnprintf(errmsg, sizeof(errmsg), "Attribute %s not found.", attrname);
164 bail_out:
165    PyErr_SetString(PyExc_AttributeError, errmsg);
166    return NULL;
167 }
168
169
170 /* Set Job variables */
171 /*  Returns:   0 for OK
172  *            -1 for error
173  */
174 int job_setattr(PyObject *self, char *attrname, PyObject *value)
175 {
176    JCR *jcr;
177    bool found = false;
178    char *strval = NULL;
179    int intval = 0;
180    int i;
181
182    Dmsg2(100, "In job_setattr=%s val=%p.\n", attrname, value);
183    if (value == NULL) {                /* Cannot delete variables */
184        goto bail_out;
185    }
186    jcr = get_jcr_from_PyObject(self);
187    if (!jcr) {
188       goto bail_out;
189    }
190
191    /* Find attribute name in list */
192    for (i=0; setvars[i].name; i++) {
193       if (strcmp(setvars[i].name, attrname) == 0) {
194          found = true;
195          break;
196       }
197    }
198    if (!found) {
199       goto bail_out;
200    }
201    /* Get argument value */
202    if (setvars[i].fmt != NULL) {
203       switch (setvars[i].fmt[0]) {
204       case 's':
205          if (!PyArg_Parse(value, setvars[i].fmt, &strval)) {
206             PyErr_SetString(PyExc_TypeError, "Read-only attribute");
207             return -1;
208          }
209          break;
210       case 'i':
211          if (!PyArg_Parse(value, setvars[i].fmt, &intval)) {
212             PyErr_SetString(PyExc_TypeError, "Read-only attribute");
213             return -1;
214          }
215          break;
216       }
217    }   
218    switch (i) {
219    case 0:                            /* JobReport */
220       Jmsg(jcr, M_INFO, 0, "%s", strval);
221       return 0;
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);
228          return 0;
229       } else {
230          jcr->VolumeName[0] = 0;
231       }
232       break;
233    case 2:                            /* Priority */
234       Dmsg1(000, "Set priority=%d\n", intval);
235       return 0;
236    }
237 bail_out:
238    PyErr_SetString(PyExc_AttributeError, attrname);
239    return -1;
240 }
241
242
243 static PyObject *set_job_events(PyObject *self, PyObject *arg)
244 {
245    PyObject *eObject;
246    JCR *jcr;
247
248    Dmsg0(100, "In set_job_events.\n");
249    if (!PyArg_ParseTuple(arg, "O:set_events", &eObject)) {
250       Dmsg0(000, "Error in ParseTuple\n");
251       return NULL;
252    }
253    jcr = get_jcr_from_PyObject(self);
254    Py_XDECREF((PyObject *)jcr->Python_events);
255    Py_INCREF(eObject);
256    jcr->Python_events = (void *)eObject;
257    Py_INCREF(Py_None);
258    return Py_None;
259 }
260
261 /* Run a Bacula command */
262 static PyObject *job_run(PyObject *self, PyObject *arg)
263 {
264    JCR *jcr;
265    char *item;
266    int stat;
267
268    if (!PyArg_ParseTuple(arg, "s:run", &item)) {
269       Dmsg0(000, "Error in ParseTuple\n");
270       return NULL;
271    }
272    /* Release lock due to recursion */
273    PyEval_ReleaseLock();
274    jcr = get_jcr_from_PyObject(self);
275    UAContext *ua = new_ua_context(jcr);
276    ua->batch = true;
277    pm_strcpy(ua->cmd, item);          /* copy command */
278    parse_ua_args(ua);                 /* parse command */
279    stat = run_cmd(ua, ua->cmd);
280    free_ua_context(ua);
281    PyEval_AcquireLock();
282    return PyInt_FromLong((long)stat);
283 }
284
285 static PyObject *job_write(PyObject *self, PyObject *args)
286 {
287    char *text = NULL;
288
289    if (!PyArg_ParseTuple(args, "s:write", &text)) {
290       Dmsg0(000, "Parse tuple error in job_write\n");
291       return NULL;
292    }
293    if (text) {
294       Jmsg(NULL, M_INFO, 0, "%s", text);
295    }
296    Py_INCREF(Py_None);
297    return Py_None;
298 }
299
300 static PyObject *job_cancel(PyObject *self, PyObject *args)
301 {
302    JobId_t JobId = 0;
303    JCR *jcr;
304    bool found = false;
305
306    if (!PyArg_ParseTuple(args, "i:cancel", &JobId)) {
307       Dmsg0(000, "Parse tuple error in job_write\n");
308       return NULL;
309    }
310    foreach_jcr(jcr) {
311       if (jcr->JobId == 0) {
312          free_jcr(jcr);
313          continue;
314       }
315       if (jcr->JobId == JobId) {
316          found = true;
317          break;
318       }
319    }
320    if (!found) {
321       /* ***FIXME*** raise exception */
322       return NULL;
323    }
324    PyEval_ReleaseLock();
325    UAContext *ua = new_ua_context(jcr);
326    ua->batch = true;
327    if (!cancel_job(ua, jcr)) {
328       /* ***FIXME*** raise exception */
329       return NULL;
330    }
331    free_ua_context(ua);
332    free_jcr(jcr);
333    PyEval_AcquireLock();   
334    Py_INCREF(Py_None);
335    return Py_None;
336 }
337
338 /*
339  * Generate a Job event, which means look up the event
340  *  method defined by the user, and if it exists, 
341  *  call it.
342  */
343 int generate_job_event(JCR *jcr, const char *event)
344 {
345    PyObject *method = NULL;
346    PyObject *Job = (PyObject *)jcr->Python_job;
347    PyObject *events = (PyObject *)jcr->Python_events;
348    PyObject *result = NULL;
349    int stat = 0;
350
351    if (!Job || !events) {
352       return 0;
353    }
354
355    PyEval_AcquireLock();
356
357    method = find_method(events, method, event);
358    if (!method) {
359       goto bail_out;
360    }
361
362    bstrncpy(jcr->event, event, sizeof(jcr->event));
363    result = PyObject_CallFunction(method, "O", Job);
364    jcr->event[0] = 0;             /* no event in progress */
365    if (result == NULL) {
366       if (PyErr_Occurred()) {
367          PyErr_Print();
368          Dmsg1(000, "Error in Python method %s\n", event);
369       }
370    } else {
371       stat = 1;
372    }
373    Py_XDECREF(result);
374
375 bail_out:
376    PyEval_ReleaseLock();
377    return stat;
378 }
379
380 bool python_set_prog(JCR*, char const*) { return false; }
381
382 #else
383
384 /* Dummy if Python not configured */
385 int generate_job_event(JCR *jcr, const char *event) { return 1; }
386    
387
388 #endif /* HAVE_PYTHON */