1 /*
2 BAREOS® - Backup Archiving REcovery Open Sourced
3
4 Copyright (C) 2006-2011 Free Software Foundation Europe e.V.
5
6 This program is Free Software; you can redistribute it and/or
7 modify it under the terms of version three of the GNU Affero General Public
8 License as published by the Free Software Foundation and included
9 in the file LICENSE.
10
11 This program is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Affero General Public License for more details.
15
16 You should have received a copy of the GNU Affero General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301, USA.
20 */
21 /*
22 * Manipulation routines for RunScript list
23 *
24 * Eric Bollengier, May 2006
25 */
26
27 #include "include/bareos.h"
28 #include "include/jcr.h"
29 #include "runscript.h"
30 #include "lib/util.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)(JobControlRecord *jcr, const char *cmd) = NULL;
38
39
NewRunscript()40 RunScript *NewRunscript()
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->ResetDefault();
46
47 return cmd;
48 }
49
ResetDefault(bool free_strings)50 void RunScript::ResetDefault(bool free_strings)
51 {
52 if (free_strings && command) {
53 FreePoolMemory(command);
54 }
55 if (free_strings && target) {
56 FreePoolMemory(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 job_code_callback = NULL;
66 }
67
copy_runscript(RunScript * src)68 RunScript *copy_runscript(RunScript *src)
69 {
70 Dmsg0(500, "runscript: creating new RunScript object from other\n");
71
72 RunScript *dst = (RunScript *)malloc(sizeof(RunScript));
73 memcpy(dst, src, sizeof(RunScript));
74
75 dst->command = NULL;
76 dst->target = NULL;
77
78 dst->SetCommand(src->command, src->cmd_type);
79 dst->SetTarget(src->target);
80
81 return dst;
82 }
83
FreeRunscript(RunScript * script)84 void FreeRunscript(RunScript *script)
85 {
86 Dmsg0(500, "runscript: freeing RunScript object\n");
87
88 if (script->command) {
89 FreePoolMemory(script->command);
90 }
91 if (script->target) {
92 FreePoolMemory(script->target);
93 }
94 free(script);
95 }
96
ScriptDirAllowed(JobControlRecord * jcr,RunScript * script,alist * allowed_script_dirs)97 static inline bool ScriptDirAllowed(JobControlRecord *jcr, RunScript *script, alist *allowed_script_dirs)
98 {
99 char *bp, *allowed_script_dir = nullptr;
100 bool allowed = false;
101 PoolMem script_dir(PM_FNAME);
102
103 /*
104 * If there is no explicit list of allowed dirs allow any dir.
105 */
106 if (!allowed_script_dirs) {
107 return true;
108 }
109
110 /*
111 * Determine the dir the script is in.
112 */
113 PmStrcpy(script_dir, script->command);
114 if ((bp = strrchr(script_dir.c_str(), '/'))) {
115 *bp = '\0';
116 }
117
118 /*
119 * Make sure there are no relative path elements in script dir by which the
120 * user tries to escape the allowed dir checking. For scripts we only allow
121 * absolute paths.
122 */
123 if (strstr(script_dir.c_str(), "..")) {
124 Dmsg1(200, "ScriptDirAllowed: relative pathnames not allowed: %s\n", script_dir.c_str());
125 return false;
126 }
127
128 /*
129 * Match the path the script is in against the list of allowed script directories.
130 */
131 foreach_alist(allowed_script_dir, allowed_script_dirs) {
132 if (Bstrcasecmp(script_dir.c_str(), allowed_script_dir)) {
133 allowed = true;
134 break;
135 }
136 }
137
138 Dmsg2(200, "ScriptDirAllowed: script %s %s allowed by Allowed Script Dir setting",
139 script->command, (allowed) ? "" : "NOT");
140
141 return allowed;
142 }
143
RunScripts(JobControlRecord * jcr,alist * runscripts,const char * label,alist * allowed_script_dirs)144 int RunScripts(JobControlRecord *jcr, alist *runscripts, const char *label, alist *allowed_script_dirs)
145 {
146 RunScript *script = nullptr;
147 bool runit;
148 int when;
149
150 Dmsg2(200, "runscript: running all RunScript object (%s) JobStatus=%c\n", label, jcr->JobStatus);
151
152 if (strstr(label, NT_("Before"))) {
153 when = SCRIPT_Before;
154 } else if (bstrcmp(label, NT_("ClientAfterVSS"))) {
155 when = SCRIPT_AfterVSS;
156 } else {
157 when = SCRIPT_After;
158 }
159
160 if (runscripts == NULL) {
161 Dmsg0(100, "runscript: WARNING RUNSCRIPTS list is NULL\n");
162 return 0;
163 }
164
165 foreach_alist(script, runscripts) {
166 Dmsg2(200, "runscript: try to run %s:%s\n", NPRT(script->target), NPRT(script->command));
167 runit = false;
168
169 if ((script->when & SCRIPT_Before) && (when & SCRIPT_Before)) {
170 if ((script->on_success && (jcr->JobStatus == JS_Running || jcr->JobStatus == JS_Created)) ||
171 (script->on_failure && (JobCanceled(jcr) || jcr->JobStatus == JS_Differences))) {
172 Dmsg4(200, "runscript: Run it because SCRIPT_Before (%s,%i,%i,%c)\n",
173 script->command, script->on_success, script->on_failure, jcr->JobStatus );
174 runit = true;
175 }
176 }
177
178 if ((script->when & SCRIPT_AfterVSS) && (when & SCRIPT_AfterVSS)) {
179 if ((script->on_success && (jcr->JobStatus == JS_Blocked)) ||
180 (script->on_failure && JobCanceled(jcr))) {
181 Dmsg4(200, "runscript: Run it because SCRIPT_AfterVSS (%s,%i,%i,%c)\n",
182 script->command, script->on_success, script->on_failure, jcr->JobStatus );
183 runit = true;
184 }
185 }
186
187 if ((script->when & SCRIPT_After) && (when & SCRIPT_After)) {
188 if ((script->on_success && jcr->IsTerminatedOk()) ||
189 (script->on_failure && (JobCanceled(jcr) || jcr->JobStatus == JS_Differences))) {
190 Dmsg4(200, "runscript: Run it because SCRIPT_After (%s,%i,%i,%c)\n",
191 script->command, script->on_success, script->on_failure, jcr->JobStatus );
192 runit = true;
193 }
194 }
195
196 if (!script->IsLocal()) {
197 runit = false;
198 }
199
200 /*
201 * We execute it
202 */
203 if (runit) {
204 if (!ScriptDirAllowed(jcr, script, allowed_script_dirs)) {
205 Dmsg1(200, "runscript: Not running script %s because its not in one of the allowed scripts dirs\n",
206 script->command);
207 Jmsg(jcr, M_ERROR, 0, _("Runscript: run %s \"%s\" could not execute, "
208 "not in one of the allowed scripts dirs\n"), label, script->command);
209 jcr->setJobStatus(JS_ErrorTerminated);
210 goto bail_out;
211 }
212
213 script->run(jcr, label);
214 }
215 }
216
217 bail_out:
218 return 1;
219 }
220
IsLocal()221 bool RunScript::IsLocal()
222 {
223 if (!target || bstrcmp(target, "")) {
224 return true;
225 } else {
226 return false;
227 }
228 }
229
230 /* set this->command to cmd */
SetCommand(const char * cmd,int acmd_type)231 void RunScript::SetCommand(const char *cmd, int acmd_type)
232 {
233 Dmsg1(500, "runscript: setting command = %s\n", NPRT(cmd));
234
235 if (!cmd) {
236 return;
237 }
238
239 if (!command) {
240 command = GetPoolMemory(PM_FNAME);
241 }
242
243 PmStrcpy(command, cmd);
244 cmd_type = acmd_type;
245 }
246
247 /* set this->target to client_name */
SetTarget(const char * client_name)248 void RunScript::SetTarget(const char *client_name)
249 {
250 Dmsg1(500, "runscript: setting target = %s\n", NPRT(client_name));
251
252 if (!client_name) {
253 return;
254 }
255
256 if (!target) {
257 target = GetPoolMemory(PM_FNAME);
258 }
259
260 PmStrcpy(target, client_name);
261 }
262
run(JobControlRecord * jcr,const char * name)263 bool RunScript::run(JobControlRecord *jcr, const char *name)
264 {
265 Dmsg1(100, "runscript: running a RunScript object type=%d\n", cmd_type);
266 POOLMEM *ecmd = GetPoolMemory(PM_FNAME);
267 int status;
268 Bpipe *bpipe;
269 PoolMem line(PM_NAME);
270
271 ecmd = edit_job_codes(jcr, ecmd, this->command, "", this->job_code_callback);
272 Dmsg1(100, "runscript: running '%s'...\n", ecmd);
273 Jmsg(jcr, M_INFO, 0, _("%s: run %s \"%s\"\n"),
274 cmd_type==SHELL_CMD?"shell command":"console command", name, ecmd);
275
276 switch (cmd_type) {
277 case SHELL_CMD:
278 bpipe = OpenBpipe(ecmd, 0, "r");
279 FreePoolMemory(ecmd);
280
281 if (bpipe == NULL) {
282 BErrNo be;
283 Jmsg(jcr, M_ERROR, 0, _("Runscript: %s could not execute. ERR=%s\n"), name,
284 be.bstrerror());
285 goto bail_out;
286 }
287
288 while (fgets(line.c_str(), line.size(), bpipe->rfd)) {
289 StripTrailingJunk(line.c_str());
290 Jmsg(jcr, M_INFO, 0, _("%s: %s\n"), name, line.c_str());
291 }
292
293 status = CloseBpipe(bpipe);
294
295 if (status != 0) {
296 BErrNo be;
297 Jmsg(jcr, M_ERROR, 0, _("Runscript: %s returned non-zero status=%d. ERR=%s\n"), name,
298 be.code(status), be.bstrerror(status));
299 goto bail_out;
300 }
301
302 Dmsg0(100, "runscript OK\n");
303 break;
304 case CONSOLE_CMD:
305 if (console_command) { /* can we run console command? */
306 if (!console_command(jcr, ecmd)) { /* yes, do so */
307 goto bail_out;
308 }
309 }
310 break;
311 }
312 return true;
313
314 bail_out:
315 /* cancel running job properly */
316 if (fail_on_error) {
317 jcr->setJobStatus(JS_ErrorTerminated);
318 }
319 Dmsg1(100, "runscript failed. fail_on_error=%d\n", fail_on_error);
320 return false;
321 }
322
FreeRunscripts(alist * runscripts)323 void FreeRunscripts(alist *runscripts)
324 {
325 Dmsg0(500, "runscript: freeing all RUNSCRIPTS object\n");
326
327 RunScript *elt = nullptr;
328 foreach_alist(elt, runscripts) {
329 FreeRunscript(elt);
330 }
331 }
332
debug()333 void RunScript::debug()
334 {
335 Dmsg0(200, "runscript: debug\n");
336 Dmsg0(200, _(" --> RunScript\n"));
337 Dmsg1(200, _(" --> Command=%s\n"), NPRT(command));
338 Dmsg1(200, _(" --> Target=%s\n"), NPRT(target));
339 Dmsg1(200, _(" --> RunOnSuccess=%u\n"), on_success);
340 Dmsg1(200, _(" --> RunOnFailure=%u\n"), on_failure);
341 Dmsg1(200, _(" --> FailJobOnError=%u\n"), fail_on_error);
342 Dmsg1(200, _(" --> RunWhen=%u\n"), when);
343 }
344
SetJobCodeCallback(job_code_callback_t arg_job_code_callback)345 void RunScript::SetJobCodeCallback(job_code_callback_t arg_job_code_callback)
346 {
347 this->job_code_callback = arg_job_code_callback;
348 }
349