--- /dev/null
+Index: src/dird/fd_cmds.c
+===================================================================
+--- src/dird/fd_cmds.c (révision 6169)
++++ src/dird/fd_cmds.c (copie de travail)
+@@ -537,29 +537,35 @@
+ Dmsg2(200, "bdird: runscript %s -> %s\n", cmd->target, ehost);
+
+ if (strcmp(ehost, jcr->client->name()) == 0) {
+- pm_strcpy(msg, cmd->command);
+- bash_spaces(msg);
++ char *c;
++ char *command, cmd_type;
++ foreach_alist(c, cmd->commands) {
++ cmd->get_command(c, &cmd_type, &command);
++ if (cmd_type == SHELL_CMD) {
++ pm_strcpy(msg, command);
++ bash_spaces(msg);
+
+- Dmsg1(120, "bdird: sending runscripts to fd '%s'\n", cmd->command);
++ Dmsg1(120, "bdird: sending runscripts to fd '%s'\n", command);
+
+- /* TODO: remove this with bacula 1.42 */
+- if (cmd->old_proto) {
+- result = send_runscript_with_old_proto(jcr, cmd->when, msg);
++ /* TODO: remove this with bacula 1.42 */
++ if (cmd->old_proto) {
++ result = send_runscript_with_old_proto(jcr, cmd->when, msg);
++
++ } else {
++ fd->fsend(runscript, cmd->on_success,
++ cmd->on_failure,
++ cmd->fail_on_error,
++ cmd->when,
++ msg);
+
+- } else {
+- fd->fsend(runscript, cmd->on_success,
+- cmd->on_failure,
+- cmd->fail_on_error,
+- cmd->when,
+- msg);
+-
+- result = response(jcr, fd, OKRunScript, "RunScript", DISPLAY_ERROR);
+- launch_before_cmd = true;
++ result = response(jcr, fd, OKRunScript, "RunScript", DISPLAY_ERROR);
++ launch_before_cmd = true;
++ }
++ if (!result) {
++ goto bail_out;
++ }
++ }
+ }
+-
+- if (!result) {
+- goto bail_out;
+- }
+ }
+ /* TODO : we have to play with other client */
+ /*
+@@ -567,7 +573,7 @@
+ send command to an other client
+ }
+ */
+- }
++ }
+ }
+
+ /* Tell the FD to execute the ClientRunBeforeJob */
+Index: src/dird/dird_conf.c
+===================================================================
+--- src/dird/dird_conf.c (révision 6169)
++++ src/dird/dird_conf.c (copie de travail)
+@@ -651,16 +651,19 @@
+ }
+ }
+ if (res->res_job.RunScripts) {
+- RUNSCRIPT *script;
+- foreach_alist(script, res->res_job.RunScripts) {
+- sendit(sock, _(" --> RunScript\n"));
+- sendit(sock, _(" --> Command=%s\n"), NPRT(script->command));
+- sendit(sock, _(" --> Target=%s\n"), NPRT(script->target));
+- sendit(sock, _(" --> RunOnSuccess=%u\n"), script->on_success);
+- sendit(sock, _(" --> RunOnFailure=%u\n"), script->on_failure);
+- sendit(sock, _(" --> FailJobOnError=%u\n"), script->fail_on_error);
+- sendit(sock, _(" --> RunWhen=%u\n"), script->when);
+- }
++ char *c;
++ RUNSCRIPT *script;
++ foreach_alist(script, res->res_job.RunScripts) {
++ sendit(sock, _(" --> RunScript\n"));
++ foreach_alist(c, script->commands) {
++ sendit(sock, _(" --> Command=%s\n"), NPRT(c));
++ }
++ sendit(sock, _(" --> Target=%s\n"), NPRT(script->target));
++ sendit(sock, _(" --> RunOnSuccess=%u\n"), script->on_success);
++ sendit(sock, _(" --> RunOnFailure=%u\n"), script->on_failure);
++ sendit(sock, _(" --> FailJobOnError=%u\n"), script->fail_on_error);
++ sendit(sock, _(" --> RunWhen=%u\n"), script->when);
++ }
+ }
+ if (res->res_job.pool) {
+ sendit(sock, _(" --> "));
+@@ -1724,14 +1727,14 @@
+ }
+
+ /*
+- * Store a runscript->command as a string
++ * Store a runscript->commands as a alist(char + string)
+ */
+ static void store_runscript_cmd(LEX *lc, RES_ITEM *item, int index, int pass)
+ {
+ lex_get_token(lc, T_STRING);
+
+ if (pass == 2) {
+- ((RUNSCRIPT*)item->value)->set_command(lc->str, item->code);
++ ((RUNSCRIPT*)item->value)->add_command(lc->str, item->code);
+ }
+ scan_to_eol(lc);
+ }
+@@ -1745,7 +1748,7 @@
+ RUNSCRIPT *script = new_runscript();
+ script->set_job_code_callback(job_code_callback_filesetname);
+
+- script->set_command(lc->str);
++ script->add_command(lc->str);
+
+ /* TODO: remove all script->old_proto with bacula 1.42 */
+
+@@ -1873,7 +1876,7 @@
+ }
+
+ if (pass == 2) {
+- if (res_runscript.command == NULL) {
++ if (res_runscript.commands == NULL) {
+ scan_err2(lc, _("%s item is required in %s resource, but not found.\n"),
+ "command", "runscript");
+ }
+@@ -1883,10 +1886,11 @@
+ res_runscript.set_target("%c");
+ }
+
+- RUNSCRIPT *script = new_runscript();
+- memcpy(script, &res_runscript, sizeof(RUNSCRIPT));
++ RUNSCRIPT *script = copy_runscript(&res_runscript);
++ res_runscript.reset_default(true);
++
+ script->set_job_code_callback(job_code_callback_filesetname);
+-
++
+ if (*runscripts == NULL) {
+ *runscripts = New(alist(10, not_owned_by_alist));
+ }
+Index: src/filed/job.c
+===================================================================
+--- src/filed/job.c (révision 6169)
++++ src/filed/job.c (copie de travail)
+@@ -481,7 +481,7 @@
+
+ /* Run the command now */
+ script = new_runscript();
+- script->set_command(cmd);
++ script->add_command(cmd);
+ script->when = SCRIPT_Before;
+ ok = script->run(jcr, "ClientRunBeforeJob");
+ free_runscript(script);
+@@ -529,7 +529,7 @@
+ unbash_spaces(msg);
+
+ cmd = new_runscript();
+- cmd->set_command(msg);
++ cmd->add_command(msg);
+ cmd->on_success = true;
+ cmd->on_failure = false;
+ cmd->when = SCRIPT_After;
+@@ -567,7 +567,7 @@
+ cmd->fail_on_error = fail_on_error;
+ unbash_spaces(msg);
+
+- cmd->set_command(msg);
++ cmd->add_command(msg);
+ cmd->debug();
+ jcr->RunScripts->append(cmd);
+
+Index: src/lib/runscript.h
+===================================================================
+--- src/lib/runscript.h (révision 6169)
++++ src/lib/runscript.h (copie de travail)
+@@ -62,8 +62,8 @@
+ };
+
+ enum {
+- SHELL_CMD = 1,
+- CONSOLE_CMD = 2
++ SHELL_CMD = '|',
++ CONSOLE_CMD = '@'
+ };
+
+ /*
+@@ -71,10 +71,9 @@
+ */
+ class RUNSCRIPT {
+ public:
+- POOLMEM *command; /* command string */
++ alist *commands; /* list of command/console string */
+ POOLMEM *target; /* host target */
+ int when; /* SCRIPT_Before|Script_After BEFORE/AFTER JOB*/
+- int cmd_type; /* Command type -- Shell, Console */
+ char level; /* Base|Full|Incr...|All (NYI) */
+ bool on_success; /* execute command on job success (After) */
+ bool on_failure; /* execute command on job failure (After) */
+@@ -85,8 +84,10 @@
+ /* Optional callback function passed to edit_job_code */
+
+ bool run(JCR *job, const char *name=""); /* name must contain "Before" or "After" keyword */
++ bool run_command(const char *cmd, JCR *job, const char *name="");
+ bool can_run_at_level(int JobLevel) { return true;}; /* TODO */
+- void set_command(const POOLMEM *cmd, int cmd_type = SHELL_CMD);
++ void add_command(const POOLMEM *cmd, char cmd_type = SHELL_CMD);
++ void get_command(const char *cmd, char *cmd_type, char **cmd);
+ void set_target(const POOLMEM *client_name);
+ void reset_default(bool free_string = false);
+ bool is_local(); /* true if running on local host */
+Index: src/lib/runscript.c
+===================================================================
+--- src/lib/runscript.c (révision 6169)
++++ src/lib/runscript.c (copie de travail)
+@@ -59,15 +59,20 @@
+
+ void RUNSCRIPT::reset_default(bool free_strings)
+ {
+- if (free_strings && command) {
+- free_pool_memory(command);
++ char *c;
++ if (free_strings && commands) {
++ foreach_alist(c, commands) {
++ free_pool_memory(c);
++ }
+ }
+ if (free_strings && target) {
+ free_pool_memory(target);
+ }
+-
+ target = NULL;
+- command = NULL;
++ if (commands) {
++ delete commands;
++ commands = NULL;
++ }
+ on_success = true;
+ on_failure = false;
+ fail_on_error = true;
+@@ -76,17 +81,23 @@
+ job_code_callback = NULL;
+ }
+
+-RUNSCRIPT *copy_runscript(RUNSCRIPT *src)
++Runscript *copy_runscript(RUNSCRIPT *src)
+ {
+ Dmsg0(500, "runscript: creating new RUNSCRIPT object from other\n");
+
+ RUNSCRIPT *dst = (RUNSCRIPT *)malloc(sizeof(RUNSCRIPT));
+ memcpy(dst, src, sizeof(RUNSCRIPT));
+
+- dst->command = NULL;
++ dst->commands = New(alist(5, not_owned_by_alist));
++ char *c;
++ POOLMEM *m;
++ foreach_alist(c, src->commands) {
++ m = get_pool_memory(PM_FNAME);
++ pm_strcpy(m, c);
++ dst->commands->append(m);
++ }
++
+ dst->target = NULL;
+-
+- dst->set_command(src->command, src->cmd_type);
+ dst->set_target(src->target);
+
+ return dst;
+@@ -95,9 +106,12 @@
+ void free_runscript(RUNSCRIPT *script)
+ {
+ Dmsg0(500, "runscript: freeing RUNSCRIPT object\n");
+-
+- if (script->command) {
+- free_pool_memory(script->command);
++ POOLMEM *c;
++ if (script->commands) {
++ foreach_alist(c, script->commands) {
++ free_pool_memory(c);
++ }
++ delete script->commands;
+ }
+ if (script->target) {
+ free_pool_memory(script->target);
+@@ -108,60 +122,15 @@
+ int run_scripts(JCR *jcr, alist *runscripts, const char *label)
+ {
+ Dmsg2(200, "runscript: running all RUNSCRIPT object (%s) JobStatus=%c\n", label, jcr->JobStatus);
+-
+- RUNSCRIPT *script;
+- bool runit;
+
+- int when;
+-
+- if (strstr(label, NT_("Before"))) {
+- when = SCRIPT_Before;
+- } else {
+- when = SCRIPT_After;
+- }
+-
+ if (runscripts == NULL) {
+ Dmsg0(100, "runscript: WARNING RUNSCRIPTS list is NULL\n");
+ return 0;
+ }
+-
++
++ RUNSCRIPT *script;
+ foreach_alist(script, runscripts) {
+- Dmsg2(200, "runscript: try to run %s:%s\n", NPRT(script->target), NPRT(script->command));
+- runit = false;
+-
+- if ((script->when & SCRIPT_Before) && (when & SCRIPT_Before)) {
+- if ((script->on_success
+- && (jcr->JobStatus == JS_Running || jcr->JobStatus == JS_Created))
+- || (script->on_failure && job_canceled(jcr))
+- )
+- {
+- Dmsg4(200, "runscript: Run it because SCRIPT_Before (%s,%i,%i,%c)\n",
+- script->command, script->on_success, script->on_failure,
+- jcr->JobStatus );
+- runit = true;
+- }
+- }
+-
+- if ((script->when & SCRIPT_After) && (when & SCRIPT_After)) {
+- if ((script->on_success && (jcr->JobStatus == JS_Terminated))
+- || (script->on_failure && job_canceled(jcr))
+- )
+- {
+- Dmsg4(200, "runscript: Run it because SCRIPT_After (%s,%i,%i,%c)\n",
+- script->command, script->on_success, script->on_failure,
+- jcr->JobStatus );
+- runit = true;
+- }
+- }
+-
+- if (!script->is_local()) {
+- runit = false;
+- }
+-
+- /* we execute it */
+- if (runit) {
+- script->run(jcr, label);
+- }
++ script->run(jcr, label);
+ }
+ return 1;
+ }
+@@ -176,7 +145,7 @@
+ }
+
+ /* set this->command to cmd */
+-void RUNSCRIPT::set_command(const POOLMEM *cmd, int acmd_type)
++void RUNSCRIPT::add_command(const POOLMEM *cmd, char acmd_type)
+ {
+ Dmsg1(500, "runscript: setting command = %s\n", NPRT(cmd));
+
+@@ -184,14 +153,21 @@
+ return;
+ }
+
+- if (!command) {
+- command = get_pool_memory(PM_FNAME);
++ if (!commands) {
++ commands = New(alist(5, not_owned_by_alist));
+ }
+
+- pm_strcpy(command, cmd);
+- cmd_type = acmd_type;
++ POOLMEM *c = get_pool_memory(PM_FNAME);
++ Mmsg(c, "%c%s", acmd_type, cmd);
++ commands->append(c);
+ }
+
++void RUNSCRIPT::get_command(const char* command, char *acmd_type, char **cmd)
++{
++ *acmd_type = command[0];
++ *cmd = (char *)command + 1;
++}
++
+ /* set this->target to client_name */
+ void RUNSCRIPT::set_target(const POOLMEM *client_name)
+ {
+@@ -210,14 +186,71 @@
+
+ bool RUNSCRIPT::run(JCR *jcr, const char *name)
+ {
+- Dmsg1(100, "runscript: running a RUNSCRIPT object type=%d\n", cmd_type);
++ char *c;
++ bool runit;
++
++ int when;
++
++ if (strstr(name, NT_("Before"))) {
++ when = SCRIPT_Before;
++ } else {
++ when = SCRIPT_After;
++ }
++
++ foreach_alist(c, this->commands) {
++ Dmsg2(200, "runscript: try to run %s:%s\n", NPRT(this->target), NPRT(c));
++ runit = false;
++
++ if ((this->when & SCRIPT_Before) && (when & SCRIPT_Before)) {
++ if ((this->on_success
++ && (jcr->JobStatus == JS_Running || jcr->JobStatus == JS_Created))
++ || (this->on_failure && job_canceled(jcr))
++ )
++ {
++ Dmsg4(200, "runscript: Run it because SCRIPT_Before (%s,%i,%i,%c)\n",
++ c, this->on_success, this->on_failure,
++ jcr->JobStatus );
++ runit = true;
++ }
++ }
++
++ if ((this->when & SCRIPT_After) && (when & SCRIPT_After)) {
++ if ((this->on_success && (jcr->JobStatus == JS_Terminated))
++ || (this->on_failure && job_canceled(jcr))
++ )
++ {
++ Dmsg4(200, "runscript: Run it because SCRIPT_After (%s,%i,%i,%c)\n",
++ c, this->on_success, this->on_failure,
++ jcr->JobStatus );
++ runit = true;
++ }
++ }
++
++ if (!this->is_local()) {
++ runit = false;
++ }
++
++ /* we execute it */
++ if (runit) {
++ this->run_command(c, jcr, name);
++ }
++ }
++ return 1;
++}
++
++bool RUNSCRIPT::run_command(const char *command, JCR *jcr, const char *name)
++{
+ POOLMEM *ecmd = get_pool_memory(PM_FNAME);
+ int status;
+ BPIPE *bpipe;
+ char line[MAXSTRING];
++ char cmd_type;
++ char *cmd;
+
+- ecmd = edit_job_codes(jcr, ecmd, this->command, "", this->job_code_callback);
+- Dmsg1(100, "runscript: running '%s'...\n", ecmd);
++ this->get_command(command, &cmd_type, &cmd);
++ ecmd = edit_job_codes(jcr, ecmd, cmd, "", this->job_code_callback);
++
++ Dmsg2(100, "runscript: running '%s' object type=%c...\n", ecmd, cmd_type);
+ Jmsg(jcr, M_INFO, 0, _("%s: run %s \"%s\"\n"),
+ cmd_type==SHELL_CMD?"shell command":"console command", name, ecmd);
+
+@@ -278,9 +311,12 @@
+
+ void RUNSCRIPT::debug()
+ {
++ char *c;
+ Dmsg0(200, "runscript: debug\n");
+ Dmsg0(200, _(" --> RunScript\n"));
+- Dmsg1(200, _(" --> Command=%s\n"), NPRT(command));
++ foreach_alist(c, commands) {
++ Dmsg1(200, _(" --> Command=%s\n"), NPRT(c));
++ }
+ Dmsg1(200, _(" --> Target=%s\n"), NPRT(target));
+ Dmsg1(200, _(" --> RunOnSuccess=%u\n"), on_success);
+ Dmsg1(200, _(" --> RunOnFailure=%u\n"), on_failure);