]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/runscript.c
Backport from Bacula Enterprise
[bacula/bacula] / bacula / src / lib / runscript.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2015 Kern Sibbald
5    Copyright (C) 2006-2014 Free Software Foundation Europe e.V.
6
7    The original author of Bacula is Kern Sibbald, with contributions
8    from many others, a complete list can be found in the file AUTHORS.
9
10    You may use this file and others of this release according to the
11    license defined in the LICENSE file, which includes the Affero General
12    Public License, v3.0 ("AGPLv3") and some additional permissions and
13    terms pursuant to its AGPLv3 Section 7.
14
15    This notice must be preserved when any source code is 
16    conveyed and/or propagated.
17
18    Bacula(R) is a registered trademark of Kern Sibbald.
19 */
20 /*
21  * Manipulation routines for RunScript list
22  *
23  *  Eric Bollengier, May 2006
24  *
25  */
26
27
28 #include "bacula.h"
29 #include "jcr.h"
30 #include "runscript.h"
31
32 /*
33  * This function pointer is set only by the Director (dird.c),
34  * and is not set in the File daemon, because the File
35  * daemon cannot run console commands.
36  */
37 bool (*console_command)(JCR *jcr, const char *cmd) = NULL;
38
39
40 RUNSCRIPT *new_runscript()
41 {
42    Dmsg0(500, "runscript: creating new RUNSCRIPT object\n");
43    RUNSCRIPT *cmd = (RUNSCRIPT *)malloc(sizeof(RUNSCRIPT));
44    memset(cmd, 0, sizeof(RUNSCRIPT));
45    cmd->reset_default();
46
47    return cmd;
48 }
49
50 void RUNSCRIPT::reset_default(bool free_strings)
51 {
52    if (free_strings && command) {
53      free_pool_memory(command);
54    }
55    if (free_strings && target) {
56      free_pool_memory(target);
57    }
58
59    target = NULL;
60    command = NULL;
61    on_success = true;
62    on_failure = false;
63    fail_on_error = true;
64    when = SCRIPT_Never;
65    old_proto = false;        /* TODO: drop this with bacula 1.42 */
66    job_code_callback = NULL;
67 }
68
69 RUNSCRIPT *copy_runscript(RUNSCRIPT *src)
70 {
71    Dmsg0(500, "runscript: creating new RUNSCRIPT object from other\n");
72
73    RUNSCRIPT *dst = (RUNSCRIPT *)malloc(sizeof(RUNSCRIPT));
74    memcpy(dst, src, sizeof(RUNSCRIPT));
75
76    dst->command = NULL;
77    dst->target = NULL;
78
79    dst->set_command(src->command, src->cmd_type);
80    dst->set_target(src->target);
81
82    return dst;
83 }
84
85 void free_runscript(RUNSCRIPT *script)
86 {
87    Dmsg0(500, "runscript: freeing RUNSCRIPT object\n");
88
89    if (script->command) {
90       free_pool_memory(script->command);
91    }
92    if (script->target) {
93       free_pool_memory(script->target);
94    }
95    free(script);
96 }
97
98 int run_scripts(JCR *jcr, alist *runscripts, const char *label)
99 {
100    Dmsg2(200, "runscript: running all RUNSCRIPT object (%s) JobStatus=%c\n", label, jcr->JobStatus);
101
102    RUNSCRIPT *script;
103    bool runit;
104
105    int when;
106
107    if (strstr(label, NT_("Before"))) {
108       when = SCRIPT_Before;
109    } else if (bstrcmp(label, NT_("ClientAfterVSS"))) {
110       when = SCRIPT_AfterVSS;
111    } else {
112       when = SCRIPT_After;
113    }
114
115    if (runscripts == NULL) {
116       Dmsg0(100, "runscript: WARNING RUNSCRIPTS list is NULL\n");
117       return 0;
118    }
119
120    foreach_alist(script, runscripts) {
121       Dmsg2(200, "runscript: try to run %s:%s\n", NPRT(script->target), NPRT(script->command));
122       runit = false;
123
124       if ((script->when & SCRIPT_Before) && (when & SCRIPT_Before)) {
125          if ((script->on_success &&
126               (jcr->JobStatus == JS_Running || jcr->JobStatus == JS_Created))
127             || (script->on_failure &&
128                 (job_canceled(jcr) || jcr->JobStatus == JS_Differences))
129             )
130          {
131             Dmsg4(200, "runscript: Run it because SCRIPT_Before (%s,%i,%i,%c)\n",
132                   script->command, script->on_success, script->on_failure,
133                   jcr->JobStatus );
134             runit = true;
135          }
136       }
137
138       if ((script->when & SCRIPT_AfterVSS) && (when & SCRIPT_AfterVSS)) {
139          if ((script->on_success && (jcr->JobStatus == JS_Blocked))
140             || (script->on_failure && job_canceled(jcr))
141             )
142          {
143             Dmsg4(200, "runscript: Run it because SCRIPT_AfterVSS (%s,%i,%i,%c)\n",
144                   script->command, script->on_success, script->on_failure,
145                   jcr->JobStatus );
146             runit = true;
147          }
148       }
149
150       if ((script->when & SCRIPT_After) && (when & SCRIPT_After)) {
151          if ((script->on_success &&
152               (jcr->JobStatus == JS_Terminated || jcr->JobStatus == JS_Warnings))
153             || (script->on_failure &&
154                 (job_canceled(jcr) || jcr->JobStatus == JS_Differences))
155             )
156          {
157             Dmsg4(200, "runscript: Run it because SCRIPT_After (%s,%i,%i,%c)\n",
158                   script->command, script->on_success, script->on_failure,
159                   jcr->JobStatus );
160             runit = true;
161          }
162       }
163
164       if (!script->is_local()) {
165          runit = false;
166       }
167
168       /* we execute it */
169       if (runit) {
170          script->run(jcr, label);
171       }
172    }
173    return 1;
174 }
175
176 bool RUNSCRIPT::is_local()
177 {
178    if (!target || (strcmp(target, "") == 0)) {
179       return true;
180    } else {
181       return false;
182    }
183 }
184
185 /* set this->command to cmd */
186 void RUNSCRIPT::set_command(const char *cmd, int acmd_type)
187 {
188    Dmsg1(500, "runscript: setting command = %s\n", NPRT(cmd));
189
190    if (!cmd) {
191       return;
192    }
193
194    if (!command) {
195       command = get_pool_memory(PM_FNAME);
196    }
197
198    pm_strcpy(command, cmd);
199    cmd_type = acmd_type;
200 }
201
202 /* set this->target to client_name */
203 void RUNSCRIPT::set_target(const char *client_name)
204 {
205    Dmsg1(500, "runscript: setting target = %s\n", NPRT(client_name));
206
207    if (!client_name) {
208       return;
209    }
210
211    if (!target) {
212       target = get_pool_memory(PM_FNAME);
213    }
214
215    pm_strcpy(target, client_name);
216 }
217
218 bool RUNSCRIPT::run(JCR *jcr, const char *name)
219 {
220    Dmsg1(100, "runscript: running a RUNSCRIPT object type=%d\n", cmd_type);
221    POOLMEM *ecmd = get_pool_memory(PM_FNAME);
222    int status;
223    BPIPE *bpipe;
224    char line[MAXSTRING];
225
226    ecmd = edit_job_codes(jcr, ecmd, this->command, "", this->job_code_callback);
227    Dmsg1(100, "runscript: running '%s'...\n", ecmd);
228    Jmsg(jcr, M_INFO, 0, _("%s: run %s \"%s\"\n"),
229         cmd_type==SHELL_CMD?"shell command":"console command", name, ecmd);
230
231    switch (cmd_type) {
232    case SHELL_CMD:
233       bpipe = open_bpipe(ecmd, 0, "r");
234       free_pool_memory(ecmd);
235       if (bpipe == NULL) {
236          berrno be;
237          Jmsg(jcr, M_ERROR, 0, _("Runscript: %s could not execute. ERR=%s\n"), name,
238             be.bstrerror());
239          goto bail_out;
240       }
241       while (fgets(line, sizeof(line), bpipe->rfd)) {
242          int len = strlen(line);
243          if (len > 0 && line[len-1] == '\n') {
244             line[len-1] = 0;
245          }
246          Jmsg(jcr, M_INFO, 0, _("%s: %s\n"), name, line);
247       }
248       status = close_bpipe(bpipe);
249       if (status != 0) {
250          berrno be;
251          Jmsg(jcr, M_ERROR, 0, _("Runscript: %s returned non-zero status=%d. ERR=%s\n"), name,
252             be.code(status), be.bstrerror(status));
253          goto bail_out;
254       }
255       Dmsg0(100, "runscript OK\n");
256       break;
257    case CONSOLE_CMD:
258       if (console_command) {                 /* can we run console command? */
259          if (!console_command(jcr, ecmd)) {  /* yes, do so */
260             goto bail_out;
261          }
262       }
263       break;
264    }
265    return true;
266
267 bail_out:
268    /* cancel running job properly */
269    if (fail_on_error) {
270       jcr->setJobStatus(JS_ErrorTerminated);
271    }
272    Dmsg1(100, "runscript failed. fail_on_error=%d\n", fail_on_error);
273    return false;
274 }
275
276 void free_runscripts(alist *runscripts)
277 {
278    Dmsg0(500, "runscript: freeing all RUNSCRIPTS object\n");
279
280    RUNSCRIPT *elt;
281    foreach_alist(elt, runscripts) {
282       free_runscript(elt);
283    }
284 }
285
286 void RUNSCRIPT::debug()
287 {
288    Dmsg0(200, "runscript: debug\n");
289    Dmsg0(200,  _(" --> RunScript\n"));
290    Dmsg1(200,  _("  --> Command=%s\n"), NPRT(command));
291    Dmsg1(200,  _("  --> Target=%s\n"),  NPRT(target));
292    Dmsg1(200,  _("  --> RunOnSuccess=%u\n"),  on_success);
293    Dmsg1(200,  _("  --> RunOnFailure=%u\n"),  on_failure);
294    Dmsg1(200,  _("  --> FailJobOnError=%u\n"),  fail_on_error);
295    Dmsg1(200,  _("  --> RunWhen=%u\n"),  when);
296 }
297
298 void RUNSCRIPT::set_job_code_callback(job_code_callback_t arg_job_code_callback)
299 {
300    this->job_code_callback = arg_job_code_callback;
301 }