diff --git a/src/extra-tools/deps-exe/execl-toc b/src/extra-tools/deps-exe/execl-toc new file mode 100644 index 0000000000000000000000000000000000000000..aa08a1493330806f8a201d878aa14bb087db05a3 --- /dev/null +++ b/src/extra-tools/deps-exe/execl-toc @@ -0,0 +1,3 @@ +-loblibs +-lskarnet + diff --git a/src/extra-tools/execl-toc.c b/src/extra-tools/execl-toc.c new file mode 100644 index 0000000000000000000000000000000000000000..0ae9706972fa5408b99d06782f5e59b4646e1ba2 --- /dev/null +++ b/src/extra-tools/execl-toc.c @@ -0,0 +1,913 @@ +/* + * 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) ; +}