Skip to content
GitLab
Explore
Sign in
Register
Primary navigation
Search or go to…
Project
6
66
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Eric Vidal
66
Commits
fe905b5e
Commit
fe905b5e
authored
1 year ago
by
Eric Vidal
Browse files
Options
Downloads
Patches
Plain Diff
add execl-toc from 66-tools
parent
943f2d67
No related branches found
Branches containing commit
No related tags found
Tags containing commit
No related merge requests found
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
src/extra-tools/deps-exe/execl-toc
+3
-0
3 additions, 0 deletions
src/extra-tools/deps-exe/execl-toc
src/extra-tools/execl-toc.c
+913
-0
913 additions, 0 deletions
src/extra-tools/execl-toc.c
with
916 additions
and
0 deletions
src/extra-tools/deps-exe/execl-toc
0 → 100644
+
3
−
0
View file @
fe905b5e
-loblibs
-lskarnet
This diff is collapsed.
Click to expand it.
src/extra-tools/execl-toc.c
0 → 100644
+
913
−
0
View file @
fe905b5e
/*
* 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
)
;
}
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment