Skip to content
Snippets Groups Projects
Commit fe905b5e authored by Eric Vidal's avatar Eric Vidal :speech_balloon:
Browse files

add execl-toc from 66-tools

parent 943f2d67
No related branches found
No related tags found
No related merge requests found
-loblibs
-lskarnet
/*
* execl-toc.c
*
* Copyright (c) 2018-2021 Eric Vidal <eric@obarun.org>
*
* All rights reserved.
*
* This file is part of Obarun. It is subject to the license terms in
* the LICENSE file found in the top-level directory of this
* distribution.
* This file may not be copied, modified, propagated, or distributed
* except according to the terms contained in the LICENSE file./
*
* This program is inspired by https://github.com/skarnet/s6/blob/master/src/conn-tools/s6-ipcserver-socketbinder.c
* and https://github.com/skarnet/s6-portable-utils/blob/master/src/skaembutils/s6-test.c.
*
*/
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <stdlib.h>//getenv
#include <stdio.h>//getenv
#include <sys/mount.h>
#include <oblibs/log.h>
#include <oblibs/directory.h>
#include <oblibs/types.h>
#include <oblibs/sastr.h>
#include <oblibs/string.h>
#include <skalibs/buffer.h>
#include <skalibs/types.h>
#include <skalibs/sgetopt.h>
#include <skalibs/djbunix.h>
#include <skalibs/socket.h>
#include <skalibs/exec.h>
#define USAGE "execl-toc [ -h ] [ -v verbosity ] [ -n ] [ -t ] [ -D ] [ -X ] [ -d|p|S|m|L|e|b|c|k|n|g|r|s|t|u|w|x|f|z|O|U|N|V|E element ] [ -o opts ] [ -t type ] [ -d device ] [ -g gid ] [ -u uid ] [ -m mode ] [ -M mode ] [ -s|D|B|b ] [ -b backlog ] prog..."
static inline void info_help (void)
{
static char const *help =
"execl-toc <main_options> <test_options> element <create_options> prog\n"
"\n"
"main_options :\n"
" -h: print this help\n"
" -v: increase/decrease verbosity\n"
" -n: negate the test\n"
" -t: exit 0 if the test fails\n"
" -D: only test and disable creation\n"
" -X: do not execute prog\n"
"\n"
"test_options :\n"
"\n"
" test and create\n\n"
" -d: true if file is a directory\n"
" -p: true if file is a pipe\n"
" -S: true if file is a socket\n"
" -m: true if file is a mountpoint\n"
" -L: true if file is a symlink\n"
"\n"
" test only\n\n"
" -e: true if file exists\n"
" -b: true if file is a block\n"
" -c: true if file is a character\n"
" -k: true if sticky bit is set for file\n"
" -n: true if the length of file is non-zero\n"
" -g: true if file is set-group-id\n"
" -r: true if file is readable\n"
" -s: true if the size of file is greater than zero\n"
" -t: true if file is open and refers to a terminal\n"
" -u: true if set-user-id bit is set for file\n"
" -w: true if file is writable\n"
" -x: true if file is executable\n"
" -f: true if file is regular file\n"
" -z: true if the length of file is zero\n"
" -O: true if file is owned by the effective user id\n"
" -U: true if file is owned by the effective group id\n"
" -N: true if file has been modified since it was last read\n"
" -V: true if file exists on the environment\n"
" -E: true if file is an empty directory\n"
"\n"
"create_options :\n"
" -o: mount options(same as mount -o)\n"
" -t: type mount options(same as mount -t), target for symlink\n"
" -d: device mount options (same as mount -t type device /directory)\n"
" -u: changes element's owner to (numeric/name) uid after the creation\n"
" -g: changes element's owner to (numeric/name) gid after the creation\n"
" -m: creates element's permissions to (numeric) mode.\n"
" -M: creates parent directories of the *element* with permissions to (numeric) mode.\n"
" -s: type of element will be SOCK_DGRAM instead of SOCK_STREAM\n"
" -D: disallow instant rebinding at element to the same path\n"
" -B: the element will be blocking.\n"
" -b: set a maximum of backlog connections on the element.\n"
;
if (buffer_putsflush(buffer_1, help) < 0)
log_dieusys(LOG_EXIT_SYS, "write to stdout") ;
}
enum opts_e
{
// can create
T_DIR , // -d expect mode chown
T_FIFO , // -p expect mode chown
T_SOCKET , // -S expect mode chow style
T_MNT ,// -m mountpoint expect mount_opts device type
T_SYMLINK , // -L
// just test
T_EXIST , // -e create empty file
T_BLOCK , // -b
T_CHAR , // -c
T_STICKY , // -k
T_NONZERO , // -n
T_SGID , // -g
T_READABLE , // -r
T_NONZEROFILE , // -s
T_TERM , // -t fd
T_SUID , // -u
T_WRITABLE , // -w
T_EXECUTABLE , // -x
T_REGULAR , // -f
T_ZERO , // -z
T_EUID , // -O
T_EGID , // -U
T_MODIFIED , // -N
T_ENV , // -V environment value
T_EMPTY // -E empty directories
} ;
typedef struct opts_common_s opts_common_t, *opts_common_t_ref ;
struct opts_common_s
{
char const *func_name ; // name of the function
char const *test_on ; // argument to test
uint8_t not_create ; // 0 create, 1 simply test
uint8_t noprog ; // 0 execute prog, 1 do not execute prog
int argc ;
char **argv ;
char const *const *envp ;
enum opts_e test ;
char const *minus_o ; // -o -> opt for mount
char const *minus_t ; // -t -> type for mount, target for symlink
char const *minus_d ; // -d -> device for mount
gid_t minus_g ; // -g
uid_t minus_u ; // -u
mode_t minus_m ; // -m ou -a for socket
mode_t minus_M ; // -M directory of the element permissions
// socket option
uint8_t minus_s ; // -s -> 0 inactive meaning SOCK_STREAM(default), 1 active SOCK_DGRAM
int minus_D ; //-D -> 0 inactive(default), 1 active
unsigned int minus_B ; //-B -> O_NONBLOCK(default), 0 blocking
int minus_b ; // -b -> -1 inactive(default), other value active
} ;
#define OPTS_COMMON_ZERO \
{ \
0 , \
0 , \
0 , \
0 , \
0 , \
0 , \
0 , \
0 , \
0 , \
0 , \
0 , \
-1 , \
-1 , \
0 , \
0 , \
0 , \
0 , \
O_NONBLOCK , \
-1 \
}
typedef int execl_func_t (opts_common_t *arguments, char **nargv) ;
typedef execl_func_t *execl_func_t_ref ;
execl_func_t execl_directory ;
execl_func_t execl_fifo ;
execl_func_t execl_socket ;
execl_func_t execl_mountpoint ;
execl_func_t execl_symlink ;
execl_func_t execl_common ;
void parse(opts_common_t *arguments,char **nargv)
{
int n = 0, i = 0 ;
int argc = arguments->argc ;
char **argv = arguments->argv ;
{
subgetopt l = SUBGETOPT_ZERO ;
for (;;)
{
int opt = subgetopt_r(argc,(char const *const *)argv, "o:t:d:u:g:m:M:sDBb:", &l) ;
if (opt == -1) break ;
switch (opt)
{
case 'o' : arguments->minus_o = l.arg ; break ;
case 't' : arguments->minus_t = l.arg ; break ;
case 'd' : arguments->minus_d = l.arg ; break ;
case 'u' : if (!uid0_scan(l.arg, &arguments->minus_u))
if (get_uidbyname(l.arg,&arguments->minus_u) == -1)
log_dieusys(LOG_EXIT_SYS,"get uid of: ",l.arg) ;
break ;
case 'g' :
{
if (!gid0_scan(l.arg, &arguments->minus_g))
{
if (get_gidbyname(l.arg,&arguments->minus_g) == 1)
{
stralloc ngid = STRALLOC_ZERO ;
if (get_groupbygid(arguments->minus_g,&ngid) == -1)
log_dieusys(LOG_EXIT_SYS,"get gid of: ",l.arg) ;
if (ngid.len)
{
if (!stralloc_0(&ngid)) log_die_nomem("stralloc") ;
if (get_gidbygroup(ngid.s,&arguments->minus_g) == -1)
log_dieusys(LOG_EXIT_SYS,"get gid of: ",l.arg) ;
}else if (get_gidbygroup(l.arg,&arguments->minus_g) == -1)
log_dieusys(LOG_EXIT_SYS,"get gid of: ",l.arg) ;
stralloc_free(&ngid) ;
}
else if (get_gidbygroup(l.arg,&arguments->minus_g) == -1)
log_dieusys(LOG_EXIT_SYS,"get gid of: ",l.arg) ;
}
break ;
}
case 'm' : if (!uint0_oscan(l.arg, &arguments->minus_m)) log_usage(USAGE) ; break ;
case 'M' : if (!uint0_oscan(l.arg, &arguments->minus_M)) log_usage(USAGE) ; break ;
// socket special case
case 's' : arguments->minus_s = 1 ; break ;
case 'D' : arguments->minus_D = 1 ; break ;
case 'B' : arguments->minus_B = 0 ; break ;
case 'b' : if (!uint0_scan(l.arg,(unsigned int *)&arguments->minus_b)) log_usage(USAGE) ; break ;
default : log_usage(USAGE) ;
}
}
argc -= l.ind ; argv += l.ind ;
}
for (; i < argc ; i++)
nargv[n++] = (char *)argv[i] ;
nargv[n] = 0 ;
arguments->argc = n ;
arguments->argv = nargv ;
}
static int auto_dir(opts_common_t *arguments)
{
int r ;
char const *dest = arguments->test_on ;
size_t len = strlen(dest) ;
char dir[len + 1] ;
if (!ob_dirname(dir,dest)) log_dieu(LOG_EXIT_SYS,"get dirname of: ",dest) ;
mode_t mode = !arguments->minus_M ? 0755 : arguments->minus_M ;
r = scan_mode(dir,S_IFDIR) ;
if (r == -1) return 0 ;
if (!r) {
if (arguments->minus_M) umask(0) ;
if (!dir_create_parent(dir,mode))
log_dieusys(LOG_EXIT_SYS,"create dir: ",dir) ;
/** dir_create_parent do not warn if the parent directory
* can not be created, so check it */
r = scan_mode(dir,S_IFDIR) ;
if (!r) return 0 ;
/** only the last element should match the uid and gid
* given at commandline */
//if (chown(dir, arguments->minus_u,arguments->minus_g) == -1)
// log_dieusys(LOG_EXIT_SYS,"chown: ",dir) ;
}
return 1 ;
}
/* @Return -1 doesn't exist
* @Return 0 if mounted
* @Return 1 if not mounted */
static int is_mnt(char const *str)
{
struct stat st;
size_t slen = strlen(str) ;
int is_not_mnt = 0 ;
if (lstat(str,&st) < 0) return -1 ;
if (S_ISDIR(st.st_mode))
{
dev_t st_dev = st.st_dev ; ino_t st_ino = st.st_ino ;
char p[slen+4] ;
memcpy(p,str,slen) ;
memcpy(p + slen,"/..",3) ;
p[slen+3] = 0 ;
if (!stat(p,&st))
is_not_mnt = (st_dev == st.st_dev) && (st_ino != st.st_ino) ;
}else return 0 ;
return is_not_mnt ? 1 : 0 ;
}
int execl_directory(opts_common_t *arguments,char **nargv)
{
int r ;
parse(arguments,nargv) ;
if (!arguments->argc && !arguments->noprog) log_die(LOG_EXIT_USER,"missing argument prog") ;
mode_t mode = !arguments->minus_m ? 0755 : arguments->minus_m ;
r = scan_mode(arguments->test_on,S_IFDIR) ;
if (r == -1) log_warn_return(LOG_EXIT_ZERO,arguments->test_on," exist but it is not a directory") ;
if (!r && !arguments->not_create) {
if (arguments->minus_m) umask(0) ;
if (!dir_create_parent(arguments->test_on,mode))
log_dieusys(LOG_EXIT_SYS,"create dir: ",arguments->test_on) ;
if (chown(arguments->test_on, arguments->minus_u,arguments->minus_g) == -1)
log_dieusys(LOG_EXIT_SYS,"chown: ",arguments->test_on) ;
return 1 ;
}
if (!r) log_warn_return(LOG_EXIT_ZERO,arguments->test_on," is not a directory") ;
return 1 ;
}
int execl_fifo(opts_common_t *arguments,char **nargv)
{
int r ;
parse(arguments,nargv) ;
if (!arguments->argc && !arguments->noprog) log_die(LOG_EXIT_USER,"missing argument prog") ;
mode_t mode = !arguments->minus_m ? S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH : arguments->minus_m ;
r = scan_mode(arguments->test_on,S_IFIFO) ;
if (r == -1) log_warn_return(LOG_EXIT_ZERO,arguments->test_on," exist but it is not a fifo") ;
if (!r && !arguments->not_create) {
if (!auto_dir(arguments)) {
errno = EEXIST ;
log_diesys(LOG_EXIT_SYS,"conflicting format in parent directories of: ",arguments->test_on) ;
}
umask(S_IXUSR|S_IXGRP|S_IXOTH) ;
if (mkfifo(arguments->test_on,mode) < 0)
log_dieusys(LOG_EXIT_SYS,"create fifo: ",arguments->test_on) ;
if (chown(arguments->test_on, arguments->minus_u,arguments->minus_g) == -1)
log_dieusys(LOG_EXIT_SYS,"chown: ",arguments->test_on) ;
return 1 ;
}
if (!r) log_warn_return(LOG_EXIT_ZERO,arguments->test_on," is not a fifo") ;
return 1 ;
}
int execl_mountpoint(opts_common_t *arguments,char **nargv)
{
pid_t pid ;
int r, wstat ;
char const *newargv[8] ;
unsigned int m = 0 ;
parse(arguments,nargv) ;
if (!arguments->argc && !arguments->noprog) log_die(LOG_EXIT_USER,"missing argument prog") ;
if ((!arguments->minus_t || !arguments->minus_d) && !arguments->not_create) log_usage(USAGE) ;
mode_t mode = !arguments->minus_m ? 0755 : arguments->minus_m ;
r = is_mnt(arguments->test_on) ;
if (r == -1 || r == 1)
{
if (!arguments->not_create) {
if (arguments->minus_m) umask(0) ;
if (!dir_create_parent(arguments->test_on,mode))
log_dieusys(LOG_EXIT_SYS,"create dir: ",arguments->test_on) ;
goto mount ;
}
log_warn_return(LOG_EXIT_ZERO,arguments->test_on," is not a mountpoint") ;
}
if (!r) {
// mounted, nothing to do
return 1 ;
}
if (r == 1 && arguments->not_create) log_warn_return(LOG_EXIT_ZERO,arguments->test_on," is not a mountpoint") ;
mount:/*
log_info("d->",arguments->minus_d) ;
log_info("test->on",arguments->test_on) ;
log_info("t->",arguments->minus_t) ;
log_info("o->",arguments->minus_o) ;
if (mount(0,arguments->test_on,arguments->minus_t,0,arguments->minus_o) < 0)
log_dieusys(LOG_EXIT_SYS,"mount: ",arguments->test_on) ;
*/
newargv[m++] = "mount" ;
if (arguments->minus_o) {
newargv[m++] = "-o" ;
newargv[m++] = arguments->minus_o ;
}
newargv[m++] = "-t" ;
newargv[m++] = arguments->minus_t ;
newargv[m++] = arguments->minus_d ;
newargv[m++] = arguments->test_on ;
newargv[m++] = 0 ;
pid = child_spawn0(newargv[0],newargv,arguments->envp) ;
if (waitpid_nointr(pid,&wstat, 0) < 0)
log_dieusys(LOG_EXIT_SYS,"wait for mount") ;
if (wstat) log_dieu(LOG_EXIT_SYS,"mount: ",arguments->test_on) ;
return 1 ;
}
int execl_socket(opts_common_t *arguments,char **nargv)
{
int r ;
struct stat st ;
parse(arguments,nargv) ;
if (!arguments->argc && !arguments->noprog) log_die(LOG_EXIT_USER,"missing argument prog") ;
close(0) ;
int flagdgram = arguments->minus_s ? 1 : 0 ;
int flag = arguments->minus_B ;
int flagreuse = arguments->minus_D ? 0 : 1 ;
unsigned int backlog = arguments->minus_b > 0 ? arguments->minus_b : SOMAXCONN ;
mode_t mode = !arguments->minus_m ? 0777 : arguments->minus_m ;
r = stat(arguments->test_on, &st) ;
if (!r && !S_ISSOCK(st.st_mode)) log_warn_return(LOG_EXIT_ZERO,arguments->test_on," exist but it is not a socket") ;
if (r == -1 && !arguments->not_create)
{
if (!auto_dir(arguments)) {
errno = EEXIST ;
log_diesys(LOG_EXIT_SYS,"conflicting format in parent directories of: ",arguments->test_on) ;
}
if (flagdgram ? ipc_datagram_internal(flag) : ipc_stream_internal(flag))
log_dieusys(LOG_EXIT_SYS, "create socket") ;
{
mode_t m = umask(~mode & 0777) ;
if ((flagreuse ? ipc_bind_reuse(0, arguments->test_on) : ipc_bind(0, arguments->test_on)) < 0)
log_dieusys(LOG_EXIT_SYS,"bind to: ", arguments->test_on) ;
umask(m) ;
}
if (backlog && ipc_listen(0, backlog) < 0)
log_dieusys(LOG_EXIT_SYS, "listen to: ", arguments->test_on) ;
return 1 ;
}
if (r == -1) log_warn_return(LOG_EXIT_ZERO,arguments->test_on," is not a socket") ;
return 1 ;
}
int execl_symlink(opts_common_t *arguments,char **nargv)
{
int r ;
parse(arguments,nargv) ;
if (!arguments->argc && !arguments->noprog) log_die(LOG_EXIT_USER,"missing argument prog") ;
if (!arguments->minus_t) log_usage(USAGE) ;
struct stat st ;
r = lstat(arguments->test_on, &st) ;
if (r == -1 && !arguments->not_create) {
if (!auto_dir(arguments)) {
errno = EEXIST ;
log_diesys(LOG_EXIT_SYS,"conflicting format in parent directories of: ",arguments->test_on) ;
}
if (symlink(arguments->minus_t,arguments->test_on) < 0)
log_dieusys(LOG_EXIT_SYS,"create symlink: ",arguments->test_on) ;
return 1 ;
}
if (!S_ISLNK(st.st_mode)) log_warn_return(LOG_EXIT_ZERO,arguments->test_on," is not a symlink") ;
return 1 ;
}
int execl_common(opts_common_t *arguments, char **nargv)
{
int r, n = 0, i = 0, e = 1 ;
struct stat st ;
char const *test = arguments->test_on ;
int argc = arguments->argc ;
char **argv = arguments->argv ;
switch (arguments->test)
{
case T_EXIST :
if (stat(test, &st) == -1)
{ log_warn(test," doesn't exist") ; e = 0 ; }
break ;
case T_BLOCK :
r = scan_mode(test,S_IFBLK) ;
if (r == -1){ log_warn(test," exist but it is not a block") ; e = 0 ; }
if (!r) { log_warn(test," is not a block") ; e = 0 ; }
break ;
case T_CHAR :
r = scan_mode(test,S_IFCHR) ;
if (r == -1){ log_warn(test," exist but it is not a character") ; e = 0 ; }
if (!r) { log_warn(test," is not a character") ; e = 0 ; }
break ;
case T_REGULAR :
r = scan_mode(test,S_IFREG) ;
if (r == -1) { log_warn(test," exist but it is not a regular file") ; e = 0 ; }
if (!r) { log_warn(test," is not a regular file") ; e = 0 ; }
break ;
case T_SGID :
if (stat(test, &st) == -1) {
log_warnusys("stat: ",test) ;
e = 0 ;
break ;
}
if (!(st.st_mode & S_ISGID))
{ log_warn(test," is not set-group-id") ; e = 0 ; }
break ;
case T_SUID :
if (stat(test, &st) == -1) {
log_warnusys("stat: ",test) ;
e = 0 ;
break ;
}
if (!(st.st_mode & S_ISUID))
{ log_warn("set-user-id bit is not set for: ",test) ; e = 0 ; }
break ;
case T_STICKY :
if (stat(test, &st) == -1) {
log_warnusys("stat: ",test) ;
e = 0 ;
break ;
}
if(!(st.st_mode & S_ISVTX))
{ log_warn("not sticky bit set for: ",test) ; e = 0 ; }
break ;
case T_NONZEROFILE :
if (stat(test, &st) == -1) {
log_warnusys("stat: ",test) ;
e = 0 ;
break ;
}
if (!st.st_size)
{ log_warn("file size of: ",test," is zero") ; e = 0 ; }
break ;
case T_MODIFIED :
if (stat(test, &st) == -1) {
log_warnusys("stat: ",test) ;
e = 0 ;
break ;
}
if (st.st_mtime < st.st_atime)
{ log_warn(test," was not modified") ; e = 0 ; }
break ;
case T_EUID :
if (stat(test, &st) == -1) {
log_warnusys("stat: ",test) ;
e = 0 ;
break ;
}
if (st.st_uid != geteuid())
{ log_warn(test," is not owned by the effective user id") ; e = 0 ; }
break ;
case T_EGID :
if (stat(test, &st) == -1) {
log_warnusys("stat: ",test) ;
e = 0 ;
break ;
}
if (st.st_gid != getegid())
{ log_warn(test," is not owned by the effective group id") ; e = 0 ; }
break ;
case T_READABLE :
if (stat(test, &st) == -1) {
log_warnusys("stat: ",test) ;
e = 0 ;
break ;
}
if (st.st_uid == geteuid()) {
if (!(st.st_mode & S_IRUSR))
{ log_warn(test," is not readable") ; e = 0 ; }
}
else if (st.st_gid == getegid()) {
if (!(st.st_mode & S_IRGRP))
{ log_warn(test," is not writable") ; e = 0 ; }
}
else if (!(st.st_mode & S_IROTH)) {
{ log_warn(test," is not writable") ; e = 0 ; }
}
break ;
case T_WRITABLE :
if (stat(test, &st) == -1) {
log_warnusys("stat: ",test) ;
e = 0 ;
break ;
}
if (st.st_uid == geteuid()) {
if (!(st.st_mode & S_IWUSR))
{ log_warn(test," is not writable") ; e = 0 ; }
}
else if (st.st_gid == getegid()) {
if (!(st.st_mode & S_IWGRP))
{ log_warn(test," is not writable") ; e = 0 ; }
}
else if (!(st.st_mode & S_IWOTH)) {
{ log_warn(test," is not writable") ; e = 0 ; }
}
break ;
case T_EXECUTABLE :
if (stat(test, &st) == -1) {
log_warnusys("stat: ",test) ;
e = 0 ;
break ;
}
if (st.st_uid == geteuid()) {
if (!(st.st_mode & S_IXUSR))
{ log_warn(test," is not executable") ; e = 0 ; }
}
else if (st.st_gid == getegid()) {
if (!(st.st_mode & S_IXGRP))
{ log_warn(test," is not executable") ; e = 0 ; }
}
else if (!(st.st_mode & S_IXOTH)) {
{ log_warn(test," is not executable") ; e = 0 ; }
}
break ;
case T_TERM :
{
unsigned int fd ;
if (!uint0_scan(test, &fd)) {
log_warnusys("scan: ",test) ;
e = 0 ;
break ;
}
if (!isatty(fd)) { log_warn(test," do not refer to a terminal") ; e = 0 ; }
break ;
}
case T_NONZERO :
if (!test[0]) { log_warn(test," is empty") ; e = 0 ; }
break ;
case T_ZERO :
if (arguments->test_on[0]) { log_warn(test," is not empty") ; e = 0 ; }
break ;
case T_ENV :
if (!getenv(arguments->test_on)) { log_warn("variable: ", test," is not set") ; e = 0 ; }
break ;
case T_EMPTY :
{
stralloc satree = STRALLOC_ZERO ;
char const *exclude[1] = { 0 } ;
if (!sastr_dir_get(&satree,test,exclude,S_IFBLK|S_IFCHR|S_IFIFO|S_IFREG|S_IFDIR|S_IFLNK)) {
log_warnusys("get contain of directory: ",test) ;
e = 0 ;
break ;
}
if (satree.len) { log_warn("directory: ",test," is not empty") ; e = 0 ; }
break ;
}
default: log_die(LOG_EXIT_SYS, "operation not implemented") ;
}
argc -= 1 ; argv += 1 ;
for (; i < argc ; i++)
nargv[n++] = (char *)argv[i] ;
nargv[n] = 0 ;
arguments->argc = n ;
arguments->argv = nargv ;
if (!arguments->argc && !arguments->noprog) log_die(LOG_EXIT_USER,"missing argument prog") ;
return e ;
}
int main(int argc,char const *const *argv, char const *const *envp)
{
int r, f = 0, n = 0 ;
uint8_t negat = 0, treat_zero = 0 ;
char *nargv[argc + 2] ;
execl_func_t_ref func = 0 ;
opts_common_t arguments = OPTS_COMMON_ZERO ;
// do not output nothing by default except system error
VERBOSITY = 0 ;
PROG = "execl-toc" ;
{
subgetopt l = SUBGETOPT_ZERO ;
for (;;)
{
int opt = subgetopt_r(argc, argv, "hv:ntDX", &l) ;
if (opt == -1) break ;
switch (opt)
{
case 'h' : info_help(); return 0 ;
case 'v' : if (!uint0_scan(l.arg, &VERBOSITY))
log_usage(USAGE) ;
break ;
case 'n' : negat = 1 ; break ;
case 't' : treat_zero = 1 ; break ;
case 'D' : arguments.not_create = 1 ; break ;
case 'X' : arguments.noprog = 1 ; break ;
default : break ;
}
}
argc -= l.ind ; argv += l.ind ;
}
argc++ ; argv-- ;
nargv[n++] = "fake_opts" ;
for (int i = 0 ; i < argc ; i++)
nargv[n++] = (char *)argv[i] ;
nargv[n] = 0 ;
{
subgetopt l = SUBGETOPT_ZERO ;
for (;;)
{
int opt = subgetopt_r(3,(char const *const *)nargv, "d:p:S:m:L:b:c:k:n:g:r:s:t:u:w:x:e:f:z:O:U:N:V:E:", &l) ;
if (opt == -1) break ;
switch (opt)
{
case 'd' : func = &execl_directory ;
arguments.test_on = l.arg ;
arguments.func_name = "execl_directory" ;
break ;
case 'p' : func = &execl_fifo ;
arguments.test_on = l.arg ;
arguments.func_name = "execl_fifo" ;
break ;
case 'S' : func = &execl_socket ;
arguments.test_on = l.arg ;
arguments.func_name = "execl_socket" ;
break ;
case 'm' : func = &execl_mountpoint ;
arguments.test_on = l.arg ;
arguments.func_name = "execl_mountpoint" ;
break ;
case 'L' : func = &execl_symlink ;
arguments.test_on = l.arg ;
arguments.func_name = "execl_symlink" ;
break ;
// only test
case 'b' : arguments.test = T_BLOCK ;
func = &execl_common ;
arguments.test_on = l.arg ;
arguments.func_name = "execl_common" ;
break ;
case 'c' : arguments.test = T_CHAR ;
func = &execl_common ;
arguments.test_on = l.arg ;
arguments.func_name = "execl_common" ;
break ;
case 'k' : arguments.test = T_STICKY ;
func = &execl_common ;
arguments.test_on = l.arg ;
arguments.func_name = "execl_common" ;
break ;
case 'n' : arguments.test = T_NONZERO ;
func = &execl_common ;
arguments.test_on = l.arg ;
arguments.func_name = "execl_common" ;
break ;
case 'g' : arguments.test = T_SGID ;
func = &execl_common ;
arguments.test_on = l.arg ;
arguments.func_name = "execl_common" ;
break ;
case 'r' : arguments.test = T_READABLE ;
func = &execl_common ;
arguments.test_on = l.arg ;
arguments.func_name = "execl_common" ;
break ;
case 's' : arguments.test = T_NONZEROFILE ;
func = &execl_common ;
arguments.test_on = l.arg ;
arguments.func_name = "execl_common" ;
break ;
case 't' : arguments.test = T_TERM ;
func = &execl_common ;
arguments.test_on = l.arg ;
arguments.func_name = "execl_common" ;
break ;
case 'u' : arguments.test = T_SUID ;
func = &execl_common ;
arguments.test_on = l.arg ;
arguments.func_name = "execl_common" ;
break ;
case 'w' : arguments.test = T_WRITABLE ;
func = &execl_common ;
arguments.test_on = l.arg ;
arguments.func_name = "execl_common" ;
break ;
case 'x' : arguments.test = T_EXECUTABLE ;
func = &execl_common ;
arguments.test_on = l.arg ;
arguments.func_name = "execl_common" ;
break ;
case 'e' : arguments.test = T_EXIST ;
func = &execl_common ;
arguments.test_on = l.arg ;
arguments.func_name = "execl_common" ;
break ;
case 'f' : arguments.test = T_REGULAR ;
func = &execl_common ;
arguments.test_on = l.arg ;
arguments.func_name = "execl_common" ;
break ;
case 'z' : arguments.test = T_ZERO ;
func = &execl_common ;
arguments.test_on = l.arg ;
arguments.func_name = "execl_common" ;
break ;
case 'O' : arguments.test = T_EUID ;
func = &execl_common ;
arguments.test_on = l.arg ;
arguments.func_name = "execl_common" ;
break ;
case 'U' : arguments.test = T_EGID ;
func = &execl_common ;
arguments.test_on = l.arg ;
arguments.func_name = "execl_common" ;
break ;
case 'N' : arguments.test = T_MODIFIED ;
func = &execl_common ;
arguments.test_on = l.arg ;
arguments.func_name = "execl_common" ;
break ;
case 'V' : arguments.test = T_ENV ;
func = &execl_common ;
arguments.test_on = l.arg ;
arguments.func_name = "execl_common" ;
break ;
case 'E' : func = &execl_common ;
arguments.test = T_EMPTY ;
arguments.test_on = l.arg ;
arguments.func_name = "execl_common" ;
break ;
default : for (int i = 0 ; i < n ; i++)
{
if (!argv[l.ind]) log_usage(USAGE) ;
if (obstr_equal(nargv[i],argv[l.ind]))
f = 1 ;
}
if (!f) nargv[n++] = (char *)argv[l.ind] ;
f = 0 ;
break ;
}
}
argc -= l.ind ; argv += l.ind ;
}
if (argc <= 0 && !arguments.noprog) log_usage(USAGE) ;
n = 0 ;
argc++ ; argv-- ;
nargv[n++] = (char *)arguments.func_name ;
for (int i = 0 ; i < argc ; i++)
nargv[n++] = (char *)argv[i] ;
nargv[n] = 0 ;
arguments.argc = n ;
arguments.argv = nargv ;
arguments.envp = envp ;
// argument doesn't exit
if (!arguments.func_name) log_die(LOG_EXIT_SYS, "operation not implemented") ;
r = (*func)(&arguments,nargv) ;
if (negat == r) return treat_zero ? LOG_EXIT_ZERO : LOG_EXIT_SYS ;
if (arguments.noprog) return LOG_EXIT_ZERO ;
xexec_ae(arguments.argv[0],(char const *const *)arguments.argv,arguments.envp) ;
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment