/*
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 .
*/
#include "replace.h"
#include "system/filesys.h"
#include "system/dir.h"
#include "system/glob.h"
#include
#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;
ret = 0;
goto done;
nomem:
ret = ENOMEM;
talloc_free(script_list);
done:
if (namelist != NULL && count != -1) {
for (i=0; i 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 < 0 || (size_t)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 < 0 || (size_t)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;
}