2
Copyright (c) 2007-2012 Red Hat, Inc. <http://www.redhat.com>
3
This file is part of GlusterFS.
5
This file is licensed to you under your choice of the GNU Lesser
6
General Public License, version 3 or any later version (LGPLv3 or
7
later), or the GNU General Public License, version 2 (GPLv2), in all
8
cases as published by the Free Software Foundation.
11
#include <glusterfs/dict.h>
12
#include <glusterfs/logging.h>
13
#include <glusterfs/run.h>
14
#include <glusterfs/syscall.h>
15
#include <glusterfs/compat.h>
16
#include <glusterfs/compat-errno.h>
17
#include "glusterd-sm.h"
18
#include "glusterd-op-sm.h"
19
#include "glusterd-utils.h"
20
#include "glusterd-store.h"
21
#include "glusterd-hooks.h"
22
#include "glusterd-messages.h"
27
char glusterd_hook_dirnames[GD_OP_MAX][256] = {
29
[GD_OP_CREATE_VOLUME] = "create",
30
[GD_OP_START_BRICK] = EMPTY,
31
[GD_OP_STOP_BRICK] = EMPTY,
32
[GD_OP_DELETE_VOLUME] = "delete",
33
[GD_OP_START_VOLUME] = "start",
34
[GD_OP_STOP_VOLUME] = "stop",
35
[GD_OP_DEFRAG_VOLUME] = EMPTY,
36
[GD_OP_ADD_BRICK] = "add-brick",
37
[GD_OP_REMOVE_BRICK] = "remove-brick",
38
[GD_OP_REPLACE_BRICK] = EMPTY,
39
[GD_OP_SET_VOLUME] = "set",
40
[GD_OP_RESET_VOLUME] = "reset",
41
[GD_OP_SYNC_VOLUME] = EMPTY,
42
[GD_OP_LOG_ROTATE] = EMPTY,
43
[GD_OP_GSYNC_CREATE] = "gsync-create",
44
[GD_OP_GSYNC_SET] = EMPTY,
45
[GD_OP_PROFILE_VOLUME] = EMPTY,
46
[GD_OP_QUOTA] = EMPTY,
47
[GD_OP_STATUS_VOLUME] = EMPTY,
48
[GD_OP_REBALANCE] = EMPTY,
49
[GD_OP_HEAL_VOLUME] = EMPTY,
50
[GD_OP_STATEDUMP_VOLUME] = EMPTY,
51
[GD_OP_LIST_VOLUME] = EMPTY,
52
[GD_OP_CLEARLOCKS_VOLUME] = EMPTY,
53
[GD_OP_DEFRAG_BRICK_VOLUME] = EMPTY,
54
[GD_OP_RESET_BRICK] = EMPTY,
59
glusterd_is_hook_enabled(char *script)
61
return (script[0] == 'S' && (fnmatch("*.rpmsave", script, 0) != 0) &&
62
(fnmatch("*.rpmnew", script, 0) != 0));
66
glusterd_hooks_create_hooks_directory(char *basedir)
70
int type = GD_COMMIT_HOOK_NONE;
71
char version_dir[PATH_MAX] = {
74
char path[PATH_MAX] = {
77
char *cmd_subdir = NULL;
78
char type_subdir[GD_COMMIT_HOOK_MAX][256] = {{
83
glusterd_conf_t *priv = NULL;
86
xlator_t *this = THIS;
89
snprintf(path, sizeof(path), "%s/hooks", basedir);
90
ret = mkdir_p(path, 0755, _gf_true);
92
gf_smsg(this->name, GF_LOG_CRITICAL, errno, GD_MSG_CREATE_DIR_FAILED,
93
"Path=%s", path, NULL);
97
GLUSTERD_GET_HOOKS_DIR(version_dir, GLUSTERD_HOOK_VER, priv);
98
ret = mkdir_p(version_dir, 0755, _gf_true);
100
gf_smsg(this->name, GF_LOG_CRITICAL, errno, GD_MSG_CREATE_DIR_FAILED,
101
"Directory=%s", version_dir, NULL);
105
for (op = GD_OP_NONE + 1; op < GD_OP_MAX; op++) {
106
cmd_subdir = glusterd_hooks_get_hooks_cmd_subdir(op);
107
if (strlen(cmd_subdir) == 0)
110
len = snprintf(path, sizeof(path), "%s/%s", version_dir, cmd_subdir);
111
if ((len < 0) || (len >= sizeof(path))) {
112
gf_smsg(this->name, GF_LOG_ERROR, errno, GD_MSG_COPY_FAIL, NULL);
116
ret = mkdir_p(path, 0755, _gf_true);
118
gf_smsg(this->name, GF_LOG_CRITICAL, errno,
119
GD_MSG_CREATE_DIR_FAILED, "Path=%s", path, NULL);
123
for (type = GD_COMMIT_HOOK_PRE; type < GD_COMMIT_HOOK_MAX; type++) {
124
len = snprintf(path, sizeof(path), "%s/%s/%s", version_dir,
125
cmd_subdir, type_subdir[type]);
126
if ((len < 0) || (len >= sizeof(path))) {
127
gf_smsg(this->name, GF_LOG_ERROR, errno, GD_MSG_COPY_FAIL,
132
ret = mkdir_p(path, 0755, _gf_true);
134
gf_smsg(this->name, GF_LOG_CRITICAL, errno,
135
GD_MSG_CREATE_DIR_FAILED, "Path=%s", path, NULL);
147
glusterd_hooks_get_hooks_cmd_subdir(glusterd_op_t op)
149
GF_ASSERT((op > GD_OP_NONE) && (op < GD_OP_MAX));
151
return glusterd_hook_dirnames[op];
155
glusterd_hooks_add_working_dir(runner_t *runner, glusterd_conf_t *priv)
157
runner_argprintf(runner, "--gd-workdir=%s", priv->workdir);
161
glusterd_hooks_add_op(runner_t *runner, char *op)
163
runner_argprintf(runner, "--volume-op=%s", op);
167
glusterd_hooks_add_hooks_version(runner_t *runner)
169
runner_argprintf(runner, "--version=%d", GLUSTERD_HOOK_VER);
173
glusterd_hooks_add_custom_args(dict_t *dict, runner_t *runner)
175
char *hooks_args = NULL;
177
xlator_t *this = THIS;
179
GF_VALIDATE_OR_GOTO(this->name, dict, out);
180
GF_VALIDATE_OR_GOTO(this->name, runner, out);
182
ret = dict_get_str(dict, "hooks_args", &hooks_args);
184
gf_msg_debug(this->name, 0, "No Hooks Arguments.");
186
gf_msg_debug(this->name, 0, "Hooks Args = %s", hooks_args);
189
runner_argprintf(runner, "%s", hooks_args);
196
glusterd_hooks_set_volume_args(dict_t *dict, runner_t *runner)
207
char *inet_family = NULL;
208
xlator_t *this = THIS;
210
ret = dict_get_int32(dict, "count", &count);
212
gf_smsg(this->name, GF_LOG_ERROR, -ret, GD_MSG_DICT_GET_FAILED,
217
/* This will not happen unless op_ctx
220
gf_smsg(this->name, GF_LOG_ERROR, errno, GD_MSG_INVALID_ENTRY, "count",
225
runner_add_arg(runner, "-o");
226
for (i = 1; ret == 0; i++) {
227
snprintf(query, sizeof(query), "key%d", i);
228
ret = dict_get_str(dict, query, &key);
232
snprintf(query, sizeof(query), "value%d", i);
233
ret = dict_get_str(dict, query, &value);
237
runner_argprintf(runner, "%s=%s", key, value);
238
if ((strncmp(key, "cluster.enable-shared-storage",
239
SLEN("cluster.enable-shared-storage")) == 0 ||
240
strncmp(key, "enable-shared-storage",
241
SLEN("enable-shared-storage")) == 0) &&
242
strncmp(value, "enable", SLEN("enable")) == 0)
246
glusterd_hooks_add_custom_args(dict, runner);
248
ret = dict_get_str(this->options, "transport.address-family",
251
runner_argprintf(runner, "transport.address-family=%s",
262
glusterd_hooks_add_op_args(runner_t *runner, glusterd_op_t op, dict_t *op_ctx,
263
glusterd_commit_hook_type_t type)
266
gf_boolean_t truth = _gf_false;
267
glusterd_volinfo_t *voliter = NULL;
268
glusterd_conf_t *priv = NULL;
271
priv = THIS->private;
272
cds_list_for_each_entry(voliter, &priv->volumes, vol_list)
274
if (glusterd_is_volume_started(voliter))
280
case GD_OP_START_VOLUME:
281
if (type == GD_COMMIT_HOOK_PRE && vol_count == 0)
284
else if (type == GD_COMMIT_HOOK_POST && vol_count == 1)
290
runner_argprintf(runner, "--first=%s", truth ? "yes" : "no");
292
glusterd_hooks_add_hooks_version(runner);
293
glusterd_hooks_add_op(runner, "start");
294
glusterd_hooks_add_working_dir(runner, priv);
298
case GD_OP_STOP_VOLUME:
299
if (type == GD_COMMIT_HOOK_PRE && vol_count == 1)
302
else if (type == GD_COMMIT_HOOK_POST && vol_count == 0)
308
runner_argprintf(runner, "--last=%s", truth ? "yes" : "no");
311
case GD_OP_SET_VOLUME:
312
ret = glusterd_hooks_set_volume_args(op_ctx, runner);
313
glusterd_hooks_add_working_dir(runner, priv);
316
case GD_OP_GSYNC_CREATE:
317
glusterd_hooks_add_custom_args(op_ctx, runner);
320
case GD_OP_ADD_BRICK:
321
glusterd_hooks_add_hooks_version(runner);
322
glusterd_hooks_add_op(runner, "add-brick");
323
glusterd_hooks_add_working_dir(runner, priv);
326
case GD_OP_RESET_VOLUME:
327
glusterd_hooks_add_hooks_version(runner);
328
glusterd_hooks_add_op(runner, "reset");
329
glusterd_hooks_add_working_dir(runner, priv);
340
glusterd_hooks_run_hooks(char *hooks_path, glusterd_op_t op, dict_t *op_ctx,
341
glusterd_commit_hook_type_t type)
343
xlator_t *this = THIS;
348
struct dirent *entry = NULL;
349
struct dirent scratch[2] = {
354
char *volname = NULL;
356
int N = 8; /*arbitrary*/
361
ret = dict_get_str(op_ctx, "volname", &volname);
363
gf_msg(this->name, GF_LOG_CRITICAL, -ret, GD_MSG_DICT_GET_FAILED,
364
"Failed to get volname "
365
"from operation context");
369
hookdir = sys_opendir(hooks_path);
372
gf_msg(this->name, GF_LOG_ERROR, errno, GD_MSG_DIR_OP_FAILED,
373
"Failed to open dir %s", hooks_path);
377
lines = GF_CALLOC(1, N * sizeof(*lines), gf_gld_mt_charptr);
379
gf_smsg(this->name, GF_LOG_ERROR, errno, GD_MSG_NO_MEMORY, NULL);
387
while ((entry = sys_readdir(hookdir, scratch))) {
388
if (gf_irrelevant_entry(entry))
390
if (line_count == N - 1) {
392
lines = GF_REALLOC(lines, N * sizeof(char *));
394
gf_smsg(this->name, GF_LOG_ERROR, errno, GD_MSG_NO_MEMORY,
400
if (glusterd_is_hook_enabled(entry->d_name)) {
401
lines[line_count] = gf_strdup(entry->d_name);
406
lines[line_count] = NULL;
407
lines = GF_REALLOC(lines, (line_count + 1) * sizeof(char *));
411
qsort(lines, line_count, sizeof(*lines), glusterd_compare_lines);
413
for (lineno = 0; lineno < line_count; lineno++) {
415
runner_argprintf(&runner, "%s/%s", hooks_path, lines[lineno]);
416
/*Add future command line arguments to hook scripts below*/
417
runner_argprintf(&runner, "--volname=%s", volname);
418
ret = glusterd_hooks_add_op_args(&runner, op, op_ctx, type);
420
gf_msg(this->name, GF_LOG_ERROR, 0, GD_MSG_ADD_OP_ARGS_FAIL,
422
"command specific arguments");
426
ret = runner_run_reuse(&runner);
428
runner_log(&runner, this->name, GF_LOG_ERROR,
429
"Failed to execute script");
431
runner_log(&runner, this->name, GF_LOG_INFO, "Ran script");
439
for (lineno = 0; lineno < line_count + 1; lineno++)
440
GF_FREE(lines[lineno]);
446
sys_closedir(hookdir);
452
glusterd_hooks_post_stub_enqueue(char *scriptdir, glusterd_op_t op,
456
glusterd_hooks_stub_t *stub = NULL;
457
glusterd_hooks_private_t *hooks_priv = NULL;
458
glusterd_conf_t *conf = NULL;
460
conf = THIS->private;
461
hooks_priv = conf->hooks_priv;
463
ret = glusterd_hooks_stub_init(&stub, scriptdir, op, op_ctx);
467
pthread_mutex_lock(&hooks_priv->mutex);
469
hooks_priv->waitcount++;
470
cds_list_add_tail(&stub->all_hooks, &hooks_priv->list);
471
pthread_cond_signal(&hooks_priv->cond);
473
pthread_mutex_unlock(&hooks_priv->mutex);
481
glusterd_hooks_stub_init(glusterd_hooks_stub_t **stub, char *scriptdir,
482
glusterd_op_t op, dict_t *op_ctx)
485
glusterd_hooks_stub_t *hooks_stub = NULL;
487
xlator_t *this = THIS;
492
hooks_stub = GF_CALLOC(1, sizeof(*hooks_stub), gf_gld_mt_hooks_stub_t);
494
gf_smsg(this->name, GF_LOG_ERROR, errno, GD_MSG_NO_MEMORY, NULL);
498
CDS_INIT_LIST_HEAD(&hooks_stub->all_hooks);
500
hooks_stub->scriptdir = gf_strdup(scriptdir);
501
if (!hooks_stub->scriptdir) {
502
gf_smsg(this->name, GF_LOG_ERROR, errno, GD_MSG_STRDUP_FAILED,
503
"scriptdir=%s", scriptdir, NULL);
507
hooks_stub->op_ctx = dict_copy_with_ref(op_ctx, hooks_stub->op_ctx);
508
if (!hooks_stub->op_ctx) {
509
gf_smsg(this->name, GF_LOG_ERROR, errno, GD_MSG_DICT_COPY_FAIL, NULL);
517
gf_smsg(this->name, GF_LOG_ERROR, 0, GD_MSG_POST_HOOK_STUB_INIT_FAIL,
519
glusterd_hooks_stub_cleanup(hooks_stub);
526
glusterd_hooks_stub_cleanup(glusterd_hooks_stub_t *stub)
529
gf_msg_callingfn(THIS->name, GF_LOG_WARNING, 0, GD_MSG_HOOK_STUB_NULL,
530
"hooks_stub is NULL");
535
dict_unref(stub->op_ctx);
537
GF_FREE(stub->scriptdir);
543
hooks_worker(void *args)
545
glusterd_conf_t *conf = NULL;
546
glusterd_hooks_private_t *hooks_priv = NULL;
547
glusterd_hooks_stub_t *stub = NULL;
550
conf = THIS->private;
551
hooks_priv = conf->hooks_priv;
554
pthread_mutex_lock(&hooks_priv->mutex);
556
while (cds_list_empty(&hooks_priv->list)) {
557
pthread_cond_wait(&hooks_priv->cond, &hooks_priv->mutex);
559
stub = cds_list_entry(hooks_priv->list.next, glusterd_hooks_stub_t,
561
cds_list_del_init(&stub->all_hooks);
562
hooks_priv->waitcount--;
564
pthread_mutex_unlock(&hooks_priv->mutex);
566
glusterd_hooks_run_hooks(stub->scriptdir, stub->op, stub->op_ctx,
567
GD_COMMIT_HOOK_POST);
568
glusterd_hooks_stub_cleanup(stub);
575
glusterd_hooks_priv_init(glusterd_hooks_private_t **new)
578
glusterd_hooks_private_t *hooks_priv = NULL;
580
xlator_t *this = THIS;
583
gf_smsg(this->name, GF_LOG_ERROR, errno, GD_MSG_INVALID_ARGUMENT, NULL);
587
hooks_priv = GF_CALLOC(1, sizeof(*hooks_priv), gf_gld_mt_hooks_priv_t);
589
gf_smsg(this->name, GF_LOG_ERROR, errno, GD_MSG_NO_MEMORY, NULL);
593
pthread_mutex_init(&hooks_priv->mutex, NULL);
594
pthread_cond_init(&hooks_priv->cond, NULL);
595
CDS_INIT_LIST_HEAD(&hooks_priv->list);
596
hooks_priv->waitcount = 0;
605
glusterd_hooks_spawn_worker(xlator_t *this)
608
glusterd_conf_t *conf = NULL;
609
glusterd_hooks_private_t *hooks_priv = NULL;
611
ret = glusterd_hooks_priv_init(&hooks_priv);
615
conf = this->private;
616
conf->hooks_priv = hooks_priv;
617
ret = gf_thread_create(&hooks_priv->worker, NULL, hooks_worker,
618
(void *)this, "gdhooks");
620
gf_msg(this->name, GF_LOG_CRITICAL, errno, GD_MSG_SPAWN_THREADS_FAIL,
621
"Failed to spawn post "
622
"hooks worker thread");