1 /*
2 * virhook.c: implementation of the synchronous hooks support
3 *
4 * Copyright (C) 2010-2014 Red Hat, Inc.
5 * Copyright (C) 2010 Daniel Veillard
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library. If not, see
19 * <http://www.gnu.org/licenses/>.
20 */
21
22 #include <config.h>
23
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <unistd.h>
27
28 #include "virerror.h"
29 #include "virhook.h"
30 #include "virutil.h"
31 #include "virlog.h"
32 #include "viralloc.h"
33 #include "virfile.h"
34 #include "configmake.h"
35 #include "vircommand.h"
36 #include "virstring.h"
37 #include "virglibutil.h"
38
39 #define VIR_FROM_THIS VIR_FROM_HOOK
40
41 VIR_LOG_INIT("util.hook");
42
43 #define LIBVIRT_HOOK_DIR SYSCONFDIR "/libvirt/hooks"
44
45 VIR_ENUM_DECL(virHookDriver);
46 VIR_ENUM_DECL(virHookDaemonOp);
47 VIR_ENUM_DECL(virHookSubop);
48 VIR_ENUM_DECL(virHookQemuOp);
49 VIR_ENUM_DECL(virHookLxcOp);
50 VIR_ENUM_DECL(virHookNetworkOp);
51 VIR_ENUM_DECL(virHookLibxlOp);
52 VIR_ENUM_DECL(virHookBhyveOp);
53
54 VIR_ENUM_IMPL(virHookDriver,
55 VIR_HOOK_DRIVER_LAST,
56 "daemon",
57 "qemu",
58 "lxc",
59 "network",
60 "libxl",
61 "bhyve",
62 );
63
64 VIR_ENUM_IMPL(virHookDaemonOp,
65 VIR_HOOK_DAEMON_OP_LAST,
66 "start",
67 "shutdown",
68 "reload",
69 );
70
71 VIR_ENUM_IMPL(virHookSubop,
72 VIR_HOOK_SUBOP_LAST,
73 "-",
74 "begin",
75 "end",
76 );
77
78 VIR_ENUM_IMPL(virHookQemuOp,
79 VIR_HOOK_QEMU_OP_LAST,
80 "start",
81 "stopped",
82 "prepare",
83 "release",
84 "migrate",
85 "started",
86 "reconnect",
87 "attach",
88 "restore",
89 );
90
91 VIR_ENUM_IMPL(virHookLxcOp,
92 VIR_HOOK_LXC_OP_LAST,
93 "start",
94 "stopped",
95 "prepare",
96 "release",
97 "started",
98 "reconnect",
99 );
100
101 VIR_ENUM_IMPL(virHookNetworkOp,
102 VIR_HOOK_NETWORK_OP_LAST,
103 "start",
104 "started",
105 "stopped",
106 "port-created",
107 "port-deleted",
108 "updated",
109 );
110
111 VIR_ENUM_IMPL(virHookLibxlOp,
112 VIR_HOOK_LIBXL_OP_LAST,
113 "start",
114 "stopped",
115 "prepare",
116 "release",
117 "migrate",
118 "started",
119 "reconnect",
120 );
121
122 VIR_ENUM_IMPL(virHookBhyveOp,
123 VIR_HOOK_BHYVE_OP_LAST,
124 "start",
125 "stopped",
126 "prepare",
127 "release",
128 "started",
129 );
130
131 static int virHooksFound = -1;
132
133 /**
134 * virHookCheck:
135 * @driver: the driver name "daemon", "qemu", "lxc"...
136 *
137 * Check is there is an installed hook for the given driver, if this
138 * is the case register it. Then subsequent calls to virHookCall
139 * will call the hook if found.
140 *
141 * Returns 1 if found, 0 if not found, and -1 in case of error
142 */
143 static int
virHookCheck(int no,const char * driver)144 virHookCheck(int no, const char *driver)
145 {
146 int ret;
147 g_autoptr(DIR) dir = NULL;
148 struct dirent *entry;
149 g_autofree char *path = NULL;
150 g_autofree char *dir_path = NULL;
151
152 if (driver == NULL) {
153 virReportError(VIR_ERR_INTERNAL_ERROR,
154 _("Invalid hook name for #%d"), no);
155 return -1;
156 }
157
158 virBuildPath(&path, LIBVIRT_HOOK_DIR, driver);
159
160 if (!virFileExists(path)) {
161 VIR_DEBUG("No hook script %s", path);
162 } else if (!virFileIsExecutable(path)) {
163 VIR_WARN("Non-executable hook script %s", path);
164 } else {
165 VIR_DEBUG("Found hook script %s", path);
166 return 1;
167 }
168
169 dir_path = g_strdup_printf("%s.d", path);
170
171 if (!virFileIsExecutable(dir_path) && errno != EISDIR) {
172 VIR_DEBUG("Hook dir %s is not accessible", dir_path);
173 return 0;
174 }
175
176 if ((ret = virDirOpenIfExists(&dir, dir_path)) < 0)
177 return -1;
178
179 if (!ret) {
180 VIR_DEBUG("No hook script dir %s", dir_path);
181 return 0;
182 }
183
184 while ((ret = virDirRead(dir, &entry, dir_path)) > 0) {
185 g_autofree char *entry_path = g_build_filename(dir_path,
186 entry->d_name,
187 NULL);
188 if (!virFileIsExecutable(entry_path)) {
189 VIR_WARN("Non-executable hook script %s", entry_path);
190 continue;
191 }
192
193 VIR_DEBUG("Found hook script %s", entry_path);
194 ret = 1;
195 break;
196 }
197
198 return ret;
199 }
200
201 /*
202 * virHookInitialize:
203 *
204 * Initialize synchronous hooks support.
205 * Check is there is an installed hook for all the drivers
206 *
207 * Returns the number of hooks found or -1 in case of failure
208 */
209 int
virHookInitialize(void)210 virHookInitialize(void)
211 {
212 size_t i;
213 int res, ret = 0;
214
215 virHooksFound = 0;
216 for (i = 0; i < VIR_HOOK_DRIVER_LAST; i++) {
217 res = virHookCheck(i, virHookDriverTypeToString(i));
218 if (res < 0)
219 return -1;
220
221 if (res == 1) {
222 virHooksFound |= (1 << i);
223 ret++;
224 }
225 }
226 return ret;
227 }
228
229 /**
230 * virHookPresent:
231 * @driver: the driver number (from virHookDriver enum)
232 *
233 * Check if a hook exists for the given driver, this is needed
234 * to avoid unnecessary work if the hook is not present
235 *
236 * Returns 1 if present, 0 otherwise
237 */
238 int
virHookPresent(int driver)239 virHookPresent(int driver)
240 {
241 if ((driver < VIR_HOOK_DRIVER_DAEMON) ||
242 (driver >= VIR_HOOK_DRIVER_LAST))
243 return 0;
244 if (virHooksFound == -1)
245 return 0;
246
247 if ((virHooksFound & (1 << driver)) == 0)
248 return 0;
249 return 1;
250 }
251
252 /**
253 * virRunScript:
254 * @path: the script path
255 * @id: an id for the object '-' if non available for example on daemon hooks
256 * @op: the operation on the id
257 * @subop: a sub_operation, currently unused
258 * @extra: optional string information
259 * @input: extra input given to the script on stdin
260 * @output: optional address of variable to store malloced result buffer
261 *
262 * Implement a execution of script. This is a synchronous call, we wait for
263 * execution completion. If @output is non-NULL, *output is guaranteed to be
264 * allocated after successful virRunScript, and is best-effort allocated after
265 * failed virRunScript; the caller is responsible for freeing *output.
266 *
267 * Returns: 0 if the execution succeeded, -1 if script returned an error
268 */
269 static int
virRunScript(const char * path,const char * id,const char * op,const char * subop,const char * extra,const char * input,char ** output)270 virRunScript(const char *path,
271 const char *id,
272 const char *op,
273 const char *subop,
274 const char *extra,
275 const char *input,
276 char **output)
277 {
278 int ret;
279 g_autoptr(virCommand) cmd = NULL;
280
281 VIR_DEBUG("Calling hook %s id=%s op=%s subop=%s extra=%s",
282 path, id, op, subop, extra);
283
284 cmd = virCommandNewArgList(path, id, op, subop, extra, NULL);
285
286 virCommandAddEnvPassCommon(cmd);
287
288 if (input)
289 virCommandSetInputBuffer(cmd, input);
290 if (output)
291 virCommandSetOutputBuffer(cmd, output);
292
293 ret = virCommandRun(cmd, NULL);
294 if (ret < 0) {
295 /* Convert INTERNAL_ERROR into known error. */
296 virReportError(VIR_ERR_HOOK_SCRIPT_FAILED, "%s",
297 virGetLastErrorMessage());
298 }
299
300 return ret;
301 }
302
303 /**
304 * virHookCall:
305 * @driver: the driver number (from virHookDriver enum)
306 * @id: an id for the object '-' if non available for example on daemon hooks
307 * @op: the operation on the id e.g. VIR_HOOK_QEMU_OP_START
308 * @sub_op: a sub_operation, currently unused
309 * @extra: optional string information
310 * @input: extra input given to the script on stdin
311 * @output: optional address of variable to store malloced result buffer
312 *
313 * Implement a hook call, where the external scripts for the driver are
314 * called with the given information. This is a synchronous call, we wait for
315 * execution completion. If @output is non-NULL, *output is guaranteed to be
316 * allocated after successful virHookCall, and is best-effort allocated after
317 * failed virHookCall; the caller is responsible for freeing *output.
318 *
319 * The script from LIBVIRT_HOOK_DIR is executed the first, followed by scripts
320 * found under "$driver.d/" directory (sorted alphabetically. If output from
321 * the hook script is expected, then the output produced by LIBVIRT_HOOK_DIR
322 * script is fed as input to the first script from the "$driver.d/" directory
323 * and its output is fed as input to the second and so on.
324 *
325 * Returns: 0 if the execution succeeded, 1 if the script was not found or
326 * invalid parameters, and -1 if script returned an error
327 */
328 int
virHookCall(int driver,const char * id,int op,int sub_op,const char * extra,const char * input,char ** output)329 virHookCall(int driver,
330 const char *id,
331 int op,
332 int sub_op,
333 const char *extra,
334 const char *input,
335 char **output)
336 {
337 int ret, script_ret;
338 g_autoptr(DIR) dir = NULL;
339 struct dirent *entry;
340 g_autofree char *path = NULL;
341 g_autofree char *dir_path = NULL;
342 g_autoptr(virGSListString) entries = NULL;
343 const char *drvstr;
344 const char *opstr;
345 const char *subopstr;
346 GSList *next;
347
348 if (output)
349 *output = NULL;
350
351 if ((driver < VIR_HOOK_DRIVER_DAEMON) ||
352 (driver >= VIR_HOOK_DRIVER_LAST))
353 return 1;
354
355 /*
356 * We cache the availability of the script to minimize impact at
357 * runtime if no script is defined, this is being reset on SIGHUP
358 */
359 if ((virHooksFound == -1) ||
360 ((driver == VIR_HOOK_DRIVER_DAEMON) &&
361 (op == VIR_HOOK_DAEMON_OP_RELOAD ||
362 op == VIR_HOOK_DAEMON_OP_SHUTDOWN)))
363 virHookInitialize();
364
365 if ((virHooksFound & (1 << driver)) == 0)
366 return 1;
367
368 drvstr = virHookDriverTypeToString(driver);
369
370 opstr = NULL;
371 switch (driver) {
372 case VIR_HOOK_DRIVER_DAEMON:
373 opstr = virHookDaemonOpTypeToString(op);
374 break;
375 case VIR_HOOK_DRIVER_QEMU:
376 opstr = virHookQemuOpTypeToString(op);
377 break;
378 case VIR_HOOK_DRIVER_LXC:
379 opstr = virHookLxcOpTypeToString(op);
380 break;
381 case VIR_HOOK_DRIVER_LIBXL:
382 opstr = virHookLibxlOpTypeToString(op);
383 break;
384 case VIR_HOOK_DRIVER_NETWORK:
385 opstr = virHookNetworkOpTypeToString(op);
386 break;
387 case VIR_HOOK_DRIVER_BHYVE:
388 opstr = virHookBhyveOpTypeToString(op);
389 break;
390 }
391 if (opstr == NULL) {
392 virReportError(VIR_ERR_INTERNAL_ERROR,
393 _("Hook for %s, failed to find operation #%d"),
394 drvstr, op);
395 return 1;
396 }
397 subopstr = virHookSubopTypeToString(sub_op);
398 if (subopstr == NULL)
399 subopstr = "-";
400 if (extra == NULL)
401 extra = "-";
402
403 virBuildPath(&path, LIBVIRT_HOOK_DIR, drvstr);
404
405 script_ret = 1;
406
407 if (virFileIsExecutable(path)) {
408 script_ret = virRunScript(path, id, opstr, subopstr, extra,
409 input, output);
410 }
411
412 dir_path = g_strdup_printf("%s.d", path);
413
414 if ((ret = virDirOpenIfExists(&dir, dir_path)) < 0)
415 return -1;
416
417 if (!ret)
418 return script_ret;
419
420 while ((ret = virDirRead(dir, &entry, dir_path)) > 0) {
421 g_autofree char *entry_path = g_build_filename(dir_path,
422 entry->d_name,
423 NULL);
424 if (!virFileIsExecutable(entry_path))
425 continue;
426
427 entries = g_slist_prepend(entries, g_steal_pointer(&entry_path));
428 }
429
430 if (ret < 0)
431 return -1;
432
433 if (!entries)
434 return script_ret;
435
436 entries = g_slist_sort(entries, (GCompareFunc) strcmp);
437
438 for (next = entries; next; next = next->next) {
439 int entry_ret;
440 const char *entry_input;
441 g_autofree char *entry_output = NULL;
442 const char *filename = next->data;
443
444 /* Get input from previous output */
445 entry_input = (!script_ret && output &&
446 !virStringIsEmpty(*output)) ? *output : input;
447 entry_ret = virRunScript(filename, id, opstr,
448 subopstr, extra, entry_input,
449 (output) ? &entry_output : NULL);
450 if (entry_ret < script_ret)
451 script_ret = entry_ret;
452
453 /* Replace output to new output from item */
454 if (!entry_ret && output && !virStringIsEmpty(entry_output)) {
455 g_free(*output);
456 *output = g_steal_pointer(&entry_output);
457 }
458 }
459
460 return script_ret;
461 }
462