1
0
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:
Martin Schwenke 2018-07-12 13:20:55 +10:00 committed by Amitay Isaacs
parent 56e248de60
commit a7a4ee439d
5 changed files with 565 additions and 1 deletions

246
ctdb/common/event_script.c Normal file
View 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;
}

View 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__ */

View 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

View 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);
}

View File

@ -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: