@@ -9,16 +9,52 @@
# Process event connector is a netlink connector that reports process events
# to userspace. It sends events such as fork, exec, id change and exit.
+TST_OPTS="n:"
TST_SETUP=setup
TST_TESTFUNC=test
+TST_PARSE_ARGS=parse_args
+TST_USAGE=usage
TST_NEEDS_ROOT=1
TST_NEEDS_TMPDIR=1
TST_TEST_DATA="fork exec exit uid gid"
-NUM_EVENTS=1
-
. tst_test.sh
+num_events=10
+
+usage()
+{
+ cat << EOF
+usage: $0 [-n <nevents>]
+
+OPTIONS
+-n The number of evetns to generate per test (default 10)
+EOF
+}
+
+parse_args()
+{
+ case $1 in
+ n) num_events=$2;;
+ esac
+}
+
+# Find a free file handle
+free_fd()
+{
+ local fd
+
+ for fd in $(seq 200); do
+ # Sapwn a new sh, because redirecting to a non existing file handle
+ # will trigger a syntax error.
+ /bin/sh -c ": 2>/dev/null >&$fd || : 2>/dev/null <&$fd" 2>/dev/null
+ if [ $? -eq 2 ]; then
+ echo $fd
+ return
+ fi
+ done
+}
+
setup()
{
if ! grep -q cn_proc /proc/net/connector; then
@@ -31,7 +67,9 @@ setup()
test()
{
local event=$2
- local expected_events lis_rc pid
+ local expected_events lis_rc pid fd_act failed act_nevents exp act
+
+ tst_res TINFO "Testing $2 event (nevents=$num_events)"
pec_listener >lis_$event.log 2>lis_$event.err &
pid=$!
@@ -39,28 +77,66 @@ test()
tst_sleep 100ms
# generator must be in PATH
- ROD event_generator -n $NUM_EVENTS -e $event \>gen_$event.log 2\>gen_$event.err
+ ROD event_generator -n $num_events -e $event \>gen_$event.log 2\>gen_$event.err
- # sleep until pec_listener has seen and handled all of the generated events
- tst_sleep 100ms
kill -s INT $pid 2> /dev/null
wait $pid
lis_rc=$?
if [ ! -s gen_$event.log ]; then
- tst_brk TBROK "failed to generate process events"
+ tst_brk TBROK "failed to generate process events: $(cat gen_$event.err)"
fi
if [ $lis_rc -ne 0 ]; then
tst_brk TBROK "failed to execute the listener: $(cat lis_$event.err)"
fi
- expected_events="$(cat gen_$event.log)"
- if grep -q "$expected_events" lis_$event.log; then
- tst_res TPASS "$event detected by listener"
+ # The listener writes the same messages as the generator, but it can
+ # also see more events (e.g. for testing exit, a fork is generated).
+ # So: The events generated by the generator have to be in the same order
+ # as the events printed by the listener, but my interleaved with other
+ # messages. To correctly compare them, we have to open both logs
+ # and iterate over both of them at the same time, skipping messages
+ # in the listener log, that are not of interest.
+ # Because some messages may be multiple times in the listener log,
+ # we have to open it only once!
+ # This however does not check, if the listener sees more messages,
+ # than expected.
+
+ fd_act=$(free_fd)
+ [ -z "$fd_act" ] && tst_brk TBROK "No free filehandle found"
+ eval "exec ${fd_act}<lis_$event.log"
+
+ failed=0
+ act_nevents=0
+ while read -r exp; do
+ local found=0
+ act_nevents=$((act_nevents + 1))
+ while read -r act; do
+ if [ "$exp" = "$act" ]; then
+ found=1
+ break
+ fi
+ done <&${fd_act}
+ if [ $found -ne 1 ]; then
+ failed=1
+ tst_res TFAIL "Event was not detected by the event listener: $exp"
+ break
+ fi
+ done <gen_$event.log
+
+ eval "exec ${fd_act}<&-"
+
+ if [ $failed -eq 0 ]; then
+ if [ $act_nevents -ne $num_events ]; then
+ tst_res TBROK "Expected $num_events, but $act_nevents generated"
+ else
+ tst_res TPASS "All events detected"
+ fi
else
- tst_res TFAIL "$event not detected by listener"
- fi
+ # TFAIL message is already printed in the loop above
+ cat lis_$event.log
+ fi
}
tst_run
@@ -93,16 +93,8 @@ static void gen_exec(void)
*/
static inline void gen_fork(void)
{
- pid_t pid;
- int status;
-
- pid = SAFE_FORK();
- if (pid == 0) {
- printf("fork parent: %d, child: %d\n", getppid(), getpid());
- exit(0);
- } else { /* Parent should wait for the child */
- wait(&status);
- }
+ /* The actual fork is already done in main */
+ printf("fork parent: %d, child: %d\n", getppid(), getpid());
}
/**
@@ -110,16 +102,10 @@ static inline void gen_fork(void)
*/
static inline void gen_exit(void)
{
- pid_t pid;
- int status;
-
- pid = SAFE_FORK();
- if (pid == 0) {
- printf("exit pid: %d exit_code: %d\n", getpid(), 0);
- exit(0);
- } else {
- wait(&status);
- }
+ /* exit_signal will always be SIGCHLD, if the process terminates cleanly */
+ printf("exit pid: %d exit_code: %d exit_signal: %d\n",
+ getpid(), 0, SIGCHLD);
+ /* exit is called by main already */
}
/*
@@ -128,7 +114,7 @@ static inline void gen_exit(void)
static inline void gen_uid(void)
{
SAFE_SETUID(ltp_uid);
- printf("uid pid: %d euid: %d\n", getpid(), ltp_uid);
+ printf("uid pid: %d euid: %d ruid: %d\n", getpid(), ltp_uid, ltp_uid);
}
/*
@@ -137,7 +123,7 @@ static inline void gen_uid(void)
static inline void gen_gid(void)
{
SAFE_SETGID(ltp_gid);
- printf("gid pid: %d egid: %d\n", getpid(), ltp_gid);
+ printf("gid pid: %d egid: %d rgid: %u\n", getpid(), ltp_gid, ltp_gid);
}
/*
@@ -210,8 +196,6 @@ int main(int argc, char **argv)
ltp_uid = ent->pw_uid;
ltp_gid = ent->pw_gid;
- signal(SIGCHLD, SIG_IGN);
-
/* special processing for gen_exec, see comments above gen_exec() */
if (gen_event == gen_exec) {
exec_argv = argv;
@@ -223,8 +207,26 @@ int main(int argc, char **argv)
}
/* other events */
- for (i = 0; i < nr_event; i++)
- gen_event();
+ for (i = 0; i < nr_event; i++) {
+ pid_t pid;
+ int status;
+
+ pid = SAFE_FORK();
+ if (pid == 0) {
+ gen_event();
+ exit(0);
+ } else {
+ if (pid != SAFE_WAITPID(pid, &status, 0)) {
+ fprintf(stderr,
+ "Child process did not terminate as expected\n");
+ return 1;
+ }
+ if (WEXITSTATUS(status) != 0) {
+ fprintf(stderr, "Child process did not terminate with 0\n");
+ return 1;
+ }
+ }
+ }
return 0;
}