diff --git a/Dockerfile b/Dockerfile index 7c000ba26f0422dd6dbcd7b702f819b9e9743850..6d0dddb7549efcd07fcbec70c3f2094b986a1e24 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,8 +4,6 @@ RUN echo "Hello from Docker" RUN mkdir -p /linux/tools/labs/skels/assignments/3-raid RUN mkdir -p /linux/tools/labs/skels/assignments/3-raid-checker -COPY ./checker/3-raid-checker /linux/tools/labs/skels/assignments/3-raid-checker - COPY ./checker/checker_daemons/so2_vm_checker_daemon.sh /linux/tools/labs/rootfs/etc/init.d RUN chmod +x /linux/tools/labs/rootfs/etc/init.d/so2_vm_checker_daemon.sh RUN chroot /linux/tools/labs/rootfs update-rc.d so2_vm_checker_daemon.sh defaults diff --git a/checker/4-stp-checker/Makefile b/checker/4-stp-checker/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..e0c2b42807f22f2a94bf684002d7fa6a86aa8891 --- /dev/null +++ b/checker/4-stp-checker/Makefile @@ -0,0 +1,17 @@ +objects = _test/stp_test.o + +.PHONY: all clean _test_subdir_all _test_subdir_clean + +all: stp_test + +stp_test: _test_subdir_all $(objects) + $(CC) -Wall -g -m32 -static $(objects) -Wl,--whole-archive -lpthread -Wl,--no-whole-archive -o $@ + +_test_subdir_all: + make -C _test + +clean: _test_subdir_clean + -rm -f stp_test *~ + +_test_subdir_clean: + make -C _test clean diff --git a/checker/4-stp-checker/README b/checker/4-stp-checker/README new file mode 100644 index 0000000000000000000000000000000000000000..a1d04d84f8653e98d3812f0a9ecbfa26d64c5956 --- /dev/null +++ b/checker/4-stp-checker/README @@ -0,0 +1,87 @@ += STP TEST SUITE == + +Test suite for SO2 Tranport Protocol + +== FILES == + +README + * this file + +Makefile + +_checker + * script to run all tests defined in _test/stp_test.c + +_test/Makefile + * test suite internal Makefile (creates necessary object files) + +_test/stp_test.c + * test suite for SO2 Transport Protocol + +_test/stp_test.h + * test suite header file + +_test/stp.h + * SO2 Transport Protocol header file (macros and structures) + +_test/test.h + * useful macros for testing + +_test/debug.h + * debugging macros + +_test/util.h + * useful macros for generic use (error processing) + +== BUILDING == + + +== RUNNING == + +Copy your af_stp.ko module and _checker and stp_test +to fsimg/root directory on your QEMU/KVM virtual machine. + +In order to run the test suite you can either use the _checker +script or run the stp_test executable. + +The _checker script runs all tests and computes assignment grade: + + ./_checker + +In order to run a specific test pass the test number (1 .. 32) to the +stp_test executable. + + ./stp_test 5 + +== TESTS == + +Tests are basically unit tests. A single function in the test_fun_array (see +stp_test.c) is called each time the stp_test executable is invoked, +testing a single functionality (and assuming previous tests have been run and +passed). + +The EXIT_IF_FAIL macro (see test.h) is unnecessary since after each test, the +program completes. + +Each test function follows the unit test pattern: initialization, action, +evaluation. The test macro (see test.h) is invoked at the end of each test +for evaluating and grading the test. + +== DEBUGGING == + +The debug.h header file consists of several macros useful for debugging +(dprintf, dlog). There are multiple uses of these macros throughout the above +files. + +In order to turn debug messages on, you must define the DEBUG macro, either in +a header file, or, I suggest, in the Makefile. The LOG_LEVEL macro limits the +log message types that are to be printed, by default LOG_WARNING (see enum in +debug.h). You may redefine it in a header file or in the Makefile. + +Rapid enabling of debug messages is achieved by commenting out the CPPFLAGS +line in the Makefile. It turns on debugging and enables all log messages +(LOG_DEBUG). + +== OTHER == + +srand48() and drand48() are used for generating random numbers. diff --git a/checker/4-stp-checker/_checker b/checker/4-stp-checker/_checker new file mode 100755 index 0000000000000000000000000000000000000000..810e77f5e98e5337f30ad6e54d707baacaca1f4f --- /dev/null +++ b/checker/4-stp-checker/_checker @@ -0,0 +1,24 @@ +#!/bin/sh + +first_test=1 +last_test=32 +executable=stp_test + +for i in $(seq $first_test $last_test); do + ./"$executable" $i +done | tee results.txt + +cat results.txt | grep '\[.*\]$' | awk -F '[] /[]+' ' +BEGIN { + sum=0 +} + +{ + sum += $2; +} + +END { + printf "\n%66s [%d/100]\n", "Total:", sum; +}' + +rm -f results.txt diff --git a/checker/4-stp-checker/_test/Makefile b/checker/4-stp-checker/_test/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..d5074dd464a5f5fa80cf42324e336ebdb975f529 --- /dev/null +++ b/checker/4-stp-checker/_test/Makefile @@ -0,0 +1,11 @@ +#CPPFLAGS = -DDEBUG -DLOG_LEVEL=LOG_DEBUG +CFLAGS = -Wall -g -m32 + +.PHONY: all clean + +all: stp_test.o + +stp_test.o: stp_test.c stp_test.h stp.h test.h util.h debug.h + +clean: + -rm -f *~ *.o diff --git a/checker/4-stp-checker/_test/debug.h b/checker/4-stp-checker/_test/debug.h new file mode 100644 index 0000000000000000000000000000000000000000..a54e96226471814a6f8595bc0a3c59cce37c5210 --- /dev/null +++ b/checker/4-stp-checker/_test/debug.h @@ -0,0 +1,77 @@ +/* + * debugging macros + * heavily inspired by previous work and Internet resources + * + * uses C99 variadic macros + * uses non-standard usage of the token-paste operator (##) for + * removing the comma symbol (,) when not followed by a token + * uses non-standard __FUNCTION__ macro (MSVC doesn't support __func__) + * tested on gcc 4.4.5 and Visual Studio 2008 (9.0), compiler version 15.00 + * + * Razvan Deaconescu, razvan.deaconescu@cs.pub.ro + */ + +#ifndef DEBUG_H_ +#define DEBUG_H_ 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdio.h> + +/* log levels */ +enum { + LOG_EMERG = 1, + LOG_ALERT, + LOG_CRIT, + LOG_ERR, + LOG_WARNING, + LOG_NOTICE, + LOG_INFO, + LOG_DEBUG +}; + +/* + * initialize default loglevel (for dlog) + * may be redefined in the including code + */ + +#ifndef LOG_LEVEL +#define LOG_LEVEL LOG_WARNING +#endif + +/* + * define DEBUG macro as a compiler option: + * -DDEBUG for GCC + * /DDEBUG for MSVC + */ + +#if defined DEBUG +#define dprintf(format, ...) \ + fprintf(stderr, " [%s(), %s:%u] " format, \ + __FUNCTION__, __FILE__, __LINE__, \ + ##__VA_ARGS__) +#else +#define dprintf(format, ...) \ + do { \ + } while (0) +#endif + +#if defined DEBUG +#define dlog(level, format, ...) \ + do { \ + if (level <= LOG_LEVEL) \ + dprintf(format, ##__VA_ARGS__); \ + } while (0) +#else +#define dlog(level, format, ...) \ + do { \ + } while (0) +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/checker/4-stp-checker/_test/stp.h b/checker/4-stp-checker/_test/stp.h new file mode 100644 index 0000000000000000000000000000000000000000..838f9936bf55c535d5e6ed640bb5e617effd1a26 --- /dev/null +++ b/checker/4-stp-checker/_test/stp.h @@ -0,0 +1,51 @@ +/* + * SO2 Transport Protocol + */ + +#ifndef STP_H_ +#define STP_H_ 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#include <linux/types.h> + +/* STP reuses the defines of ancient protocols like Econet and Xerox PUP + * because adding a new protocol would involve patching the kernel, which we + * don't want to do and besides that, they are probably not used anymore. + */ +#define AF_STP 19 +#define PF_STP AF_STP +#define ETH_P_STP 0x0a00 + +struct stp_hdr { + __be16 dst; /* Destination port */ + __be16 src; /* Source port */ + __be16 len; /* Total length, including header */ + __u8 flags; /* */ + __u8 csum; /* xor of all bytes, including header */ +}; + +struct sockaddr_stp { + unsigned short sas_family; /* Always AF_STP */ + int sas_ifindex; /* Interface index */ + __be16 sas_port; /* Port */ + __u8 sas_addr[6]; /* MAC address */ +}; + +/* STP protocol name; used as identifier in /proc/net/protocols */ +#define STP_PROTO_NAME "STP" + +/* + * STP uses proc interface to communicate statistical information to + * user space (in /proc/net/). + */ +#define STP_PROC_NET_FILENAME "stp_stats" +#define STP_PROC_FULL_FILENAME "/proc/net/" STP_PROC_NET_FILENAME + +#ifdef __cplusplus +} +#endif + +#endif /* STP_H_ */ diff --git a/checker/4-stp-checker/_test/stp_test.c b/checker/4-stp-checker/_test/stp_test.c new file mode 100644 index 0000000000000000000000000000000000000000..d6c729e344e634a0a90eced63918d7953cb8faf6 --- /dev/null +++ b/checker/4-stp-checker/_test/stp_test.c @@ -0,0 +1,1331 @@ +/* + * SO2 Transport Protocol - test suite + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> +#include <assert.h> +#include <time.h> +#include <signal.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/ether.h> +#include <net/if.h> +#include <assert.h> +#include <sys/wait.h> +#include <semaphore.h> +#include <fcntl.h> + +#include "test.h" +#include "debug.h" +#include "util.h" + +#include "stp.h" +#include "stp_test.h" + +#define SSA struct sockaddr +#define BUFLEN 32 + +/* declared in test.h; used for printing information in test macro */ +int max_points = 100; + +/* values read from STP_PROC_FULL_FILENAME */ +static int rx_pkts, hdr_err, csum_err, no_sock, no_buffs, tx_pkts; + +enum socket_action { + ACTION_SENDTO, + ACTION_SENDMSG, + ACTION_SEND, + ACTION_SENDTO_PING_PONG, + ACTION_SENDMSG_PING_PONG, + ACTION_SEND_PING_PONG, +}; + +/* + * Do initialization for STP test functions. + */ + +static void init_test(void) +{ + system("insmod " MODULE_FILENAME); +} + +/* + * Do cleanup for STP test functions. + */ + +static void cleanup_test(void) +{ + system("rmmod " MODULE_NAME); +} + +/* + * Check for successful module insertion and removal from the kernel. + */ + +static void test_insmod_rmmod(void) +{ + int rc; + + rc = system("insmod " MODULE_FILENAME); + test("test_insmod", rc == 0, 1); + + rc = system("rmmod " MODULE_NAME); + test("test_rmmod", rc == 0, 1); + + rc = system("insmod " MODULE_FILENAME); + test(__FUNCTION__, rc == 0, 1); + + system("rmmod " MODULE_NAME); +} + +/* + * Check /proc/net/protocols for STP protocol. Grep for line starting with + * the string identified by STP_PROTO_NAME. + */ + +static void test_proto_name_exists_after_insmod(void) +{ + int rc; + + init_test(); + + rc = system("grep '^" STP_PROTO_NAME "' /proc/net/protocols > /dev/null 2>&1"); + test(__FUNCTION__, rc == 0, 2); + + cleanup_test(); +} + +/* + * STP entry in /proc/net/protocols is deleted when module is removed. + */ + +static void test_proto_name_inexistent_after_rmmod(void) +{ + int rc; + + init_test(); + cleanup_test(); + + rc = system("grep '^" STP_PROTO_NAME "' /proc/net/protocols > /dev/null 2>&1"); + test(__FUNCTION__, rc != 0, 2); +} + +/* + * Check for proc entry for STP statistics. + */ + +static void test_proc_entry_exists_after_insmod(void) +{ + int rc; + + init_test(); + + rc = access(STP_PROC_FULL_FILENAME, F_OK); + test(__FUNCTION__, rc == 0, 2); + + cleanup_test(); +} + +/* + * STP statistics file in /proc/net/ is deleted when module is removed. + */ + +static void test_proc_entry_inexistent_after_rmmod(void) +{ + int rc; + + init_test(); + cleanup_test(); + + rc = system("file " STP_PROC_FULL_FILENAME " > /dev/null 2>&1"); + test(__FUNCTION__, rc != 0, 2); +} + +/* + * Call socket(2) with proper arguments for creating an AF_STP socket. + */ + +static void test_socket(void) +{ + int s; + + init_test(); + + s = socket(AF_STP, SOCK_DGRAM, 0); + test(__FUNCTION__, s > 0, 5); + + close(s); + cleanup_test(); +} + +/* + * Create two AF_STP sockets using socket(2). + */ + +static void test_two_sockets(void) +{ + int s1, s2; + + init_test(); + + s1 = socket(AF_STP, SOCK_DGRAM, 0); + s2 = socket(AF_STP, SOCK_DGRAM, 0); + test(__FUNCTION__, s1 > 0 && s2 > 0 && s1 != s2, 2); + + close(s1); + close(s2); + cleanup_test(); +} + +/* + * Pass bad socket type argument to socket(2) (second argument). + * Call should fail. + */ + +static void test_socket_bad_socket_type(void) +{ + int s; + + init_test(); + + s = socket(AF_STP, SOCK_STREAM, 0); + test(__FUNCTION__, s < 0, 1); + + close(s); + cleanup_test(); +} + +/* + * Pass bad protocol argument to socket(2) (third argument). + * Call should fail. + */ + +static void test_socket_bad_protocol(void) +{ + int s; + + init_test(); + + s = socket(AF_STP, SOCK_DGRAM, IPPROTO_TCP); + test(__FUNCTION__, s < 0, 1); + + close(s); + cleanup_test(); +} + +/* + * Close open socket using close(2). + */ + +static void test_close(void) +{ + int s; + int rc; + + init_test(); + + s = socket(AF_STP, SOCK_DGRAM, 0); + + rc = close(s); + test(__FUNCTION__, rc == 0, 2); + + cleanup_test(); +} + +/* + * Pass closed socket descriptor to close(2). Call should fail. + */ + +static void test_close_closed_socket(void) +{ + int s; + int rc; + + init_test(); + + s = socket(AF_STP, SOCK_DGRAM, 0); + + close(s); + rc = close(s); + + test(__FUNCTION__, rc < 0, 2); + + cleanup_test(); +} + +/* + * Bind socket to proper address. Use "all" interface. + */ + +static void test_bind(void) +{ + int s; + int rc; + struct sockaddr_stp sas; + const unsigned short port = 12345; + + init_test(); + + s = socket(AF_STP, SOCK_DGRAM, 0); + + sas.sas_family = AF_STP; + sas.sas_port = htons(port); + sas.sas_ifindex = 0; + rc = bind(s, (struct sockaddr *) &sas, sizeof(struct sockaddr_stp)); + + test(__FUNCTION__, rc == 0, 5); + + close(s); + cleanup_test(); +} + +/* + * Bind socket to proper address. Use "eth0" interface. + */ + +static void test_bind_eth0(void) +{ + int s; + int rc; + struct sockaddr_stp sas; + const unsigned short port = 12345; + + init_test(); + + s = socket(AF_STP, SOCK_DGRAM, 0); + + sas.sas_family = AF_STP; + sas.sas_port = htons(port); + sas.sas_ifindex = if_nametoindex("eth0"); + rc = bind(s, (struct sockaddr *) &sas, sizeof(struct sockaddr_stp)); + + test(__FUNCTION__, rc == 0, 2); + + close(s); + cleanup_test(); +} + +/* + * Use bind(2) on two AF_STP sockets. + */ + +static void test_two_binds(void) +{ + int s1, s2; + int rc1, rc2; + struct sockaddr_stp sas1, sas2; + const unsigned short port1 = 12345, port2 = 54321; + + init_test(); + + s1 = socket(AF_STP, SOCK_DGRAM, 0); + + sas1.sas_family = AF_STP; + sas1.sas_port = htons(port1); + sas1.sas_ifindex = 0; + rc1 = bind(s1, (struct sockaddr *) &sas1, sizeof(struct sockaddr_stp)); + + s2 = socket(AF_STP, SOCK_DGRAM, 0); + + sas2.sas_family = AF_STP; + sas2.sas_port = htons(port2); + sas2.sas_ifindex = 0; + rc2 = bind(s2, (struct sockaddr *) &sas2, sizeof(struct sockaddr_stp)); + + test(__FUNCTION__, rc1 == 0 && rc2 == 0, 2); + + close(s1); close(s2); + cleanup_test(); +} + +/* + * Pass bad address to bind(2) (second argument). + * Call should fail. + */ + +static void test_bind_bad_address(void) +{ + int s; + int rc; + struct sockaddr_stp sas; + const unsigned short port = 12345; + + init_test(); + + s = socket(AF_STP, SOCK_DGRAM, 0); + + sas.sas_family = AF_INET; /* invalid */ + sas.sas_port = htons(port); + sas.sas_ifindex = 0; + rc = bind(s, (struct sockaddr *) &sas, sizeof(struct sockaddr_stp)); + + test(__FUNCTION__, rc != 0, 1); + + close(s); + cleanup_test(); +} + +/* + * Use bind(2) on two AF_STP sockets using same port and "all" interface. + * Call should fail. + */ + +static void test_two_binds_same_if(void) +{ + int s1, s2; + int rc1, rc2; + struct sockaddr_stp sas1, sas2; + const unsigned short port = 12345; + + init_test(); + + s1 = socket(AF_STP, SOCK_DGRAM, 0); + + sas1.sas_family = AF_STP; + sas1.sas_port = htons(port); + sas1.sas_ifindex = 0; + rc1 = bind(s1, (struct sockaddr *) &sas1, sizeof(struct sockaddr_stp)); + + s2 = socket(AF_STP, SOCK_DGRAM, 0); + + sas2.sas_family = AF_STP; + sas2.sas_port = htons(port); + sas2.sas_ifindex = 0; + rc2 = bind(s2, (struct sockaddr *) &sas2, sizeof(struct sockaddr_stp)); + + test(__FUNCTION__, rc1 == 0 && rc2 < 0, 2); + + close(s1); close(s2); + cleanup_test(); +} + +/* + * Use bind(2) on two AF_STP sockets using same port and same interface. + * Call should fail. + */ + +static void test_two_binds_same_if_eth0(void) +{ + int s1, s2; + int rc1, rc2; + struct sockaddr_stp sas1, sas2; + const unsigned short port = 12345; + + init_test(); + + s1 = socket(AF_STP, SOCK_DGRAM, 0); + + sas1.sas_family = AF_STP; + sas1.sas_port = htons(port); + sas1.sas_ifindex = if_nametoindex("eth0"); + rc1 = bind(s1, (struct sockaddr *) &sas1, sizeof(struct sockaddr_stp)); + + s2 = socket(AF_STP, SOCK_DGRAM, 0); + + sas2.sas_family = AF_STP; + sas2.sas_port = htons(port); + sas2.sas_ifindex = if_nametoindex("eth0"); + rc2 = bind(s2, (struct sockaddr *) &sas2, sizeof(struct sockaddr_stp)); + + test(__FUNCTION__, rc1 == 0 && rc2 < 0, 2); + + close(s1); close(s2); + cleanup_test(); +} + +/* + * Use bind(2) on two AF_STP sockets using same port and "all" interface and + * "eth0". + * Call should fail. + */ + +static void test_two_binds_same_if_all_eth0(void) +{ + int s1, s2; + int rc1, rc2; + struct sockaddr_stp sas1, sas2; + const unsigned short port = 12345; + + init_test(); + + s1 = socket(AF_STP, SOCK_DGRAM, 0); + + sas1.sas_family = AF_STP; + sas1.sas_port = htons(port); + sas1.sas_ifindex = 0; + rc1 = bind(s1, (struct sockaddr *) &sas1, sizeof(struct sockaddr_stp)); + + s2 = socket(AF_STP, SOCK_DGRAM, 0); + + sas2.sas_family = AF_STP; + sas2.sas_port = htons(port); + sas2.sas_ifindex = if_nametoindex("eth0"); + rc2 = bind(s2, (struct sockaddr *) &sas2, sizeof(struct sockaddr_stp)); + + test(__FUNCTION__, rc1 == 0 && rc2 < 0, 2); + + close(s1); close(s2); + cleanup_test(); +} + +/* + * Use bind(2) on two AF_STP sockets using same port and "eth0" interface and + * "all". + * Call should fail. + */ + +static void test_two_binds_same_if_eth0_all(void) +{ + int s1, s2; + int rc1, rc2; + struct sockaddr_stp sas1, sas2; + const unsigned short port = 12345; + + init_test(); + + s1 = socket(AF_STP, SOCK_DGRAM, 0); + + sas1.sas_family = AF_STP; + sas1.sas_port = htons(port); + sas1.sas_ifindex = if_nametoindex("eth0"); + rc1 = bind(s1, (struct sockaddr *) &sas1, sizeof(struct sockaddr_stp)); + + s2 = socket(AF_STP, SOCK_DGRAM, 0); + + sas2.sas_family = AF_STP; + sas2.sas_port = htons(port); + sas2.sas_ifindex = 0; + rc2 = bind(s2, (struct sockaddr *) &sas2, sizeof(struct sockaddr_stp)); + + test(__FUNCTION__, rc1 == 0 && rc2 < 0, 2); + + close(s1); close(s2); + cleanup_test(); +} + +static ssize_t sendto_message(int sockfd, struct sockaddr_stp *sas, + char *buf, size_t len) +{ + return sendto(sockfd, buf, len, 0, (SSA *) sas, sizeof(*sas)); +} + +static ssize_t sendmsg_message(int sockfd, struct sockaddr_stp *sas, + char *buf, size_t len) +{ + struct iovec iov; + struct msghdr msg; + + iov.iov_base = buf; + iov.iov_len = len; + msg.msg_name = sas; + msg.msg_namelen = sizeof(*sas); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + return sendmsg(sockfd, &msg, 0); +} + +static ssize_t send_message(int sockfd, char *buf, size_t len) +{ + return send(sockfd, buf, len, 0); +} + +/* + * Use recvfrom(2) to receive message. We don't care what is the source + * address of the message. + */ + +static ssize_t recvfrom_message(int sockfd, char *buf, size_t len) +{ + dprintf("ready to receive using recvfrom\n"); + return recvfrom(sockfd, buf, len, 0, NULL, NULL); +} + +/* + * Use recvmsg(2) to receive message. We don't care what is the source + * address of the message. + */ + +static ssize_t recvmsg_message(int sockfd, char *buf, size_t len) +{ + struct iovec iov; + struct msghdr msg; + + iov.iov_base = buf; + iov.iov_len = len; + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + return recvmsg(sockfd, &msg, 0); +} + +/* + * Can not use recv(2) on datagram sockets. call recvfrom_message(). + */ + +static ssize_t recv_message(int sockfd, char *buf, size_t len) +{ + dprintf("ready to receive using recv\n"); + return recv(sockfd, buf, len, 0); +} + +/* + * Use sendto(2) on a socket. + */ + +static void test_sendto(void) +{ + int s; + int rc; + struct sockaddr_stp sas, remote_sas; + const unsigned short port = 12345, remote_port = 54321; + char bufout[BUFLEN] = DEFAULT_SENDER_MESSAGE; + + init_test(); + + s = socket(AF_STP, SOCK_DGRAM, 0); + + sas.sas_family = AF_STP; + sas.sas_port = htons(port); + sas.sas_ifindex = if_nametoindex("lo"); + bind(s, (struct sockaddr *) &sas, sizeof(struct sockaddr_stp)); + + remote_sas.sas_family = AF_STP; + remote_sas.sas_port = htons(remote_port); + remote_sas.sas_ifindex = 0; + memcpy(remote_sas.sas_addr, ether_aton("00:00:00:00:00:00"), + sizeof(remote_sas.sas_addr)); + + rc = sendto_message(s, &remote_sas, bufout, BUFLEN); + + test(__FUNCTION__, rc >= 0, 5); + + close(s); + cleanup_test(); +} + +/* + * Use sendmsg(2) on a socket. + */ + +static void test_sendmsg(void) +{ + int s; + int rc; + struct sockaddr_stp sas, remote_sas; + const unsigned short port = 12345, remote_port = 54321; + char bufout[BUFLEN] = DEFAULT_SENDER_MESSAGE; + + init_test(); + + s = socket(AF_STP, SOCK_DGRAM, 0); + + sas.sas_family = AF_STP; + sas.sas_port = htons(port); + sas.sas_ifindex = if_nametoindex("lo"); + bind(s, (struct sockaddr *) &sas, sizeof(struct sockaddr_stp)); + + remote_sas.sas_family = AF_STP; + remote_sas.sas_port = htons(remote_port); + remote_sas.sas_ifindex = 0; + memcpy(remote_sas.sas_addr, ether_aton("00:00:00:00:00:00"), + sizeof(remote_sas.sas_addr)); + + rc = sendmsg_message(s, &remote_sas, bufout, BUFLEN); + + test(__FUNCTION__, rc >= 0, 3); + + close(s); + cleanup_test(); +} + +/* + * Connect local socket to remote AF_STP socket. + */ + +static void test_connect(void) +{ + int s; + int rc; + struct sockaddr_stp sas, remote_sas; + const unsigned short port = 12345, remote_port = 54321; + + init_test(); + + s = socket(AF_STP, SOCK_DGRAM, 0); + + sas.sas_family = AF_STP; + sas.sas_port = htons(port); + sas.sas_ifindex = if_nametoindex("lo"); + bind(s, (struct sockaddr *) &sas, sizeof(struct sockaddr_stp)); + + remote_sas.sas_family = AF_STP; + remote_sas.sas_port = htons(remote_port); + remote_sas.sas_ifindex = 0; + memcpy(remote_sas.sas_addr, ether_aton("00:00:00:00:00:00"), + sizeof(remote_sas.sas_addr)); + + rc = connect(s, (struct sockaddr *) &remote_sas, sizeof(remote_sas)); + + test(__FUNCTION__, rc >= 0, 5); + + close(s); + cleanup_test(); +} + +/* + * Use send(2) on a connected socket. + */ + +static void test_send(void) +{ + int s; + int rc; + struct sockaddr_stp sas, remote_sas; + const unsigned short port = 12345, remote_port = 54321; + char bufout[BUFLEN] = DEFAULT_SENDER_MESSAGE; + + init_test(); + + s = socket(AF_STP, SOCK_DGRAM, 0); + + sas.sas_family = AF_STP; + sas.sas_port = htons(port); + sas.sas_ifindex = if_nametoindex("lo"); + bind(s, (struct sockaddr *) &sas, sizeof(struct sockaddr_stp)); + + remote_sas.sas_family = AF_STP; + remote_sas.sas_port = htons(remote_port); + remote_sas.sas_ifindex = 0; + memcpy(remote_sas.sas_addr, ether_aton("00:00:00:00:00:00"), + sizeof(remote_sas.sas_addr)); + + rc = connect(s, (SSA *) &remote_sas, sizeof(remote_sas)); + assert(rc == 0); + + rc = send_message(s, bufout, BUFLEN); + + test(__FUNCTION__, rc >= 0, 5); + + close(s); + cleanup_test(); +} + +/* + * Read values from STP_PROC_FULL_FILENAME. + */ + +static int stp_proc_read_values(void) +{ + char buffer[256]; + FILE *f; + + f = fopen(STP_PROC_FULL_FILENAME, "rt"); + if (f == NULL) + return -1; + + /* read column line */ + fgets(buffer, 256, f); + + /* read values line */ + fscanf(f, "%d %d %d %d %d %d", + &rx_pkts, &hdr_err, &csum_err, &no_sock, &no_buffs, &tx_pkts); + dprintf("read: %d %d %d %d %d %d\n", + rx_pkts, hdr_err, csum_err, no_sock, no_buffs, tx_pkts); + + fclose(f); + + return 0; +} + +/* + * Send packet updates RxPkts column in STP_PROC_FULL_FILENAME. + * Expected values are 1, 1. + */ + +static void test_stat_tx(void) +{ + int s; + int rc; + struct sockaddr_stp sas, remote_sas; + const unsigned short port = 12345, remote_port = 54321; + char bufout[BUFLEN] = DEFAULT_SENDER_MESSAGE; + + init_test(); + + s = socket(AF_STP, SOCK_DGRAM, 0); + + sas.sas_family = AF_STP; + sas.sas_port = htons(port); + sas.sas_ifindex = if_nametoindex("lo"); + bind(s, (struct sockaddr *) &sas, sizeof(struct sockaddr_stp)); + + remote_sas.sas_family = AF_STP; + remote_sas.sas_port = htons(remote_port); + remote_sas.sas_ifindex = 0; + memcpy(remote_sas.sas_addr, ether_aton("00:00:00:00:00:00"), + sizeof(remote_sas.sas_addr)); + + rc = connect(s, (SSA *) &remote_sas, sizeof(remote_sas)); + assert(rc == 0); + + send_message(s, bufout, BUFLEN); + + close(s); + + stp_proc_read_values(); + + test(__FUNCTION__, tx_pkts == 1, 3); + + cleanup_test(); +} + +/* + * Start sender process. + * + * action switches between sendto(2), sendmsg(2), send(2) and whether + * to do ping_pong or not. + */ + +static pid_t start_sender(enum socket_action action) +{ + pid_t pid; + int s; + struct sockaddr_stp sas, remote_sas; + const unsigned short port = 12345, remote_port = 54321; + char bufin[BUFLEN]; + char bufout[BUFLEN] = DEFAULT_SENDER_MESSAGE; + ssize_t bytes_recv = 0, bytes_sent = 0; + sem_t *sem; + + /* set bufin to 0 for testing purposes (it should be overwritten) */ + memset(bufin, 0, BUFLEN); + + pid = fork(); + DIE(pid < 0, "fork"); + + switch (pid) { + case 0: /* child process */ + break; + + default: /* parent process */ + return pid; + } + + /* only child process (sender) is running */ + + sem = sem_open(SEM_NAME_SENDER, 0); + if (sem == SEM_FAILED) + exit(EXIT_FAILURE); + + s = socket(AF_STP, SOCK_DGRAM, 0); + + sas.sas_family = AF_STP; + sas.sas_port = htons(port); + sas.sas_ifindex = if_nametoindex("lo"); + bind(s, (struct sockaddr *) &sas, sizeof(struct sockaddr_stp)); + + remote_sas.sas_family = AF_STP; + remote_sas.sas_port = htons(remote_port); + remote_sas.sas_ifindex = 0; + memcpy(remote_sas.sas_addr, ether_aton("00:00:00:00:00:00"), + sizeof(remote_sas.sas_addr)); + if (action == ACTION_SEND || action == ACTION_SEND_PING_PONG) { + int rc; + + rc = connect(s, (SSA *) &remote_sas, sizeof(remote_sas)); + assert(rc == 0); + } + + switch (action) { + case ACTION_SENDTO: + case ACTION_SENDTO_PING_PONG: + bytes_sent = sendto_message(s, &remote_sas, bufout, BUFLEN); + dprintf("sent %s\n", bufout); + break; + + case ACTION_SENDMSG: + case ACTION_SENDMSG_PING_PONG: + bytes_sent = sendmsg_message(s, &remote_sas, bufout, BUFLEN); + dprintf("sent %s\n", bufout); + break; + + case ACTION_SEND: + case ACTION_SEND_PING_PONG: + bytes_sent = send_message(s, bufout, BUFLEN); + dprintf("sent %s\n", bufout); + break; + + default: + break; + } + + switch (action) { + case ACTION_SENDTO_PING_PONG: + bytes_recv = recvfrom_message(s, bufin, BUFLEN); + dprintf("received %s\n", bufin); + break; + case ACTION_SENDMSG_PING_PONG: + bytes_recv = recvmsg_message(s, bufin, BUFLEN); + dprintf("received %s\n", bufin); + break; + case ACTION_SEND_PING_PONG: + bytes_recv = recv_message(s, bufin, BUFLEN); + dprintf("received %s\n", bufin); + break; + default: + break; + } + + /* Let the parent know we're done. */ + sem_post(sem); + + /* exit with EXIT_SUCCESS in case of successful communication */ + switch (action) { + case ACTION_SENDTO: + case ACTION_SEND: + case ACTION_SENDMSG: + if (bytes_sent > 0) + exit(EXIT_SUCCESS); + break; + + case ACTION_SENDMSG_PING_PONG: + case ACTION_SENDTO_PING_PONG: + case ACTION_SEND_PING_PONG: + dprintf("(ping_pong) bytes_sent: %d, bytes_recv: %d, strcmp: %d\n", + bytes_sent, bytes_recv, strcmp(bufin, bufout)); + dprintf("bufin: #%s#, bufout: #%s#\n", bufin, bufout); + if (bytes_sent > 0 && bytes_recv > 0 && + strcmp(bufin, DEFAULT_RECEIVER_MESSAGE) == 0) + exit(EXIT_SUCCESS); + break; + } + + exit(EXIT_FAILURE); + + /* is not reached */ + return 0; +} + +/* + * Start receiver process. + * + * action switches between sendto(2), sendmsg(2), send(2) and whether + * to do ping_pong or not. + */ + +static pid_t start_receiver(enum socket_action action) +{ + pid_t pid; + int s; + struct sockaddr_stp sas, remote_sas; + const unsigned short port = 54321, remote_port = 12345; + char bufin[BUFLEN]; + char bufout[BUFLEN] = DEFAULT_RECEIVER_MESSAGE; + ssize_t bytes_recv = 0, bytes_sent = 0; + sem_t *sem; + + /* set bufin to 0 for testing purposes (it should be overwritten) */ + memset(bufin, 0, BUFLEN); + + pid = fork(); + DIE(pid < 0, "fork"); + + switch (pid) { + case 0: /* child process */ + break; + + default: /* parent process */ + return pid; + } + + /* only child process (receiver) is running */ + + sem = sem_open(SEM_NAME_RECEIVER, 0); + if (sem == SEM_FAILED) + exit(EXIT_FAILURE); + + s = socket(AF_STP, SOCK_DGRAM, 0); + + sas.sas_family = AF_STP; + sas.sas_port = htons(port); + sas.sas_ifindex = if_nametoindex("lo"); + bind(s, (struct sockaddr *) &sas, sizeof(struct sockaddr_stp)); + + remote_sas.sas_family = AF_STP; + remote_sas.sas_port = htons(remote_port); + remote_sas.sas_ifindex = 0; + memcpy(remote_sas.sas_addr, ether_aton("00:00:00:00:00:00"), + sizeof(remote_sas.sas_addr)); + + if (action == ACTION_SEND || action == ACTION_SEND_PING_PONG) { + int rc; + + rc = connect(s, (SSA *) &remote_sas, sizeof(remote_sas)); + assert(rc == 0); + dprintf("connected\n"); + } + + /* We're set up, let the parent know. */ + sem_post(sem); + + switch (action) { + case ACTION_SENDTO: + case ACTION_SENDTO_PING_PONG: + bytes_recv = recvfrom_message(s, bufin, BUFLEN); + dprintf("received %s\n", bufin); + break; + + case ACTION_SENDMSG: + case ACTION_SENDMSG_PING_PONG: + bytes_recv = recvmsg_message(s, bufin, BUFLEN); + dprintf("received %s\n", bufin); + break; + + case ACTION_SEND: + case ACTION_SEND_PING_PONG: + bytes_recv = recv_message(s, bufin, BUFLEN); + dprintf("received %s\n", bufin); + break; + + default: + break; + } + + switch (action) { + case ACTION_SENDTO_PING_PONG: + bytes_sent = sendto_message(s, &remote_sas, bufout, BUFLEN); + dprintf("sent %s\n", bufout); + break; + case ACTION_SENDMSG_PING_PONG: + bytes_sent = sendmsg_message(s, &remote_sas, bufout, BUFLEN); + dprintf("sent %s\n", bufout); + break; + case ACTION_SEND_PING_PONG: + bytes_sent = send_message(s, bufout, BUFLEN); + dprintf("sent %s\n", bufout); + break; + default: + break; + } + + /* Let the parent know we're done. */ + sem_post(sem); + + /* exit with EXIT_SUCCESS in case of successful communication */ + switch (action) { + case ACTION_SENDTO: + case ACTION_SEND: + case ACTION_SENDMSG: + if (bytes_recv > 0) + exit(EXIT_SUCCESS); + break; + + case ACTION_SENDMSG_PING_PONG: + case ACTION_SENDTO_PING_PONG: + case ACTION_SEND_PING_PONG: + dprintf("(ping_pong) bytes_sent: %d, bytes_recv: %d\n", + bytes_sent, bytes_recv); + dprintf("bufin: #%s#, bufout: #%s#\n", bufin, bufout); + if (bytes_recv > 0 && bytes_sent > 0 && + strcmp(bufin, DEFAULT_SENDER_MESSAGE) == 0) + exit(EXIT_SUCCESS); + break; + } + + exit(EXIT_FAILURE); + + /* is not reached */ + return 0; +} + +int wait_for_semaphore(sem_t *sem, unsigned int secs) +{ + struct timespec ts; + int ret; + + ret = clock_gettime(CLOCK_REALTIME, &ts); + assert(ret == 0); + + ts.tv_sec += secs; + + ret = sem_timedwait(sem, &ts); + return ret; +} + +/* + * Wrapper call for running a sender and a receiver process. + * + * action switches between sendto(2), sendmsg(2), send(2) and whether + * to do ping_pong or not. + * + * Returns boolean value: 1 in case of successful run, 0 otherwise. + */ + +static int run_sender_receiver(enum socket_action action) +{ + pid_t pid_r = 0, pid_s = 0; + int rc1, rc2, ret; + int status1, status2; + sem_t *sem_r, *sem_s; + + /* Create two named semaphores used to communicate + * with the child processes + */ + sem_r = sem_open(SEM_NAME_RECEIVER, O_CREAT, (mode_t)0644, 0); + assert(sem_r != SEM_FAILED); + sem_s = sem_open(SEM_NAME_SENDER, O_CREAT, (mode_t)0644, 0); + assert(sem_s != SEM_FAILED); + + /* start the receiver */ + pid_r = start_receiver(action); + assert(pid_r > 0); + /* wait for it to bind */ + wait_for_semaphore(sem_r, RECV_TIMEOUT); + + /* Receiver is set up, start the sender now. */ + pid_s = start_sender(action); + assert(pid_s > 0); + + /* Wait for both to finish. */ + rc1 = wait_for_semaphore(sem_r, SENDRECV_TIMEOUT); + ret = waitpid(pid_r, &status1, rc1 ? WNOHANG : 0); + assert(ret >= 0); + kill(pid_r, SIGTERM); kill(pid_r, SIGKILL); + + rc2 = wait_for_semaphore(sem_s, SENDRECV_TIMEOUT); + ret = waitpid(pid_s, &status2, rc2 ? WNOHANG : 0); + assert(ret >= 0); + kill(pid_s, SIGTERM); kill(pid_s, SIGKILL); + + sem_close(sem_r); sem_unlink(SEM_NAME_RECEIVER); + sem_close(sem_s); sem_unlink(SEM_NAME_SENDER); + + return !rc1 && !rc2 && + WIFEXITED(status1) && WEXITSTATUS(status1) == EXIT_SUCCESS && + WIFEXITED(status2) && WEXITSTATUS(status2) == EXIT_SUCCESS; +} + +/* + * Send a datagram on one end and receive it on the other end. + * Use sendto(2) and recvfrom(2). + */ + +static void test_sendto_recvfrom(void) +{ + int rc; + + init_test(); + + rc = run_sender_receiver(ACTION_SENDTO); + + test(__FUNCTION__, rc != 0, 10); + + cleanup_test(); +} + +/* + * Send and receive packet updates RxPkts and TxPkts columns in + * STP_PROC_FULL_FILENAME. Expected values are 1, 1. + */ + +static void test_stat_tx_rx(void) +{ + init_test(); + + run_sender_receiver(ACTION_SENDTO); + + stp_proc_read_values(); + + test(__FUNCTION__, tx_pkts == 1 && rx_pkts == 1, 3); + + cleanup_test(); +} + +/* + * Send a packet and then wait for a reply. + */ + +static void test_sendto_recvfrom_ping_pong(void) +{ + int rc; + + init_test(); + + rc = run_sender_receiver(ACTION_SENDTO_PING_PONG); + + test(__FUNCTION__, rc != 0, 5); + + cleanup_test(); +} + +/* + * Send and receive ping pong updates RxPkts and TxPkts column in + * STP_PROC_FULL_FILENAME. Expected values are 2, 2. + */ + +static void test_stat_tx_rx_ping_pong(void) +{ + init_test(); + + run_sender_receiver(ACTION_SENDTO_PING_PONG); + + stp_proc_read_values(); + stp_proc_read_values(); + + test(__FUNCTION__, tx_pkts == 2 && rx_pkts == 2, 3); + + cleanup_test(); +} + +/* + * Send a datagram on one end and receive it on the other end. + * Use sendmsg(2) and recvmsg(2). + */ + +static void test_sendmsg_recvmsg(void) +{ + int rc; + + init_test(); + + rc = run_sender_receiver(ACTION_SENDMSG); + + test(__FUNCTION__, rc != 0, 5); + + cleanup_test(); +} + +/* + * Send a packet and then wait for a reply. + */ + +static void test_sendmsg_recvmsg_ping_pong(void) +{ + int rc; + + init_test(); + + rc = run_sender_receiver(ACTION_SENDMSG_PING_PONG); + + test(__FUNCTION__, rc != 0, 3); + + cleanup_test(); +} + +/* + * Send a packet on one end and receive it on the other end. + * Use send(2) and recv(2). + */ + +static void test_send_receive(void) +{ + int rc; + + init_test(); + + rc = run_sender_receiver(ACTION_SEND); + + test(__FUNCTION__, rc != 0, 5); + + cleanup_test(); +} + +/* + * Send a packet and then wait for a reply. + */ + +static void test_send_receive_ping_pong(void) +{ + int rc; + + init_test(); + + rc = run_sender_receiver(ACTION_SEND_PING_PONG); + + test(__FUNCTION__, rc != 0, 3); + + cleanup_test(); +} + +static void (*test_fun_array[])(void) = { + NULL, + test_insmod_rmmod, + test_proto_name_exists_after_insmod, + test_proto_name_inexistent_after_rmmod, + test_proc_entry_exists_after_insmod, + test_proc_entry_inexistent_after_rmmod, + test_socket, + test_two_sockets, + test_socket_bad_socket_type, + test_socket_bad_protocol, + test_close, + test_close_closed_socket, + test_bind, + test_bind_eth0, + test_two_binds, + test_bind_bad_address, + test_two_binds_same_if, + test_two_binds_same_if_eth0, + test_two_binds_same_if_all_eth0, + test_two_binds_same_if_eth0_all, + test_sendto, + test_sendmsg, + test_connect, + test_send, + test_stat_tx, + test_sendto_recvfrom, + test_stat_tx_rx, + test_sendto_recvfrom_ping_pong, + test_stat_tx_rx_ping_pong, + test_sendmsg_recvmsg, + test_sendmsg_recvmsg_ping_pong, + test_send_receive, + test_send_receive_ping_pong, +}; + +/* + * Usage message for invalid executable call. + */ + +static void usage(const char *argv0) +{ + fprintf(stderr, "Usage: %s test_no\n\n", argv0); + exit(EXIT_FAILURE); +} + +int main(int argc, char **argv) +{ + int test_idx; + + if (argc != 2) + usage(argv[0]); + + test_idx = atoi(argv[1]); + + if (test_idx < 1 || + test_idx >= sizeof(test_fun_array)/sizeof(test_fun_array[0])) { + fprintf(stderr, "Error: test index %d is out of bounds\n", + test_idx); + exit(EXIT_FAILURE); + } + + srand(time(NULL)); + srand48(time(NULL)); + test_fun_array[test_idx](); + + return 0; +} diff --git a/checker/4-stp-checker/_test/stp_test.h b/checker/4-stp-checker/_test/stp_test.h new file mode 100644 index 0000000000000000000000000000000000000000..fb708433c0269f38454ccb3d6903e2c8c410468a --- /dev/null +++ b/checker/4-stp-checker/_test/stp_test.h @@ -0,0 +1,31 @@ +/* + * SO2 Transport Protocol - test suite specific header + */ + +#ifndef STP_TEST_H_ +#define STP_TEST_H_ 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* STP test suite macros and structures */ +#define MODULE_NAME "af_stp" +#define MODULE_FILENAME MODULE_NAME ".ko" + +#define SEM_NAME_RECEIVER "/receiver_sem" +#define SEM_NAME_SENDER "/sender_sem" + +/* timeouts waiting for receiver/sender */ +#define RECV_TIMEOUT 1 +#define SENDRECV_TIMEOUT 3 + +/* messages used for "ping-pong" between sender and receiver */ +#define DEFAULT_SENDER_MESSAGE "You called down the thunder" +#define DEFAULT_RECEIVER_MESSAGE "now reap the whirlwind" + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/checker/4-stp-checker/_test/test.h b/checker/4-stp-checker/_test/test.h new file mode 100644 index 0000000000000000000000000000000000000000..4bcafad9c7d0f5b241a0241071301954c3bf9601 --- /dev/null +++ b/checker/4-stp-checker/_test/test.h @@ -0,0 +1,63 @@ +/* + * generic test suite + * + * test macros and headers + */ + +#ifndef TEST_H_ +#define TEST_H_ 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdio.h> + +/* to be defined by calling program */ +extern int max_points; + +/* + * uncommend EXIT_IF_FAIL macro in order to stop test execution + * at first failed test + */ + +/*#define EXIT_IF_FAIL 1*/ + +#if defined(EXIT_IF_FAIL) +#define test_do_fail(points) \ + do { \ + printf("failed\n"); \ + exit(EXIT_FAILURE); \ + } while (0) +#else +#define test_do_fail(points) \ + printf("failed [ 0/%3d]\n", max_points) +#endif + +#define test_do_pass(points) \ + printf("passed [%3d/%3d]\n", points, max_points) + +#define test(message, test, points) \ + do { \ + size_t i; \ + int t = (test); \ + \ + printf("%s", message); \ + fflush(stdout); \ + \ + for (i = 0; i < 60 - strlen(message); i++) \ + putchar('.'); \ + \ + if (!t) \ + test_do_fail(points); \ + else \ + test_do_pass(points); \ + \ + fflush(stdout); \ + } while (0) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/checker/4-stp-checker/_test/util.h b/checker/4-stp-checker/_test/util.h new file mode 100644 index 0000000000000000000000000000000000000000..f06cb833b99635e27c494498fd6b981e50564dbc --- /dev/null +++ b/checker/4-stp-checker/_test/util.h @@ -0,0 +1,69 @@ +/* + * useful structures/macros + */ + +#ifndef UTIL_H_ +#define UTIL_H_ 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdio.h> +#include <stdlib.h> + +#if defined(_WIN32) + +#include <windows.h> + +static VOID PrintLastError(const PCHAR message) +{ + CHAR errBuff[1024]; + + FormatMessage( + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_MAX_WIDTH_MASK, + NULL, + GetLastError(), + 0, + errBuff, + sizeof(errBuff) - 1, + NULL); + + fprintf(stderr, "%s: %s\n", message, errBuff); +} + +#define ERR(call_description) \ + do { \ + fprintf(stderr, "(%s, %d): ", \ + __FILE__, __LINE__); \ + PrintLastError(call_description); \ + } while (0) + +#elif defined(__linux__) + +/* error printing macro */ +#define ERR(call_description) \ + do { \ + fprintf(stderr, "(%s, %d): ", \ + __FILE__, __LINE__); \ + perror(call_description); \ + } while (0) + +#else + #error "Unknown platform" +#endif + +/* print error (call ERR) and exit */ +#define DIE(assertion, call_description) \ + do { \ + if (assertion) { \ + ERR(call_description); \ + exit(EXIT_FAILURE); \ + } \ + } while (0) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/checker/checker.sh b/checker/checker.sh index 8c1cbb4ff602a247f3f3125d24ad8ae3fb424438..e461e54e6efef9aa1b426a88513fe0dcec5588da 100755 --- a/checker/checker.sh +++ b/checker/checker.sh @@ -1,7 +1,8 @@ #!/bin/bash SO2_WORKSPACE=/linux/tools/labs - +SO2_VM_LOG=/tmp/so2_vm_log.txt +DMESG_LOG="" ASSIGNMENT0_TIMEOUT=300 # 5 min ASSIGNMENT0_MOD=list.ko @@ -41,6 +42,16 @@ ASSIGNMENT3_FINISHED=${SO2_WORKSPACE}/skels/3-raid-finished ASSIGNMENT3_HEADER_OVERWRITE=${SO2_WORKSPACE}/templates/assignments/3-raid/ssr.h ASSIGNMENT3_CHECKER_AUX_LIST="${ASSIGNMENT3_CHECKER_DIR}/_test/run-test" +ASSIGNMENT4_TIMEOUT=300 # 5 min +ASSIGNMENT4_MOD=af_stp.ko +ASSIGNMENT4_DIR=${SO2_WORKSPACE}/skels/assignments/4-stp +ASSIGNMENT4_CHECKER_LOCAL_DIR=checker/4-stp-checker +ASSIGNMENT4_CHECKER_DIR=${SO2_WORKSPACE}/skels/assignments/4-stp-checker +ASSIGNMENT4_OUTPUT=${SO2_WORKSPACE}/skels/4-stp-output +ASSIGNMENT4_FINISHED=${SO2_WORKSPACE}/skels/4-stp-finished +ASSIGNMENT4_HEADER_OVERWRITE=${SO2_WORKSPACE}/templates/assignments/4-stp/stp.h +#ASSIGNMENT4_CHECKER_AUX_LIST="${ASSIGNMENT3_CHECKER_DIR}/_test/run-test" + usage() { @@ -49,6 +60,13 @@ usage() } +configure_logger() +{ + DMESG_LOG="/linux/tools/labs/skels/log.txt" + cp ./checker/checker_daemons/so2_vm_checker_logger.sh /linux/tools/labs/rootfs/etc/init.d + chmod +x /linux/tools/labs/rootfs/etc/init.d/so2_vm_checker_logger.sh + chroot /linux/tools/labs/rootfs update-rc.d so2_vm_checker_logger.sh defaults 0 0 +} recover_grade_from_timeout() { @@ -66,18 +84,32 @@ recover_grade_from_timeout() fi } +dmesg_log_dump() +{ + if [[ $DMESG_LOG != "" ]]; then + echo "dumping DMESG_LOG=${DMESG_LOG} output" + echo ">>>>---------------DMESG_LOG_STARTS_HERE------------------<<<<<" + cat $DMESG_LOG + echo ">>>>----------------DMESG_LOG_ENDS_HERE-------------------<<<<<" + fi +} + timeout_exceeded() { local output=$1 pkill -SIGKILL qemu echo "" echo "TIMEOUT EXCEEDED !!! killing the process" + + dmesg_log_dump + if [[ $RECOVER_GRADE_TIMEOUT == 0 ]]; then if [ -f $output ]; then echo "$output not available" else cat $output fi + echo "The Recover Grade Timeout option is not set! Please contact a teaching assistant!" else recover_grade_from_timeout $output @@ -195,7 +227,7 @@ run_checker() done fi - LINUX_ADD_CMDLINE="so2=$assignment" make checker &> /dev/null & + LINUX_ADD_CMDLINE="so2=$assignment" make checker &> ${SO2_VM_LOG} & timeout=0 echo -n "CHECKER IS RUNNING" @@ -204,7 +236,7 @@ run_checker() if ((timeout >= TIMEOUT)); then if [ -f $output ]; then echo "" - dump_output $output + dump_output $output $timeout compute_total $output fi timeout_exceeded $output @@ -219,6 +251,8 @@ run_checker() popd &> /dev/null } + + case $1 in 0-list) TIMEOUT=$ASSIGNMENT0_TIMEOUT @@ -233,6 +267,7 @@ case $1 in 2-uart) TIMEOUT=$ASSIGNMENT2_TIMEOUT RECOVER_GRADE_TIMEOUT=1 # If set to 1, in case of a timeout, will calculate the total grade based on the output directory + configure_logger run_checker $ASSIGNMENT2_MOD $ASSIGNMENT2_DIR $ASSIGNMENT2_CHECKER_LOCAL_DIR $ASSIGNMENT2_CHECKER_DIR $ASSIGNMENT2_OUTPUT $ASSIGNMENT2_FINISHED $1 $ASSIGNMENT2_HEADER_OVERWRITE $ASSIGNMENT2_CHECKER_AUX_LIST ;; 3-raid) @@ -240,6 +275,12 @@ case $1 in RECOVER_GRADE_TIMEOUT=0 # If set to 1, in case of a timeout, will calculate the total grade based on the output directory run_checker $ASSIGNMENT3_MOD $ASSIGNMENT3_DIR $ASSIGNMENT3_CHECKER_LOCAL_DIR $ASSIGNMENT3_CHECKER_DIR $ASSIGNMENT3_OUTPUT $ASSIGNMENT3_FINISHED $1 $ASSIGNMENT3_HEADER_OVERWRITE $ASSIGNMENT3_CHECKER_AUX_LIST ;; + 4-stp) + TIMEOUT=$ASSIGNMENT4_TIMEOUT + RECOVER_GRADE_TIMEOUT=0 # If set to 1, in case of a timeout, will calculate the total grade based on the output file + run_checker $ASSIGNMENT4_MOD $ASSIGNMENT4_DIR $ASSIGNMENT4_CHECKER_LOCAL_DIR $ASSIGNMENT4_CHECKER_DIR $ASSIGNMENT4_OUTPUT $ASSIGNMENT4_FINISHED $1 $ASSIGNMENT4_HEADER_OVERWRITE + ;; + *) usage ;; diff --git a/checker/checker_daemons/so2_vm_checker_daemon.sh b/checker/checker_daemons/so2_vm_checker_daemon.sh index d7e6b18c0b2a650c08f7a65c7b8e2520565d488a..546f236afdea29a8851f21106f3b317387856ee9 100644 --- a/checker/checker_daemons/so2_vm_checker_daemon.sh +++ b/checker/checker_daemons/so2_vm_checker_daemon.sh @@ -18,37 +18,49 @@ ASSIGNMENT3_CHECKER=/home/root/skels/assignments/3-raid-checker ASSIGNMENT3_OUTPUT=/home/root/skels/3-raid-output ASSIGNMENT3_FINISHED=/home/root/skels/3-raid-finished +ASSIGNMENT4_CHECKER=/home/root/skels/assignments/4-stp-checker +ASSIGNMENT4_OUTPUT=/home/root/skels/4-stp-output +ASSIGNMENT4_FINISHED=/home/root/skels/4-stp-finished + assign0_list() { - cd $ASSIGNMENT0_CHECKER - sh _checker &> $ASSIGNMENT0_OUTPUT - echo FINISHED &> $ASSIGNMENT0_FINISHED - cd - + cd $ASSIGNMENT0_CHECKER + sh _checker &> $ASSIGNMENT0_OUTPUT + echo FINISHED &> $ASSIGNMENT0_FINISHED + cd - } assign1_tracer() { - cd $ASSIGNMENT1_CHECKER - sh _checker &> $ASSIGNMENT1_OUTPUT - echo FINISHED &> $ASSIGNMENT1_FINISHED - cd - + cd $ASSIGNMENT1_CHECKER + sh _checker &> $ASSIGNMENT1_OUTPUT + echo FINISHED &> $ASSIGNMENT1_FINISHED + cd - } assign2_uart() { - cd $ASSIGNMENT2_CHECKER - sh _checker &> $ASSIGNMENT2_OUTPUT - echo FINISHED &> $ASSIGNMENT2_FINISHED - cd - + cd $ASSIGNMENT2_CHECKER + sh _checker &> $ASSIGNMENT2_OUTPUT + echo FINISHED &> $ASSIGNMENT2_FINISHED + cd - } assign3_raid() { - cd $ASSIGNMENT3_CHECKER - sh _checker &> $ASSIGNMENT3_OUTPUT - echo FINISHED &> $ASSIGNMENT3_FINISHED - cd - + cd $ASSIGNMENT3_CHECKER + sh _checker &> $ASSIGNMENT3_OUTPUT + echo FINISHED &> $ASSIGNMENT3_FINISHED + cd - +} + +assign4_stp() +{ + cd $ASSIGNMENT4_CHECKER + sh _checker &> $ASSIGNMENT4_OUTPUT + echo FINISHED &> $ASSIGNMENT4_FINISHED + cd - } start() @@ -67,6 +79,9 @@ start() 3-raid) assign3_raid ;; + 4-stp) + assign4_stp + ;; *) echo "Unknown option" exit 0 diff --git a/checker/checker_daemons/so2_vm_checker_logger.sh b/checker/checker_daemons/so2_vm_checker_logger.sh new file mode 100644 index 0000000000000000000000000000000000000000..fe61fb61e4ac9e42e3f99ef9ced25f0f8312a3b8 --- /dev/null +++ b/checker/checker_daemons/so2_vm_checker_logger.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +# THIS SCRIPT RUNS INSIDE THE SO2 VM + +LOG_FILE=/home/root/skels/log.txt + +start() +{ + set -x + echo "" > ${LOG_FILE} + while true + do + sleep 1 + dmesg -c >> $LOG_FILE + done +} + +# Carry out specific functions when asked to by the system +case "$1" in + start) + echo "Starting so2_vm_checker_logger.sh..." + start & # start in background + ;; + *) + echo "Usage: /etc/init.d/foo {start|stop}" + exit 1 + ;; + esac + +exit 0 +