-2003-08-02 Version 1.32 02Sep03 Beta
+2003-08-02 Version 1.32 06Sep03 Beta
+- Implement ClientRunBeforeJob and ClientRunAfterJob.
+- Corrected BSRatEOF to be BSFatEOF in btape --
+ reported by Lars Koller.
+- Documented BSFatEof
+- Eliminated save_level in FD replaced by JobLevel.
+- Increased MAX_RES_ITEMS from 35 to 50 to handle
+ new ClientRunBeforeJob ...
+- Add line number in error message for restore from file.
+- Correct editing of jobids (misplaced comma).
+- Implement restore files.
+- Quickie patch to allow Purged Volumes to be mounted.
+ Must review algorithm.
- Eliminate a duplicated query from query.sql
- Restructure ua_restore.c so that I can add restore files.
- Correct positioning problem at beginning of a second volume
Release Notes for Bacula 1.32
- Bacula code: Total files = 259 Total lines = 76,690 (*.h *.c *.in)
+ Bacula code: Total files = 259 Total lines = 77,120 (*.h *.c *.in)
Major Changes this Release:
- Fixed gnome-console to compile with RH9 (Gnome 2.0)
- Implemented a single routine to read_records. It also returns
a different record packet for each session. This means
that multiple simultaneous jobs should work.
-- Implemented forward space file/block whenever possible.
+- Implemented forward space file/block whenever possible
+ during restore.
- Added SDConnectTimeout to FD.
+- Added ClientRunBeforeJob and ClientRunAfterJob.
Other Changes this Release:
goto bail_out;
}
+ if (!send_run_before_and_after_commands(jcr)) {
+ goto bail_out;
+ }
+
/* Send backup command */
bnet_fsend(fd, backupcmd);
if (!response(jcr, fd, OKbackup, "backup", DISPLAY_ERROR)) {
{"prunevolumes", store_yesno, ITEM(res_job.PruneVolumes), 1, ITEM_DEFAULT, 0},
{"runbeforejob", store_str, ITEM(res_job.RunBeforeJob), 0, 0, 0},
{"runafterjob", store_str, ITEM(res_job.RunAfterJob), 0, 0, 0},
+ {"clientrunbeforejob", store_str, ITEM(res_job.ClientRunBeforeJob), 0, 0, 0},
+ {"clientrunafterjob", store_str, ITEM(res_job.ClientRunAfterJob), 0, 0, 0},
{"spoolattributes", store_yesno, ITEM(res_job.SpoolAttributes), 1, ITEM_DEFAULT, 0},
{"writebootstrap", store_dir, ITEM(res_job.WriteBootstrap), 0, 0, 0},
{"maximumconcurrentjobs", store_pint, ITEM(res_job.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
if (res->res_job.RunAfterJob) {
free(res->res_job.RunAfterJob);
}
+ if (res->res_job.ClientRunBeforeJob) {
+ free(res->res_job.ClientRunBeforeJob);
+ }
+ if (res->res_job.ClientRunAfterJob) {
+ free(res->res_job.ClientRunAfterJob);
+ }
break;
case R_MSGS:
- if (res->res_msgs.mail_cmd)
+ if (res->res_msgs.mail_cmd) {
free(res->res_msgs.mail_cmd);
- if (res->res_msgs.operator_cmd)
+ }
+ if (res->res_msgs.operator_cmd) {
free(res->res_msgs.operator_cmd);
+ }
free_msgs_res((MSGS *)res); /* free message resource */
res = NULL;
break;
char *RestoreBootstrap; /* Bootstrap file */
char *RunBeforeJob; /* Run program before Job */
char *RunAfterJob; /* Run program after Job */
+ char *ClientRunBeforeJob; /* Run client program before Job */
+ char *ClientRunAfterJob; /* Run client program after Job */
char *WriteBootstrap; /* Where to write bootstrap Job updates */
int replace; /* How (overwrite, ..) */
utime_t MaxRunTime; /* max run time in seconds */
static char exc[] = "exclude\n";
static char jobcmd[] = "JobId=%d Job=%s SDid=%u SDtime=%u Authorization=%s\n";
static char levelcmd[] = "level = %s%s mtime_only=%d\n";
+static char runbefore[] = "RunBeforeJob %s\n";
+static char runafter[] = "RunAfterJob %s\n";
/* Responses received from File daemon */
static char OKjob[] = "2000 OK Job";
static char OKbootstrap[] = "2000 OK bootstrap\n";
static char OKlevel[] = "2000 OK level\n";
+static char OKRunBefore[] = "2000 OK RunBefore\n";
+static char OKRunAfter[] = "2000 OK RunAfter\n";
/* Forward referenced functions */
int send_include_list(JCR *jcr)
{
BSOCK *fd = jcr->file_bsock;
- fd->msglen = sprintf(fd->msg, inc);
+ fd->msglen = pm_strcpy(&fd->msg, inc);
bnet_send(fd);
return send_list(jcr, INC_LIST);
}
int send_exclude_list(JCR *jcr)
{
BSOCK *fd = jcr->file_bsock;
- fd->msglen = sprintf(fd->msg, exc);
+ fd->msglen = pm_strcpy(&fd->msg, exc);
bnet_send(fd);
return send_list(jcr, EXC_LIST);
}
set_jcr_job_status(jcr, JS_ErrorTerminated);
return 0;
}
- strcpy(fd->msg, bootstrap);
- fd->msglen = strlen(fd->msg);
- bnet_send(fd);
+ bnet_fsend(fd, bootstrap);
while (fgets(buf, sizeof(buf), bs)) {
- fd->msglen = Mmsg(&fd->msg, "%s", buf);
- bnet_send(fd);
+ bnet_fsend(fd, "%s", buf);
}
bnet_sig(fd, BNET_EOD);
fclose(bs);
return 1;
}
+/*
+ * Send ClientRunBeforeJob and ClientRunAfterJob to File daemon
+ */
+int send_run_before_and_after_commands(JCR *jcr)
+{
+ POOLMEM *msg = get_pool_memory(PM_FNAME);
+ BSOCK *fd = jcr->file_bsock;
+ if (jcr->job->ClientRunBeforeJob) {
+ pm_strcpy(&msg, jcr->job->ClientRunBeforeJob);
+ bash_spaces(msg);
+ bnet_fsend(fd, runbefore, msg);
+ if (!response(jcr, fd, OKRunBefore, "ClientRunBeforeJob", DISPLAY_ERROR)) {
+ set_jcr_job_status(jcr, JS_ErrorTerminated);
+ free_pool_memory(msg);
+ return 0;
+ }
+ }
+ if (jcr->job->ClientRunAfterJob) {
+ fd->msglen = pm_strcpy(&msg, jcr->job->ClientRunAfterJob);
+ bash_spaces(msg);
+ bnet_fsend(fd, runafter, msg);
+ if (!response(jcr, fd, OKRunAfter, "ClientRunAfterJob", DISPLAY_ERROR)) {
+ set_jcr_job_status(jcr, JS_ErrorTerminated);
+ free_pool_memory(msg);
+ return 0;
+ }
+ }
+ free_pool_memory(msg);
+ return 1;
+}
+
/*
* Read the attributes from the File daemon for
extern int put_file_into_catalog(JCR *jcr, long file_index, char *fname,
char *link, char *attr, int stream);
extern void get_level_since_time(JCR *jcr, char *since, int since_len);
+extern int send_run_before_and_after_commands(JCR *jcr);
/* getmsg.c */
enum e_prtmsg {
}
}
+ if (!send_run_before_and_after_commands(jcr)) {
+ restore_cleanup(jcr, JS_ErrorTerminated);
+ return 0;
+ }
+
/* Send restore command */
char replace, *where;
goto bail_out;
}
+ if (!send_run_before_and_after_commands(jcr)) {
+ goto bail_out;
+ }
+
/*
* Send verify command/level to File daemon
*/
static void filed_free_jcr(JCR *jcr);
static int open_sd_read_session(JCR *jcr);
static int send_bootstrap_file(JCR *jcr);
+static int runbefore_cmd(JCR *jcr);
+static int runafter_cmd(JCR *jcr);
+static int run_cmd(JCR *jcr, char *cmd, char *name);
/* Exported functions */
* The following are the recognized commands from the Director.
*/
static struct s_cmds cmds[] = {
- {"backup", backup_cmd},
- {"cancel", cancel_cmd},
- {"setdebug=", setdebug_cmd},
- {"estimate", estimate_cmd},
- {"exclude", exclude_cmd},
- {"Hello", hello_cmd},
- {"include", include_cmd},
- {"JobId=", job_cmd},
- {"level = ", level_cmd},
- {"restore", restore_cmd},
- {"session", session_cmd},
- {"status", status_cmd},
- {"storage ", storage_cmd},
- {"verify", verify_cmd},
- {"bootstrap",bootstrap_cmd},
+ {"backup", backup_cmd},
+ {"cancel", cancel_cmd},
+ {"setdebug=", setdebug_cmd},
+ {"estimate", estimate_cmd},
+ {"exclude", exclude_cmd},
+ {"Hello", hello_cmd},
+ {"include", include_cmd},
+ {"JobId=", job_cmd},
+ {"level = ", level_cmd},
+ {"restore", restore_cmd},
+ {"session", session_cmd},
+ {"status", status_cmd},
+ {"storage ", storage_cmd},
+ {"verify", verify_cmd},
+ {"bootstrap", bootstrap_cmd},
+ {"RunBeforeJob", runbefore_cmd},
+ {"RunAfterJob", runafter_cmd},
{NULL, NULL} /* list terminator */
};
static char restorecmd1[] = "restore replace=%c prelinks=%d where=\n";
static char verifycmd[] = "verify level=%30s\n";
static char estimatecmd[] = "estimate listing=%d\n";
+static char runbefore[] = "RunBeforeJob %s\n";
+static char runafter[] = "RunAfterJob %s\n";
/* Responses sent to Director */
static char errmsg[] = "2999 Invalid command\n";
static char OKsetdebug[] = "2000 OK setdebug=%d\n";
static char BADjob[] = "2901 Bad Job\n";
static char EndJob[] = "2800 End Job TermCode=%d JobFiles=%u ReadBytes=%s JobBytes=%s Errors=%u\n";
+static char OKRunBefore[] = "2000 OK RunBefore\n";
+static char OKRunAfter[] = "2000 OK RunAfter\n";
/* Responses received from Storage Daemon */
static char OK_end[] = "3000 OK end\n";
bnet_sig(jcr->store_bsock, BNET_TERMINATE);
}
+ if (jcr->RunAfterJob && !job_canceled(jcr)) {
+ run_cmd(jcr, jcr->RunAfterJob, "ClientRunAfterJob");
+ }
+
/* Inform Director that we are done */
bnet_sig(dir, BNET_TERMINATE);
return bnet_fsend(dir, OKjob);
}
+static int runbefore_cmd(JCR *jcr)
+{
+ int stat;
+ BSOCK *dir = jcr->dir_bsock;
+ POOLMEM *cmd = get_memory(dir->msglen+1);
+
+ Dmsg1(100, "runbefore_cmd: %s", dir->msg);
+ if (sscanf(dir->msg, runbefore, cmd) != 1) {
+ pm_strcpy(&jcr->errmsg, dir->msg);
+ Jmsg1(jcr, M_FATAL, 0, _("Bad RunBeforeJob command: %s\n"), jcr->errmsg);
+ bnet_fsend(dir, "2905 Bad RunBeforeJob command.\n");
+ free_memory(cmd);
+ return 0;
+ }
+ unbash_spaces(cmd);
+
+ /* Run the command now */
+ stat = run_cmd(jcr, cmd, "ClientRunBeforeJob");
+ free_memory(cmd);
+ if (stat) {
+ bnet_fsend(dir, OKRunBefore);
+ return 1;
+ } else {
+ bnet_fsend(dir, "2905 Bad RunBeforeJob command.\n");
+ return 0;
+ }
+}
+
+static int runafter_cmd(JCR *jcr)
+{
+ BSOCK *dir = jcr->dir_bsock;
+ POOLMEM *msg = get_memory(dir->msglen+1);
+
+ Dmsg1(100, "runafter_cmd: %s", dir->msg);
+ if (sscanf(dir->msg, runafter, msg) != 1) {
+ pm_strcpy(&jcr->errmsg, dir->msg);
+ Jmsg1(jcr, M_FATAL, 0, _("Bad RunAfter command: %s\n"), jcr->errmsg);
+ bnet_fsend(dir, "2905 Bad RunAfterJob command.\n");
+ free_memory(msg);
+ return 0;
+ }
+ unbash_spaces(msg);
+ if (jcr->RunAfterJob) {
+ free_pool_memory(jcr->RunAfterJob);
+ }
+ jcr->RunAfterJob = get_pool_memory(PM_FNAME);
+ pm_strcpy(&jcr->RunAfterJob, msg);
+ free_pool_memory(msg);
+ return bnet_fsend(dir, OKRunAfter);
+}
+
+static int run_cmd(JCR *jcr, char *cmd, char *name)
+{
+ POOLMEM *ecmd = get_pool_memory(PM_FNAME);
+ int status;
+ BPIPE *bpipe;
+ char line[MAXSTRING];
+
+ ecmd = edit_job_codes(jcr, ecmd, cmd, "");
+ bpipe = open_bpipe(ecmd, 0, "r");
+ free_pool_memory(ecmd);
+ while (fgets(line, sizeof(line), bpipe->rfd)) {
+ Jmsg(jcr, M_INFO, 0, _("%s: %s"), name, line);
+ }
+ status = close_bpipe(bpipe);
+ if (status != 0) {
+ Jmsg(jcr, M_FATAL, 0, _("%s returned non-zero status=%d\n"), name,
+ status);
+ set_jcr_job_status(jcr, JS_FatalError);
+ return 0;
+ }
+ return 1;
+}
+
+
#define INC_LIST 0
#define EXC_LIST 1
}
/* Base backup requested? */
if (strcmp(level, "base") == 0) {
- jcr->save_level = L_BASE;
+ jcr->JobLevel = L_BASE;
/* Full backup requested? */
} else if (strcmp(level, "full") == 0) {
- jcr->save_level = L_FULL;
+ jcr->JobLevel = L_FULL;
/*
* Backup requested since <date> <time>
* This form is also used for incremental and differential
*/
} else if (strcmp(level, "since") == 0) {
- jcr->save_level = L_SINCE;
+ jcr->JobLevel = L_SINCE;
if (sscanf(dir->msg, "level = since %d-%d-%d %d:%d:%d mtime_only=%d",
&tm.tm_year, &tm.tm_mon, &tm.tm_mday,
&tm.tm_hour, &tm.tm_min, &tm.tm_sec, &mtime_only) != 7) {
if (jcr->last_fname) {
free_pool_memory(jcr->last_fname);
}
+ if (jcr->RunAfterJob) {
+ free_pool_memory(jcr->RunAfterJob);
+ }
return;
}
int mtime_only; /* compare only mtime and not ctime as well */
int listing; /* job listing in estimate */
long Ticket; /* Ticket */
- int save_level; /* save level */
char *big_buf; /* I/O buffer */
POOLMEM *compress_buf; /* Compression buffer */
int32_t compress_buf_size; /* Length of compression buffer */
uint32_t EndBlock;
pthread_t heartbeat_id; /* id of heartbeat thread */
volatile BSOCK *hb_bsock; /* duped SD socket */
+ POOLMEM *RunAfterJob; /* Command to run after job */
#endif /* FILE_DAEMON */
/* For storing name_addr items in res_items table */
#define ITEM(x) ((void **)&res_all.x)
-#define MAX_RES_ITEMS 32 /* maximum resource items per RES */
+#define MAX_RES_ITEMS 50 /* maximum resource items per RES */
/* This is the universal header that is
* at the beginning of every resource
if (stat == 1) {
Pmsg0(-1, "\n\nIt looks like the test worked this time, please add:\n\n"
" Hardware End of Medium = No\n"
- " BSR at EOM = yes\n\n"
+ " BSF at EOM = yes\n\n"
"to your Device resource in the Storage conf file.\n");
goto all_done;
}
#undef VERSION
#define VERSION "1.32"
#define VSTRING "1"
-#define BDATE "04 Sep 2003"
-#define LSMDATE "04Sep03"
+#define BDATE "06 Sep 2003"
+#define LSMDATE "06Sep03"
/* Debug flags */
#undef DEBUG