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