From ec5a4ec2639dc12fb79faad0d76513a9b1e4e87a Mon Sep 17 00:00:00 2001 From: Kern Sibbald Date: Tue, 19 Apr 2005 16:49:55 +0000 Subject: [PATCH] - Fix new Python code to work for Director. - Move lib/python.c to lib/pythonlib.c so that debug output is easier to read (can distinguish lib from dird, ...). git-svn-id: https://bacula.svn.sourceforge.net/svnroot/bacula/trunk@1937 91ce42f0-d328-0410-95d8-f526ca767f89 --- bacula/examples/python/DirStartUp.py | 90 ++++++++++++++++++ bacula/examples/python/FDStartUp.py | 92 +++++++++++++++++++ bacula/examples/python/SDStartUp.py | 92 +++++++++++++++++++ bacula/src/dird/dird.c | 9 +- bacula/src/dird/job.c | 5 +- bacula/src/dird/python.c | 80 +++++++++++----- bacula/src/dird/ua_cmds.c | 4 +- bacula/src/filed/filed.c | 3 +- bacula/src/filed/python.c | 3 +- bacula/src/jcr.h | 3 +- bacula/src/lib/Makefile.in | 6 +- bacula/src/lib/jcr.c | 11 ++- bacula/src/lib/{python.c => pythonlib.c} | 111 +++++++++++++++-------- bacula/src/stored/python.c | 3 +- bacula/src/stored/stored.c | 3 +- 15 files changed, 432 insertions(+), 83 deletions(-) create mode 100644 bacula/examples/python/DirStartUp.py create mode 100644 bacula/examples/python/FDStartUp.py create mode 100644 bacula/examples/python/SDStartUp.py rename bacula/src/lib/{python.c => pythonlib.c} (78%) diff --git a/bacula/examples/python/DirStartUp.py b/bacula/examples/python/DirStartUp.py new file mode 100644 index 0000000000..2a810f4bc7 --- /dev/null +++ b/bacula/examples/python/DirStartUp.py @@ -0,0 +1,90 @@ +# +# 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() diff --git a/bacula/examples/python/FDStartUp.py b/bacula/examples/python/FDStartUp.py new file mode 100644 index 0000000000..2fe0fb27ee --- /dev/null +++ b/bacula/examples/python/FDStartUp.py @@ -0,0 +1,92 @@ +# +# 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() diff --git a/bacula/examples/python/SDStartUp.py b/bacula/examples/python/SDStartUp.py new file mode 100644 index 0000000000..2fe0fb27ee --- /dev/null +++ b/bacula/examples/python/SDStartUp.py @@ -0,0 +1,92 @@ +# +# 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() diff --git a/bacula/src/dird/dird.c b/bacula/src/dird/dird.c index a6f072d8b7..c1c0547945 100644 --- a/bacula/src/dird/dird.c +++ b/bacula/src/dird/dird.c @@ -222,8 +222,8 @@ int main (int argc, char *argv[]) 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 */); @@ -255,12 +255,13 @@ int main (int argc, char *argv[]) /* 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 */ diff --git a/bacula/src/dird/job.c b/bacula/src/dird/job.c index bf9a4ea8e4..a42b66bc24 100644 --- a/bacula/src/dird/job.c +++ b/bacula/src/dird/job.c @@ -93,7 +93,6 @@ JobId_t run_job(JCR *jcr) } jcr->term_wait_inited = true; - generate_daemon_event(jcr, "StartJob"); /* * Open database @@ -113,6 +112,7 @@ JobId_t run_job(JCR *jcr) } Dmsg0(50, "DB opened\n"); + /* * Create Job record */ @@ -124,10 +124,11 @@ JobId_t run_job(JCR *jcr) 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; } diff --git a/bacula/src/dird/python.c b/bacula/src/dird/python.c index e717b1271e..f5445366e8 100644 --- a/bacula/src/dird/python.c +++ b/bacula/src/dird/python.c @@ -36,30 +36,27 @@ #include 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; @@ -85,7 +82,7 @@ static struct s_vars vars[] = { }; /* Return Job variables */ -static PyObject *jcr_get(PyObject *self, PyObject *args) +static PyObject *job_get(PyObject *self, PyObject *args) { JCR *jcr; char *item; @@ -93,6 +90,7 @@ static PyObject *jcr_get(PyObject *self, PyObject *args) int i; char buf[10]; + Dmsg0(100, "In jcr_get.\n"); if (!PyArg_ParseTuple(args, "s:get", &item)) { return NULL; } @@ -146,6 +144,8 @@ static PyObject *jcr_set(PyObject *self, PyObject *args, PyObject *keyw) 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; @@ -155,27 +155,39 @@ static PyObject *jcr_set(PyObject *self, PyObject *args, PyObject *keyw) 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; } @@ -184,6 +196,7 @@ static PyObject *set_jcr_events(PyObject *self, PyObject *args) static PyObject *jcr_write(PyObject *self, PyObject *args) { char *text; + if (!PyArg_ParseTuple(args, "s:write", &text)) { return NULL; } @@ -219,22 +232,39 @@ static PyObject *jcr_run(PyObject *self, PyObject *args) 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 diff --git a/bacula/src/dird/ua_cmds.c b/bacula/src/dird/ua_cmds.c index 7125d4e57f..5c0da912ad 100644 --- a/bacula/src/dird/ua_cmds.c +++ b/bacula/src/dird/ua_cmds.c @@ -571,8 +571,8 @@ static int python_cmd(UAContext *ua, const char *cmd) { 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")); diff --git a/bacula/src/filed/filed.c b/bacula/src/filed/filed.c index 9fdd1983f7..bcfa65c5ed 100644 --- a/bacula/src/filed/filed.c +++ b/bacula/src/filed/filed.c @@ -230,8 +230,7 @@ int main (int argc, char *argv[]) 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); diff --git a/bacula/src/filed/python.c b/bacula/src/filed/python.c index ea7c0cdb6d..3af306a477 100644 --- a/bacula/src/filed/python.c +++ b/bacula/src/filed/python.c @@ -36,7 +36,8 @@ #include 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); diff --git a/bacula/src/jcr.h b/bacula/src/jcr.h index 6cccbdcd02..271655b635 100644 --- a/bacula/src/jcr.h +++ b/bacula/src/jcr.h @@ -143,7 +143,8 @@ struct JCR { 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 */ diff --git a/bacula/src/lib/Makefile.in b/bacula/src/lib/Makefile.in index 47b9c2d2d0..df440b5e9f 100644 --- a/bacula/src/lib/Makefile.in +++ b/bacula/src/lib/Makefile.in @@ -29,7 +29,7 @@ LIBSRCS = alloc.c attr.c base64.c berrno.c bsys.c bget_msg.c \ 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 \ @@ -41,7 +41,7 @@ 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@ @@ -72,7 +72,7 @@ Makefile: $(srcdir)/Makefile.in $(topdir)/config.status 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: diff --git a/bacula/src/lib/jcr.c b/bacula/src/lib/jcr.c index 44a0e48488..709a30d9b2 100755 --- a/bacula/src/lib/jcr.c +++ b/bacula/src/lib/jcr.c @@ -354,9 +354,6 @@ void free_jcr(JCR *jcr) #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) { @@ -369,6 +366,14 @@ void free_jcr(JCR *jcr) 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(); diff --git a/bacula/src/lib/python.c b/bacula/src/lib/pythonlib.c similarity index 78% rename from bacula/src/lib/python.c rename to bacula/src/lib/pythonlib.c index cfc3ebde90..7ad1030995 100644 --- a/bacula/src/lib/python.c +++ b/bacula/src/lib/pythonlib.c @@ -36,6 +36,9 @@ #undef _POSIX_C_SOURCE #include +/* Imported subroutines */ +extern PyMethodDef JobMethods[]; + static PyObject *bacula_module = NULL; /* We create this */ static PyObject *StartUp_module = NULL; /* We import this */ @@ -44,11 +47,10 @@ 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); +PyObject *find_method(PyObject *eventsObject, PyObject *method, const char *name); /* Define Bacula daemon method entry points */ static PyMethodDef BaculaMethods[] = { @@ -63,16 +65,16 @@ static PyMethodDef BaculaMethods[] = { * to the jcr. That is all we need, but the user's script may keep * local data attached to this. */ -typedef struct JCRObject { +typedef struct s_JobObject { PyObject_HEAD JCR *jcr; -} JCRObject; +} JobObject; -static PyTypeObject JCRType = { +static PyTypeObject JobType = { PyObject_HEAD_INIT(NULL) 0, /*ob_size*/ "bacula.jcr", /*tp_name*/ - sizeof(JCRObject), /*tp_basicsize*/ + sizeof(JobObject), /*tp_basicsize*/ 0, /*tp_itemsize*/ 0, /*tp_dealloc*/ 0, /*tp_print*/ @@ -90,14 +92,14 @@ static PyTypeObject JCRType = { 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT, /*tp_flags*/ - "JCR objects", /* tp_doc */ + "Job objects", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ - 0, /* tp_methods */ + JobMethods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ @@ -110,10 +112,10 @@ static PyTypeObject JCRType = { 0, /* tp_new */ }; -/* Return the JCR pointer from the JCRObject */ +/* Return the JCR pointer from the JobObject */ JCR *get_jcr_from_PyObject(PyObject *self) { - return ((JCRObject *)self)->jcr; + return ((JobObject *)self)->jcr; } @@ -122,28 +124,36 @@ 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) { - Jmsg(NULL, M_ERROR_TERM, 0, "Could not initialize Python\n"); + 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) { - Jmsg(NULL, M_ERROR_TERM, 0, "Could not Run Python string %s\n", buf); + Jmsg1(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"); + 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) { - Jmsg(NULL, M_ERROR_TERM, 0, "Could not import script %s/%s.\n", + Emsg2(M_ERROR, 0, "Could not import Python script %s/%s. Python disabled.\n", scripts, module); - PyErr_Clear(); + if (PyErr_Occurred()) { + PyErr_Print(); + Dmsg0(000, "Python Import error.\n"); + } } PyEval_ReleaseLock(); } @@ -151,13 +161,17 @@ void init_python_interpreter(const char *progname, const char *scripts, void term_python_interpreter() { - Py_XDECREF(StartUp_module); - Py_Finalize(); + 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; } @@ -188,10 +202,10 @@ static PyObject *bacula_write(PyObject *self, PyObject *args) /* * Check that a method exists and is callable. */ -PyObject *find_method(PyObject *eventsObject, PyObject *method, char *name) +PyObject *find_method(PyObject *eventsObject, PyObject *method, const char *name) { Py_XDECREF(method); /* release old method if any */ - method = PyObject_GetAttrString(eventsObject, name); + method = PyObject_GetAttrString(eventsObject, (char *)name); if (method == NULL) { Dmsg1(000, "Python method %s not found\n", name); } else if (PyCallable_Check(method) == 0) { @@ -199,7 +213,7 @@ PyObject *find_method(PyObject *eventsObject, PyObject *method, char *name) Py_XDECREF(method); method = NULL; } else { - Dmsg1(000, "Got method %s\n", name); + Dmsg1(100, "Got method %s\n", name); } return method; } @@ -215,10 +229,14 @@ PyObject *find_method(PyObject *eventsObject, PyObject *method, char *name) */ int generate_daemon_event(JCR *jcr, const char *event) { - PyObject *pJCR; + PyObject *pJob; int stat = -1; PyObject *result = NULL; + if (!StartUp_module) { + return 0; + } + PyEval_AcquireLock(); if (strcmp(event, "JobStart") == 0) { if (!JobStart_method) { @@ -226,50 +244,68 @@ int generate_daemon_event(JCR *jcr, const char *event) 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"); + pJob = (PyObject *)PyObject_New(JobObject, &JobType); + if (!pJob) { + Jmsg(jcr, M_ERROR, 0, "Could not create Python Job Object.\n"); goto bail_out; } - ((JCRObject *)pJCR)->jcr = jcr; + ((JobObject *)pJob)->jcr = jcr; bstrncpy(jcr->event, event, sizeof(jcr->event)); - result = PyObject_CallFunction(JobStart_method, "O", pJCR); + 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(pJCR); + Py_XDECREF(pJob); goto bail_out; } - jcr->Python_jcr = (void *)pJCR; + jcr->Python_job = (void *)pJob; stat = 1; /* OK */ + goto jobstart_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); + 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); - Py_XDECREF((PyObject *)jcr->Python_jcr); goto bail_out; } - Py_XDECREF((PyObject *)jcr->Python_jcr); 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; @@ -304,7 +340,7 @@ bail_out: if (!pJCR) { Py_DECREF(pArgs); Py_DECREF(pModule); - Jmsg(jcr, M_ERROR, 0, "Could not create JCR Python Object.\n"); + Jmsg0(jcr, M_ERROR, 0, "Could not create JCR Python Object.\n"); return -1; } Py_INCREF(pJCR); @@ -322,15 +358,16 @@ bail_out: } else { Py_DECREF(pModule); PyErr_Print(); - Jmsg(jcr, M_ERROR, 0, "Error running Python module: %s\n", event); + 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); } - Jmsg(jcr, M_ERROR, 0, "Python function \"%s\" not found in module.\n", event); + Jmsg1(jcr, M_ERROR, 0, "Python function \"%s\" not found in module.\n", event); return -1; /* function not found */ } Py_DECREF(pModule); diff --git a/bacula/src/stored/python.c b/bacula/src/stored/python.c index 00e95f78ad..aa9e7e86a1 100644 --- a/bacula/src/stored/python.c +++ b/bacula/src/stored/python.c @@ -36,7 +36,8 @@ #include 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); diff --git a/bacula/src/stored/stored.c b/bacula/src/stored/stored.c index d45ca8b0d3..0f40434933 100644 --- a/bacula/src/stored/stored.c +++ b/bacula/src/stored/stored.c @@ -208,8 +208,7 @@ int main (int argc, char *argv[]) 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); -- 2.39.5