mirror of
https://github.com/samba-team/samba.git
synced 2025-01-08 21:18:16 +03:00
ctdb-common: Factor out basic script abstraction
Provides for listing of scripts and chmod enable/disable. BUG: https://bugzilla.samba.org/show_bug.cgi?id=13551 Signed-off-by: Martin Schwenke <martin@meltin.net> Reviewed-by: Amitay Isaacs <amitay@gmail.com>
This commit is contained in:
parent
56e248de60
commit
a7a4ee439d
246
ctdb/common/event_script.c
Normal file
246
ctdb/common/event_script.c
Normal file
@ -0,0 +1,246 @@
|
||||
/*
|
||||
Low level event script handling
|
||||
|
||||
Copyright (C) Amitay Isaacs 2017
|
||||
Copyright (C) Martin Schwenke 2018
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "replace.h"
|
||||
#include "system/filesys.h"
|
||||
#include "system/dir.h"
|
||||
#include "system/glob.h"
|
||||
|
||||
#include <talloc.h>
|
||||
|
||||
#include "common/event_script.h"
|
||||
|
||||
static int script_filter(const struct dirent *de)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Match a script pattern */
|
||||
ret = fnmatch("[0-9][0-9].*.script", de->d_name, 0);
|
||||
if (ret == 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int event_script_get_list(TALLOC_CTX *mem_ctx,
|
||||
const char *script_dir,
|
||||
struct event_script_list **out)
|
||||
{
|
||||
struct dirent **namelist = NULL;
|
||||
struct event_script_list *script_list = NULL;
|
||||
size_t ds_len;
|
||||
int count, ret;
|
||||
int i;
|
||||
|
||||
count = scandir(script_dir, &namelist, script_filter, alphasort);
|
||||
if (count == -1) {
|
||||
ret = errno;
|
||||
goto done;
|
||||
}
|
||||
|
||||
script_list = talloc_zero(mem_ctx, struct event_script_list);
|
||||
if (script_list == NULL) {
|
||||
goto nomem;
|
||||
}
|
||||
|
||||
if (count == 0) {
|
||||
ret = 0;
|
||||
*out = script_list;
|
||||
goto done;
|
||||
}
|
||||
|
||||
script_list->num_scripts = count;
|
||||
script_list->script = talloc_zero_array(script_list,
|
||||
struct event_script *,
|
||||
count);
|
||||
if (script_list->script == NULL) {
|
||||
goto nomem;
|
||||
}
|
||||
|
||||
ds_len = strlen(".script");
|
||||
for (i = 0; i < count; i++) {
|
||||
struct event_script *s;
|
||||
struct stat statbuf;
|
||||
|
||||
s = talloc_zero(script_list->script, struct event_script);
|
||||
if (s == NULL) {
|
||||
goto nomem;
|
||||
}
|
||||
|
||||
script_list->script[i] = s;
|
||||
|
||||
s->name = talloc_strndup(script_list->script,
|
||||
namelist[i]->d_name,
|
||||
strlen(namelist[i]->d_name) - ds_len);
|
||||
if (s->name == NULL) {
|
||||
goto nomem;
|
||||
}
|
||||
|
||||
s->path = talloc_asprintf(script_list->script,
|
||||
"%s/%s",
|
||||
script_dir,
|
||||
namelist[i]->d_name);
|
||||
if (s->path == NULL) {
|
||||
goto nomem;
|
||||
}
|
||||
|
||||
ret = stat(s->path, &statbuf);
|
||||
if (ret == 0) {
|
||||
/*
|
||||
* If ret != 0 this is either a dangling
|
||||
* symlink or it has just disappeared. Either
|
||||
* way, it isn't executable. See the note
|
||||
* below about things that have disappeared.
|
||||
*/
|
||||
if (statbuf.st_mode & S_IXUSR) {
|
||||
s->enabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*out = script_list;
|
||||
return 0;
|
||||
|
||||
nomem:
|
||||
ret = ENOMEM;
|
||||
talloc_free(script_list);
|
||||
|
||||
done:
|
||||
if (namelist != NULL && count != -1) {
|
||||
for (i=0; i<count; i++) {
|
||||
free(namelist[i]);
|
||||
}
|
||||
free(namelist);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int event_script_chmod(const char *script_dir,
|
||||
const char *script_name,
|
||||
bool enable)
|
||||
{
|
||||
const char *dot_script = ".script";
|
||||
size_t ds_len = strlen(dot_script);
|
||||
size_t sn_len = strlen(script_name);
|
||||
DIR *dirp;
|
||||
struct dirent *de;
|
||||
char buf[PATH_MAX];
|
||||
const char *script_file;
|
||||
int ret, new_mode;
|
||||
char filename[PATH_MAX];
|
||||
struct stat st;
|
||||
bool found;
|
||||
ino_t found_inode;
|
||||
int fd = -1;
|
||||
|
||||
/* Allow script_name to already have ".script" suffix */
|
||||
if (sn_len > ds_len &&
|
||||
strcmp(&script_name[sn_len - ds_len], dot_script) == 0) {
|
||||
script_file = script_name;
|
||||
} else {
|
||||
ret = snprintf(buf, sizeof(buf), "%s.script", script_name);
|
||||
if (ret >= sizeof(buf)) {
|
||||
return ENAMETOOLONG;
|
||||
}
|
||||
script_file = buf;
|
||||
}
|
||||
|
||||
dirp = opendir(script_dir);
|
||||
if (dirp == NULL) {
|
||||
return errno;
|
||||
}
|
||||
|
||||
found = false;
|
||||
while ((de = readdir(dirp)) != NULL) {
|
||||
if (strcmp(de->d_name, script_file) == 0) {
|
||||
/* check for valid script names */
|
||||
ret = script_filter(de);
|
||||
if (ret == 0) {
|
||||
closedir(dirp);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
found = true;
|
||||
found_inode = de->d_ino;
|
||||
break;
|
||||
}
|
||||
}
|
||||
closedir(dirp);
|
||||
|
||||
if (! found) {
|
||||
return ENOENT;
|
||||
}
|
||||
|
||||
ret = snprintf(filename,
|
||||
sizeof(filename),
|
||||
"%s/%s",
|
||||
script_dir,
|
||||
script_file);
|
||||
if (ret >= sizeof(filename)) {
|
||||
return ENAMETOOLONG;
|
||||
}
|
||||
|
||||
fd = open(filename, O_RDWR);
|
||||
if (fd == -1) {
|
||||
ret = errno;
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = fstat(fd, &st);
|
||||
if (ret != 0) {
|
||||
ret = errno;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the directory entry inode number doesn't match the one
|
||||
* returned by fstat() then this is probably a symlink, so the
|
||||
* caller should not be calling this function. Note that this
|
||||
* is a cheap sanity check to catch most programming errors.
|
||||
* This doesn't cost any extra system calls but can still miss
|
||||
* the unlikely case where the symlink is to a file on a
|
||||
* different filesystem with the same inode number as the
|
||||
* symlink.
|
||||
*/
|
||||
if (found && found_inode != st.st_ino) {
|
||||
ret = EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (enable) {
|
||||
new_mode = st.st_mode | (S_IXUSR | S_IXGRP | S_IXOTH);
|
||||
} else {
|
||||
new_mode = st.st_mode & ~(S_IXUSR | S_IXGRP | S_IXOTH);
|
||||
}
|
||||
|
||||
ret = fchmod(fd, new_mode);
|
||||
if (ret != 0) {
|
||||
ret = errno;
|
||||
goto done;
|
||||
}
|
||||
|
||||
done:
|
||||
if (fd != -1) {
|
||||
close(fd);
|
||||
}
|
||||
return ret;
|
||||
}
|
72
ctdb/common/event_script.h
Normal file
72
ctdb/common/event_script.h
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
Low level event script handling
|
||||
|
||||
Copyright (C) Amitay Isaacs 2017
|
||||
Copyright (C) Martin Schwenke 2018
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __CTDB_SCRIPT_H__
|
||||
#define __CTDB_SCRIPT_H__
|
||||
|
||||
#include "replace.h"
|
||||
#include "system/filesys.h"
|
||||
|
||||
#include <talloc.h>
|
||||
|
||||
/**
|
||||
* @file script.h
|
||||
*
|
||||
* @brief Script listing and manipulation
|
||||
*/
|
||||
|
||||
|
||||
struct event_script {
|
||||
char *name;
|
||||
char *path;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
struct event_script_list {
|
||||
unsigned int num_scripts;
|
||||
struct event_script **script;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Retrieve a list of scripts
|
||||
*
|
||||
* @param[in] mem_ctx Talloc memory context
|
||||
* @param[in] script_dir Directory containing scripts
|
||||
* @param[out] out List of scripts
|
||||
* @return 0 on success, errno on failure
|
||||
*/
|
||||
int event_script_get_list(TALLOC_CTX *mem_ctx,
|
||||
const char *script_dir,
|
||||
struct event_script_list **out);
|
||||
|
||||
/**
|
||||
* @brief Make a script executable or not executable
|
||||
*
|
||||
* @param[in] script_dir Directory containing script
|
||||
* @param[in] script_name Name of the script to enable
|
||||
* @param[in] executable True if script should be made executable
|
||||
* @return 0 on success, errno on failure
|
||||
*/
|
||||
int event_script_chmod(const char *script_dir,
|
||||
const char *script_name,
|
||||
bool executable);
|
||||
|
||||
#endif /* __CTDB_SCRIPT_H__ */
|
125
ctdb/tests/cunit/event_script_test_001.sh
Executable file
125
ctdb/tests/cunit/event_script_test_001.sh
Executable file
@ -0,0 +1,125 @@
|
||||
#!/bin/sh
|
||||
|
||||
. "${TEST_SCRIPTS_DIR}/unit.sh"
|
||||
|
||||
scriptdir="${TEST_VAR_DIR}/cunit/scriptdir"
|
||||
mkdir -p "${scriptdir}"
|
||||
|
||||
test_cleanup "rm -rf ${scriptdir}"
|
||||
|
||||
# Invalid path
|
||||
invalid="${scriptdir}/notfound"
|
||||
ok <<EOF
|
||||
Script list ${invalid} failed with result=$(errcode ENOENT)
|
||||
EOF
|
||||
unit_test event_script_test list "${invalid}"
|
||||
|
||||
# Empty directory
|
||||
ok <<EOF
|
||||
No scripts found
|
||||
EOF
|
||||
unit_test event_script_test list "$scriptdir"
|
||||
|
||||
# Invalid script, doesn't end in ".script"
|
||||
touch "${scriptdir}/prog"
|
||||
|
||||
ok <<EOF
|
||||
No scripts found
|
||||
EOF
|
||||
unit_test event_script_test list "$scriptdir"
|
||||
|
||||
# Is not found because enabling "prog" actually looks for "prog.script"
|
||||
ok <<EOF
|
||||
Script enable ${scriptdir} prog completed with result=$(errcode ENOENT)
|
||||
EOF
|
||||
unit_test event_script_test enable "$scriptdir" "prog"
|
||||
|
||||
required_result 1 <<EOF
|
||||
EOF
|
||||
unit_test test -x "${scriptdir}/prog"
|
||||
|
||||
# Is not found because enabling "prog" actually looks for "prog.script"
|
||||
ok <<EOF
|
||||
Script disable ${scriptdir} prog completed with result=$(errcode ENOENT)
|
||||
EOF
|
||||
unit_test event_script_test disable "$scriptdir" "prog"
|
||||
|
||||
# Valid script
|
||||
touch "$scriptdir/11.foo.script"
|
||||
|
||||
ok <<EOF
|
||||
11.foo
|
||||
EOF
|
||||
unit_test event_script_test list "$scriptdir"
|
||||
|
||||
ok <<EOF
|
||||
Script enable ${scriptdir} 11.foo completed with result=0
|
||||
EOF
|
||||
unit_test event_script_test enable "$scriptdir" "11.foo"
|
||||
|
||||
ok <<EOF
|
||||
EOF
|
||||
unit_test test -x "${scriptdir}/11.foo.script"
|
||||
|
||||
ok <<EOF
|
||||
Script disable ${scriptdir} 11.foo.script completed with result=0
|
||||
EOF
|
||||
unit_test event_script_test disable "$scriptdir" "11.foo.script"
|
||||
|
||||
required_result 1 <<EOF
|
||||
EOF
|
||||
unit_test test -x "${scriptdir}/11.foo.script"
|
||||
|
||||
# Multiple scripts
|
||||
touch "${scriptdir}/22.bar.script"
|
||||
|
||||
ok <<EOF
|
||||
11.foo
|
||||
22.bar
|
||||
EOF
|
||||
unit_test event_script_test list "$scriptdir"
|
||||
|
||||
# Symlink to existing file
|
||||
ln -s "${scriptdir}/prog" "${scriptdir}/33.link.script"
|
||||
|
||||
ok <<EOF
|
||||
11.foo
|
||||
22.bar
|
||||
33.link
|
||||
EOF
|
||||
unit_test event_script_test list "$scriptdir"
|
||||
|
||||
ok <<EOF
|
||||
Script enable ${scriptdir} 33.link completed with result=$(errcode EINVAL)
|
||||
EOF
|
||||
unit_test event_script_test enable "$scriptdir" "33.link"
|
||||
|
||||
|
||||
ok <<EOF
|
||||
Script disable ${scriptdir} 33.link.script completed with result=$(errcode EINVAL)
|
||||
EOF
|
||||
unit_test event_script_test disable "$scriptdir" "33.link.script"
|
||||
|
||||
# Dangling symlink
|
||||
rm "${scriptdir}/33.link.script"
|
||||
ln -s "${scriptdir}/nosuchfile" "${scriptdir}/33.link.script"
|
||||
|
||||
ok <<EOF
|
||||
11.foo
|
||||
22.bar
|
||||
33.link
|
||||
EOF
|
||||
unit_test event_script_test list "$scriptdir"
|
||||
|
||||
ok <<EOF
|
||||
Script enable ${scriptdir} 33.link completed with result=$(errcode ENOENT)
|
||||
EOF
|
||||
unit_test event_script_test enable "$scriptdir" "33.link"
|
||||
|
||||
|
||||
ok <<EOF
|
||||
Script disable ${scriptdir} 33.link.script completed with result=$(errcode ENOENT)
|
||||
EOF
|
||||
unit_test event_script_test disable "$scriptdir" "33.link.script"
|
||||
|
||||
exit 0
|
119
ctdb/tests/src/event_script_test.c
Normal file
119
ctdb/tests/src/event_script_test.c
Normal file
@ -0,0 +1,119 @@
|
||||
/*
|
||||
Low level event script handling tests
|
||||
|
||||
Copyright (C) Martin Schwenke 2018
|
||||
|
||||
Based on run_event_test.c:
|
||||
|
||||
Copyright (C) Amitay Isaacs 2017
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "replace.h"
|
||||
|
||||
#include <popt.h>
|
||||
#include <talloc.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "common/event_script.c"
|
||||
|
||||
static void usage(const char *prog)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"Usage: %s list <scriptdir>\n",
|
||||
prog);
|
||||
fprintf(stderr,
|
||||
" %s chmod enable <scriptdir> <scriptname>\n",
|
||||
prog);
|
||||
fprintf(stderr,
|
||||
" %s chmod diable <scriptdir> <scriptname>\n",
|
||||
prog);
|
||||
}
|
||||
|
||||
static void do_list(TALLOC_CTX *mem_ctx, int argc, const char **argv)
|
||||
{
|
||||
struct event_script_list *script_list = NULL;
|
||||
int ret, i;
|
||||
|
||||
if (argc != 3) {
|
||||
usage(argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ret = event_script_get_list(mem_ctx, argv[2], &script_list);
|
||||
if (ret != 0) {
|
||||
printf("Script list %s failed with result=%d\n", argv[2], ret);
|
||||
return;
|
||||
}
|
||||
|
||||
if (script_list == NULL || script_list->num_scripts == 0) {
|
||||
printf("No scripts found\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (i=0; i < script_list->num_scripts; i++) {
|
||||
struct event_script *s = script_list->script[i];
|
||||
printf("%s\n", s->name);
|
||||
}
|
||||
}
|
||||
|
||||
static void do_chmod(TALLOC_CTX *mem_ctx,
|
||||
int argc,
|
||||
const char **argv,
|
||||
bool enable)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (argc != 4) {
|
||||
usage(argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ret = event_script_chmod(argv[2], argv[3], enable);
|
||||
|
||||
printf("Script %s %s %s completed with result=%d\n",
|
||||
argv[1], argv[2], argv[3], ret);
|
||||
}
|
||||
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
TALLOC_CTX *mem_ctx;
|
||||
|
||||
if (argc < 3) {
|
||||
usage(argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
mem_ctx = talloc_new(NULL);
|
||||
if (mem_ctx == NULL) {
|
||||
fprintf(stderr, "talloc_new() failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (strcmp(argv[1], "list") == 0) {
|
||||
do_list(mem_ctx, argc, argv);
|
||||
} else if (strcmp(argv[1], "enable") == 0) {
|
||||
do_chmod(mem_ctx, argc, argv, true);
|
||||
} else if (strcmp(argv[1], "disable") == 0) {
|
||||
do_chmod(mem_ctx, argc, argv, false);
|
||||
} else {
|
||||
fprintf(stderr, "Invalid command %s\n", argv[2]);
|
||||
usage(argv[0]);
|
||||
}
|
||||
|
||||
talloc_free(mem_ctx);
|
||||
exit(0);
|
||||
}
|
@ -402,7 +402,8 @@ def build(bld):
|
||||
pkt_read.c pkt_write.c comm.c
|
||||
logging.c rb_tree.c tunable.c
|
||||
pidfile.c run_proc.c
|
||||
hash_count.c run_event.c
|
||||
hash_count.c
|
||||
run_event.c event_script.c
|
||||
sock_client.c version.c
|
||||
cmdline.c path.c conf.c line.c
|
||||
'''),
|
||||
@ -869,6 +870,7 @@ def build(bld):
|
||||
'cmdline_test',
|
||||
'conf_test',
|
||||
'line_test',
|
||||
'event_script_test',
|
||||
]
|
||||
|
||||
for target in ctdb_unit_tests:
|
||||
|
Loading…
Reference in New Issue
Block a user