From e1a7a5b1fad240f4c371d8264ab1d98f7d1c3fc3 Mon Sep 17 00:00:00 2001 From: Dragos Iulian ARGINT <dragos.argint@stud.acs.upb.ro> Date: Mon, 25 Mar 2024 23:10:40 +0200 Subject: [PATCH] Update everything to the latest working version * Update scripts to the ones in 0-list * Update Dockerfile * Update Docker Image Signed-off-by: Dragos Iulian ARGINT <dragos.argint@stud.acs.upb.ro> --- Dockerfile | 2 - checker/2-uart-checker/Makefile | 14 + checker/2-uart-checker/README | 38 + checker/2-uart-checker/_checker | 4 + checker/2-uart-checker/_test/solution.ko | Bin 0 -> 228092 bytes checker/2-uart-checker/_test/test.c | 593 ++++++ checker/2-uart-checker/_test/uart16550.h | 46 + checker/3-raid-checker/Makefile | 14 + checker/3-raid-checker/README | 40 + checker/3-raid-checker/_checker | 4 + checker/3-raid-checker/_test/Makefile | 15 + checker/3-raid-checker/_test/run-test.c | 106 + checker/3-raid-checker/_test/run-test.h | 25 + checker/3-raid-checker/_test/ssr.h | 26 + checker/3-raid-checker/_test/test.c | 1769 +++++++++++++++++ checker/4-stp-checker/Makefile | 17 + checker/4-stp-checker/README | 87 + checker/4-stp-checker/_checker | 24 + checker/4-stp-checker/_test/Makefile | 11 + checker/4-stp-checker/_test/debug.h | 77 + checker/4-stp-checker/_test/stp.h | 51 + checker/4-stp-checker/_test/stp_test.c | 1331 +++++++++++++ checker/4-stp-checker/_test/stp_test.h | 31 + checker/4-stp-checker/_test/test.h | 63 + checker/4-stp-checker/_test/util.h | 69 + checker/checker.sh | 133 +- .../checker_daemons/so2_vm_checker_daemon.sh | 61 +- local.sh | 6 +- 28 files changed, 4625 insertions(+), 32 deletions(-) create mode 100644 checker/2-uart-checker/Makefile create mode 100644 checker/2-uart-checker/README create mode 100755 checker/2-uart-checker/_checker create mode 100644 checker/2-uart-checker/_test/solution.ko create mode 100644 checker/2-uart-checker/_test/test.c create mode 100644 checker/2-uart-checker/_test/uart16550.h create mode 100644 checker/3-raid-checker/Makefile create mode 100644 checker/3-raid-checker/README create mode 100755 checker/3-raid-checker/_checker create mode 100644 checker/3-raid-checker/_test/Makefile create mode 100644 checker/3-raid-checker/_test/run-test.c create mode 100644 checker/3-raid-checker/_test/run-test.h create mode 100644 checker/3-raid-checker/_test/ssr.h create mode 100644 checker/3-raid-checker/_test/test.c create mode 100644 checker/4-stp-checker/Makefile create mode 100644 checker/4-stp-checker/README create mode 100755 checker/4-stp-checker/_checker create mode 100644 checker/4-stp-checker/_test/Makefile create mode 100644 checker/4-stp-checker/_test/debug.h create mode 100644 checker/4-stp-checker/_test/stp.h create mode 100644 checker/4-stp-checker/_test/stp_test.c create mode 100644 checker/4-stp-checker/_test/stp_test.h create mode 100644 checker/4-stp-checker/_test/test.h create mode 100644 checker/4-stp-checker/_test/util.h diff --git a/Dockerfile b/Dockerfile index ef39da2..e368339 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,8 +4,6 @@ RUN echo "Hello from Docker" RUN mkdir -p /linux/tools/labs/skels/assignments/1-tracer RUN mkdir -p /linux/tools/labs/skels/assignments/1-tracer-checker -COPY ./checker/1-tracer-checker /linux/tools/labs/skels/assignments/1-tracer-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/2-uart-checker/Makefile b/checker/2-uart-checker/Makefile new file mode 100644 index 0000000..9cb87cb --- /dev/null +++ b/checker/2-uart-checker/Makefile @@ -0,0 +1,14 @@ +CFLAGS = -Wall -g -static -m32 + +.PHONY: all run clean + +all: test solution.ko + +test: _test/test.o + $(CC) $(CFLAGS) -o $@ $^ + +solution.ko: _test/solution.ko + ln -s $< $@ + +clean: + -rm -f *~ test _test/test.o solution.ko diff --git a/checker/2-uart-checker/README b/checker/2-uart-checker/README new file mode 100644 index 0000000..67ae723 --- /dev/null +++ b/checker/2-uart-checker/README @@ -0,0 +1,38 @@ += UART16550 TEST SUITE == + +Test suite for UART16550 + +== FILES == + +README + * this file + +Makefile + * Makefile to build the test suite executable + +_checker + * script to run all tests defined in _test/test.c + +_test/test.c + * test suite for UART16550 + +_test/solution.ko + * kernel module implementing UART16550, + used to transmit/receive data to/from your kernel module + +== BUILDING == + +Use the Makefile to properly build the test executable: + + make + +== RUNNING == + +Copy your uart16550.ko module and _checker, test and solution.ko +to fsimg/root directory on your QEMU/KVM virtual machine. + +In order to run the test suite you can use the _checker script. + +The _checker script runs all tests and computes assignment grade: + + ./_checker diff --git a/checker/2-uart-checker/_checker b/checker/2-uart-checker/_checker new file mode 100755 index 0000000..a118d02 --- /dev/null +++ b/checker/2-uart-checker/_checker @@ -0,0 +1,4 @@ +#!/bin/sh + +insmod uart16550.ko; cat /proc/modules > /dev/kmsg; rmmod uart16550 +./test diff --git a/checker/2-uart-checker/_test/solution.ko b/checker/2-uart-checker/_test/solution.ko new file mode 100644 index 0000000000000000000000000000000000000000..996dd21ac572b8fd91bc14645e9bf6e57f4093d8 GIT binary patch literal 228092 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%0ATh=WZM^l7lL~%i!DcBr#?p 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 literal 0 HcmV?d00001 diff --git a/checker/2-uart-checker/_test/test.c b/checker/2-uart-checker/_test/test.c new file mode 100644 index 0000000..6e87f70 --- /dev/null +++ b/checker/2-uart-checker/_test/test.c @@ -0,0 +1,593 @@ +#include <errno.h> +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <stdarg.h> +#include <time.h> +#include <string.h> +#include <assert.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/wait.h> +#include <signal.h> + +#include "uart16550.h" + +#define UART16550_MAJOR 42 +#define COM1_MAJOR 42 +#define COM2_MAJOR 42 +#define STR(x) #x +#define XSTR(x) STR(x) +#define OPTION_COM1_ONLY 1 +#define OPTION_COM2_ONLY 2 +#define OPTION_BOTH 3 + +#define MODULE_NAME "uart16550" +#define SOLUTION_NAME "solution" + +#define PAD_CHARS 60 + +#define UART0 "/dev/uart0" +#define UART1 "/dev/uart1" +#define UART10 "/dev/uart10" + +#define INFILE "testfile.in" +#define OUTFILE "testfile.out" + +#define fail(s) do { \ + printf("%s:%d: ", __func__, __LINE__); \ + fflush(stdout); \ + perror(s); \ + exit(EXIT_FAILURE); \ + } while (0) + + +#define test(d, v, e, p) do_test((d), (v), (e), 0, 0, (p)) +#define not_test(d, v, e, p) do_test((d), (v), (e), 1, 0, (p)) +#define fatal_test(d, v, e,p) do_test((d), (v), (e), 0, 1, (p)) + +#define GENERIC_TEST_TIMEOUT 3 +const int total = 92; + +void sig_handler(int signum) { + fprintf(stderr, "Child process pid=%d of checker (that issues read/write syscalls to the driver) got killed after TIMEOUT=%ds\n", getpid(), GENERIC_TEST_TIMEOUT); + fprintf(stderr, "\tThis might be because you didn't implement read/write or there is a bug in the implementation\n"); + exit(EXIT_FAILURE); +} + +/* + * if the test passes it will return 0 + * if it fails it returns the number of points given as argument + */ +static float +do_test(const char *description, int value, int expected, int negate, int fatal, float points) +{ + int num_chars; + + num_chars = printf("%s", description); + for (; num_chars < PAD_CHARS - strlen("passed"); num_chars++) + putchar('.'); + fflush(stdout); + if (!negate) { + if (value == expected) { + printf("passed [%.1f/%d]\n", points, total); + fflush(stdout); + return 0; + } else { + printf("failed [0/%d]\n", total); + fflush(stdout); + if (fatal) + exit(EXIT_FAILURE); + } + } else { + if (value != expected) { + printf("passed [%.1f/%d]\n", points, total); + fflush(stdout); + return 0; + } else { + printf("failed [0/%d]\n", total); + fflush(stdout); + if (fatal) + exit(EXIT_FAILURE); + } + } + return points; +} + +static void +test_title(const char *title) +{ + int len = strlen(title); + int pad = (PAD_CHARS - len) / 2 - 1; + int mod = (PAD_CHARS - len) % 2; + int i; + + assert(pad >= 1); + putchar('\n'); + for (i = 0; i < pad; i++) + putchar('='); + printf(" %s ", title); + for (i = 0; i < pad + mod; i++) + putchar('='); + putchar('\n'); +} + +static void +make_nodes(void) +{ + mknod(UART0, S_IFCHR, COM1_MAJOR<<8); + mknod(UART1, S_IFCHR, (COM2_MAJOR<<8) + 1); + mknod(UART10, S_IFCHR, (UART16550_MAJOR<<8)+10); +} + +static void +remove_nodes(void) +{ + unlink(UART0); + unlink(UART1); + unlink(UART10); +} + +static float +test1(void) +{ + float total = 16; + + test_title("Test 1. Module insertion and removal"); + + /* Insert module with default params and test. */ + total -= fatal_test("insmod " MODULE_NAME ", default options", + system("insmod " MODULE_NAME ".ko"), 0, 1); + total -= test("major", + system("cat /proc/devices | grep '" XSTR(COM1_MAJOR) " " MODULE_NAME "' >/dev/null 2>&1"), + 0, 1); + total -= test("ioports COM1", + system("cat /proc/ioports | grep '03f8-03ff : " MODULE_NAME "' > /dev/null 2>&1"), + 0, 1); + total -= test("ioports COM2", + system("cat /proc/ioports | grep '02f8-02ff : " MODULE_NAME "' > /dev/null 2>&1"), + 0, 1); + total -= test("interrupts COM1", + system("cat /proc/interrupts | grep '4:.*" MODULE_NAME "' > /dev/null 2>&1"), + 0, 1); + total -= test("interrupts COM2", + system("cat /proc/interrupts | grep '3:.*" MODULE_NAME "' > /dev/null 2>&1"), + 0, 1); + total -= test("rmmod", system("rmmod " MODULE_NAME), 0, 0.5); + + /* Insert module with different major. */ + total -= fatal_test("insmod " MODULE_NAME ", major=" XSTR(COM2_MAJOR), + system("insmod " MODULE_NAME ".ko major=" XSTR(COM2_MAJOR)), 0, 1); + total -= test("major", + system("cat /proc/devices | grep '" XSTR(COM2_MAJOR) " " MODULE_NAME "' >/dev/null 2>&1"), + 0, 1); + total -= test("rmmod", system("rmmod " MODULE_NAME), 0, 0.5); + + /* Insert module only for COM2, check that it works side by side + * with solution. + */ + total -= fatal_test("insmod " MODULE_NAME ", COM2 only", + system("insmod " MODULE_NAME ".ko option=" XSTR(OPTION_COM2_ONLY)), + 0, 1); + total -= fatal_test("insmod " SOLUTION_NAME ", COM1 only", + system("insmod " SOLUTION_NAME ".ko option=" XSTR(OPTION_COM1_ONLY)), + 0, 1); + total -= test("ioports COM1", + system("cat /proc/ioports | grep '03f8-03ff : " SOLUTION_NAME "' > /dev/null 2>&1"), + 0, 1); + total -= test("ioports COM2", + system("cat /proc/ioports | grep '02f8-02ff : " MODULE_NAME "' > /dev/null 2>&1"), + 0, 1); + total -= test("interrupts COM1", + system("cat /proc/interrupts | grep '4:.*" SOLUTION_NAME "' > /dev/null 2>&1"), + 0, 1); + total -= test("interrupts COM2", + system("cat /proc/interrupts | grep '3:.*" MODULE_NAME "' > /dev/null 2>&1"), + 0, 1); + total -= test("rmmod " MODULE_NAME, system("rmmod " MODULE_NAME), 0, 0.5); + total -= test("rmmod " SOLUTION_NAME, system("rmmod " SOLUTION_NAME), 0, 0.5); + + return total; +} + +static float +test2(void) +{ + float total = 5.5; + int fd; + + test_title("Test 2. Invalid parameters"); + + /* Check ioctl sanity. */ + total -= fatal_test("insmod", system("insmod " MODULE_NAME ".ko"), 0, 1); + fd = open(UART0, O_RDWR); + if (fd == -1) + fail("open " UART0); +#define ioctl_test(n) test("invalid ioctl " XSTR((n)), \ + ioctl(fd, UART16550_IOCTL_SET_LINE, (n)), -1, 1) + total -= ioctl_test(0xdeadbeef); + total -= ioctl_test(0x1337cafe); +#undef ioctl_test + total -= test("invalid ioctl wrong operation", ioctl(fd, 0xffff), -1, 1); + close(fd); + total -= test("rmmod", system("rmmod " MODULE_NAME), 0, 0.5); + + /* Check invalid module parameters. */ + total -= not_test("insmod " MODULE_NAME ", option=0xdeadbabe", + system("insmod " MODULE_NAME ".ko option=0xdeadbabe"), + 0, 1); + + return total; +} + +/* Speed sets: + * 0 -> 1200, 2400, 4800 + * 1 -> 9600, 19200, 38400, 56000 + * 2 -> 115200 + */ +static const struct { + int num; + unsigned char speed[4]; + int bufsizes[2]; /* min and max */ +} speed_sets[3] = { + { + .num = 3, + .speed = { UART16550_BAUD_1200, + UART16550_BAUD_2400, + UART16550_BAUD_4800, -1 }, + .bufsizes = { 128, 256 }, + }, + { + .num = 4, + .speed = { UART16550_BAUD_9600, + UART16550_BAUD_19200, + UART16550_BAUD_38400, + UART16550_BAUD_56000 }, + .bufsizes = { 256, 1024 }, + }, + { + .num = 1, + .speed = { UART16550_BAUD_115200, -1, -1, -1 }, + .bufsizes = { 2048, 2048 }, + }, +}; + +static void +gen_params(struct uart16550_line_info *line, int speed_set) +{ + int r; + + line->baud = speed_sets[speed_set].speed[rand() % + speed_sets[speed_set].num]; + line->len = UART16550_LEN_8; + line->stop = rand() % 2 * 4; + r = rand() % 4; + line->par = r < 2 ? r*8 : 0x18 + (r-2) * 8; +} + +int do_read(int fd, unsigned char *buffer, int size) +{ + int n, from = 0; + + while (1) { + n = read(fd, &buffer[from], size - from); + if (n <= 0) + return -1; + if (n + from == size) + return 0; + from += n; + } +} + +int do_write(int fd, unsigned char *buffer, unsigned int size) +{ + int n, from = 0; + + while (1) { + n = write(fd, &buffer[from], size - from); + if (n <= 0) { + perror("write"); + return -1; + } + if (n + from == size) + return 0; + from += n; + } +} + +static int +gen_test_file(char *fname, int speed_set) +{ + int size, min, max; + char comm[1024]; + + min = speed_sets[speed_set].bufsizes[0]; + max = speed_sets[speed_set].bufsizes[1]; + size = (min == max) ? min : rand() % (min - max) + min; + sprintf(comm, + "dd if=/dev/urandom of=%s bs=1 count=%d >/dev/null 2>/dev/null", + fname, + size); + if (system(comm)) + fprintf(stderr, "failed to generate random file (%s)\n", comm); + return size; +} + +static void +copy_file(int fdr, int fdw, int len) +{ +#define COPY_BUF_SIZE 128 + unsigned char buf[COPY_BUF_SIZE]; + + do { + int partial, rc; + + partial = len < COPY_BUF_SIZE ? len : COPY_BUF_SIZE; + if (partial == 0) + break; + rc = read(fdr, buf, partial); + if (rc == 0) + break; + if (rc == -1) + fail("read"); + len -= rc; + rc = do_write(fdw, buf, rc); + if (rc < 0) + fail("write"); + } while (1); +} + +static int +copy_test(int fd0, int fd1, int speed_set) +{ + pid_t rpid, wpid, kpid; + int len, status, fd; + int rc1, rc2, rc3, exit_status1, exit_status2, exit_status3; + int i; + + len = gen_test_file(INFILE, speed_set); + rpid = fork(); + switch (rpid) { + case 0: + fd = open(INFILE, O_RDONLY); + if (fd < 0) + fail("open " INFILE); + copy_file(fd, fd0, len); + close(fd); + exit(EXIT_SUCCESS); + break; + default: + break; + } + + wpid = fork(); + switch (wpid) { + case 0: + fd = open(OUTFILE, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd < 0) + fail("open " OUTFILE); + copy_file(fd1, fd, len); + close(fd); + exit(EXIT_SUCCESS); + break; + default: + break; + } + + kpid = fork(); + switch (kpid) { + case 0: + for (i = 0; i < GENERIC_TEST_TIMEOUT; i++) { + /* + * check if procs still exist. kill with arg 0 + * will succed (ret 0) if the pid exists + */ + if (!kill(rpid, 0)) { + sleep(1); + continue; + } else if (!kill(wpid, 0)) { + sleep(1); + continue; + } else + break; + + } + kill(rpid, SIGTERM); + kill(wpid, SIGTERM); + exit(EXIT_SUCCESS); + break; + default: + break; + + } + + rc1 = waitpid(rpid, &status, 0); + exit_status1 = WEXITSTATUS(status); + + + rc2 = waitpid(wpid, &status, 0); + exit_status2 = WEXITSTATUS(status); + + rc3 = waitpid(kpid, &status, 0); + exit_status3 = WEXITSTATUS(status); + + if (rc1 < 0 || rc2 < 0 || rc3 < 0 || + exit_status1 || exit_status2 || exit_status3) + return -1; + + return system("diff " INFILE " " OUTFILE "> /dev/null 2> /dev/null"); +} + +static float +generic_test(const char *reader, const char *writer, int speed_set, + int num_tests) +{ + int fd0, fd1, i; + float total = num_tests * 1.5 + (reader != writer ? 6 : 4) * 0.5; + char dbuf[1024], cbuf[1024]; + struct uart16550_line_info uli; + + if (reader != writer) { + sprintf(dbuf, "insmod %s", reader); + sprintf(cbuf, "insmod %s.ko option=%d", + reader, OPTION_COM2_ONLY); + total -= fatal_test(dbuf, system(cbuf), 0, 0.5); + sprintf(dbuf, "insmod %s", writer); + sprintf(cbuf, "insmod %s.ko option=%d", + writer, OPTION_COM1_ONLY); + total -= fatal_test(dbuf, system(cbuf), 0, 0.5); + } else { + sprintf(dbuf, "insmod %s", reader); + sprintf(cbuf, "insmod %s.ko", reader); + total -= fatal_test(dbuf, system(cbuf), 0, 0.5); + } + + gen_params(&uli, speed_set); + fd0 = open(UART0, O_WRONLY); + if (fd0 == -1) + fail("open " UART0); + fd1 = open(UART1, O_RDONLY); + if (fd1 == -1) + fail("open " UART1); + total -= test("ioctl reader", + ioctl(fd1, UART16550_IOCTL_SET_LINE, &uli), 0, 0.5); + total -= test("ioctl writer", + ioctl(fd0, UART16550_IOCTL_SET_LINE, &uli), 0, 0.5); + + for (i = 0; i < num_tests; i++) { + sprintf(dbuf, "test %02d", i + 1); + total -= test(dbuf, copy_test(fd0, fd1, speed_set), 0, 1.5); + } + + close(fd0); + close(fd1); + + if (reader != writer) { + sprintf(dbuf, "rmmod %s", reader); + sprintf(cbuf, "rmmod %s.ko", reader); + total -= fatal_test(dbuf, system(cbuf), 0, 0.5); + sprintf(dbuf, "rmmod %s", writer); + sprintf(cbuf, "rmmod %s.ko", writer); + total -= fatal_test(dbuf, system(cbuf), 0, 0.5); + } else { + sprintf(dbuf, "rmmod %s", reader); + sprintf(cbuf, "rmmod %s.ko", reader); + total -= fatal_test(dbuf, system(cbuf), 0, 0.5); + } + + return total; +} + +#define choose_one(rd, wr) do { \ + int r = rand() % 2; \ + if (r == 0) { \ + rd = MODULE_NAME; \ + wr = SOLUTION_NAME; \ + } else { \ + rd = SOLUTION_NAME; \ + wr = MODULE_NAME; \ + } \ + } while (0) + +static float +test3(void) +{ + const char *rd, *wr; + + rd = MODULE_NAME; + wr = SOLUTION_NAME; + test_title("Test 3. Read, small speed"); + return generic_test(rd, wr, 0, 5); +} + +static float +test4(void) +{ + const char *rd, *wr; + + rd = SOLUTION_NAME; + wr = MODULE_NAME; + test_title("Test 4. Write, small speed"); + return generic_test(rd, wr, 0, 5); +} + +static float +test5(void) +{ + const char *rd, *wr; + + rd = wr = MODULE_NAME; + test_title("Test 5. Back-to-back, small speed"); + return generic_test(rd, wr, 0, 5); +} + +static float +test6(void) +{ + const char *rd, *wr; + + choose_one(rd, wr); + test_title("Test 6. Read/Write, medium speed"); + return generic_test(rd, wr, 1, 5); +} + +static float +test7(void) +{ + const char *rd, *wr; + + rd = wr = MODULE_NAME; + test_title("Test 7. Back-to-back, medium speed"); + return generic_test(rd, wr, 1, 5); +} + +static float +test8(void) +{ + const char *rd, *wr; + + choose_one(rd, wr); + test_title("Test 8. Read/Write, high speed"); + return generic_test(rd, wr, 2, 5); +} + +static float +test9(void) +{ + const char *rd, *wr; + + rd = wr = MODULE_NAME; + test_title("Test 9. Back-to-back, high speed"); + return generic_test(rd, wr, 2, 5); +} + +int +main(void) +{ + float num_passed = 0; + + signal(SIGTERM, sig_handler); + srand(time(NULL)); + make_nodes(); + + num_passed += test1(); + num_passed += test2(); + num_passed += test3(); + num_passed += test4(); + num_passed += test5(); + num_passed += test6(); + num_passed += test7(); + num_passed += test8(); + num_passed += test9(); + + remove_nodes(); + unlink(INFILE); + unlink(OUTFILE); + printf("\nTotal: [%.1f/%d]\n", num_passed, total); + + return 0; +} + +/* Extra 2 lines so the file is the proper size. */ diff --git a/checker/2-uart-checker/_test/uart16550.h b/checker/2-uart-checker/_test/uart16550.h new file mode 100644 index 0000000..7300892 --- /dev/null +++ b/checker/2-uart-checker/_test/uart16550.h @@ -0,0 +1,46 @@ +#ifndef UART16550_H +#define UART16550_H + +#define OPTION_COM1 1 +#define OPTION_COM2 2 +#define OPTION_BOTH 3 + +#define UART16550_COM1_SELECTED 0x01 +#define UART16550_COM2_SELECTED 0x02 + +#define MAX_NUMBER_DEVICES 2 + +#ifndef _UART16550_REGS_H + +#define UART16550_BAUD_1200 96 +#define UART16550_BAUD_2400 48 +#define UART16550_BAUD_4800 24 +#define UART16550_BAUD_9600 12 +#define UART16550_BAUD_19200 6 +#define UART16550_BAUD_38400 3 +#define UART16550_BAUD_56000 2 +#define UART16550_BAUD_115200 1 + +#define UART16550_LEN_5 0x00 +#define UART16550_LEN_6 0x01 +#define UART16550_LEN_7 0x02 +#define UART16550_LEN_8 0x03 + +#define UART16550_STOP_1 0x00 +#define UART16550_STOP_2 0x04 + +#define UART16550_PAR_NONE 0x00 +#define UART16550_PAR_ODD 0x08 +#define UART16550_PAR_EVEN 0x18 +#define UART16550_PAR_STICK 0x20 + +#endif + +#define UART16550_IOCTL_SET_LINE 1 + +struct uart16550_line_info { + unsigned char baud, len, par, stop; +}; + +#endif + diff --git a/checker/3-raid-checker/Makefile b/checker/3-raid-checker/Makefile new file mode 100644 index 0000000..9eea19f --- /dev/null +++ b/checker/3-raid-checker/Makefile @@ -0,0 +1,14 @@ +CFLAGS = -Wall -Wextra -g -m32 +LDFLAGS = -static -m32 + +.PHONY: all clean + +all: + +test: + make -C _test/ + ln -sf _test/run-test run-test + +clean: + -make -C _test/ clean + rm -rf run-test diff --git a/checker/3-raid-checker/README b/checker/3-raid-checker/README new file mode 100644 index 0000000..8fb41fd --- /dev/null +++ b/checker/3-raid-checker/README @@ -0,0 +1,40 @@ += SOFTWARE RAID TEST SUITE == + +Test suite for software RAID + +== FILES == + +README + * this file + +Makefile + * Makefile for automating the build process + +_checker + * script to run all tests defined in _test/test.c + +_test/test.c + * test suite for software RAID + +== RUNNING == + +In order to run the test suite you can either use the _checker +script or run the run-test executable. + +The kernel module must be named ssr.ko and must be in the current folder. + +The run-test executable has to be in the current folder. You can create +a link using: + + ln -sf _test/run-test run-test + +The _checker script runs all tests and computes assignment grade. You +can use any of the two commands below. + + make test + ./_checker + +In order to run a specific test, pass the test number (1 .. 78) to the +run-test executable. + + ./run-test 5 diff --git a/checker/3-raid-checker/_checker b/checker/3-raid-checker/_checker new file mode 100755 index 0000000..da4e405 --- /dev/null +++ b/checker/3-raid-checker/_checker @@ -0,0 +1,4 @@ +#!/bin/sh + +/bin/dmesg -c > /dev/null 2>&1 +./run-test diff --git a/checker/3-raid-checker/_test/Makefile b/checker/3-raid-checker/_test/Makefile new file mode 100644 index 0000000..a8a98a2 --- /dev/null +++ b/checker/3-raid-checker/_test/Makefile @@ -0,0 +1,15 @@ +CFLAGS = -Wall -Wextra -Wno-unused-function -g -m32 +LDFLAGS = -static -m32 + +.PHONY: all clean + +all: run-test + +run-test: run-test.o test.o + +run-test.o: run-test.c run-test.h + +test.o: test.c run-test.h + +clean: + -rm -f *~ test.o run-test.o run-test test diff --git a/checker/3-raid-checker/_test/run-test.c b/checker/3-raid-checker/_test/run-test.c new file mode 100644 index 0000000..d8f15ca --- /dev/null +++ b/checker/3-raid-checker/_test/run-test.c @@ -0,0 +1,106 @@ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <time.h> + +#include "run-test.h" + +/* Enable/disable exiting when program fails. */ +//#define EXIT_IF_FAIL + +static size_t test_index; +static size_t total_points = 0; + +static void test_do_fail(size_t points) +{ + printf("failed [ 0/%3zu]\n", max_points); + fflush(stdout); +#ifdef EXIT_IF_FAIL + exit(EXIT_FAILURE); +#endif +} + +static void test_do_pass(size_t points) +{ + total_points += points; + printf("passed [%3zu/%3zu]\n", points, max_points); + fflush(stdout); +} + +void basic_test(int condition) +{ + size_t i; + char *description = test_array[test_index].description; + size_t desc_len = strlen(description); + size_t points = test_array[test_index].points; + + printf("(%3zu) %s", test_index + 1, description); + for (i = 0; i < 56 - desc_len; i++) + printf("."); + if (condition) + test_do_pass(points); + else + test_do_fail(points); +} + +static void print_test_total(void) +{ + size_t i; + + for (i = 0; i < 62; i++) + printf(" "); + printf("Total: [%3zu/%3zu]\n", total_points, max_points); +} + +static void run_test(void) +{ + test_array[test_index].function(); +} + +int main(int argc, char **argv) +{ + size_t num_tests = get_num_tests(); + + if (argc > 2) { + fprintf(stderr, "Usage: %s [test_number]\n", argv[0]); + fprintf(stderr, " 1 <= test_number <= %zu\n", num_tests); + exit(EXIT_FAILURE); + } + + /* Randomize time quantums. */ + srand(time(NULL)); + + /* In case of no arguments run all tests. */ + if (argc == 1) { + init_world(); + for (test_index = 0; test_index < num_tests; test_index++) + run_test(); + print_test_total(); + cleanup_world(); + return 0; + } + + /* If provided, argument is test index. */ + test_index = strtoul(argv[1], NULL, 10); + if (errno == EINVAL || errno == ERANGE) { + fprintf(stderr, "%s is not a number\n", argv[1]); + exit(EXIT_FAILURE); + } + + if (test_index == 0 || test_index > num_tests) { + fprintf(stderr, "Error: Test index is out of range " + "(1 <= test_index <= %zu).\n", num_tests); + exit(EXIT_FAILURE); + } + + /* test_index is one less than what the user provides. */ + test_index--; + + /* Run test_index test. */ + init_world(); + run_test(); + cleanup_world(); + + return 0; +} diff --git a/checker/3-raid-checker/_test/run-test.h b/checker/3-raid-checker/_test/run-test.h new file mode 100644 index 0000000..e4d64f6 --- /dev/null +++ b/checker/3-raid-checker/_test/run-test.h @@ -0,0 +1,25 @@ +#ifndef _RUN_TEST_H_ +#define _RUN_TEST_H_ + +/* functions exported by the framework */ +void basic_test(int condition); + +/* function exported by the test */ +void init_world(void); +void cleanup_world(void); +size_t get_num_tests(void); + +/* test function prototype */ +typedef void (test_f)(void); + +struct run_test_t { + test_f *function; /* test/evaluation function */ + char *description; /* test description */ + size_t points; /* points for each test */ +}; + +/* Use test_index to pass through test_array. */ +extern struct run_test_t test_array[]; +extern size_t max_points; + +#endif /* _RUN_TEST_H_ */ diff --git a/checker/3-raid-checker/_test/ssr.h b/checker/3-raid-checker/_test/ssr.h new file mode 100644 index 0000000..5aa4107 --- /dev/null +++ b/checker/3-raid-checker/_test/ssr.h @@ -0,0 +1,26 @@ +/* + * Simple Software Raid - Linux header file + */ + +#ifndef SSR_H_ +#define SSR_H_ 1 + +#define SSR_MAJOR 240 +#define SSR_FIRST_MINOR 0 +#define SSR_NUM_MINORS 1 + +#define PHYSICAL_DISK1_NAME "/dev/vdb" +#define PHYSICAL_DISK2_NAME "/dev/vdc" + +/* sector size */ +#define KERNEL_SECTOR_SIZE 512 + +/* physical partition size - 95 MB (more than this results in error) */ +#define LOGICAL_DISK_NAME "/dev/ssr" +#define LOGICAL_DISK_SIZE (95 * 1024 * 1024) +#define LOGICAL_DISK_SECTORS ((LOGICAL_DISK_SIZE) / (KERNEL_SECTOR_SIZE)) + +/* sync data */ +#define SSR_IOCTL_SYNC 1 + +#endif diff --git a/checker/3-raid-checker/_test/test.c b/checker/3-raid-checker/_test/test.c new file mode 100644 index 0000000..b6a36ec --- /dev/null +++ b/checker/3-raid-checker/_test/test.c @@ -0,0 +1,1769 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <fcntl.h> +#include <assert.h> + +#include "run-test.h" +#include "ssr.h" + +#define SSR_BASE_NAME "ssr" +#define SSR_LIN_EXT ".ko" +#define SSR_MOD_NAME SSR_BASE_NAME SSR_LIN_EXT + +#define CRC_SIZE 4 + +#define ONE_SECTOR KERNEL_SECTOR_SIZE +#define ONE_PAGE 4096 +#define TWO_PAGES 8192 +#define TEN_PAGES 40960 +#define ONE_MEG 1048576 + +/* Read/write buffers. */ +static unsigned char *log_rd_buf, *log_wr_buf; +static unsigned char *phys1_rd_buf, *phys1_wr_buf; +static unsigned char *phys2_rd_buf, *phys2_wr_buf; +static unsigned char *log_rd_crc, *log_wr_crc; +static unsigned char *phys1_rd_crc, *phys1_wr_crc; +static unsigned char *phys2_rd_crc, *phys2_wr_crc; + +/* File descriptors. */ +static int log_fd, phys1_fd, phys2_fd; + +enum { + START = 0, + MIDDLE, + END +}; + +enum { + PHYS_FILL_DATA = 'P', + LOG_FILL_DATA = 'L', + CORRUPT_DATA = 'C', + PHYS1_DISK_DIRTY_DATA = 'a', + PHYS1_BUF_DIRTY_DATA = 'A', + PHYS2_DISK_DIRTY_DATA = 'b', + PHYS2_BUF_DIRTY_DATA = 'B', + LOG_DISK_DIRTY_DATA = 'd', + LOG_BUF_DIRTY_DATA = 'D', +}; + +/* + * "upgraded" read routine + */ + +static ssize_t xread(int fd, void *buffer, size_t len) +{ + ssize_t ret; + ssize_t n; + + n = 0; + while (n < (ssize_t) len) { + ret = read(fd, (char *) buffer + n, len - n); + if (ret < 0) + return -1; + if (ret == 0) + break; + n += ret; + } + + return n; +} + +/* + * "upgraded" write routine + */ + +static ssize_t xwrite(int fd, const void *buffer, size_t len) +{ + ssize_t ret; + ssize_t n; + + n = 0; + while (n < (ssize_t) len) { + ret = write(fd, (const char *) buffer + n, len - n); + if (ret < 0) + return -1; + if (ret == 0) + break; + n += ret; + } + + return n; +} + +/* + * Compute CRC32. + */ + +static unsigned int crc32(unsigned int seed, + const unsigned char *p, unsigned int len) +{ + size_t i; + unsigned int crc = seed; + + while (len--) { + crc ^= *p++; + for (i = 0; i < 8; i++) + crc = (crc >> 1) ^ ((crc & 1) ? 0xedb88320 : 0); + } + + return crc; +} + +static void compute_crc(const void *data_buffer, void *crc_buffer, size_t len) +{ + size_t i; + unsigned int crc; + + for (i = 0; i < len; i += ONE_SECTOR) { + crc = crc32(0, (const unsigned char *) data_buffer + i, ONE_SECTOR); + memcpy((char *) crc_buffer + i / ONE_SECTOR * CRC_SIZE, + &crc, CRC_SIZE); + } +} + +static off_t data_offset_from_whence(int whence, size_t len) +{ + switch (whence) { + case START: + return 0; + case MIDDLE: + return LOGICAL_DISK_SIZE / 2 - len; + case END: + return LOGICAL_DISK_SIZE - len; + default: + return -1; + } +} + +static off_t crc_offset_from_whence(int whence, size_t len) +{ + off_t data_offset = data_offset_from_whence(whence, len); + + return LOGICAL_DISK_SIZE + data_offset / ONE_SECTOR * CRC_SIZE; +} + +static void fill_buffer(void *buffer, int c, size_t len) +{ + memset(buffer, c, len); +} + +static void log_fill_buffer(size_t len) +{ + fill_buffer(log_wr_buf, LOG_FILL_DATA, len); +} + +static void phys_fill_buffer(size_t len) +{ + fill_buffer(phys1_wr_buf, PHYS_FILL_DATA, len); + fill_buffer(phys2_wr_buf, PHYS_FILL_DATA, len); +} + +static ssize_t read_whence_data(int fd, void *buffer, size_t len, int whence) +{ + off_t offset = data_offset_from_whence(whence, len); + + lseek(fd, offset, SEEK_SET); + return xread(fd, buffer, len); +} + +static ssize_t read_whence_crc(int fd, void *crc_buffer, size_t data_len, + int whence) +{ + off_t offset = crc_offset_from_whence(whence, data_len); + + lseek(fd, offset, SEEK_SET); + return xread(fd, crc_buffer, data_len / ONE_SECTOR * CRC_SIZE); +} + +static ssize_t write_whence_data(int fd, const void *buffer, + size_t len, int whence) +{ + off_t offset = data_offset_from_whence(whence, len); + + lseek(fd, offset, SEEK_SET); + return xwrite(fd, buffer, len); +} + +static ssize_t write_whence_crc(int fd, void *crc_buffer, size_t data_len, + int whence) +{ + off_t offset = crc_offset_from_whence(whence, data_len); + + lseek(fd, offset, SEEK_SET); + return xwrite(fd, crc_buffer, data_len / ONE_SECTOR * CRC_SIZE); +} + +static ssize_t log_read_whence(size_t len, int whence) +{ + ssize_t n; + + n = read_whence_data(log_fd, log_rd_buf, len, whence); + if (n < 0) + return -1; + compute_crc(log_rd_buf, log_rd_crc, len); + return n; +} + +static ssize_t log_write_whence(size_t len, int whence) +{ + compute_crc(log_wr_buf, log_wr_crc, len); + return write_whence_data(log_fd, log_wr_buf, len, whence); +} + +static ssize_t phys_read_whence(size_t id, size_t len, int whence) +{ + ssize_t n_data, n_crc; + int fd = ((id == 1) ? phys1_fd : phys2_fd); + + unsigned char *data_buf = ((id == 1) ? phys1_rd_buf : phys2_rd_buf); + unsigned char *crc_buf = ((id == 1) ? phys1_rd_crc : phys2_rd_crc); + + n_data = read_whence_data(fd, data_buf, len, whence); + if (n_data < 0) + return -1; + n_crc = read_whence_crc(fd, crc_buf, len, whence); + if (n_crc < 0) + return -1; + return n_data; +} + +static ssize_t phys_write_whence(size_t id, size_t len, int whence) +{ + ssize_t n_data, n_crc; + int fd = ((id == 1) ? phys1_fd : phys2_fd); + unsigned char *data_buf = ((id == 1) ? phys1_wr_buf : phys2_wr_buf); + unsigned char *crc_buf = ((id == 1) ? phys1_wr_crc : phys2_wr_crc); + + compute_crc(data_buf, crc_buf, len); + n_data = write_whence_data(fd, data_buf, len, whence); + if (n_data < 0) + return -1; + n_crc = write_whence_crc(fd, crc_buf, len, whence); + if (n_crc < 0) + return -1; + return n_data; +} + +static void corrupt_buffer(void *buffer, size_t sectors) +{ + size_t i; + + for (i = 0; i < sectors; i++) + ((unsigned char *) buffer)[i * ONE_SECTOR] = CORRUPT_DATA; +} + +static ssize_t phys_corrupt_and_write_whence(size_t id, size_t len, + size_t sectors, int whence) +{ + ssize_t n_data, n_crc; + int fd = ((id == 1) ? phys1_fd : phys2_fd); + unsigned char *data_buf = ((id == 1) ? phys1_wr_buf : phys2_wr_buf); + unsigned char *crc_buf = ((id == 1) ? phys1_wr_crc : phys2_wr_crc); + + compute_crc(data_buf, crc_buf, len); + corrupt_buffer(data_buf, sectors); + n_data = write_whence_data(fd, data_buf, len, whence); + if (n_data < 0) + return -1; + n_crc = write_whence_crc(fd, crc_buf, len, whence); + if (n_crc < 0) + return -1; + return n_data; +} + +static ssize_t log_read_start(size_t len) +{ + return log_read_whence(len, START); +} + +static ssize_t log_read_middle(size_t len) +{ + return log_read_whence(len, MIDDLE); +} + +static ssize_t log_read_end(size_t len) +{ + return log_read_whence(len, END); +} + +static ssize_t log_write_start(size_t len) +{ + return log_write_whence(len, START); +} + +static ssize_t log_write_middle(size_t len) +{ + return log_write_whence(len, MIDDLE); +} + +static ssize_t log_write_end(size_t len) +{ + return log_write_whence(len, END); +} + +static ssize_t phys1_read_start(size_t len) +{ + return phys_read_whence(1, len, START); +} + +#if 0 +static ssize_t phys1_read_middle(size_t len) +{ + return phys_read_whence(1, len, MIDDLE); +} + +static ssize_t phys1_read_end(size_t len) +{ + return phys_read_whence(1, len, END); +} +#endif + +static ssize_t phys1_write_start(size_t len) +{ + return phys_write_whence(1, len, START); +} + +static ssize_t phys1_corrupt_and_write_start(size_t len, size_t sectors) +{ + return phys_corrupt_and_write_whence(1, len, sectors, START); +} + +#if 0 +static ssize_t phys1_write_middle(size_t len) +{ + return phys_write_whence(1, len, MIDDLE); +} + +static ssize_t phys1_write_end(size_t len) +{ + return phys_write_whence(1, len, END); +} +#endif + +static ssize_t phys2_read_start(size_t len) +{ + return phys_read_whence(2, len, START); +} + +#if 0 +static ssize_t phys2_read_middle(size_t len) +{ + return phys_read_whence(2, len, MIDDLE); +} + +static ssize_t phys2_read_end(size_t len) +{ + return phys_read_whence(2, len, END); +} +#endif + +static ssize_t phys2_write_start(size_t len) +{ + return phys_write_whence(2, len, START); +} + +static ssize_t phys2_corrupt_and_write_start(size_t len, size_t sectors) +{ + return phys_corrupt_and_write_whence(2, len, sectors, START); +} + +#if 0 +static ssize_t phys2_write_middle(size_t len) +{ + return phys_write_whence(2, len, MIDDLE); +} + +static ssize_t phys2_write_end(size_t len) +{ + return phys_write_whence(2, len, END); +} +#endif + +static int cmp_data_log_rd_phys1_wr(size_t len) +{ + return memcmp(log_rd_buf, phys1_wr_buf, len); +} + +static int cmp_data_log_rd_phys2_wr(size_t len) +{ + return memcmp(log_rd_buf, phys2_wr_buf, len); +} + +static int cmp_data_log_rd_phys1_rd(size_t len) +{ + return memcmp(log_rd_buf, phys1_rd_buf, len); +} + +static int cmp_data_log_rd_phys2_rd(size_t len) +{ + return memcmp(log_rd_buf, phys2_rd_buf, len); +} + +static int cmp_data_log_wr_phys1_rd(size_t len) +{ + return memcmp(log_wr_buf, phys1_rd_buf, len); +} + +static int cmp_data_log_wr_phys2_rd(size_t len) +{ + return memcmp(log_wr_buf, phys2_rd_buf, len); +} + +static int cmp_crc_log_rd_phys1_wr(size_t data_len) +{ + return memcmp(log_rd_crc, phys1_wr_crc, data_len / ONE_SECTOR * CRC_SIZE); +} + +static int cmp_crc_log_rd_phys2_wr(size_t data_len) +{ + return memcmp(log_rd_crc, phys2_wr_crc, data_len / ONE_SECTOR * CRC_SIZE); +} + +static int cmp_crc_log_rd_phys1_rd(size_t data_len) +{ + return memcmp(log_rd_crc, phys1_rd_crc, data_len / ONE_SECTOR * CRC_SIZE); +} + +static int cmp_crc_log_rd_phys2_rd(size_t data_len) +{ + return memcmp(log_rd_crc, phys2_rd_crc, data_len / ONE_SECTOR * CRC_SIZE); +} + +static int cmp_crc_log_wr_phys1_rd(size_t data_len) +{ + return memcmp(log_wr_crc, phys1_rd_crc, data_len / ONE_SECTOR * CRC_SIZE); +} + +static int cmp_crc_log_wr_phys2_rd(size_t data_len) +{ + return memcmp(log_wr_crc, phys2_rd_crc, data_len / ONE_SECTOR * CRC_SIZE); +} + +static void drop_caches(void) +{ + int fd; + char buf[] = "1\n"; + + fd = open("/proc/sys/vm/drop_caches", O_WRONLY); + assert(fd >= 0); + write(fd, buf, strlen(buf)); + close(fd); +} + +static void flush_disk_buffers(void) +{ + sync(); + //system("/bin/echo 1 > /proc/sys/vm/drop_caches"); + drop_caches(); +} + +static void dump_data(const void *buf, size_t len, const char *header) +{ + size_t i; + + printf("%s:", header); + for (i = 0; i < len / sizeof(unsigned int); i++) { + if (i % 4 == 0) + printf("\n\t"); + printf(" %08x", ((unsigned int *) buf)[i]); + } + printf("\n\n"); +} + +void init_world(void) +{ + /* Cleanup if required. */ + flush_disk_buffers(); + system("/sbin/rmmod " SSR_BASE_NAME " > /dev/null 2>&1"); + system("/bin/cat /proc/devices | /bin/grep " SSR_BASE_NAME " > /dev/null"); + system("/bin/rm -f " LOGICAL_DISK_NAME); + + assert(system("/sbin/insmod " SSR_MOD_NAME) == 0); + assert(system("/bin/cat /proc/devices | /bin/grep " SSR_BASE_NAME + " > /dev/null") == 0); + assert(access(PHYSICAL_DISK1_NAME, F_OK) == 0); + assert(access(PHYSICAL_DISK2_NAME, F_OK) == 0); + assert(access(LOGICAL_DISK_NAME, F_OK) == 0); + + log_rd_buf = calloc(1024 * 1024, 1); + assert(log_rd_buf != NULL); + log_wr_buf = calloc(1024 * 1024, 1); + assert(log_rd_buf != NULL); + phys1_rd_buf = calloc(1024 * 1024, 1); + assert(phys1_rd_buf != NULL); + phys1_wr_buf = calloc(1024 * 1024, 1); + assert(phys1_wr_buf != NULL); + phys2_rd_buf = calloc(1024 * 1024, 1); + assert(phys2_rd_buf != NULL); + phys2_wr_buf = calloc(1024 * 1024, 1); + assert(phys2_wr_buf != NULL); + log_rd_crc = calloc(8 * 1024, 1); + assert(log_rd_crc != NULL); + log_wr_crc = calloc(8 * 1024, 1); + assert(log_rd_crc != NULL); + phys1_rd_crc = calloc(8 * 1024, 1); + assert(phys1_rd_crc != NULL); + phys1_wr_crc = calloc(8 * 1024, 1); + assert(phys1_wr_crc != NULL); + phys2_rd_crc = calloc(8 * 1024, 1); + assert(phys2_rd_crc != NULL); + phys2_wr_crc = calloc(8 * 1024, 1); + assert(phys2_wr_crc != NULL); +} + +void cleanup_world(void) +{ + flush_disk_buffers(); + system("/sbin/rmmod " SSR_BASE_NAME); + system("/bin/cat /proc/devices | /bin/grep " SSR_BASE_NAME " > /dev/null"); + system("/bin/rm -f " LOGICAL_DISK_NAME); + free(log_rd_buf); free(log_wr_buf); + free(phys1_rd_buf); free(phys1_wr_buf); + free(phys2_rd_buf); free(phys2_wr_buf); + free(log_rd_crc); free(log_wr_crc); + free(phys1_rd_crc); free(phys1_wr_crc); + free(phys2_rd_crc); free(phys2_wr_crc); +} + +static void make_disks_dirty(void) +{ + fill_buffer(phys1_wr_buf, PHYS1_DISK_DIRTY_DATA, ONE_MEG); + fill_buffer(phys1_wr_crc, PHYS1_DISK_DIRTY_DATA, ONE_MEG / ONE_SECTOR * CRC_SIZE); + fill_buffer(phys2_wr_buf, PHYS2_DISK_DIRTY_DATA, ONE_MEG); + fill_buffer(phys2_wr_crc, PHYS2_DISK_DIRTY_DATA, ONE_MEG / ONE_SECTOR * CRC_SIZE); + phys1_write_start(ONE_MEG); + phys2_write_start(ONE_MEG); +} + +static void make_buffers_dirty(void) +{ + fill_buffer(phys1_wr_buf, PHYS1_BUF_DIRTY_DATA, ONE_MEG); + fill_buffer(phys1_wr_crc, PHYS1_BUF_DIRTY_DATA, ONE_MEG / ONE_SECTOR * CRC_SIZE); + fill_buffer(phys1_rd_buf, PHYS1_BUF_DIRTY_DATA, ONE_MEG); + fill_buffer(phys1_rd_crc, PHYS1_BUF_DIRTY_DATA, ONE_MEG / ONE_SECTOR * CRC_SIZE); + fill_buffer(phys2_wr_buf, PHYS2_BUF_DIRTY_DATA, ONE_MEG); + fill_buffer(phys2_wr_crc, PHYS2_BUF_DIRTY_DATA, ONE_MEG / ONE_SECTOR * CRC_SIZE); + fill_buffer(phys2_rd_buf, PHYS2_BUF_DIRTY_DATA, ONE_MEG); + fill_buffer(phys2_rd_crc, PHYS2_BUF_DIRTY_DATA, ONE_MEG / ONE_SECTOR * CRC_SIZE); + fill_buffer(log_wr_buf, LOG_BUF_DIRTY_DATA, ONE_MEG); + fill_buffer(log_wr_crc, LOG_BUF_DIRTY_DATA, ONE_MEG / ONE_SECTOR * CRC_SIZE); + fill_buffer(log_rd_buf, LOG_BUF_DIRTY_DATA, ONE_MEG); + fill_buffer(log_rd_crc, LOG_BUF_DIRTY_DATA, ONE_MEG / ONE_SECTOR * CRC_SIZE); +} + +static void init_test(void) +{ + flush_disk_buffers(); + log_fd = open(LOGICAL_DISK_NAME, O_RDWR); + assert(log_fd >= 0); + phys1_fd = open(PHYSICAL_DISK1_NAME, O_RDWR); + assert(phys1_fd >= 0); + phys2_fd = open(PHYSICAL_DISK2_NAME, O_RDWR); + assert(phys2_fd >= 0); + make_disks_dirty(); + make_buffers_dirty(); + flush_disk_buffers(); +} + +static void cleanup_test(void) +{ + close(log_fd); + close(phys1_fd); + close(phys2_fd); +} + +static void open_logical(void) +{ + int fd; + + fd = open(LOGICAL_DISK_NAME, O_RDWR); + basic_test(fd >= 0); + close(fd); +} + +static void close_logical(void) +{ + int fd, rc; + + fd = open(LOGICAL_DISK_NAME, O_RDWR); + rc = close(fd); + basic_test(rc == 0); +} + +static void use_after_close_invalid(void) +{ + int fd, val; + ssize_t n; + + fd = open(LOGICAL_DISK_NAME, O_RDWR); + close(fd); + n = read(fd, &val, sizeof(val)); + basic_test(n < 0); +} + +static void lseek_logical(void) +{ + off_t offset; + + init_test(); + offset = lseek(log_fd, LOGICAL_DISK_SIZE / 2, SEEK_SET); + basic_test(offset == LOGICAL_DISK_SIZE / 2); + cleanup_test(); +} + +static void read_one_sector_start(void) +{ + ssize_t n; + + init_test(); + n = log_read_start(ONE_SECTOR); + basic_test(n == ONE_SECTOR); + cleanup_test(); +} + +static void read_one_sector_middle(void) +{ + ssize_t n; + + init_test(); + n = log_read_middle(ONE_SECTOR); + basic_test(n == ONE_SECTOR); + cleanup_test(); +} + +static void read_one_sector_end(void) +{ + ssize_t n; + + init_test(); + n = log_read_end(ONE_SECTOR); + basic_test(n == ONE_SECTOR); + cleanup_test(); +} + +static void write_one_sector_start(void) +{ + ssize_t n; + + init_test(); + n = log_write_start(ONE_SECTOR); + basic_test(n == ONE_SECTOR); + cleanup_test(); +} + +static void write_one_sector_middle(void) +{ + ssize_t n; + + init_test(); + n = log_write_middle(ONE_SECTOR); + basic_test(n == ONE_SECTOR); + cleanup_test(); +} + +static void write_one_sector_end(void) +{ + ssize_t n; + + init_test(); + n = log_write_end(ONE_SECTOR); + basic_test(n == ONE_SECTOR); + cleanup_test(); +} + +static void read_one_page_start(void) +{ + ssize_t n; + + init_test(); + n = log_read_start(ONE_PAGE); + basic_test(n == ONE_PAGE); + cleanup_test(); +} + +static void read_one_page_middle(void) +{ + ssize_t n; + + init_test(); + n = log_read_middle(ONE_PAGE); + basic_test(n == ONE_PAGE); + cleanup_test(); +} + +static void read_one_page_end(void) +{ + ssize_t n; + + init_test(); + n = log_read_end(ONE_PAGE); + basic_test(n == ONE_PAGE); + cleanup_test(); +} + +static void write_one_page_start(void) +{ + ssize_t n; + + init_test(); + n = log_write_start(ONE_PAGE); + basic_test(n == ONE_PAGE); + cleanup_test(); +} + +static void write_one_page_middle(void) +{ + ssize_t n; + + init_test(); + n = log_write_middle(ONE_PAGE); + basic_test(n == ONE_PAGE); + cleanup_test(); +} + +static void write_one_page_end(void) +{ + ssize_t n; + + init_test(); + n = log_write_end(ONE_PAGE); + basic_test(n == ONE_PAGE); + cleanup_test(); +} + +static void read_two_pages_start(void) +{ + ssize_t n; + + init_test(); + n = log_read_start(TWO_PAGES); + basic_test(n == TWO_PAGES); + cleanup_test(); +} + +static void read_two_pages_middle(void) +{ + ssize_t n; + + init_test(); + n = log_read_middle(TWO_PAGES); + basic_test(n == TWO_PAGES); + cleanup_test(); +} + +static void read_two_pages_end(void) +{ + ssize_t n; + + init_test(); + n = log_read_end(TWO_PAGES); + basic_test(n == TWO_PAGES); + cleanup_test(); +} + +static void write_two_pages_start(void) +{ + ssize_t n; + + init_test(); + n = log_write_start(TWO_PAGES); + basic_test(n == TWO_PAGES); + cleanup_test(); +} + +static void write_two_pages_middle(void) +{ + ssize_t n; + + init_test(); + n = log_write_middle(TWO_PAGES); + basic_test(n == TWO_PAGES); + cleanup_test(); +} + +static void write_two_pages_end(void) +{ + ssize_t n; + + init_test(); + n = log_write_end(TWO_PAGES); + basic_test(n == TWO_PAGES); + cleanup_test(); +} + +static void read_one_meg_start(void) +{ + ssize_t n; + + init_test(); + n = log_read_start(ONE_MEG); + basic_test(n == ONE_MEG); + cleanup_test(); +} + +static void read_one_meg_middle(void) +{ + ssize_t n; + + init_test(); + n = log_read_middle(ONE_MEG); + basic_test(n == ONE_MEG); + cleanup_test(); +} + +static void read_one_meg_end(void) +{ + ssize_t n; + + init_test(); + n = log_read_end(ONE_MEG); + basic_test(n == ONE_MEG); + cleanup_test(); +} + +static void write_one_meg_start(void) +{ + ssize_t n; + + init_test(); + n = log_write_start(ONE_MEG); + basic_test(n == ONE_MEG); + cleanup_test(); +} + +static void write_one_meg_middle(void) +{ + ssize_t n; + + init_test(); + n = log_write_middle(ONE_MEG); + basic_test(n == ONE_MEG); + cleanup_test(); +} + +static void write_one_meg_end(void) +{ + ssize_t n; + + init_test(); + n = log_write_end(ONE_MEG); + basic_test(n == ONE_MEG); + cleanup_test(); +} + +static void read_boundary_one_sector(void) +{ + ssize_t n; + + init_test(); + lseek(log_fd, LOGICAL_DISK_SIZE, SEEK_SET); + n = xread(log_fd, log_rd_buf, ONE_SECTOR); + basic_test(n == 0); + cleanup_test(); +} + +static void read_boundary_one_page(void) +{ + ssize_t n; + + init_test(); + lseek(log_fd, LOGICAL_DISK_SIZE, SEEK_SET); + n = xread(log_fd, log_rd_buf, ONE_PAGE); + basic_test(n == 0); + cleanup_test(); +} + +static void read_boundary_two_pages(void) +{ + ssize_t n; + + init_test(); + lseek(log_fd, LOGICAL_DISK_SIZE, SEEK_SET); + n = xread(log_fd, log_rd_buf, TWO_PAGES); + basic_test(n == 0); + cleanup_test(); +} + +static void read_boundary_one_meg(void) +{ + ssize_t n; + + init_test(); + lseek(log_fd, LOGICAL_DISK_SIZE, SEEK_SET); + n = xread(log_fd, log_rd_buf, ONE_MEG); + basic_test(n == 0); + cleanup_test(); +} + +static void write_boundary_one_sector(void) +{ + ssize_t n; + + init_test(); + lseek(log_fd, LOGICAL_DISK_SIZE, SEEK_SET); + n = xwrite(log_fd, log_rd_buf, ONE_SECTOR); + basic_test(n < 0); + cleanup_test(); +} + +static void write_boundary_one_page(void) +{ + ssize_t n; + + init_test(); + lseek(log_fd, LOGICAL_DISK_SIZE, SEEK_SET); + n = xwrite(log_fd, log_wr_buf, ONE_PAGE); + basic_test(n < 0); + cleanup_test(); +} + +static void write_boundary_two_pages(void) +{ + ssize_t n; + + init_test(); + lseek(log_fd, LOGICAL_DISK_SIZE, SEEK_SET); + n = xwrite(log_fd, log_wr_buf, TWO_PAGES); + basic_test(n < 0); + cleanup_test(); +} + +static void write_boundary_one_meg(void) +{ + ssize_t n; + + init_test(); + lseek(log_fd, LOGICAL_DISK_SIZE, SEEK_SET); + n = xwrite(log_fd, log_wr_buf, ONE_MEG); + basic_test(n < 0); + cleanup_test(); +} + +static size_t get_free_memory(void) +{ + FILE *f; + size_t i; + char buf[256]; + char *p; + + f = fopen("/proc/meminfo", "rt"); + assert(f != NULL); + /* Second line is 'MemFree: ...' */ + fgets(buf, 256, f); + fgets(buf, 256, f); + fclose(f); + + p = NULL; + for (i = 0; i < 256; i++) + if (buf[i] == ':') { + p = buf+i+1; + break; + } + + return strtoul(p, NULL, 10); +} + +static void memory_is_freed(void) +{ + size_t mem_used_before, mem_used_after; + size_t i; + + init_test(); + mem_used_before = get_free_memory(); + for (i = 0; i < 5; i++) + log_write_start(ONE_MEG); + mem_used_after = get_free_memory(); + + /* We assume 3MB (3072KB) is a reasonable memory usage in writes. */ + basic_test(mem_used_after < mem_used_before + 3072 && + mem_used_before < mem_used_after + 3072); + cleanup_test(); +} + +static void write_one_sector_check_phys1(void) +{ + int rc; + size_t len = ONE_SECTOR; + + init_test(); + log_fill_buffer(len); + log_write_start(len); + flush_disk_buffers(); + phys1_read_start(len); + rc = cmp_data_log_wr_phys1_rd(len); + basic_test(rc == 0); + cleanup_test(); +} + +static void write_one_page_check_phys1(void) +{ + int rc; + size_t len = ONE_PAGE; + + init_test(); + log_fill_buffer(len); + log_write_start(len); + flush_disk_buffers(); + phys1_read_start(len); + rc = cmp_data_log_wr_phys1_rd(len); + basic_test(rc == 0); + cleanup_test(); +} + +static void write_two_pages_check_phys1(void) +{ + int rc; + size_t len = TWO_PAGES; + + init_test(); + log_fill_buffer(len); + log_write_start(len); + flush_disk_buffers(); + phys1_read_start(len); + rc = cmp_data_log_wr_phys1_rd(len); + basic_test(rc == 0); + cleanup_test(); +} + +static void write_one_meg_check_phys1(void) +{ + int rc; + size_t len = ONE_MEG; + + init_test(); + log_fill_buffer(len); + log_write_start(len); + flush_disk_buffers(); + phys1_read_start(len); + rc = cmp_data_log_wr_phys1_rd(len); + basic_test(rc == 0); + cleanup_test(); +} + +static void write_one_sector_check_phys(void) +{ + int rc1, rc2; + size_t len = ONE_SECTOR; + + init_test(); + log_fill_buffer(len); + log_write_start(len); + flush_disk_buffers(); + phys1_read_start(len); + phys2_read_start(len); + rc1 = cmp_data_log_wr_phys1_rd(len); + rc2 = cmp_data_log_wr_phys2_rd(len); + basic_test(rc1 == 0 && rc2 == 0); + cleanup_test(); +} + +static void write_one_page_check_phys(void) +{ + int rc1, rc2; + size_t len = ONE_PAGE; + + init_test(); + log_fill_buffer(len); + log_write_start(len); + flush_disk_buffers(); + phys1_read_start(len); + phys2_read_start(len); + rc1 = cmp_data_log_wr_phys1_rd(len); + rc2 = cmp_data_log_wr_phys2_rd(len); + basic_test(rc1 == 0 && rc2 == 0); + cleanup_test(); +} + +static void write_two_pages_check_phys(void) +{ + int rc1, rc2; + size_t len = TWO_PAGES; + + init_test(); + log_fill_buffer(len); + log_write_start(len); + flush_disk_buffers(); + phys1_read_start(len); + phys2_read_start(len); + rc1 = cmp_data_log_wr_phys1_rd(len); + rc2 = cmp_data_log_wr_phys2_rd(len); + basic_test(rc1 == 0 && rc2 == 0); + cleanup_test(); +} + +static void write_one_meg_check_phys(void) +{ + int rc1, rc2; + size_t len = ONE_MEG; + + init_test(); + log_fill_buffer(len); + log_write_start(len); + flush_disk_buffers(); + phys1_read_start(len); + phys2_read_start(len); + rc1 = cmp_data_log_wr_phys1_rd(len); + rc2 = cmp_data_log_wr_phys2_rd(len); + basic_test(rc1 == 0 && rc2 == 0); + cleanup_test(); +} + +static void read_one_sector_after_write(void) +{ + int rc; + size_t len = ONE_SECTOR; + + init_test(); + phys_fill_buffer(len); + phys1_write_start(len); + phys2_write_start(len); + flush_disk_buffers(); + log_read_start(len); + rc = cmp_data_log_rd_phys1_wr(len); + basic_test(rc == 0); + cleanup_test(); +} + +static void read_one_page_after_write(void) +{ + int rc; + size_t len = ONE_PAGE; + + init_test(); + phys_fill_buffer(len); + phys1_write_start(len); + phys2_write_start(len); + flush_disk_buffers(); + log_read_start(len); + rc = cmp_data_log_rd_phys1_wr(len); + basic_test(rc == 0); + cleanup_test(); +} + +static void read_two_pages_after_write(void) +{ + int rc; + size_t len = TWO_PAGES; + + init_test(); + phys_fill_buffer(len); + phys1_write_start(len); + phys2_write_start(len); + flush_disk_buffers(); + log_read_start(len); + rc = cmp_data_log_rd_phys1_wr(len); + basic_test(rc == 0); + cleanup_test(); +} + +static void read_one_meg_after_write(void) +{ + int rc; + size_t len = ONE_MEG; + + init_test(); + phys_fill_buffer(len); + phys1_write_start(len); + phys2_write_start(len); + flush_disk_buffers(); + log_read_start(len); + rc = cmp_data_log_rd_phys1_wr(len); + basic_test(rc == 0); + cleanup_test(); +} + +static void write_one_sector_check_phys1_crc(void) +{ + int rc; + size_t len = ONE_SECTOR; + + init_test(); + log_fill_buffer(len); + log_write_start(len); + flush_disk_buffers(); + phys1_read_start(len); + rc = cmp_crc_log_wr_phys1_rd(len); + basic_test(rc == 0); + cleanup_test(); +} + +static void write_one_page_check_phys1_crc(void) +{ + int rc; + size_t len = ONE_PAGE; + + init_test(); + log_fill_buffer(len); + log_write_start(len); + flush_disk_buffers(); + phys1_read_start(len); + rc = cmp_crc_log_wr_phys1_rd(len); + basic_test(rc == 0); + cleanup_test(); +} + +static void write_two_pages_check_phys1_crc(void) +{ + int rc; + size_t len = TWO_PAGES; + + init_test(); + log_fill_buffer(len); + log_write_start(len); + flush_disk_buffers(); + phys1_read_start(len); + rc = cmp_crc_log_wr_phys1_rd(len); + basic_test(rc == 0); + cleanup_test(); +} + +static void write_one_meg_check_phys1_crc(void) +{ + int rc; + size_t len = ONE_MEG; + + init_test(); + log_fill_buffer(len); + log_write_start(len); + flush_disk_buffers(); + phys1_read_start(len); + rc = cmp_crc_log_wr_phys1_rd(len); + basic_test(rc == 0); + cleanup_test(); +} + +static void write_one_sector_check_phys_crc(void) +{ + int rc1, rc2; + size_t len = ONE_SECTOR; + + init_test(); + log_fill_buffer(len); + log_write_start(len); + flush_disk_buffers(); + phys1_read_start(len); + phys2_read_start(len); + rc1 = cmp_crc_log_wr_phys1_rd(len); + rc2 = cmp_crc_log_wr_phys2_rd(len); + basic_test(rc1 == 0 && rc2 == 0); + cleanup_test(); +} + +static void write_one_page_check_phys_crc(void) +{ + int rc1, rc2; + size_t len = ONE_PAGE; + + init_test(); + log_fill_buffer(len); + log_write_start(len); + flush_disk_buffers(); + phys1_read_start(len); + phys2_read_start(len); + rc1 = cmp_crc_log_wr_phys1_rd(len); + rc2 = cmp_crc_log_wr_phys2_rd(len); + basic_test(rc1 == 0 && rc2 == 0); + cleanup_test(); +} + +static void write_two_pages_check_phys_crc(void) +{ + int rc1, rc2; + size_t len = TWO_PAGES; + + init_test(); + log_fill_buffer(len); + log_write_start(len); + flush_disk_buffers(); + phys1_read_start(len); + phys2_read_start(len); + rc1 = cmp_crc_log_wr_phys1_rd(len); + rc2 = cmp_crc_log_wr_phys2_rd(len); + basic_test(rc1 == 0 && rc2 == 0); + cleanup_test(); +} + +static void write_one_meg_check_phys_crc(void) +{ + int rc1, rc2; + size_t len = ONE_MEG; + + init_test(); + log_fill_buffer(len); + log_write_start(len); + flush_disk_buffers(); + phys1_read_start(len); + phys2_read_start(len); + rc1 = cmp_crc_log_wr_phys1_rd(len); + rc2 = cmp_crc_log_wr_phys2_rd(len); + basic_test(rc1 == 0 && rc2 == 0); + cleanup_test(); +} + +static void corrupt_read_correct_one_sector_disk1(void) +{ + int rc_data, rc_crc; + size_t len = ONE_SECTOR; + + init_test(); + phys_fill_buffer(len); + phys1_corrupt_and_write_start(len, 1); + phys2_write_start(len); + flush_disk_buffers(); + log_read_start(len); + rc_data = cmp_data_log_rd_phys2_wr(len); + rc_crc = cmp_crc_log_rd_phys2_wr(len); + basic_test(rc_data == 0 && rc_crc == 0); + cleanup_test(); +} + +static void corrupt_read_correct_one_sector_in_page_disk1(void) +{ + int rc_data, rc_crc; + size_t len = ONE_PAGE; + + init_test(); + phys_fill_buffer(len); + phys1_corrupt_and_write_start(len, 1); + phys2_write_start(len); + flush_disk_buffers(); + log_read_start(len); + rc_data = cmp_data_log_rd_phys2_wr(len); + rc_crc = cmp_crc_log_rd_phys2_wr(len); + basic_test(rc_data == 0 && rc_crc == 0); + cleanup_test(); +} + +static void corrupt_read_correct_one_page_disk1(void) +{ + int rc_data, rc_crc; + size_t len = ONE_PAGE; + + init_test(); + phys_fill_buffer(len); + phys1_corrupt_and_write_start(len, ONE_PAGE / ONE_SECTOR); + phys2_write_start(len); + flush_disk_buffers(); + log_read_start(len); + rc_data = cmp_data_log_rd_phys2_wr(len); + rc_crc = cmp_crc_log_rd_phys2_wr(len); + basic_test(rc_data == 0 && rc_crc == 0); + cleanup_test(); +} + +static void corrupt_read_correct_ten_pages_in_one_meg_disk1(void) +{ + int rc_data, rc_crc; + size_t len = ONE_MEG; + + init_test(); + phys_fill_buffer(len); + phys1_corrupt_and_write_start(len, TEN_PAGES / ONE_SECTOR); + phys2_write_start(len); + flush_disk_buffers(); + log_read_start(len); + rc_data = cmp_data_log_rd_phys2_wr(len); + rc_crc = cmp_crc_log_rd_phys2_wr(len); + basic_test(rc_data == 0 && rc_crc == 0); + cleanup_test(); +} + +static void corrupt_read_correct_one_meg_disk1(void) +{ + int rc_data, rc_crc; + size_t len = ONE_MEG; + + init_test(); + phys_fill_buffer(len); + phys1_corrupt_and_write_start(len, ONE_MEG / ONE_SECTOR); + phys2_write_start(len); + flush_disk_buffers(); + log_read_start(len); + rc_data = cmp_data_log_rd_phys2_wr(len); + rc_crc = cmp_crc_log_rd_phys2_wr(len); + basic_test(rc_data == 0 && rc_crc == 0); + cleanup_test(); +} + +static void recover_one_sector_disk1(void) +{ + int rc_data, rc_crc; + size_t len = ONE_SECTOR; + + init_test(); + phys_fill_buffer(len); + phys1_corrupt_and_write_start(len, 1); + phys2_write_start(len); + flush_disk_buffers(); + log_read_start(len); + flush_disk_buffers(); + phys1_read_start(len); + rc_data = cmp_data_log_rd_phys1_rd(len); + rc_crc = cmp_crc_log_rd_phys1_rd(len); + basic_test(rc_data == 0 && rc_crc == 0); + cleanup_test(); +} + +static void recover_one_sector_in_page_disk1(void) +{ + int rc_data, rc_crc; + size_t len = ONE_PAGE; + + init_test(); + phys_fill_buffer(len); + phys1_corrupt_and_write_start(len, 1); + phys2_write_start(len); + flush_disk_buffers(); + log_read_start(len); + flush_disk_buffers(); + phys1_read_start(len); + rc_data = cmp_data_log_rd_phys1_rd(len); + rc_crc = cmp_crc_log_rd_phys1_rd(len); + basic_test(rc_data == 0 && rc_crc == 0); + cleanup_test(); +} + +static void recover_one_page_disk1(void) +{ + int rc_data, rc_crc; + size_t len = ONE_PAGE; + + init_test(); + phys_fill_buffer(len); + phys1_corrupt_and_write_start(len, ONE_PAGE / ONE_SECTOR); + phys2_write_start(len); + flush_disk_buffers(); + log_read_start(len); + flush_disk_buffers(); + phys1_read_start(len); + rc_data = cmp_data_log_rd_phys1_rd(len); + rc_crc = cmp_crc_log_rd_phys1_rd(len); + basic_test(rc_data == 0 && rc_crc == 0); + cleanup_test(); +} + +static void recover_ten_page_in_one_meg_disk1(void) +{ + int rc_data, rc_crc; + size_t len = ONE_MEG; + + init_test(); + phys_fill_buffer(len); + phys1_corrupt_and_write_start(len, TEN_PAGES / ONE_SECTOR); + phys2_write_start(len); + flush_disk_buffers(); + log_read_start(len); + flush_disk_buffers(); + phys1_read_start(len); + rc_data = cmp_data_log_rd_phys1_rd(len); + rc_crc = cmp_crc_log_rd_phys1_rd(len); + basic_test(rc_data == 0 && rc_crc == 0); + cleanup_test(); +} + +static void recover_one_meg_disk1(void) +{ + int rc_data, rc_crc; + size_t len = ONE_MEG; + + init_test(); + phys_fill_buffer(len); + phys1_corrupt_and_write_start(len, ONE_MEG / ONE_SECTOR); + phys2_write_start(len); + flush_disk_buffers(); + log_read_start(len); + flush_disk_buffers(); + phys1_read_start(len); + rc_data = cmp_data_log_rd_phys1_rd(len); + rc_crc = cmp_crc_log_rd_phys1_rd(len); + basic_test(rc_data == 0 && rc_crc == 0); + cleanup_test(); +} + +static void corrupt_read_correct_one_sector_disk2(void) +{ + int rc_data, rc_crc; + size_t len = ONE_SECTOR; + + init_test(); + phys_fill_buffer(len); + phys1_write_start(len); + phys2_corrupt_and_write_start(len, 1); + flush_disk_buffers(); + log_read_start(len); + rc_data = cmp_data_log_rd_phys1_wr(len); + rc_crc = cmp_crc_log_rd_phys1_wr(len); + basic_test(rc_data == 0 && rc_crc == 0); + cleanup_test(); +} + +static void corrupt_read_correct_one_sector_in_page_disk2(void) +{ + int rc_data, rc_crc; + size_t len = ONE_PAGE; + + init_test(); + phys_fill_buffer(len); + phys1_write_start(len); + phys2_corrupt_and_write_start(len, 1); + flush_disk_buffers(); + log_read_start(len); + rc_data = cmp_data_log_rd_phys1_wr(len); + rc_crc = cmp_crc_log_rd_phys1_wr(len); + basic_test(rc_data == 0 && rc_crc == 0); + cleanup_test(); +} + +static void corrupt_read_correct_one_page_disk2(void) +{ + int rc_data, rc_crc; + size_t len = ONE_PAGE; + + init_test(); + phys_fill_buffer(len); + phys1_write_start(len); + phys2_corrupt_and_write_start(len, ONE_PAGE / ONE_SECTOR); + flush_disk_buffers(); + log_read_start(len); + rc_data = cmp_data_log_rd_phys1_wr(len); + rc_crc = cmp_crc_log_rd_phys1_wr(len); + basic_test(rc_data == 0 && rc_crc == 0); + cleanup_test(); +} + +static void corrupt_read_correct_ten_pages_in_one_meg_disk2(void) +{ + int rc_data, rc_crc; + size_t len = ONE_MEG; + + init_test(); + phys_fill_buffer(len); + phys1_write_start(len); + phys2_corrupt_and_write_start(len, TEN_PAGES / ONE_SECTOR); + flush_disk_buffers(); + log_read_start(len); + rc_data = cmp_data_log_rd_phys1_wr(len); + rc_crc = cmp_crc_log_rd_phys1_wr(len); + basic_test(rc_data == 0 && rc_crc == 0); + flush_disk_buffers(); + cleanup_test(); +} + +static void corrupt_read_correct_one_meg_disk2(void) +{ + int rc_data, rc_crc; + size_t len = ONE_MEG; + + init_test(); + phys_fill_buffer(len); + phys1_write_start(len); + phys2_corrupt_and_write_start(len, ONE_MEG / ONE_SECTOR); + flush_disk_buffers(); + log_read_start(len); + rc_data = cmp_data_log_rd_phys1_wr(len); + rc_crc = cmp_crc_log_rd_phys1_wr(len); + basic_test(rc_data == 0 && rc_crc == 0); + cleanup_test(); +} + +static void recover_one_sector_disk2(void) +{ + int rc_data, rc_crc; + size_t len = ONE_SECTOR; + + init_test(); + phys_fill_buffer(len); + phys1_write_start(len); + phys2_corrupt_and_write_start(len, 1); + flush_disk_buffers(); + log_read_start(len); + flush_disk_buffers(); + phys2_read_start(len); + rc_data = cmp_data_log_rd_phys2_rd(len); + rc_crc = cmp_crc_log_rd_phys2_rd(len); + basic_test(rc_data == 0 && rc_crc == 0); + cleanup_test(); +} + +static void recover_one_sector_in_page_disk2(void) +{ + int rc_data, rc_crc; + size_t len = ONE_PAGE; + + init_test(); + phys_fill_buffer(len); + phys1_write_start(len); + phys2_corrupt_and_write_start(len, 1); + flush_disk_buffers(); + log_read_start(len); + flush_disk_buffers(); + phys2_read_start(len); + rc_data = cmp_data_log_rd_phys2_rd(len); + rc_crc = cmp_crc_log_rd_phys2_rd(len); + basic_test(rc_data == 0 && rc_crc == 0); + cleanup_test(); +} + +static void recover_one_page_disk2(void) +{ + int rc_data, rc_crc; + size_t len = ONE_PAGE; + + init_test(); + phys_fill_buffer(len); + phys1_write_start(len); + phys2_corrupt_and_write_start(len, ONE_PAGE / ONE_SECTOR); + flush_disk_buffers(); + log_read_start(len); + flush_disk_buffers(); + phys2_read_start(len); + rc_data = cmp_data_log_rd_phys2_rd(len); + rc_crc = cmp_crc_log_rd_phys2_rd(len); + basic_test(rc_data == 0 && rc_crc == 0); + cleanup_test(); +} + +static void recover_ten_page_in_one_meg_disk2(void) +{ + int rc_data, rc_crc; + size_t len = ONE_MEG; + + init_test(); + phys_fill_buffer(len); + phys1_write_start(len); + phys2_corrupt_and_write_start(len, TEN_PAGES / ONE_SECTOR); + flush_disk_buffers(); + log_read_start(len); + flush_disk_buffers(); + phys2_read_start(len); + rc_data = cmp_data_log_rd_phys2_rd(len); + rc_crc = cmp_crc_log_rd_phys2_rd(len); + basic_test(rc_data == 0 && rc_crc == 0); + cleanup_test(); +} + +static void recover_one_meg_disk2(void) +{ + int rc_data, rc_crc; + size_t len = ONE_MEG; + + init_test(); + phys_fill_buffer(len); + phys1_write_start(len); + phys2_corrupt_and_write_start(len, ONE_MEG / ONE_SECTOR); + flush_disk_buffers(); + log_read_start(len); + flush_disk_buffers(); + phys2_read_start(len); + rc_data = cmp_data_log_rd_phys2_rd(len); + rc_crc = cmp_crc_log_rd_phys2_rd(len); + basic_test(rc_data == 0 && rc_crc == 0); + cleanup_test(); +} + +static void dual_error(void) +{ + ssize_t n; + size_t len = ONE_SECTOR; + + init_test(); + phys_fill_buffer(len); + phys1_corrupt_and_write_start(len, 1); + phys2_corrupt_and_write_start(len, 1); + flush_disk_buffers(); + n = log_read_start(len); + basic_test(n <= 0); + cleanup_test(); +} + +struct run_test_t test_array[] = { + { open_logical, "open(" LOGICAL_DISK_NAME ")", 4 }, + { close_logical, "close(" LOGICAL_DISK_NAME ")", 4 }, + { use_after_close_invalid, "use after close is invalid", 4 }, + { lseek_logical, "lseek(" LOGICAL_DISK_NAME ")", 4 }, + { read_one_sector_start, "read one sector from the start", 5 }, + { read_one_sector_middle, "read one sector from the middle", 5 }, + { read_one_sector_end, "read one sector from the end", 5 }, + { write_one_sector_start, "write one sector from the start", 5 }, + { write_one_sector_middle, "write one sector from the middle", 5 }, + { write_one_sector_end, "write one sector from the end", 5 }, + { read_one_page_start, "read one page from the start", 5 }, + { read_one_page_middle, "read one page from the middle", 5 }, + { read_one_page_end, "read one page from the end", 5 }, + { write_one_page_start, "write one page from the start", 5 }, + { write_one_page_middle, "write one page from the middle", 5 }, + { write_one_page_end, "write one page from the end", 5 }, + { read_two_pages_start, "read two pages from the start", 5 }, + { read_two_pages_middle, "read two pages from the middle", 5 }, + { read_two_pages_end, "read two pages from the end", 5 }, + { write_two_pages_start, "write two pages from the start", 5 }, + { write_two_pages_middle, "write two pages from the middle", 5 }, + { write_two_pages_end, "write two pages from the end", 5 }, + { read_one_meg_start, "read 1MB from the start", 5 }, + { read_one_meg_middle, "read 1MB from the middle", 5 }, + { read_one_meg_end, "read 1MB from the end", 5 }, + { write_one_meg_start, "write 1MB from the start", 5 }, + { write_one_meg_middle, "write 1MB from the middle", 5 }, + { write_one_meg_end, "write 1MB from the end", 5 }, + { read_boundary_one_sector, "read one sector outside boundary", 7 }, + { read_boundary_one_page, "read one page with contents outside boundary", 7 }, + { read_boundary_two_pages, "read two pages with contents outside boundary", 7 }, + { read_boundary_one_meg, "read 1MB with contents outside boundary", 7 }, + { write_boundary_one_sector, "write one sector outside boundary", 7 }, + { write_boundary_one_page, "write one page with contents outside boundary", 7 }, + { write_boundary_two_pages, "write two pages with contents outside boundary", 7 }, + { write_boundary_one_meg, "write 1MB with contents outside boundary", 7 }, + { memory_is_freed, "check memory is freed", 24 }, + { write_one_sector_check_phys1, "write one sector and check disk1 (no CRC check)", 15 }, + { write_one_page_check_phys1, "write one page and check disk1 (no CRC check)", 15 }, + { write_two_pages_check_phys1, "write two pages and check disk1 (no CRC check)", 15 }, + { write_one_meg_check_phys1, "write 1MB and check disk1 (no CRC check)", 15 }, + { write_one_sector_check_phys, "write one sector and check disks (no CRC check)", 15 }, + { write_one_page_check_phys, "write one page and check disks (no CRC check)", 15 }, + { write_two_pages_check_phys, "write two pages and check disks (no CRC check)", 15 }, + { write_one_meg_check_phys, "write 1MB and check disks (no CRC check)", 15 }, + { read_one_sector_after_write, "read one sector after physical write (correct CRC)", 16 }, + { read_one_page_after_write, "read one page after physical write (correct CRC)", 16 }, + { read_two_pages_after_write, "read two pages after physical write (correct CRC)", 16 }, + { read_one_meg_after_write, "read 1MB after physical write (correct CRC)", 16 }, + { write_one_sector_check_phys1_crc, "write one sector and check disk1 (do CRC check)", 16 }, + { write_one_page_check_phys1_crc, "write one page and check disk1 (do CRC check)", 16 }, + { write_two_pages_check_phys1_crc, "write two pages and check disk1 (do CRC check)", 16 }, + { write_one_meg_check_phys1_crc, "write 1MB and check disk1 (do CRC check)", 16 }, + { write_one_sector_check_phys_crc, "write one sector and check disks (do CRC check)", 16 }, + { write_one_page_check_phys_crc, "write one page and check disks (do CRC check)", 16 }, + { write_two_pages_check_phys_crc, "write two pages and check disks (do CRC check)", 16 }, + { write_one_meg_check_phys_crc, "write 1MB and check disks (do CRC check)", 16 }, + { corrupt_read_correct_one_sector_disk1, "read corrected one sector error from disk1", 18 }, + { corrupt_read_correct_one_sector_in_page_disk1, "read corrected one sector in page error from disk1", 18 }, + { corrupt_read_correct_one_page_disk1, "read corrected one page error from disk1", 18 }, + { corrupt_read_correct_ten_pages_in_one_meg_disk1, "read corrected ten pages error in one meg from disk1", 18 }, + { corrupt_read_correct_one_meg_disk1, "read corrected one meg error from disk1", 18 }, + { recover_one_sector_disk1, "recover one sector error from disk1", 18 }, + { recover_one_sector_in_page_disk1, "recover one sector error in one page from disk1", 18 }, + { recover_one_page_disk1, "recover one page filled with errors from disk1", 18 }, + { recover_ten_page_in_one_meg_disk1, "recover ten pages error in 1MB from disk1", 18 }, + { recover_one_meg_disk1, "recover 1MB filled with errors from disk1", 18 }, + { corrupt_read_correct_one_sector_disk2, "read corrected one sector error from disk2", 18 }, + { corrupt_read_correct_one_sector_in_page_disk2, "read corrected one sector in page error from disk2", 18 }, + { corrupt_read_correct_one_page_disk2, "read corrected one page error from disk2", 18 }, + { corrupt_read_correct_ten_pages_in_one_meg_disk2, "read corrected ten pages error in one meg from disk2", 18 }, + { corrupt_read_correct_one_meg_disk2, "read corrected one meg error from disk2", 18 }, + { recover_one_sector_disk2, "recover one sector error from disk2", 18 }, + { recover_one_sector_in_page_disk2, "recover one sector error in one page from disk2", 18 }, + { recover_one_page_disk2, "recover one page filled with errors from disk2", 18 }, + { recover_ten_page_in_one_meg_disk2, "recover ten pages error in 1MB from disk2", 18 }, + { recover_one_meg_disk2, "recover 1MB filled with errors from disk2", 18 }, + { dual_error, "signal error when both physical disks are corrupted", 12 }, +}; +size_t max_points = 900; + +/* Return number of tests in test_array. */ +size_t get_num_tests(void) +{ + return sizeof(test_array) / sizeof(test_array[0]); +} diff --git a/checker/4-stp-checker/Makefile b/checker/4-stp-checker/Makefile new file mode 100644 index 0000000..e0c2b42 --- /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 0000000..a1d04d8 --- /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 0000000..810e77f --- /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 0000000..d5074dd --- /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 0000000..a54e962 --- /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 0000000..838f993 --- /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 0000000..d6c729e --- /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 0000000..fb70843 --- /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 0000000..4bcafad --- /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 0000000..f06cb83 --- /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 3e1d1a6..424e3dc 100755 --- a/checker/checker.sh +++ b/checker/checker.sh @@ -1,34 +1,104 @@ #!/bin/bash -TIMEOUT=300 # 5 min SO2_WORKSPACE=/linux/tools/labs +SO2_VM_LOG=/tmp/so2_vm_log.txt + +ASSIGNMENT0_TIMEOUT=300 # 5 min ASSIGNMENT0_MOD=list.ko ASSIGNMENT0_DIR=${SO2_WORKSPACE}/skels/assignments/0-list +ASSIGNMENT0_CHECKER_LOCAL_DIR=checker/0-list-checker ASSIGNMENT0_CHECKER_DIR=${SO2_WORKSPACE}/skels/assignments/0-list-checker ASSIGNMENT0_OUTPUT=${SO2_WORKSPACE}/skels/0-list-output ASSIGNMENT0_FINISHED=${SO2_WORKSPACE}/skels/0-list-finished +ASSIGNMENT1_TIMEOUT=300 # 5 min ASSIGNMENT1_MOD=tracer.ko ASSIGNMENT1_DIR=${SO2_WORKSPACE}/skels/assignments/1-tracer +ASSIGNMENT1_CHECKER_LOCAL_DIR=checker/1-tracer-checker ASSIGNMENT1_CHECKER_DIR=${SO2_WORKSPACE}/skels/assignments/1-tracer-checker ASSIGNMENT1_OUTPUT=${SO2_WORKSPACE}/skels/1-tracer-output ASSIGNMENT1_FINISHED=${SO2_WORKSPACE}/skels/1-tracer-finished ASSIGNMENT1_HEADER_OVERWRITE=${SO2_WORKSPACE}/templates/assignments/1-tracer/tracer.h ASSIGNMENT1_CHECKER_AUX_LIST="${ASSIGNMENT1_CHECKER_DIR}/_helper/tracer_helper.ko" +ASSIGNMENT2_TIMEOUT=300 # 5 min +ASSIGNMENT2_MOD=uart16550.ko +ASSIGNMENT2_DIR=${SO2_WORKSPACE}/skels/assignments/2-uart +ASSIGNMENT2_CHECKER_LOCAL_DIR=checker/2-uart-checker +ASSIGNMENT2_CHECKER_DIR=${SO2_WORKSPACE}/skels/assignments/2-uart-checker +ASSIGNMENT2_OUTPUT=${SO2_WORKSPACE}/skels/2-uart-output +ASSIGNMENT2_FINISHED=${SO2_WORKSPACE}/skels/2-uart-finished +ASSIGNMENT2_HEADER_OVERWRITE=${SO2_WORKSPACE}/templates/assignments/2-uart/uart16550.h +ASSIGNMENT2_CHECKER_AUX_LIST="${ASSIGNMENT2_CHECKER_DIR}/_test/solution.ko" + +ASSIGNMENT3_TIMEOUT=360 # 6 min +ASSIGNMENT3_MOD=ssr.ko +ASSIGNMENT3_DIR=${SO2_WORKSPACE}/skels/assignments/3-raid +ASSIGNMENT3_CHECKER_LOCAL_DIR=checker/3-raid-checker +ASSIGNMENT3_CHECKER_DIR=${SO2_WORKSPACE}/skels/assignments/3-raid-checker +ASSIGNMENT3_OUTPUT=${SO2_WORKSPACE}/skels/3-raid-output +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() { echo "Usage: $0 <assignment>" exit 1 } + + +recover_grade_from_timeout() +{ + local output=$1 + if [ ! -f $output ]; then + echo "$output not available" + else + points_total=$(echo $(cat $output | grep "....passed" | egrep -o "/.*[0-9]+\.*[0-9]*.*\]" | egrep -o "[0-9]+\.*[0-9]*" | head -n 1)) + list=$(echo $(cat $output | grep "....passed" | egrep -o "\[.*[0-9]+\.*[0-9]*.*\/" | egrep -o "[0-9]+\.*[0-9]*") | sed -e 's/\s\+/,/g') + recovered_points=$(python3 -c "print(sum([$list]))") + echo "Recovered from timeout => Total: [$recovered_points/$points_total]" + echo "Please note that this is not a DIRECT checker output! Other penalties may be applied!" + echo "Please contact a teaching assistant" + python3 -c "print('Total: ' + str(int ($recovered_points * 100 / $points_total)) + '/' + '100')" + fi +} + timeout_exceeded() { - echo TIMEOUT EXCEEDED !!! killing the process - echo "<VMCK_NEXT_END>" + local output=$1 pkill -SIGKILL qemu - exit 1 + echo "" + echo "TIMEOUT EXCEEDED !!! killing the process" + if [[ $RECOVER_GRADE_TIMEOUT == 0 ]]; then + if [ -f $output ]; then + echo "$output not available" + else + cat $output + fi + echo "dumping SO2_VM_LOG=${SO2_VM_LOG} output" + cat $SO2_VM_LOG + + echo "The Recover Grade Timeout option is not set! Please contact a teaching assistant!" + else + recover_grade_from_timeout $output + fi + echo "<VMCK_NEXT_END>" + # exit successfully for vmchecker-next to process output + exit 0 # TODO: fixme } compute_total() @@ -46,8 +116,10 @@ compute_total() dump_output() { local output=$1 + local timeout=$2 echo "<VMCK_NEXT_BEGIN>" cat $output + echo "Running time $timeout/$TIMEOUT" } @@ -67,18 +139,22 @@ run_checker() { local assignment_mod=$1 local assignment_dir=$2 - local checker_dir=$3 - local output=$4 - local finished=$5 - local assignment=$6 - local header_overwrite=$7 - local aux_modules=$8 + local local_checker_dir=$3 + local checker_dir=$4 + local output=$5 + local finished=$6 + local assignment=$7 + local header_overwrite=$8 + local aux_modules=$9 local module_path="${assignment_dir}/${assignment_mod}" echo "Copying the contents of src/ into $assignment_dir" cp src/* $assignment_dir + echo "Copying the contents of $local_checker_dir into $checker_dir" + cp -r $local_checker_dir/* $checker_dir + echo "Checking if $assignment_mod exists before build" if [ -f $module_path ]; then echo "$assignment_mod shouldn't exists. Removing ${module_path}" @@ -117,7 +193,7 @@ run_checker() if [ ! -f $module_path ]; then error_message $assignment_mod # exit successfully for vmchecker-next to process output - exit 0 # TODO: changeme + exit 0 # TODO: fixme fi # copy *.ko in checker @@ -133,8 +209,9 @@ run_checker() done fi - LINUX_ADD_CMDLINE="so2=$assignment" ./qemu/run-qemu.sh &> /dev/null & - + LINUX_ADD_CMDLINE="so2=$assignment" make checker &> ${SO2_VM_LOG} & + + timeout=0 echo -n "CHECKER IS RUNNING" while [ ! -f $finished ] do @@ -144,27 +221,45 @@ run_checker() dump_output $output compute_total $output fi - timeout_exceeded + timeout_exceeded $output fi sleep 2 (( timeout += 2 )) echo -n . done echo "" - dump_output $output + dump_output $output $timeout compute_total $output popd &> /dev/null } case $1 in 0-list) - run_checker $ASSIGNMENT0_MOD $ASSIGNMENT0_DIR $ASSIGNMENT0_CHECKER_DIR $ASSIGNMENT0_OUTPUT $ASSIGNMENT0_FINISHED $1 + TIMEOUT=$ASSIGNMENT0_TIMEOUT + 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 $ASSIGNMENT0_MOD $ASSIGNMENT0_DIR $ASSIGNMENT0_CHECKER_LOCAL_DIR $ASSIGNMENT0_CHECKER_DIR $ASSIGNMENT0_OUTPUT $ASSIGNMENT0_FINISHED $1 ;; 1-tracer) - run_checker $ASSIGNMENT1_MOD $ASSIGNMENT1_DIR $ASSIGNMENT1_CHECKER_DIR $ASSIGNMENT1_OUTPUT $ASSIGNMENT1_FINISHED $1 $ASSIGNMENT1_HEADER_OVERWRITE $ASSIGNMENT1_CHECKER_AUX_LIST - - + TIMEOUT=$ASSIGNMENT1_TIMEOUT + 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 $ASSIGNMENT1_MOD $ASSIGNMENT1_DIR $ASSIGNMENT1_CHECKER_LOCAL_DIR $ASSIGNMENT1_CHECKER_DIR $ASSIGNMENT1_OUTPUT $ASSIGNMENT1_FINISHED $1 $ASSIGNMENT1_HEADER_OVERWRITE $ASSIGNMENT1_CHECKER_AUX_LIST ;; + 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 + 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) + TIMEOUT=$ASSIGNMENT3_TIMEOUT + 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 afc38b6..546f236 100644 --- a/checker/checker_daemons/so2_vm_checker_daemon.sh +++ b/checker/checker_daemons/so2_vm_checker_daemon.sh @@ -10,22 +10,58 @@ ASSIGNMENT1_CHECKER=/home/root/skels/assignments/1-tracer-checker ASSIGNMENT1_OUTPUT=/home/root/skels/1-tracer-output ASSIGNMENT1_FINISHED=/home/root/skels/1-tracer-finished +ASSIGNMENT2_CHECKER=/home/root/skels/assignments/2-uart-checker +ASSIGNMENT2_OUTPUT=/home/root/skels/2-uart-output +ASSIGNMENT2_FINISHED=/home/root/skels/2-uart-finished + +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 - +} + +assign3_raid() +{ + 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() { @@ -37,6 +73,15 @@ start() 1-tracer) assign1_tracer ;; + 2-uart) + assign2_uart + ;; + 3-raid) + assign3_raid + ;; + 4-stp) + assign4_stp + ;; *) echo "Unknown option" exit 0 diff --git a/local.sh b/local.sh index 6c0cc49..adfa8a9 100755 --- a/local.sh +++ b/local.sh @@ -153,7 +153,7 @@ docker_push() { [ -z "$token" ] && LOG_FATAL "No token provided. Push operation will be aborted..." LOG_INFO "Pushing Docker image..." - +set -x docker login "${registry}" -u "${user}" -p "${token}" docker push "${registry}/${image_name}:${tag}" } @@ -198,7 +198,7 @@ docker_interactive() { set -x cp -R ${ASSIGNMENT_CHECKER_DIR}/* "$tmpdir" - docker run $privileged --rm -it \ + docker run $privileged --rm -it --cap-add=NET_ADMIN --device /dev/net/tun:/dev/net/tun \ --mount type=bind,source="$SRC_DIR",target="$ASSIGNMENT_MOUNT_DIR" \ --mount type=bind,source="$tmpdir",target="$ASSIGNMENT_CHECKER_MOUNT_DIR" \ --workdir "$SO2_WORKSPACE" \ @@ -265,7 +265,7 @@ checker_main() { # In your checker script if you must use absolute paths please use $CI_PROJECT_DIR to reference the location of your directory, # otherwise stick to relative paths. # It is guaranteed that the current working directory in which checker.sh will run is $CI_PROJECT_DIR/checker. - docker run $privileged --rm \ + docker run $privileged --rm --cap-add=NET_ADMIN --device /dev/net/tun:/dev/net/tun \ --mount type=bind,source="$tmpdir",target="$MOUNT_PROJECT_DIRECTORY" \ "$image_name" /bin/bash -c "rm -rf /usr/local/bin/bash; cd \"$MOUNT_PROJECT_DIRECTORY\"; \"$MOUNT_PROJECT_DIRECTORY/checker/checker.sh\" \"${script_args[@]}\"" # remove bash middleware script -- GitLab