From: Subrata <sub...@us...> - 2008-06-26 09:35:49
|
Update of /cvsroot/ltp/ltp/testcases/kernel/connectors/pec In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv2548/ltp/testcases/kernel/connectors/pec Added Files: Makefile README event_generator.c pec_listener.c run_pec_test Log Message: Addition of Initial set of Process Event Connectors by Li Zefan. Process event connector is a netlink connector that reports process events to userspace, and currently we have 5 kinds of process events, i.e. fork, exit, exec, uid, gid. There are total 5 test cases to test its functionality. But the test is not run by default, because I don't find a way to decide whether the underlying kernel supports this feather or not. Signed-off-by: Li Zefan <li...@cn...>. --- NEW FILE: README --- TEST SUITE: The directory pec contains the tests related to the process event connector. Process event connector is a netlink connector that reports process events to userspace. It sends events such as fork, exec, id change and exit. There are total 5 testcases. Note: the test can be run by root only. TESTS AIM: The aim of the tests is to test the functionality of process event connector. FILES DESCRIPTION: check_connector_enabled.c ------------------ This program is used to check if the kernel supports netlink connector. event_generator.c ------------------ This program is used to generate a specified process event (fork, exec, uid, gid or exit). run_pec_test ------------------ This script runs all the 5 testcases. pec_listener.c ------------------ This program is used to ilsten to process events received through the kernel connector and print them. Makefile ------------------ The usual makefile for this directory $LTPROOT/output/pec/*.log ------------------ The outputs of event_generator and pec_listeners. README: ------------------ The one you have gone through. --- NEW FILE: Makefile --- CFLAGS += -I../../../../include -Wall LDLIBS += -L../../../../lib -lltp SRCS:=$(wildcard *.c) TARGETS:=$(patsubst %.c,%,$(SRCS)) all: $(TARGETS) install: @set -e; for i in $(TARGETS) ; do ln -f $$i ../../../bin/$$i ; chmod +x run_pec_test ; done ; ln -f run_pec_test ../../../bin/ clean: rm -f $(TARGETS) --- NEW FILE: pec_listener.c --- /******************************************************************************/ /* */ /* Copyright (c) 2008 FUJITSU LIMITED */ /* */ /* 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 2 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, write to the Free Software */ /* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* */ /* Author: Li Zefan <li...@cn...> */ /* */ /******************************************************************************/ #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <signal.h> #include <sys/socket.h> #include <sys/poll.h> #include <linux/netlink.h> #include <linux/connector.h> #include <linux/cn_proc.h> #define PEC_MSG_SIZE (sizeof(struct cn_msg) + sizeof(struct proc_event)) #define PEC_CTRL_MSG_SIZE (sizeof(struct cn_msg) + sizeof(enum proc_cn_mcast_op)) #define MAX_MSG_SIZE 256 static __u32 seq; static int exit_flag; static struct sigaction sigint_action; /* * Handler for signal int. Set exit flag. * * @signo: the signal number, not used */ static void sigint_handler(int __attribute__((unused)) signo) { exit_flag = 1; } /* * Send netlink package. * * @sd: socket descripor * @to: the destination sockaddr * @cnmsg: the pec control message */ static int netlink_send(int sd, struct sockaddr_nl *to, struct cn_msg *cnmsg) { int ret; char buf[MAX_MSG_SIZE]; struct nlmsghdr *nlhdr; struct iovec iov; struct msghdr msg; memset(buf, 0, MAX_MSG_SIZE); nlhdr = (struct nlmsghdr *)buf; nlhdr->nlmsg_seq = seq++; nlhdr->nlmsg_pid = getpid(); nlhdr->nlmsg_type = NLMSG_DONE; nlhdr->nlmsg_len = NLMSG_LENGTH(sizeof(*cnmsg) + cnmsg->len); nlhdr->nlmsg_flags = 0; memcpy(NLMSG_DATA(nlhdr), cnmsg, sizeof(*cnmsg) + cnmsg->len); memset(&iov, 0, sizeof(struct iovec)); iov.iov_base = (void *)nlhdr; iov.iov_len = nlhdr->nlmsg_len; memset(&msg, 0, sizeof(struct msghdr)); msg.msg_name = (void *)to; msg.msg_namelen = sizeof(*to); msg.msg_iov = &iov; msg.msg_iovlen = 1; ret = sendmsg(sd, &msg, 0); return ret; } /* * Receive package from netlink. * * @sd: socket descripor * @from: source sockaddr * @buf: buffer for storing the package */ static int netlink_recv(int sd, struct sockaddr_nl *from, char *buf) { int ret; struct nlmsghdr *nlhdr = (struct nlmsghdr *)buf; struct iovec iov; struct msghdr msg; memset(nlhdr, 0, NLMSG_SPACE(MAX_MSG_SIZE)); memset(&iov, 0, sizeof(iov)); memset(&msg, 0, sizeof(msg)); iov.iov_base = (void *)nlhdr; iov.iov_len = NLMSG_SPACE(MAX_MSG_SIZE); msg.msg_name = (void *)from; msg.msg_namelen = sizeof(*from); msg.msg_iov = &iov; msg.msg_iovlen = 1; ret = recvmsg(sd, &msg, 0); return ret; } /* * Send control message to PEC. * * @sd: socket descriptor * @to: the destination sockaddr * @op: control flag */ static int control_pec(int sd, struct sockaddr_nl *to, enum proc_cn_mcast_op op) { int ret; char buf[PEC_CTRL_MSG_SIZE]; struct cn_msg *cnmsg; enum proc_cn_mcast_op *pec_op; memset(buf, 0, sizeof(buf)); cnmsg = (struct cn_msg *)buf; cnmsg->id.idx = CN_IDX_PROC; cnmsg->id.val = CN_VAL_PROC; cnmsg->seq = seq++; cnmsg->ack = 0; cnmsg->len = sizeof(op); pec_op = (enum proc_cn_mcast_op *)cnmsg->data; *pec_op = op; ret = netlink_send(sd, to, cnmsg); return ret; } /* * Process PEC event. * * @nlhdr: the netlinke pacakge */ static void process_event(struct nlmsghdr *nlhdr) { struct cn_msg *msg; struct proc_event *pe; msg = (struct cn_msg *)NLMSG_DATA(nlhdr); pe = (struct proc_event *)msg->data; switch (pe->what) { case PROC_EVENT_NONE: printf("none err: %u\n", pe->event_data.ack.err); break; case PROC_EVENT_FORK: printf("fork parent: %d, child: %d\n", pe->event_data.fork.parent_pid, pe->event_data.fork.child_pid); break; case PROC_EVENT_EXEC: printf("exec pid: %d\n", pe->event_data.exec.process_pid); break; case PROC_EVENT_UID: printf("uid pid: %d euid: %d ruid: %d\n", pe->event_data.id.process_pid, pe->event_data.id.e.euid, pe->event_data.id.r.ruid); break; case PROC_EVENT_GID: printf("gid pid: %d egid: %d rgid: %d\n", pe->event_data.id.process_pid, pe->event_data.id.e.egid, pe->event_data.id.r.rgid); break; case PROC_EVENT_EXIT: printf("exit pid: %d exit_code: %d exit_signal: %d\n", pe->event_data.exit.process_pid, pe->event_data.exit.exit_code, pe->event_data.exit.exit_signal); break; default: printf("unknown event\n"); break; } } int main(int argc, char **argv) { int ret; int sd; struct sockaddr_nl l_local; struct sockaddr_nl src_addr; char buf[MAX_MSG_SIZE]; struct pollfd pfd; sigint_action.sa_flags = SA_ONESHOT; sigint_action.sa_handler = &sigint_handler; sigaction(SIGINT, &sigint_action, NULL); /* Create and bind socket */ sd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR); if (sd == -1) { fprintf(stderr, "failed to create socket\n"); exit(1); } memset(&src_addr, 0, sizeof(src_addr)); src_addr.nl_family = AF_NETLINK; src_addr.nl_pid = 0; src_addr.nl_groups = 0; memset(&l_local, 0, sizeof(l_local)); l_local.nl_family = AF_NETLINK; l_local.nl_pid = getpid(); l_local.nl_groups = CN_IDX_PROC; ret = bind(sd, (struct sockaddr *)&l_local, sizeof(struct sockaddr_nl)); if (ret == -1) { fprintf(stderr, "failed to bind socket\n"); exit(1); } /* Open PEC listening */ ret = control_pec(sd, &src_addr, PROC_CN_MCAST_LISTEN); if (!ret) { fprintf(stderr, "failed to open PEC listening\n"); exit(1); } /* Receive msg from PEC */ pfd.fd = sd; pfd.events = POLLIN; pfd.revents = 0; while (!exit_flag) { struct nlmsghdr *nlhdr; ret = poll(&pfd, 1, -1); if (ret == 0 || (ret == -1 && errno != EINTR)) { control_pec(sd, &src_addr, PROC_CN_MCAST_IGNORE); fprintf(stderr, "failed to poll\n"); exit(1); } else if (ret == -1 && errno == EINTR) break; ret = netlink_recv(sd, &src_addr, buf); if (ret == 0) break; else if (ret == -1 && errno == EINTR) break; else if (ret == -1 && errno != EINTR) { control_pec(sd, &src_addr, PROC_CN_MCAST_IGNORE); fprintf(stderr, "failed to receive from netlink\n"); exit(1); } else { nlhdr = (struct nlmsghdr *)buf; switch (nlhdr->nlmsg_type) { case NLMSG_ERROR: fprintf(stderr, "err message recieved.\n"); exit(1); break; case NLMSG_DONE: /* message sent from kernel */ if (nlhdr->nlmsg_pid == 0) process_event(nlhdr); break; default: break; } } } /* Close PEC listening */ ret = control_pec(sd, &src_addr, PROC_CN_MCAST_IGNORE); if (!ret) { fprintf(stderr, "failed to close PEC listening\n"); exit(1); } close(sd); while (fsync(STDOUT_FILENO) == -1) { if (errno != EIO) break; /* retry once every 10 secodns */ sleep(10); } return 0; } --- NEW FILE: run_pec_test --- #! /bin/bash ################################################################################ ## ## ## Copyright (c) 2008 FUJITSU LIMITED ## ## ## ## 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 2 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, write to the Free Software ## ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ## ## ## ## Author: Li Zefan <li...@cn...> ## ## ## ################################################################################ NUM_EVENTS=1 EVENT_TEST_CASES=( "fork" "exec" "exit" "uid" "gid" ) TST_TOTAL=${#EVENT_TEST_CASES[@]} if [ -z $LTPROOT ]; then LTPROOT="`cd ../../../.. && pwd`" PATH="$PATH:$LTPROOT/testcases/bin" mkdir $LTPROOT/output 2> /dev/null fi cd $LTPROOT/testcases/bin export TCID="pec01" export TST_TOTAL=5 export TST_COUNT=1 exit_status=0 if [ "$USER" != root ]; then tst_brkm TBROK ignored "Test must be run as root" exit 0; fi # Run a test case # # $1: the test number # $2: type of event run_case() { export TST_COUNT=$1 log="$LTPROOT/output/log" mkdir $log 2> /dev/null ./pec_listener > "$log/listener_$1.log" 2>&1 & pid=$! # Wait for pec_listener to start listening sleep $((1*NUM_EVENTS)) ./event_generator -e $2 > "$log/generator_$1.log" ret1=$? # Sleep until pec_listener has seen and handled all of # the generated events sleep $((1*NUM_EVENTS)) kill -s SIGINT $pid 2> /dev/null wait $pid ret2=$? if [ $ret1 -ne 0 -o ! -s "$log/generator_$1.log" ]; then tst_resm TFAIL "failed to generate process events" exit_status=1 return 1 fi if [ $ret2 -ne 0 ]; then tst_resm TFAIL "failed to listen process events" exit_status=1 return 1 fi event="`cat $log/generator_$1.log`" cat "$log/listener_$1.log" | grep "$event" > /dev/null if [ $? -eq 0 ]; then tst_resm TPASS "get event - $event" else tst_resm TFAIL "expected event - $event" exit_status=1 fi } i=1; for CASE in "${EVENT_TEST_CASES[@]}" ; do run_case $i $CASE ((i++)) done exit $exit_status --- NEW FILE: event_generator.c --- /******************************************************************************/ /* */ /* Copyright (c) 2008 FUJITSU LIMITED */ /* */ /* 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 2 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, write to the Free Software */ /* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* */ /* Author: Li Zefan <li...@cn...> */ /* */ /******************************************************************************/ #include <unistd.h> #include <string.h> #include <stdlib.h> #include <stdio.h> #include <pwd.h> #include "test.h" #define DEFAULT_EVENT_NUM 1 unsigned long nr_event = DEFAULT_EVENT_NUM; uid_t ltp_uid; gid_t ltp_gid; const char *ltp_user = "nobody"; char **exec_argv; void (*gen_event)(void); /* * Show the usage * * @status: the exit status */ static void usage(int status) { FILE *stream = (status ? stderr : stdout); fprintf(stream, "Usage: event_generator -e fork|exit|exec|uid|gid [-n nr_event]\n"); exit(status); } /* * Generate exec event. * * We can't just exec nr_event times, because the current process image * will be replaced with the new process image, so we use enviroment * viriable as event counters, as it will be inherited after exec. */ static void gen_exec(void) { char *val; char buf[10]; unsigned long nr_exec; /* get the event counter */ val = getenv("NR_EXEC"); if (!val) { nr_exec = 0; setenv("NR_EXEC", "1", 1); } else { nr_exec = atoi(val); snprintf(buf, 10, "%lu", nr_exec + 1); setenv("NR_EXEC", buf, 1); } /* stop generate exec event */ if (nr_exec >= nr_event) return; /* fflush is needed before exec */ printf("exec pid: %d\n", getpid()); fflush(stdout); execv(exec_argv[0], exec_argv); } /* * Generate fork event. */ static inline void gen_fork(void) { pid_t pid; pid = fork(); if (pid == 0) { printf("fork parent: %d, child: %d\n", getppid(), getpid()); exit(0); } else if (pid < 0) { fprintf(stderr, "fork() failed\n"); exit(1); } } /** * Generate exit event */ static inline void gen_exit(void) { pid_t pid; pid = fork(); if (pid == 0) { printf("exit pid: %d exit_code: %d\n", getpid(), 0); exit(0); } else if (pid < 0){ fprintf(stderr, "fork() failed\n"); exit(1); } } /* * Generate uid event. */ static inline void gen_uid(void) { setuid(ltp_uid); printf("uid pid: %d euid: %d\n", getpid(), ltp_uid); } /* * Generate gid event. */ static inline void gen_gid(void) { setgid(ltp_gid); printf("gid pid: %d egid: %d\n", getpid(), ltp_gid); } /* * Read option from user input. * * @argc: number of arguments * @argv: argument list */ static void process_options(int argc, char **argv) { char c; char *end; while ((c = getopt(argc, argv, "e:n:h")) != -1) { switch (c) { /* which event to generate */ case 'e': if (!strcmp(optarg, "exec")) gen_event = gen_exec; else if (!strcmp(optarg, "fork")) gen_event = gen_fork; else if (!strcmp(optarg, "exit")) gen_event = gen_exit; else if (!strcmp(optarg, "uid")) gen_event = gen_uid; else if (!strcmp(optarg, "gid")) gen_event = gen_gid; else { fprintf(stderr, "wrong -e argument!"); exit(1); } break; /* number of event to generate */ case 'n': nr_event = strtoul(optarg, &end, 10); if (*end != '\0' || nr_event == 0) { fprintf(stderr, "wrong -n argument!"); exit(1); } break; /* help */ case 'h': usage(0); default: fprintf(stderr, "unknown option!\n"); usage(1); } } if (!gen_event) { fprintf(stderr, "no event type specified!\n"); usage(1); } } int main(int argc, char **argv) { unsigned long i; struct passwd *ent; process_options(argc, argv); ent = getpwnam(ltp_user); if (ent == NULL) { fprintf(stderr, "can't get password entry for %s", ltp_user); exit(1); } 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; gen_exec(); /* won't reach here */ return 0; } /* other events */ for (i = 0; i < nr_event; i++) gen_event(); return 0; } |