2018-07-12 06:20:55 +03:00
/*
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 ;
2019-05-11 10:33:57 +03:00
ret = 0 ;
goto done ;
2018-07-12 06:20:55 +03:00
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 ) ;
2019-06-24 09:35:01 +03:00
if ( ret < 0 | | ( size_t ) ret > = sizeof ( buf ) ) {
2018-07-12 06:20:55 +03:00
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 ) ;
2019-06-24 09:35:01 +03:00
if ( ret < 0 | | ( size_t ) ret > = sizeof ( filename ) ) {
2018-07-12 06:20:55 +03:00
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 ;
}