--- /dev/null
+#
+# Bacula Python interface script
+#
+
+# You must import both sys and bacula
+import sys, bacula
+
+# This is the list of Bacula daemon events that you
+# can receive.
+class BaculaEvents:
+ def __init__(self):
+ # Called here when a new Bacula Events class is
+ # is created. Normally not used
+ noop = 1
+
+ def JobStart(self, job):
+ """
+ Called here when a new job is started. If you want
+ to do anything with the Job, you must register
+ events you want to receive.
+ """
+ events = JobEvents() # create instance of Job class
+ events.job = job # save Bacula's job pointer
+ job.set_events(events) # register events desired
+ sys.stderr = events # send error output to Bacula
+ sys.stdout = events # send stdout to Bacula
+ jobid = job.get("JobId"); client = job.get("Client");
+ numvols = job.get("NumVols")
+ job.set(JobReport="Python StartJob: JobId=%d Client=%s NumVols=%d\n" % (jobid,client,numvols))
+ return 1
+
+ # Bacula Job is going to terminate
+ def JobEnd(self, job):
+ jobid = job.get("JobId")
+ client = job.get("Client")
+ job.set(JobReport="Python EndJob output: JobId=%d Client=%s.\n" % (jobid, client))
+ if (jobid < 2) :
+ startid = job.run("run kernsave")
+ print "Python started new Job: jobid=", startid
+ return 1
+
+ # Called here when the Bacula daemon is going to exit
+ def Exit(self, job):
+ print "Daemon exiting."
+
+bacula.set_events(BaculaEvents()) # register daemon events desired
+
+"""
+ There are the Job events that you can receive.
+"""
+class JobEvents:
+ def __init__(self):
+ # Called here when you instantiate the Job. Not
+ # normally used
+ noop = 1
+
+ def NewVolume(self, job):
+ jobid = job.get("JobId")
+ client = job.get("Client")
+ numvol = job.get("NumVols");
+ print "JobId=%d Client=%s NumVols=%d" % (jobid, client, numvol)
+ job.set(JobReport="Python before New Volume set for Job.\n")
+ job.set(VolumeName="TestA-001")
+ job.set(JobReport="Python after New Volume set for Job.\n")
+ return 1
+
+
+ # Pass output back to Bacula
+ def write(self, text):
+ self.job.write(text)
+
+ # Open file to be backed up. file is the filename
+ def open(self, file):
+ print "Open %s called" % file
+ self.fd = open('m.py', 'rb')
+ jobid = self.job.get("JobId")
+ print "Open: JobId=%d" % jobid
+ print "name=%s" % bacula.name
+
+ # Read file data into Bacula memory buffer (mem)
+ # return length read. 0 => EOF, -1 => error
+ def read(self, mem):
+ print "Read called\n"
+ len = self.fd.readinto(mem)
+ print "Read %s bytes into mem.\n" % len
+ return len
+
+ # Close file
+ def close(self):
+ self.fd.close()
--- /dev/null
+#
+# Bacula Python interface script
+#
+
+# You must import both sys and bacula
+import sys, bacula
+
+# This is the list of Bacula daemon events that you
+# can receive.
+class BaculaEvents:
+ def __init__(self):
+ # Called here when a new Bacula Events class is
+ # is created. Normally not used
+ noop = 1
+
+ def JobStart(self, job):
+ """
+ Called here when a new job is started. If you want
+ to do anything with the Job, you must register
+ events you want to receive.
+ """
+ events = JobEvents() # create instance of Job class
+ events.job = job # save Bacula's job pointer
+ job.set_events(events) # register events desired
+ sys.stderr = events # send error output to Bacula
+ sys.stdout = events # send stdout to Bacula
+ jobid = job.get("JobId")
+ client = job.get("Client")
+ numvols = job.get("NumVols")
+ job.set(JobReport="Python StartJob: JobId=%d Client=%s NumVols=%d\n" % (jobid,client,numvols))
+ return 1
+
+ # Bacula Job is going to terminate
+ def JobEnd(self, job):
+ jobid = job.get("JobId")
+ client = job.get("Client")
+ job.set(JobReport="Python EndJob output: JobId=%d Client=%s.\n" % (jobid, client))
+ if (jobid < 2) :
+ startid = job.run("run kernsave")
+ print "Python started new Job: jobid=", startid
+ return 1
+
+ # Called here when the Bacula daemon is going to exit
+ def Exit(self, job):
+ noop = 1
+
+bacula.set_events(BaculaEvents()) # register daemon events desired
+
+"""
+ There are the Job events that you can receive.
+"""
+class JobEvents:
+ def __init__(self):
+ # Called here when you instantiate the Job. Not
+ # normally used
+ noop = 1
+
+ def NewVolume(self, job):
+ jobid = job.get("JobId")
+ print "JobId=", jobid
+ client = job.get("Client")
+ print "Client=" + client
+ numvol = job.get("NumVols");
+ print "NumVols=", numvol
+ job.set(JobReport="Python New Volume set for Job.\n")
+ job.set(VolumeName="TestA-001")
+ return 1
+
+
+ # Pass output back to Bacula
+ def write(self, text):
+ self.job.write(text)
+
+ # Open file to be backed up. file is the filename
+ def open(self, file):
+ print "Open %s called" % file
+ self.fd = open('m.py', 'rb')
+ jobid = self.job.get("JobId")
+ print "Open: JobId=%d" % jobid
+ print "name=%s" % bacula.name
+
+ # Read file data into Bacula memory buffer (mem)
+ # return length read. 0 => EOF, -1 => error
+ def read(self, mem):
+ print "Read called\n"
+ len = self.fd.readinto(mem)
+ print "Read %s bytes into mem.\n" % len
+ return len
+
+ # Close file
+ def close(self):
+ self.fd.close()
--- /dev/null
+#
+# Bacula Python interface script
+#
+
+# You must import both sys and bacula
+import sys, bacula
+
+# This is the list of Bacula daemon events that you
+# can receive.
+class BaculaEvents:
+ def __init__(self):
+ # Called here when a new Bacula Events class is
+ # is created. Normally not used
+ noop = 1
+
+ def JobStart(self, job):
+ """
+ Called here when a new job is started. If you want
+ to do anything with the Job, you must register
+ events you want to receive.
+ """
+ events = JobEvents() # create instance of Job class
+ events.job = job # save Bacula's job pointer
+ job.set_events(events) # register events desired
+ sys.stderr = events # send error output to Bacula
+ sys.stdout = events # send stdout to Bacula
+ jobid = job.get("JobId")
+ client = job.get("Client")
+ numvols = job.get("NumVols")
+ job.set(JobReport="Python StartJob: JobId=%d Client=%s NumVols=%d\n" % (jobid,client,numvols))
+ return 1
+
+ # Bacula Job is going to terminate
+ def JobEnd(self, job):
+ jobid = job.get("JobId")
+ client = job.get("Client")
+ job.set(JobReport="Python EndJob output: JobId=%d Client=%s.\n" % (jobid, client))
+ if (jobid < 2) :
+ startid = job.run("run kernsave")
+ print "Python started new Job: jobid=", startid
+ return 1
+
+ # Called here when the Bacula daemon is going to exit
+ def Exit(self, job):
+ noop = 1
+
+bacula.set_events(BaculaEvents()) # register daemon events desired
+
+"""
+ There are the Job events that you can receive.
+"""
+class JobEvents:
+ def __init__(self):
+ # Called here when you instantiate the Job. Not
+ # normally used
+ noop = 1
+
+ def NewVolume(self, job):
+ jobid = job.get("JobId")
+ print "JobId=", jobid
+ client = job.get("Client")
+ print "Client=" + client
+ numvol = job.get("NumVols");
+ print "NumVols=", numvol
+ job.set(JobReport="Python New Volume set for Job.\n")
+ job.set(VolumeName="TestA-001")
+ return 1
+
+
+ # Pass output back to Bacula
+ def write(self, text):
+ self.job.write(text)
+
+ # Open file to be backed up. file is the filename
+ def open(self, file):
+ print "Open %s called" % file
+ self.fd = open('m.py', 'rb')
+ jobid = self.job.get("JobId")
+ print "Open: JobId=%d" % jobid
+ print "name=%s" % bacula.name
+
+ # Read file data into Bacula memory buffer (mem)
+ # return length read. 0 => EOF, -1 => error
+ def read(self, mem):
+ print "Read called\n"
+ len = self.fd.readinto(mem)
+ print "Read %s bytes into mem.\n" % len
+ return len
+
+ # Close file
+ def close(self):
+ self.fd.close()
init_console_msg(working_directory);
- init_python_interpreter(director->hdr.name, director->scripts_directory ?
- director->scripts_directory : ".", "DirStartUp");
+ init_python_interpreter(director->hdr.name, director->scripts_directory,
+ "DirStartUp");
set_thread_concurrency(director->MaxConcurrentJobs * 2 +
4 /* UA */ + 4 /* sched+watchdog+jobsvr+misc */);
/* Cleanup and then exit */
static void terminate_dird(int sig)
{
- static int already_here = FALSE;
+ static bool already_here = false;
if (already_here) { /* avoid recursive temination problems */
exit(1);
}
- already_here = TRUE;
+ already_here = true;
+ generate_daemon_event(NULL, "Exit");
write_state_file(director->working_directory, "bacula-dir", get_first_port_host_order(director->DIRaddrs));
delete_pid_file(director->pid_directory, "bacula-dir", get_first_port_host_order(director->DIRaddrs));
// signal(SIGCHLD, SIG_IGN); /* don't worry about children now */
}
jcr->term_wait_inited = true;
- generate_daemon_event(jcr, "StartJob");
/*
* Open database
}
Dmsg0(50, "DB opened\n");
+
/*
* Create Job record
*/
goto bail_out;
}
JobId = jcr->JobId = jcr->jr.JobId;
-
Dmsg4(100, "Created job record JobId=%d Name=%s Type=%c Level=%c\n",
jcr->JobId, jcr->Job, jcr->jr.JobType, jcr->jr.JobLevel);
+ generate_daemon_event(jcr, "JobStart");
+
if (!get_or_create_client_record(jcr)) {
goto bail_out;
}
#include <Python.h>
extern JCR *get_jcr_from_PyObject(PyObject *self);
-extern PyObject *find_method(PyObject *eventsObject, PyObject *method, char *name);
+extern PyObject *find_method(PyObject *eventsObject, PyObject *method,
+ const char *name);
-static PyObject *jcr_get(PyObject *self, PyObject *args);
+static PyObject *job_get(PyObject *self, PyObject *args);
static PyObject *jcr_write(PyObject *self, PyObject *args);
static PyObject *jcr_set(PyObject *self, PyObject *args, PyObject *keyw);
-static PyObject *set_jcr_events(PyObject *self, PyObject *args);
+static PyObject *set_job_events(PyObject *self, PyObject *args);
static PyObject *jcr_run(PyObject *self, PyObject *args);
/* Define Job entry points */
PyMethodDef JobMethods[] = {
- {"get", jcr_get, METH_VARARGS, "Get Job variables."},
+ {"get", job_get, METH_VARARGS, "Get Job variables."},
{"set", (PyCFunction)jcr_set, METH_VARARGS|METH_KEYWORDS,
"Set Job variables."},
- {"set_events", set_jcr_events, METH_VARARGS, "Define Job events."},
+ {"set_events", set_job_events, METH_VARARGS, "Define Job events."},
{"write", jcr_write, METH_VARARGS, "Write output."},
{"run", (PyCFunction)jcr_run, METH_VARARGS, "Run a Bacula command."},
{NULL, NULL, 0, NULL} /* last item */
};
-static PyObject *open_method = NULL;
-static PyObject *read_method = NULL;
-static PyObject *close_method = NULL;
-
struct s_vars {
const char *name;
char *fmt;
};
/* Return Job variables */
-static PyObject *jcr_get(PyObject *self, PyObject *args)
+static PyObject *job_get(PyObject *self, PyObject *args)
{
JCR *jcr;
char *item;
int i;
char buf[10];
+ Dmsg0(100, "In jcr_get.\n");
if (!PyArg_ParseTuple(args, "s:get", &item)) {
return NULL;
}
char *msg = NULL;
char *VolumeName = NULL;
static char *kwlist[] = {"JobReport", "VolumeName", NULL};
+
+ Dmsg0(100, "In jcr_set.\n");
if (!PyArg_ParseTupleAndKeywords(args, keyw, "|ss:set", kwlist,
&msg, &VolumeName)) {
return NULL;
if (msg) {
Jmsg(jcr, M_INFO, 0, "%s", msg);
}
+
+ if (!VolumeName) {
+ Dmsg1(100, "No VolumeName. Event=%s\n", jcr->event);
+ } else {
+ Dmsg2(100, "Vol=%s Event=%s\n", VolumeName, jcr->event);
+ }
/* Make sure VolumeName is valid and we are in VolumeName event */
- if (VolumeName && strcmp("VolumeName", jcr->event) == 0 &&
+ if (VolumeName && strcmp("NewVolume", jcr->event) == 0 &&
is_volume_name_legal(NULL, VolumeName)) {
pm_strcpy(jcr->VolumeName, VolumeName);
- } else {
+ Dmsg1(100, "Set Vol=%s\n", VolumeName);
+ } else if (VolumeName) {
jcr->VolumeName[0] = 0;
+ Dmsg0(100, "Zap VolumeName.\n");
return Py_BuildValue("i", 0); /* invalid volume name */
}
return Py_BuildValue("i", 1);
}
-static PyObject *set_jcr_events(PyObject *self, PyObject *args)
+static PyObject *set_job_events(PyObject *self, PyObject *args)
{
PyObject *eObject;
+ JCR *jcr;
+
+ Dmsg0(100, "In set_job_events.\n");
if (!PyArg_ParseTuple(args, "O:set_events_hook", &eObject)) {
return NULL;
}
- Py_XINCREF(eObject);
- open_method = find_method(eObject, open_method, "open");
- read_method = find_method(eObject, read_method, "read");
- close_method = find_method(eObject, close_method, "close");
+ jcr = get_jcr_from_PyObject(self);
+ Py_XDECREF((PyObject *)jcr->Python_events);
+ Py_INCREF(eObject);
+ jcr->Python_events = (void *)eObject;
+
Py_INCREF(Py_None);
return Py_None;
}
static PyObject *jcr_write(PyObject *self, PyObject *args)
{
char *text;
+
if (!PyArg_ParseTuple(args, "s:write", &text)) {
return NULL;
}
int generate_job_event(JCR *jcr, const char *event)
{
-#ifdef xxx
- PyObject *eObject, *method;
+ PyObject *method = NULL;
+ PyObject *Job = (PyObject *)jcr->Python_job;
+ PyObject *result = NULL;
+ int stat = 0;
+
+ if (!Job) {
+ return 0;
+ }
+
PyEval_AcquireLock();
- method = find_method(eObject, method, event);
+ PyObject *events = (PyObject *)jcr->Python_events;
+ method = find_method(events, method, event);
+ if (!method) {
+ goto bail_out;
+ }
- PyObject *result = PyObject_CallFunction(method, "s", "m.py");
+ bstrncpy(jcr->event, event, sizeof(jcr->event));
+ result = PyObject_CallFunction(method, "O", Job);
+ jcr->event[0] = 0; /* no event in progress */
if (result == NULL) {
- PyErr_Print();
- PyErr_Clear();
+ if (PyErr_Occurred()) {
+ PyErr_Print();
+ Dmsg1(000, "Error in Python method %s\n", event);
+ }
+ } else {
+ stat = 1;
}
Py_XDECREF(result);
+bail_out:
PyEval_ReleaseLock();
-#endif
- return 1;
+ return stat;
}
#else
{
if (strcasecmp(ua->argk[1], _("restart")) == 0) {
term_python_interpreter();
- init_python_interpreter(director->hdr.name, director->scripts_directory ?
- director->scripts_directory : ".", "DirStartUp");
+ init_python_interpreter(director->hdr.name,
+ director->scripts_directory, "DirStartUp");
bsendmsg(ua, _("Python interpreter restarted.\n"));
} else {
bsendmsg(ua, _("Nothing done.\n"));
me += 1000000;
#endif
- init_python_interpreter(me->hdr.name, me->scripts_directory ?
- me->scripts_directory : ".", "FDStartUp");
+ init_python_interpreter(me->hdr.name, me->scripts_directory, "FDStartUp");
set_thread_concurrency(10);
#include <Python.h>
extern JCR *get_jcr_from_PyObject(PyObject *self);
-extern PyObject *find_method(PyObject *eventsObject, PyObject *method, char *name);
+extern PyObject *find_method(PyObject *eventsObject, PyObject *method,
+ const char *name);
static PyObject *jcr_get(PyObject *self, PyObject *args);
static PyObject *jcr_write(PyObject *self, PyObject *args);
bool prefix_links; /* Prefix links with Where path */
bool gui; /* set if gui using console */
bool authenticated; /* set when client authenticated */
- void *Python_jcr; /* Python Job Object */
+ void *Python_job; /* Python Job Object */
+ void *Python_events; /* Python Events Object */
/* Daemon specific part of JCR */
/* This should be empty in the library */
queue.c res.c rwlock.c scan.c serial.c sha1.c \
semlock.c signal.c smartall.c tree.c \
util.c var.c watchdog.c workq.c btimers.c \
- address_conf.c python.c
+ address_conf.c pythonlib.c
LIBOBJS = alloc.o attr.o base64.o berrno.o bsys.o bget_msg.o \
queue.o res.o rwlock.o scan.o serial.o sha1.o \
semlock.o signal.o smartall.o tree.o \
util.o var.o watchdog.o workq.o btimers.o \
- address_conf.o python.o
+ address_conf.o pythonlib.o
EXTRAOBJS = @OBJLIST@
cd $(topdir) \
&& CONFIG_FILES=$(thisdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status
-python.o: python.c
+pythonlib.o: pythonlib.c
$(CXX) $(DEFS) $(DEBUG) -c $(WCFLAGS) $(CPPFLAGS) $(python) -I$(srcdir) -I$(basedir) $(DINCLUDE) $(CFLAGS) $<
rwlock_test:
#endif
dequeue_messages(jcr);
- if (jcr->JobId != 0) {
- generate_daemon_event(jcr, "JobEnd");
- }
lock_jcr_chain();
jcr->use_count--; /* decrement use count */
if (jcr->use_count < 0) {
Dmsg2(3400, "free_jcr 0x%x use_count=%d\n", jcr, jcr->use_count);
return;
}
+
+ /*
+ * At this point, we are actually releasing the JCR, which
+ * means that the job is complete.
+ */
+ if (jcr->JobId != 0) {
+ generate_daemon_event(jcr, "JobEnd");
+ }
remove_jcr(jcr); /* remove Jcr from chain */
unlock_jcr_chain();
+++ /dev/null
-/*
- *
- * Bacula common code library interface to Python
- *
- * Kern Sibbald, November MMIV
- *
- * Version $Id$
- *
- */
-
-/*
- Copyright (C) 2004-2005 Kern Sibbald
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of
- the License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public
- License along with this program; if not, write to the Free
- Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
- MA 02111-1307, USA.
-
- */
-
-#include "bacula.h"
-#include "jcr.h"
-
-#ifdef HAVE_PYTHON
-
-#undef _POSIX_C_SOURCE
-#include <Python.h>
-
-static PyObject *bacula_module = NULL; /* We create this */
-static PyObject *StartUp_module = NULL; /* We import this */
-
-/* These are the daemon events or methods that are defined */
-static PyObject *JobStart_method = NULL;
-static PyObject *JobEnd_method = NULL;
-static PyObject *Exit_method = NULL;
-
-
-static PyObject *set_bacula_events(PyObject *self, PyObject *args);
-static PyObject *bacula_write(PyObject *self, PyObject *args);
-
-PyObject *find_method(PyObject *eventsObject, PyObject *method, char *name);
-
-/* Define Bacula daemon method entry points */
-static PyMethodDef BaculaMethods[] = {
- {"set_events", set_bacula_events, METH_VARARGS, "Define Bacula events."},
- {"write", bacula_write, METH_VARARGS, "Write output."},
- {NULL, NULL, 0, NULL} /* last item */
-};
-
-
-/*
- * This is a Bacula Job type as defined in Python. We store a pointer
- * to the jcr. That is all we need, but the user's script may keep
- * local data attached to this.
- */
-typedef struct JCRObject {
- PyObject_HEAD
- JCR *jcr;
-} JCRObject;
-
-static PyTypeObject JCRType = {
- PyObject_HEAD_INIT(NULL)
- 0, /*ob_size*/
- "bacula.jcr", /*tp_name*/
- sizeof(JCRObject), /*tp_basicsize*/
- 0, /*tp_itemsize*/
- 0, /*tp_dealloc*/
- 0, /*tp_print*/
- 0, /*tp_getattr*/
- 0, /*tp_setattr*/
- 0, /*tp_compare*/
- 0, /*tp_repr*/
- 0, /*tp_as_number*/
- 0, /*tp_as_sequence*/
- 0, /*tp_as_mapping*/
- 0, /*tp_hash */
- 0, /*tp_call*/
- 0, /*tp_str*/
- 0, /*tp_getattro*/
- 0, /*tp_setattro*/
- 0, /*tp_as_buffer*/
- Py_TPFLAGS_DEFAULT, /*tp_flags*/
- "JCR objects", /* tp_doc */
- 0, /* tp_traverse */
- 0, /* tp_clear */
- 0, /* tp_richcompare */
- 0, /* tp_weaklistoffset */
- 0, /* tp_iter */
- 0, /* tp_iternext */
- 0, /* tp_methods */
- 0, /* tp_members */
- 0, /* tp_getset */
- 0, /* tp_base */
- 0, /* tp_dict */
- 0, /* tp_descr_get */
- 0, /* tp_descr_set */
- 0, /* tp_dictoffset */
- 0, /* tp_init */
- 0, /* tp_alloc */
- 0, /* tp_new */
-};
-
-/* Return the JCR pointer from the JCRObject */
-JCR *get_jcr_from_PyObject(PyObject *self)
-{
- return ((JCRObject *)self)->jcr;
-}
-
-
-/* Start the interpreter */
-void init_python_interpreter(const char *progname, const char *scripts,
- const char *module)
-{
- char buf[MAXSTRING];
- Py_SetProgramName((char *)progname);
- Py_Initialize();
- PyEval_InitThreads();
- bacula_module = Py_InitModule("bacula", BaculaMethods);
- if (!bacula_module) {
- Jmsg(NULL, M_ERROR_TERM, 0, "Could not initialize Python\n");
- }
- bsnprintf(buf, sizeof(buf), "import sys\n"
- "sys.path.append('%s')\n", scripts);
- if (PyRun_SimpleString(buf) != 0) {
- Jmsg(NULL, M_ERROR_TERM, 0, "Could not Run Python string %s\n", buf);
- }
- JCRType.tp_methods = BaculaMethods;
- if(PyType_Ready(&JCRType) != 0) {
- Jmsg(NULL, M_ERROR_TERM, 0, "Could not initialize Python Job type.\n");
- PyErr_Print();
- }
- StartUp_module = PyImport_ImportModule((char *)module);
- if (!StartUp_module) {
- Jmsg(NULL, M_ERROR_TERM, 0, "Could not import script %s/%s.\n",
- scripts, module);
- PyErr_Clear();
- }
- PyEval_ReleaseLock();
-}
-
-
-void term_python_interpreter()
-{
- Py_XDECREF(StartUp_module);
- Py_Finalize();
-}
-
-static PyObject *set_bacula_events(PyObject *self, PyObject *args)
-{
- PyObject *eObject;
- if (!PyArg_ParseTuple(args, "O:set_bacula_events", &eObject)) {
- return NULL;
- }
- JobStart_method = find_method(eObject, JobStart_method, "JobStart");
- JobEnd_method = find_method(eObject, JobEnd_method, "JobEnd");
- Exit_method = find_method(eObject, Exit_method, "Exit");
-
- Py_XINCREF(eObject);
- Py_INCREF(Py_None);
- return Py_None;
-}
-
-/* Write text to daemon output */
-static PyObject *bacula_write(PyObject *self, PyObject *args)
-{
- char *text;
- if (!PyArg_ParseTuple(args, "s:write", &text)) {
- return NULL;
- }
- if (text) {
- Jmsg(NULL, M_INFO, 0, "%s", text);
- }
- Py_INCREF(Py_None);
- return Py_None;
-}
-
-
-/*
- * Check that a method exists and is callable.
- */
-PyObject *find_method(PyObject *eventsObject, PyObject *method, char *name)
-{
- Py_XDECREF(method); /* release old method if any */
- method = PyObject_GetAttrString(eventsObject, name);
- if (method == NULL) {
- Dmsg1(000, "Python method %s not found\n", name);
- } else if (PyCallable_Check(method) == 0) {
- Dmsg1(000, "Python object %s found but not a method.\n", name);
- Py_XDECREF(method);
- method = NULL;
- } else {
- Dmsg1(000, "Got method %s\n", name);
- }
- return method;
-}
-
-
-/*
- * Generate and process a Bacula event by importing a Python
- * module and running it.
- *
- * Returns: 0 if Python not configured or module not found
- * -1 on Python error
- * 1 OK
- */
-int generate_daemon_event(JCR *jcr, const char *event)
-{
- PyObject *pJCR;
- int stat = -1;
- PyObject *result = NULL;
-
- PyEval_AcquireLock();
- if (strcmp(event, "JobStart") == 0) {
- if (!JobStart_method) {
- stat = 0;
- goto bail_out;
- }
- /* Create JCR argument to send to function */
- pJCR = (PyObject *)PyObject_New(JCRObject, &JCRType);
- if (!pJCR) {
- Jmsg(jcr, M_ERROR, 0, "Could not create JCR Python Object.\n");
- goto bail_out;
- }
- ((JCRObject *)pJCR)->jcr = jcr;
- bstrncpy(jcr->event, event, sizeof(jcr->event));
- result = PyObject_CallFunction(JobStart_method, "O", pJCR);
- jcr->event[0] = 0; /* no event in progress */
- if (result == NULL) {
- JobStart_method = NULL;
- if (PyErr_Occurred()) {
- PyErr_Print();
- }
- Jmsg(jcr, M_ERROR, 0, "Python function \"%s\" not found.\n", event);
- Py_XDECREF(pJCR);
- goto bail_out;
- }
- jcr->Python_jcr = (void *)pJCR;
- stat = 1; /* OK */
-
- } else if (strcmp(event, "JobEnd") == 0) {
- if (!JobEnd_method) {
- Py_XDECREF((PyObject *)jcr->Python_jcr);
- stat = 0;
- goto bail_out;
- }
- bstrncpy(jcr->event, event, sizeof(jcr->event));
- result = PyObject_CallFunction(JobEnd_method, "O", jcr->Python_jcr);
- jcr->event[0] = 0; /* no event in progress */
- if (result == NULL) {
- JobEnd_method = NULL;
- if (PyErr_Occurred()) {
- PyErr_Print();
- }
- Jmsg(jcr, M_ERROR, 0, "Python function \"%s\" not found.\n", event);
- Py_XDECREF((PyObject *)jcr->Python_jcr);
- goto bail_out;
- }
- Py_XDECREF((PyObject *)jcr->Python_jcr);
- stat = 1; /* OK */
- }
-
-bail_out:
- Py_XDECREF(result);
- PyEval_ReleaseLock();
- return stat;
-}
-
-#ifdef xxx
- PyObject *pName, *pModule, *pDict, *pFunc;
- PyObject *pArgs, *pJCR, *pCall;
-
- Dmsg1(100, "Generate event %s\n", event);
- pName = PyString_FromString(event);
- if (!pName) {
- Jmsg(jcr, M_ERROR, 0, "Could not convert \"%s\" to Python string.\n", event);
- return -1; /* Could not convert string */
- }
-
- pModule = PyImport_Import(pName);
- Py_DECREF(pName); /* release pName */
-
- if (pModule != NULL) {
- pDict = PyModule_GetDict(pModule);
- /* pDict is a borrowed reference */
-
- pFunc = PyDict_GetItemString(pDict, (char *)event);
- /* pFun: Borrowed reference */
-
- if (pFunc && PyCallable_Check(pFunc)) {
- /* Create JCR argument to send to function */
- pArgs = PyTuple_New(1);
- pJCR = (PyObject *)PyObject_New(JCRObject, &JCRType);
- ((JCRObject *)pJCR)->jcr = jcr;
- if (!pJCR) {
- Py_DECREF(pArgs);
- Py_DECREF(pModule);
- Jmsg(jcr, M_ERROR, 0, "Could not create JCR Python Object.\n");
- return -1;
- }
- Py_INCREF(pJCR);
- /* pJCR reference stolen here: */
- PyTuple_SetItem(pArgs, 0, pJCR);
-
- /* Finally, we call the module here */
- bstrncpy(jcr->event, event, sizeof(jcr->event));
- pCall = PyObject_CallObject(pFunc, pArgs);
- jcr->event[0] = 0; /* no event in progress */
- Py_DECREF(pArgs);
- Py_DECREF(pJCR);
- if (pCall != NULL) {
- Py_DECREF(pCall);
- } else {
- Py_DECREF(pModule);
- PyErr_Print();
- Jmsg(jcr, M_ERROR, 0, "Error running Python module: %s\n", event);
- return 0; /* error running function */
- }
- /* pDict and pFunc are borrowed and must not be Py_DECREF-ed */
- } else {
- if (PyErr_Occurred()) {
- PyErr_Print();
- }
- Jmsg(jcr, M_ERROR, 0, "Python function \"%s\" not found in module.\n", event);
- return -1; /* function not found */
- }
- Py_DECREF(pModule);
- } else {
- return 0; /* Module not present */
- }
- Dmsg0(100, "Generate event OK\n");
- return 1;
-}
-#endif
-
-#else
-
-/*
- * No Python configured -- create external entry points and
- * dummy routines so that library code can call this without
- * problems even if it is not configured.
- */
-int generate_daemon_event(JCR *jcr, const char *event) { return 0; }
-void init_python_interpreter(const char *progname, const char *scripts,
- const char *module) { }
-void term_python_interpreter() { }
-
-#endif /* HAVE_PYTHON */
--- /dev/null
+/*
+ *
+ * Bacula common code library interface to Python
+ *
+ * Kern Sibbald, November MMIV
+ *
+ * Version $Id$
+ *
+ */
+
+/*
+ Copyright (C) 2004-2005 Kern Sibbald
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with this program; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ MA 02111-1307, USA.
+
+ */
+
+#include "bacula.h"
+#include "jcr.h"
+
+#ifdef HAVE_PYTHON
+
+#undef _POSIX_C_SOURCE
+#include <Python.h>
+
+/* Imported subroutines */
+extern PyMethodDef JobMethods[];
+
+static PyObject *bacula_module = NULL; /* We create this */
+static PyObject *StartUp_module = NULL; /* We import this */
+
+/* These are the daemon events or methods that are defined */
+static PyObject *JobStart_method = NULL;
+static PyObject *JobEnd_method = NULL;
+static PyObject *Exit_method = NULL;
+
+static PyObject *set_bacula_events(PyObject *self, PyObject *args);
+static PyObject *bacula_write(PyObject *self, PyObject *args);
+
+PyObject *find_method(PyObject *eventsObject, PyObject *method, const char *name);
+
+/* Define Bacula daemon method entry points */
+static PyMethodDef BaculaMethods[] = {
+ {"set_events", set_bacula_events, METH_VARARGS, "Define Bacula events."},
+ {"write", bacula_write, METH_VARARGS, "Write output."},
+ {NULL, NULL, 0, NULL} /* last item */
+};
+
+
+/*
+ * This is a Bacula Job type as defined in Python. We store a pointer
+ * to the jcr. That is all we need, but the user's script may keep
+ * local data attached to this.
+ */
+typedef struct s_JobObject {
+ PyObject_HEAD
+ JCR *jcr;
+} JobObject;
+
+static PyTypeObject JobType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "bacula.jcr", /*tp_name*/
+ sizeof(JobObject), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ 0, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT, /*tp_flags*/
+ "Job objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ JobMethods, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
+
+/* Return the JCR pointer from the JobObject */
+JCR *get_jcr_from_PyObject(PyObject *self)
+{
+ return ((JobObject *)self)->jcr;
+}
+
+
+/* Start the interpreter */
+void init_python_interpreter(const char *progname, const char *scripts,
+ const char *module)
+{
+ char buf[MAXSTRING];
+
+ if (!scripts || scripts[0] == 0) {
+ return;
+ }
+
+ Py_SetProgramName((char *)progname);
+ Py_Initialize();
+ PyEval_InitThreads();
+ bacula_module = Py_InitModule("bacula", BaculaMethods);
+ if (!bacula_module) {
+ Jmsg0(NULL, M_ERROR_TERM, 0, "Could not initialize Python\n");
+ }
+ bsnprintf(buf, sizeof(buf), "import sys\n"
+ "sys.path.append('%s')\n", scripts);
+ if (PyRun_SimpleString(buf) != 0) {
+ Jmsg1(NULL, M_ERROR_TERM, 0, "Could not Run Python string %s\n", buf);
+ }
+ JobType.tp_methods = JobMethods;
+ if(PyType_Ready(&JobType) != 0) {
+ Jmsg0(NULL, M_ERROR_TERM, 0, "Could not initialize Python Job type.\n");
+ PyErr_Print();
+ }
+ StartUp_module = PyImport_ImportModule((char *)module);
+ if (!StartUp_module) {
+ Emsg2(M_ERROR, 0, "Could not import Python script %s/%s. Python disabled.\n",
+ scripts, module);
+ if (PyErr_Occurred()) {
+ PyErr_Print();
+ Dmsg0(000, "Python Import error.\n");
+ }
+ }
+ PyEval_ReleaseLock();
+}
+
+
+void term_python_interpreter()
+{
+ if (StartUp_module) {
+ Py_XDECREF(StartUp_module);
+ Py_Finalize();
+ }
+}
+
+static PyObject *set_bacula_events(PyObject *self, PyObject *args)
+{
+ PyObject *eObject;
+
+ Dmsg0(100, "In set_bacula_events.\n");
+ if (!PyArg_ParseTuple(args, "O:set_bacula_events", &eObject)) {
+ return NULL;
+ }
+ JobStart_method = find_method(eObject, JobStart_method, "JobStart");
+ JobEnd_method = find_method(eObject, JobEnd_method, "JobEnd");
+ Exit_method = find_method(eObject, Exit_method, "Exit");
+
+ Py_XINCREF(eObject);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+/* Write text to daemon output */
+static PyObject *bacula_write(PyObject *self, PyObject *args)
+{
+ char *text;
+ if (!PyArg_ParseTuple(args, "s:write", &text)) {
+ return NULL;
+ }
+ if (text) {
+ Jmsg(NULL, M_INFO, 0, "%s", text);
+ }
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+
+/*
+ * Check that a method exists and is callable.
+ */
+PyObject *find_method(PyObject *eventsObject, PyObject *method, const char *name)
+{
+ Py_XDECREF(method); /* release old method if any */
+ method = PyObject_GetAttrString(eventsObject, (char *)name);
+ if (method == NULL) {
+ Dmsg1(000, "Python method %s not found\n", name);
+ } else if (PyCallable_Check(method) == 0) {
+ Dmsg1(000, "Python object %s found but not a method.\n", name);
+ Py_XDECREF(method);
+ method = NULL;
+ } else {
+ Dmsg1(100, "Got method %s\n", name);
+ }
+ return method;
+}
+
+
+/*
+ * Generate and process a Bacula event by importing a Python
+ * module and running it.
+ *
+ * Returns: 0 if Python not configured or module not found
+ * -1 on Python error
+ * 1 OK
+ */
+int generate_daemon_event(JCR *jcr, const char *event)
+{
+ PyObject *pJob;
+ int stat = -1;
+ PyObject *result = NULL;
+
+ if (!StartUp_module) {
+ return 0;
+ }
+
+ PyEval_AcquireLock();
+ if (strcmp(event, "JobStart") == 0) {
+ if (!JobStart_method) {
+ stat = 0;
+ goto bail_out;
+ }
+ /* Create JCR argument to send to function */
+ pJob = (PyObject *)PyObject_New(JobObject, &JobType);
+ if (!pJob) {
+ Jmsg(jcr, M_ERROR, 0, "Could not create Python Job Object.\n");
+ goto bail_out;
+ }
+ ((JobObject *)pJob)->jcr = jcr;
+ bstrncpy(jcr->event, event, sizeof(jcr->event));
+ result = PyObject_CallFunction(JobStart_method, "O", pJob);
+ jcr->event[0] = 0; /* no event in progress */
+ if (result == NULL) {
+ JobStart_method = NULL;
+ if (PyErr_Occurred()) {
+ PyErr_Print();
+ Dmsg0(000, "Python JobStart error.\n");
+ }
+ Jmsg(jcr, M_ERROR, 0, "Python function \"%s\" not found.\n", event);
+ Py_XDECREF(pJob);
+ goto bail_out;
+ }
+ jcr->Python_job = (void *)pJob;
+ stat = 1; /* OK */
+ goto jobstart_ok;
+
+ } else if (strcmp(event, "JobEnd") == 0) {
+ if (!JobEnd_method) {
+ stat = 0;
+ goto bail_out;
+ }
+ bstrncpy(jcr->event, event, sizeof(jcr->event));
+ result = PyObject_CallFunction(JobEnd_method, "O", jcr->Python_job);
+ jcr->event[0] = 0; /* no event in progress */
+ if (result == NULL) {
+ JobEnd_method = NULL;
+ if (PyErr_Occurred()) {
+ PyErr_Print();
+ Dmsg1(000, "Python JobEnd error. JobId=%d\n", jcr->JobId);
+ }
+ Jmsg(jcr, M_ERROR, 0, "Python function \"%s\" not found.\n", event);
+ goto bail_out;
+ }
+ stat = 1; /* OK */
+ } else if (strcmp(event, "Exit") == 0) {
+ if (!Exit_method) {
+ stat = 0;
+ goto bail_out;
+ }
+ result = PyObject_CallFunction(JobEnd_method, NULL);
+ if (result == NULL) {
+ goto bail_out;
+ }
+ stat = 1; /* OK */
+ } else {
+ Emsg1(M_ABORT, 0, "Unknown Python daemon event %s\n", event);
+ }
+
+bail_out:
+ Py_XDECREF((PyObject *)jcr->Python_job);
+ jcr->Python_job = NULL;
+ Py_XDECREF((PyObject *)jcr->Python_events);
+ jcr->Python_events = NULL;
+ /* Fall through */
+jobstart_ok:
+ Py_XDECREF(result);
+ PyEval_ReleaseLock();
+ return stat;
+}
+
+#ifdef xxx
+ PyObject *pName, *pModule, *pDict, *pFunc;
+ PyObject *pArgs, *pJCR, *pCall;
+
+ Dmsg1(100, "Generate event %s\n", event);
+ pName = PyString_FromString(event);
+ if (!pName) {
+ Jmsg(jcr, M_ERROR, 0, "Could not convert \"%s\" to Python string.\n", event);
+ return -1; /* Could not convert string */
+ }
+
+ pModule = PyImport_Import(pName);
+ Py_DECREF(pName); /* release pName */
+
+ if (pModule != NULL) {
+ pDict = PyModule_GetDict(pModule);
+ /* pDict is a borrowed reference */
+
+ pFunc = PyDict_GetItemString(pDict, (char *)event);
+ /* pFun: Borrowed reference */
+
+ if (pFunc && PyCallable_Check(pFunc)) {
+ /* Create JCR argument to send to function */
+ pArgs = PyTuple_New(1);
+ pJCR = (PyObject *)PyObject_New(JCRObject, &JCRType);
+ ((JCRObject *)pJCR)->jcr = jcr;
+ if (!pJCR) {
+ Py_DECREF(pArgs);
+ Py_DECREF(pModule);
+ Jmsg0(jcr, M_ERROR, 0, "Could not create JCR Python Object.\n");
+ return -1;
+ }
+ Py_INCREF(pJCR);
+ /* pJCR reference stolen here: */
+ PyTuple_SetItem(pArgs, 0, pJCR);
+
+ /* Finally, we call the module here */
+ bstrncpy(jcr->event, event, sizeof(jcr->event));
+ pCall = PyObject_CallObject(pFunc, pArgs);
+ jcr->event[0] = 0; /* no event in progress */
+ Py_DECREF(pArgs);
+ Py_DECREF(pJCR);
+ if (pCall != NULL) {
+ Py_DECREF(pCall);
+ } else {
+ Py_DECREF(pModule);
+ PyErr_Print();
+ Jmsg1(jcr, M_ERROR, 0, "Error running Python module: %s\n", event);
+ return 0; /* error running function */
+ }
+ /* pDict and pFunc are borrowed and must not be Py_DECREF-ed */
+ } else {
+ if (PyErr_Occurred()) {
+ PyErr_Print();
+ Dmsg1(000, "Python event %s function not callable.\n", event);
+ }
+ Jmsg1(jcr, M_ERROR, 0, "Python function \"%s\" not found in module.\n", event);
+ return -1; /* function not found */
+ }
+ Py_DECREF(pModule);
+ } else {
+ return 0; /* Module not present */
+ }
+ Dmsg0(100, "Generate event OK\n");
+ return 1;
+}
+#endif
+
+#else
+
+/*
+ * No Python configured -- create external entry points and
+ * dummy routines so that library code can call this without
+ * problems even if it is not configured.
+ */
+int generate_daemon_event(JCR *jcr, const char *event) { return 0; }
+void init_python_interpreter(const char *progname, const char *scripts,
+ const char *module) { }
+void term_python_interpreter() { }
+
+#endif /* HAVE_PYTHON */
#include <Python.h>
extern JCR *get_jcr_from_PyObject(PyObject *self);
-extern PyObject *find_method(PyObject *eventsObject, PyObject *method, char *name);
+extern PyObject *find_method(PyObject *eventsObject, PyObject *method,
+ const char *name);
static PyObject *jcr_get(PyObject *self, PyObject *args);
static PyObject *jcr_write(PyObject *self, PyObject *args);
Jmsg0(NULL, M_ABORT, 0, _("Volume Session Time is ZERO!\n"));
}
- init_python_interpreter(me->hdr.name, me->scripts_directory ?
- me->scripts_directory : ".", "SDStartUp");
+ init_python_interpreter(me->hdr.name, me->scripts_directory, "SDStartUp");
/* Make sure on Solaris we can run concurrent, watch dog + servers + misc */
set_thread_concurrency(me->max_concurrent_jobs * 2 + 4);