From 46c36b11dac8092fb79671955439f33a67ce9f54 Mon Sep 17 00:00:00 2001 From: Eclypsed Date: Sun, 31 May 2026 14:34:00 -0400 Subject: [PATCH] Removed submodules --- lab1-ptrs/.gitignore | 22 + lab1-ptrs/Makefile | 51 ++ lab1-ptrs/main.c | 80 ++ lab1-ptrs/movies.c | 97 +++ lab1-ptrs/movies.h | 16 + lab1-ptrs/tests/Makefile | 89 +++ lab1-ptrs/tests/expected/.INTEG_movies.sha | 1 + .../tests/expected/.INTEG_movies_inc.sha | 1 + lab1-ptrs/tests/expected/INTEG_movies.txt | 4 + lab1-ptrs/tests/expected/INTEG_movies_inc.txt | 4 + lab1-ptrs/tests/integration.sh | 75 ++ lab1-ptrs/tests/itests.include | 7 + lab1-ptrs/tests/public.c | 95 +++ lab1-ptrs/tests/style.sh | 53 ++ lab1-ptrs/tests/testsuite.c | 36 + lab2-cmd/.gitignore | 20 + lab2-cmd/Makefile | 51 ++ lab2-cmd/effects.c | 88 ++ lab2-cmd/effects.h | 13 + lab2-cmd/main.c | 61 ++ lab2-cmd/model.c | 130 +++ lab2-cmd/model.h | 75 ++ lab2-cmd/tests/Makefile | 88 ++ lab2-cmd/tests/expected/.INTEG_command.sha | 1 + lab2-cmd/tests/expected/.INTEG_execute.sha | 1 + .../tests/expected/.INTEG_execute_flags.sha | 1 + .../tests/expected/.INTEG_execute_name.sha | 1 + lab2-cmd/tests/expected/.INTEG_pipe.sha | 1 + lab2-cmd/tests/expected/.INTEG_pipe_flags.sha | 1 + lab2-cmd/tests/expected/INTEG_command.txt | 2 + lab2-cmd/tests/expected/INTEG_execute.txt | 4 + .../tests/expected/INTEG_execute_flags.txt | 8 + .../tests/expected/INTEG_execute_name.txt | 6 + lab2-cmd/tests/expected/INTEG_pipe.txt | 9 + lab2-cmd/tests/expected/INTEG_pipe_flags.txt | 15 + lab2-cmd/tests/integration.sh | 75 ++ lab2-cmd/tests/itests.include | 11 + lab2-cmd/tests/private.o | Bin 0 -> 26024 bytes lab2-cmd/tests/public.c | 53 ++ lab2-cmd/tests/style.sh | 53 ++ lab2-cmd/tests/testsuite.c | 34 + lab2-cmd/tests/utests.sh | 21 + lab3-proc/.gitignore | 20 + lab3-proc/Makefile | 51 ++ lab3-proc/child.c | 39 + lab3-proc/child.h | 6 + lab3-proc/main.c | 87 ++ lab3-proc/signals.c | 110 +++ lab3-proc/signals.h | 7 + lab3-proc/tests/Makefile | 87 ++ lab3-proc/tests/expected/.INTEG_child.sha | 1 + lab3-proc/tests/expected/.INTEG_kill.sha | 1 + lab3-proc/tests/expected/.INTEG_segfault.sha | 1 + lab3-proc/tests/expected/.INTEG_sigint.sha | 1 + lab3-proc/tests/expected/.INTEG_status.sha | 1 + lab3-proc/tests/expected/INTEG_child.txt | 3 + lab3-proc/tests/expected/INTEG_kill.txt | 1 + lab3-proc/tests/expected/INTEG_segfault.txt | 3 + lab3-proc/tests/expected/INTEG_sigint.txt | 3 + lab3-proc/tests/expected/INTEG_status.txt | 3 + lab3-proc/tests/integration.sh | 75 ++ lab3-proc/tests/itests.include | 10 + lab3-proc/tests/private.o | Bin 0 -> 1200 bytes lab3-proc/tests/public.c | 46 ++ lab3-proc/tests/style.sh | 53 ++ lab3-proc/tests/testsuite.c | 34 + lab4-ipc/.gitignore | 20 + lab4-ipc/Makefile | 52 ++ lab4-ipc/data/f1.txt | 1 + lab4-ipc/data/f2.txt | 1 + lab4-ipc/data/f3.txt | 1 + lab4-ipc/data/index | Bin 0 -> 36 bytes lab4-ipc/main.c | 87 ++ lab4-ipc/pipe.c | 118 +++ lab4-ipc/pipe.h | 12 + lab4-ipc/tests/Makefile | 86 ++ lab4-ipc/tests/data/f1.txt | 1 + lab4-ipc/tests/data/f2.txt | 1 + lab4-ipc/tests/data/f3.txt | 1 + lab4-ipc/tests/data/index | Bin 0 -> 36 bytes lab4-ipc/tests/expected/.INTEG_fork.sha | 1 + .../tests/expected/.INTEG_fork_no_exec.sha | 1 + .../tests/expected/.INTEG_fork_no_file.sha | 1 + lab4-ipc/tests/expected/.INTEG_spawn.sha | 1 + .../tests/expected/.INTEG_spawn_no_exec.sha | 1 + .../tests/expected/.INTEG_spawn_no_file.sha | 1 + lab4-ipc/tests/expected/INTEG_fork.txt | 1 + .../tests/expected/INTEG_fork_no_exec.txt | 1 + .../tests/expected/INTEG_fork_no_file.txt | 1 + lab4-ipc/tests/expected/INTEG_spawn.txt | 1 + .../tests/expected/INTEG_spawn_no_exec.txt | 1 + .../tests/expected/INTEG_spawn_no_file.txt | 1 + lab4-ipc/tests/integration.sh | 75 ++ lab4-ipc/tests/itests.include | 11 + lab4-ipc/tests/private.o | Bin 0 -> 6216 bytes lab4-ipc/tests/public.c | 35 + lab4-ipc/tests/style.sh | 53 ++ lab4-ipc/tests/testsuite.c | 34 + lab5-net/.gitignore | 20 + lab5-net/Makefile | 52 ++ lab5-net/client.c | 108 +++ lab5-net/client.h | 12 + lab5-net/main.c | 106 +++ lab5-net/tests/Makefile | 86 ++ lab5-net/tests/expected/.INTEG_53_ns3.sha | 1 + .../tests/expected/.INTEG_53_ns3_ipv6.sha | 1 + .../tests/expected/.INTEG_localhost_ipv4.sha | 1 + .../tests/expected/.INTEG_localhost_ipv6.sha | 1 + lab5-net/tests/expected/.INTEG_w3_ipv4.sha | 1 + lab5-net/tests/expected/.INTEG_w3_ipv6.sha | 1 + lab5-net/tests/expected/INTEG_53_ns3.txt | 1 + .../tests/expected/INTEG_53_ns3_ipv6-2.txt | 1 + lab5-net/tests/expected/INTEG_53_ns3_ipv6.txt | 1 + .../tests/expected/INTEG_localhost_ipv4.txt | 1 + .../tests/expected/INTEG_localhost_ipv6.txt | 1 + lab5-net/tests/expected/INTEG_w3_ipv4.txt | 1 + lab5-net/tests/expected/INTEG_w3_ipv6-2.txt | 1 + lab5-net/tests/expected/INTEG_w3_ipv6.txt | 1 + lab5-net/tests/integration.sh | 75 ++ lab5-net/tests/itests.include | 14 + lab5-net/tests/private.o | Bin 0 -> 15520 bytes lab5-net/tests/public.c | 100 +++ lab5-net/tests/style.sh | 53 ++ lab5-net/tests/testsuite.c | 34 + lab7-threads/.gitignore | 24 + lab7-threads/Makefile | 52 ++ lab7-threads/discrete.c | 53 ++ lab7-threads/discrete.h | 9 + lab7-threads/main.c | 200 +++++ lab7-threads/private.h | 6 + lab7-threads/pthread.c | 152 ++++ lab7-threads/pthread.h | 25 + lab7-threads/tests/Makefile | 86 ++ .../tests/expected/INTEG_logs_large.txt | 4 + .../tests/expected/INTEG_logs_large_thr.txt | 4 + .../tests/expected/INTEG_logs_medium.txt | 9 + .../tests/expected/INTEG_logs_medium_thr.txt | 9 + .../tests/expected/INTEG_logs_small.txt | 12 + .../tests/expected/INTEG_logs_small_thr.txt | 12 + lab7-threads/tests/integration.sh | 59 ++ lab7-threads/tests/itests.include | 11 + lab7-threads/tests/public.c | 217 +++++ lab7-threads/tests/testsuite.c | 32 + lab8-synch/.gitignore | 24 + lab8-synch/Makefile | 52 ++ lab8-synch/main.c | 93 +++ lab8-synch/mutex.c | 89 +++ lab8-synch/mutex.h | 10 + lab8-synch/pingpong.c | 94 +++ lab8-synch/pingpong.h | 6 + lab8-synch/tests/Makefile | 88 ++ lab8-synch/tests/expected/.INTEG_run_10.sha | 0 lab8-synch/tests/expected/.INTEG_run_5.sha | 0 .../tests/expected/.INTEG_run_mutex.sha | 1 + .../tests/expected/.INTEG_run_pingpong.sha | 1 + .../tests/expected/.INTEG_run_pingpong_10.sha | 1 + .../tests/expected/.INTEG_run_pingpong_5.sha | 1 + lab8-synch/tests/expected/INTEG_run_mutex.txt | 2 + .../tests/expected/INTEG_run_pingpong.txt | 6 + .../tests/expected/INTEG_run_pingpong_10.txt | 24 + .../tests/expected/INTEG_run_pingpong_5.txt | 14 + .../tests/expected/MIN_run_pingpong.txt | 24 + lab8-synch/tests/integration.sh | 75 ++ lab8-synch/tests/itests.include | 9 + lab8-synch/tests/public.c | 98 +++ lab8-synch/tests/testsuite.c | 32 + p1-sh/.gitignore | 24 + p1-sh/Makefile | 61 ++ p1-sh/PHASE-1.txt | 15 + p1-sh/PHASE-2.txt | 26 + p1-sh/PHASE-3.txt | 27 + p1-sh/PHASE-4.txt | 73 ++ p1-sh/cut_data/data.csv | 2 + p1-sh/cut_data/data.spaces | 2 + p1-sh/data/.hidden.txt | 1 + p1-sh/data/FIRST.txt | 1 + p1-sh/data/empty.txt | 0 p1-sh/data/pwd.txt | 2 + p1-sh/data/yat.txt | 1 + p1-sh/src/builtins.c | 281 +++++++ p1-sh/src/builtins.h | 14 + p1-sh/src/cmd.c | 202 +++++ p1-sh/src/cmd.h | 74 ++ p1-sh/src/hash.c | 284 +++++++ p1-sh/src/hash.h | 15 + p1-sh/src/main.c | 65 ++ p1-sh/src/process.c | 152 ++++ p1-sh/src/process.h | 25 + p1-sh/src/shell.c | 43 + p1-sh/src/shell.h | 6 + p1-sh/tests/Makefile | 89 +++ p1-sh/tests/bin/cat | Bin 0 -> 18992 bytes p1-sh/tests/bin/chmod | Bin 0 -> 18472 bytes p1-sh/tests/bin/cut | Bin 0 -> 20080 bytes p1-sh/tests/bin/env | Bin 0 -> 18488 bytes p1-sh/tests/bin/head | Bin 0 -> 19776 bytes p1-sh/tests/bin/ls | Bin 0 -> 21072 bytes p1-sh/tests/bin/repeat | Bin 0 -> 18096 bytes p1-sh/tests/cut_data/data.csv | 2 + p1-sh/tests/cut_data/data.spaces | 2 + p1-sh/tests/data/.hidden.txt | 1 + p1-sh/tests/data/FIRST.txt | 1 + p1-sh/tests/data/empty.txt | 0 p1-sh/tests/data/pwd.txt | 2 + p1-sh/tests/data/yat.txt | 1 + p1-sh/tests/expected/A_cat_tail.txt | 10 + p1-sh/tests/expected/A_env.txt | 17 + p1-sh/tests/expected/A_pipe.txt | 17 + p1-sh/tests/expected/A_which.txt | 6 + p1-sh/tests/expected/B_env.txt | 18 + p1-sh/tests/expected/B_export_unset.txt | 9 + p1-sh/tests/expected/B_repeat.txt | 9 + p1-sh/tests/expected/B_return_code.txt | 13 + p1-sh/tests/expected/B_setenv.txt | 14 + p1-sh/tests/expected/C_binaries.txt | 14 + p1-sh/tests/expected/C_binaries_bad-2.txt | 4 + p1-sh/tests/expected/C_binaries_bad.txt | 6 + p1-sh/tests/expected/C_binaries_flags.txt | 35 + p1-sh/tests/expected/C_cd_pwd.txt | 7 + p1-sh/tests/expected/C_chmod.txt | 35 + p1-sh/tests/expected/C_cut.txt | 17 + p1-sh/tests/expected/C_cut_bad.txt | 15 + p1-sh/tests/expected/C_echo.txt | 7 + p1-sh/tests/expected/C_echo_space.txt | 8 + p1-sh/tests/expected/C_which.txt | 18 + p1-sh/tests/expected/D_echo.txt | 6 + p1-sh/tests/expected/D_quit.txt | 2 + p1-sh/tests/integration.sh | 95 +++ p1-sh/tests/itests.include | 40 + p1-sh/tests/public.c | 114 +++ p1-sh/tests/scripts/bins.txt | 3 + p1-sh/tests/scripts/bins_bad.txt | 3 + p1-sh/tests/scripts/bins_chmod.txt | 12 + p1-sh/tests/scripts/bins_flags.txt | 12 + p1-sh/tests/scripts/cut.txt | 6 + p1-sh/tests/scripts/cut_bad.txt | 4 + p1-sh/tests/scripts/dirs.txt | 4 + p1-sh/tests/scripts/echo.txt | 3 + p1-sh/tests/scripts/echo_min.txt | 3 + p1-sh/tests/scripts/echo_space.txt | 3 + p1-sh/tests/scripts/env.txt | 5 + p1-sh/tests/scripts/env_pipe.txt | 5 + p1-sh/tests/scripts/export.txt | 5 + p1-sh/tests/scripts/pipe.txt | 6 + p1-sh/tests/scripts/quit.txt | 1 + p1-sh/tests/scripts/rc.txt | 5 + p1-sh/tests/scripts/repeat.txt | 5 + p1-sh/tests/scripts/setenv.txt | 4 + p1-sh/tests/scripts/tail.txt | 4 + p1-sh/tests/scripts/unset.txt | 8 + p1-sh/tests/scripts/which.txt | 9 + p1-sh/tests/scripts/which_exec.txt | 3 + p1-sh/tests/style.sh | 53 ++ p1-sh/tests/testsuite.c | 32 + p1-sh/utils/Makefile | 41 + p1-sh/utils/cat.c | 41 + p1-sh/utils/chmod.c | 70 ++ p1-sh/utils/cut.c | 75 ++ p1-sh/utils/env.c | 65 ++ p1-sh/utils/head.c | 58 ++ p1-sh/utils/ls.c | 148 ++++ p1-sh/utils/repeat.c | 76 ++ p2-dns/.gitignore | 22 + p2-dns/Makefile | 59 ++ p2-dns/PHASE-1.txt | 39 + p2-dns/PHASE-2.txt | 26 + p2-dns/PHASE-3.txt | 24 + p2-dns/PHASE-4.txt | 30 + p2-dns/port_utils.o | Bin 0 -> 4560 bytes p2-dns/src/client.c | 255 ++++++ p2-dns/src/dns.c | 393 +++++++++ p2-dns/src/dns.h | 62 ++ p2-dns/src/port_utils.h | 6 + p2-dns/src/utils.c | 53 ++ p2-dns/src/utils.h | 9 + p2-dns/tests/Makefile | 87 ++ p2-dns/tests/dukens | Bin 0 -> 44512 bytes p2-dns/tests/expected/A_cname_a.txt | 9 + p2-dns/tests/expected/A_cname_additional.txt | 11 + p2-dns/tests/expected/A_cname_basic.txt | 8 + p2-dns/tests/expected/A_example_soa.txt | 8 + p2-dns/tests/expected/A_jmu_soa.txt | 8 + p2-dns/tests/expected/A_no_ipv6_authority.txt | 11 + p2-dns/tests/expected/A_no_ipv6_subauth.txt | 8 + p2-dns/tests/expected/A_no_ptr.txt | 8 + p2-dns/tests/expected/A_ptr_basic.txt | 8 + p2-dns/tests/expected/A_ptr_mixed.txt | 12 + p2-dns/tests/expected/A_ptr_multiple.txt | 13 + p2-dns/tests/expected/B_a_multiple.txt | 13 + p2-dns/tests/expected/B_aaaa.txt | 13 + p2-dns/tests/expected/B_double_compress.txt | 9 + p2-dns/tests/expected/B_empty_domain.txt | 8 + p2-dns/tests/expected/B_jmu_mx.txt | 8 + p2-dns/tests/expected/B_jmu_ns.txt | 9 + p2-dns/tests/expected/B_no_ipv6.txt | 5 + p2-dns/tests/expected/C_a_basic.txt | 8 + p2-dns/tests/expected/C_a_compressed.txt | 8 + p2-dns/tests/expected/C_a_localhost.txt | 8 + p2-dns/tests/expected/C_bad_domain.txt | 5 + p2-dns/tests/expected/D_ping-2.txt | 14 + p2-dns/tests/expected/D_ping.txt | 14 + p2-dns/tests/integration.sh | 97 +++ p2-dns/tests/itests.include | 32 + p2-dns/tests/mappings.bin | Bin 0 -> 1670 bytes p2-dns/tests/style.sh | 53 ++ p2-dns/tests/testsuite.c | 0 p3-dhcps/.gitignore | 27 + p3-dhcps/Makefile | 59 ++ p3-dhcps/port_utils.o | Bin 0 -> 4560 bytes p3-dhcps/src/dhcp.c | 193 +++++ p3-dhcps/src/dhcp.h | 120 +++ p3-dhcps/src/format.c | 207 +++++ p3-dhcps/src/format.h | 12 + p3-dhcps/src/main.c | 54 ++ p3-dhcps/src/port_utils.h | 6 + p3-dhcps/src/server.c | 519 ++++++++++++ p3-dhcps/src/server.h | 30 + p3-dhcps/tests/Makefile | 93 +++ p3-dhcps/tests/client | Bin 0 -> 35248 bytes p3-dhcps/tests/data/arc-req-bad | 1 + p3-dhcps/tests/data/arc-two | 4 + p3-dhcps/tests/data/arc-two-rel | 6 + p3-dhcps/tests/data/eth-dhcp | 2 + p3-dhcps/tests/data/eth-disc | 1 + p3-dhcps/tests/data/fibre-repeat | 9 + p3-dhcps/tests/data/frame-inter | 6 + p3-dhcps/tests/data/ieee-release | 3 + p3-dhcps/tests/data/ieee-req | 1 + p3-dhcps/tests/data/multi-dhcp | 9 + p3-dhcps/tests/data/multi-release | 16 + p3-dhcps/tests/data/multi-reuse | 11 + p3-dhcps/tests/data/multi-threads | 11 + p3-dhcps/tests/data/multi-threads.old | 11 + p3-dhcps/tests/expected/A_threads.txt | 432 ++++++++++ p3-dhcps/tests/expected/B_arc_two.txt | 272 +++++++ p3-dhcps/tests/expected/B_fibre_repeat.txt | 408 ++++++++++ .../tests/expected/B_frame_interleave.txt | 272 +++++++ p3-dhcps/tests/expected/B_ieee_release.txt | 136 ++++ p3-dhcps/tests/expected/B_multi_release.txt | 756 ++++++++++++++++++ p3-dhcps/tests/expected/B_multi_reuse.txt | 560 +++++++++++++ p3-dhcps/tests/expected/C_arc_bad.txt | 53 ++ p3-dhcps/tests/expected/C_arc_two.txt | 212 +++++ p3-dhcps/tests/expected/C_eth_dhcp.txt | 106 +++ p3-dhcps/tests/expected/C_multi.txt | 475 +++++++++++ p3-dhcps/tests/expected/D_eth_disc.txt | 51 ++ p3-dhcps/tests/expected/D_ieee_req.txt | 53 ++ p3-dhcps/tests/integration.sh | 93 +++ p3-dhcps/tests/itests.include | 21 + p3-dhcps/tests/public.c | 29 + p3-dhcps/tests/style.sh | 53 ++ p3-dhcps/tests/testsuite.c | 32 + p3-dhcps/tests/threads | Bin 0 -> 35040 bytes 352 files changed, 14792 insertions(+) create mode 100644 lab1-ptrs/.gitignore create mode 100644 lab1-ptrs/Makefile create mode 100644 lab1-ptrs/main.c create mode 100644 lab1-ptrs/movies.c create mode 100644 lab1-ptrs/movies.h create mode 100644 lab1-ptrs/tests/Makefile create mode 100644 lab1-ptrs/tests/expected/.INTEG_movies.sha create mode 100644 lab1-ptrs/tests/expected/.INTEG_movies_inc.sha create mode 100644 lab1-ptrs/tests/expected/INTEG_movies.txt create mode 100644 lab1-ptrs/tests/expected/INTEG_movies_inc.txt create mode 100755 lab1-ptrs/tests/integration.sh create mode 100644 lab1-ptrs/tests/itests.include create mode 100644 lab1-ptrs/tests/public.c create mode 100755 lab1-ptrs/tests/style.sh create mode 100644 lab1-ptrs/tests/testsuite.c create mode 100644 lab2-cmd/.gitignore create mode 100644 lab2-cmd/Makefile create mode 100644 lab2-cmd/effects.c create mode 100644 lab2-cmd/effects.h create mode 100644 lab2-cmd/main.c create mode 100644 lab2-cmd/model.c create mode 100644 lab2-cmd/model.h create mode 100644 lab2-cmd/tests/Makefile create mode 100644 lab2-cmd/tests/expected/.INTEG_command.sha create mode 100644 lab2-cmd/tests/expected/.INTEG_execute.sha create mode 100644 lab2-cmd/tests/expected/.INTEG_execute_flags.sha create mode 100644 lab2-cmd/tests/expected/.INTEG_execute_name.sha create mode 100644 lab2-cmd/tests/expected/.INTEG_pipe.sha create mode 100644 lab2-cmd/tests/expected/.INTEG_pipe_flags.sha create mode 100644 lab2-cmd/tests/expected/INTEG_command.txt create mode 100644 lab2-cmd/tests/expected/INTEG_execute.txt create mode 100644 lab2-cmd/tests/expected/INTEG_execute_flags.txt create mode 100644 lab2-cmd/tests/expected/INTEG_execute_name.txt create mode 100644 lab2-cmd/tests/expected/INTEG_pipe.txt create mode 100644 lab2-cmd/tests/expected/INTEG_pipe_flags.txt create mode 100755 lab2-cmd/tests/integration.sh create mode 100644 lab2-cmd/tests/itests.include create mode 100644 lab2-cmd/tests/private.o create mode 100644 lab2-cmd/tests/public.c create mode 100755 lab2-cmd/tests/style.sh create mode 100644 lab2-cmd/tests/testsuite.c create mode 100755 lab2-cmd/tests/utests.sh create mode 100644 lab3-proc/.gitignore create mode 100644 lab3-proc/Makefile create mode 100644 lab3-proc/child.c create mode 100644 lab3-proc/child.h create mode 100644 lab3-proc/main.c create mode 100644 lab3-proc/signals.c create mode 100644 lab3-proc/signals.h create mode 100644 lab3-proc/tests/Makefile create mode 100644 lab3-proc/tests/expected/.INTEG_child.sha create mode 100644 lab3-proc/tests/expected/.INTEG_kill.sha create mode 100644 lab3-proc/tests/expected/.INTEG_segfault.sha create mode 100644 lab3-proc/tests/expected/.INTEG_sigint.sha create mode 100644 lab3-proc/tests/expected/.INTEG_status.sha create mode 100644 lab3-proc/tests/expected/INTEG_child.txt create mode 100644 lab3-proc/tests/expected/INTEG_kill.txt create mode 100644 lab3-proc/tests/expected/INTEG_segfault.txt create mode 100644 lab3-proc/tests/expected/INTEG_sigint.txt create mode 100644 lab3-proc/tests/expected/INTEG_status.txt create mode 100755 lab3-proc/tests/integration.sh create mode 100644 lab3-proc/tests/itests.include create mode 100644 lab3-proc/tests/private.o create mode 100644 lab3-proc/tests/public.c create mode 100755 lab3-proc/tests/style.sh create mode 100644 lab3-proc/tests/testsuite.c create mode 100644 lab4-ipc/.gitignore create mode 100644 lab4-ipc/Makefile create mode 100644 lab4-ipc/data/f1.txt create mode 100644 lab4-ipc/data/f2.txt create mode 100644 lab4-ipc/data/f3.txt create mode 100644 lab4-ipc/data/index create mode 100644 lab4-ipc/main.c create mode 100644 lab4-ipc/pipe.c create mode 100644 lab4-ipc/pipe.h create mode 100644 lab4-ipc/tests/Makefile create mode 100644 lab4-ipc/tests/data/f1.txt create mode 100644 lab4-ipc/tests/data/f2.txt create mode 100644 lab4-ipc/tests/data/f3.txt create mode 100644 lab4-ipc/tests/data/index create mode 100644 lab4-ipc/tests/expected/.INTEG_fork.sha create mode 100644 lab4-ipc/tests/expected/.INTEG_fork_no_exec.sha create mode 100644 lab4-ipc/tests/expected/.INTEG_fork_no_file.sha create mode 100644 lab4-ipc/tests/expected/.INTEG_spawn.sha create mode 100644 lab4-ipc/tests/expected/.INTEG_spawn_no_exec.sha create mode 100644 lab4-ipc/tests/expected/.INTEG_spawn_no_file.sha create mode 100644 lab4-ipc/tests/expected/INTEG_fork.txt create mode 100644 lab4-ipc/tests/expected/INTEG_fork_no_exec.txt create mode 100644 lab4-ipc/tests/expected/INTEG_fork_no_file.txt create mode 100644 lab4-ipc/tests/expected/INTEG_spawn.txt create mode 100644 lab4-ipc/tests/expected/INTEG_spawn_no_exec.txt create mode 100644 lab4-ipc/tests/expected/INTEG_spawn_no_file.txt create mode 100755 lab4-ipc/tests/integration.sh create mode 100644 lab4-ipc/tests/itests.include create mode 100644 lab4-ipc/tests/private.o create mode 100644 lab4-ipc/tests/public.c create mode 100755 lab4-ipc/tests/style.sh create mode 100644 lab4-ipc/tests/testsuite.c create mode 100644 lab5-net/.gitignore create mode 100644 lab5-net/Makefile create mode 100644 lab5-net/client.c create mode 100644 lab5-net/client.h create mode 100644 lab5-net/main.c create mode 100644 lab5-net/tests/Makefile create mode 100644 lab5-net/tests/expected/.INTEG_53_ns3.sha create mode 100644 lab5-net/tests/expected/.INTEG_53_ns3_ipv6.sha create mode 100644 lab5-net/tests/expected/.INTEG_localhost_ipv4.sha create mode 100644 lab5-net/tests/expected/.INTEG_localhost_ipv6.sha create mode 100644 lab5-net/tests/expected/.INTEG_w3_ipv4.sha create mode 100644 lab5-net/tests/expected/.INTEG_w3_ipv6.sha create mode 100644 lab5-net/tests/expected/INTEG_53_ns3.txt create mode 100644 lab5-net/tests/expected/INTEG_53_ns3_ipv6-2.txt create mode 100644 lab5-net/tests/expected/INTEG_53_ns3_ipv6.txt create mode 100644 lab5-net/tests/expected/INTEG_localhost_ipv4.txt create mode 100644 lab5-net/tests/expected/INTEG_localhost_ipv6.txt create mode 100644 lab5-net/tests/expected/INTEG_w3_ipv4.txt create mode 100644 lab5-net/tests/expected/INTEG_w3_ipv6-2.txt create mode 100644 lab5-net/tests/expected/INTEG_w3_ipv6.txt create mode 100755 lab5-net/tests/integration.sh create mode 100644 lab5-net/tests/itests.include create mode 100644 lab5-net/tests/private.o create mode 100644 lab5-net/tests/public.c create mode 100755 lab5-net/tests/style.sh create mode 100644 lab5-net/tests/testsuite.c create mode 100644 lab7-threads/.gitignore create mode 100644 lab7-threads/Makefile create mode 100644 lab7-threads/discrete.c create mode 100644 lab7-threads/discrete.h create mode 100644 lab7-threads/main.c create mode 100644 lab7-threads/private.h create mode 100644 lab7-threads/pthread.c create mode 100644 lab7-threads/pthread.h create mode 100644 lab7-threads/tests/Makefile create mode 100644 lab7-threads/tests/expected/INTEG_logs_large.txt create mode 100644 lab7-threads/tests/expected/INTEG_logs_large_thr.txt create mode 100644 lab7-threads/tests/expected/INTEG_logs_medium.txt create mode 100644 lab7-threads/tests/expected/INTEG_logs_medium_thr.txt create mode 100644 lab7-threads/tests/expected/INTEG_logs_small.txt create mode 100644 lab7-threads/tests/expected/INTEG_logs_small_thr.txt create mode 100755 lab7-threads/tests/integration.sh create mode 100644 lab7-threads/tests/itests.include create mode 100644 lab7-threads/tests/public.c create mode 100644 lab7-threads/tests/testsuite.c create mode 100644 lab8-synch/.gitignore create mode 100644 lab8-synch/Makefile create mode 100644 lab8-synch/main.c create mode 100644 lab8-synch/mutex.c create mode 100644 lab8-synch/mutex.h create mode 100644 lab8-synch/pingpong.c create mode 100644 lab8-synch/pingpong.h create mode 100644 lab8-synch/tests/Makefile create mode 100644 lab8-synch/tests/expected/.INTEG_run_10.sha create mode 100644 lab8-synch/tests/expected/.INTEG_run_5.sha create mode 100644 lab8-synch/tests/expected/.INTEG_run_mutex.sha create mode 100644 lab8-synch/tests/expected/.INTEG_run_pingpong.sha create mode 100644 lab8-synch/tests/expected/.INTEG_run_pingpong_10.sha create mode 100644 lab8-synch/tests/expected/.INTEG_run_pingpong_5.sha create mode 100644 lab8-synch/tests/expected/INTEG_run_mutex.txt create mode 100644 lab8-synch/tests/expected/INTEG_run_pingpong.txt create mode 100644 lab8-synch/tests/expected/INTEG_run_pingpong_10.txt create mode 100644 lab8-synch/tests/expected/INTEG_run_pingpong_5.txt create mode 100644 lab8-synch/tests/expected/MIN_run_pingpong.txt create mode 100755 lab8-synch/tests/integration.sh create mode 100644 lab8-synch/tests/itests.include create mode 100644 lab8-synch/tests/public.c create mode 100644 lab8-synch/tests/testsuite.c create mode 100644 p1-sh/.gitignore create mode 100644 p1-sh/Makefile create mode 100644 p1-sh/PHASE-1.txt create mode 100644 p1-sh/PHASE-2.txt create mode 100644 p1-sh/PHASE-3.txt create mode 100644 p1-sh/PHASE-4.txt create mode 100644 p1-sh/cut_data/data.csv create mode 100644 p1-sh/cut_data/data.spaces create mode 100755 p1-sh/data/.hidden.txt create mode 100644 p1-sh/data/FIRST.txt create mode 100644 p1-sh/data/empty.txt create mode 100644 p1-sh/data/pwd.txt create mode 100755 p1-sh/data/yat.txt create mode 100644 p1-sh/src/builtins.c create mode 100644 p1-sh/src/builtins.h create mode 100644 p1-sh/src/cmd.c create mode 100644 p1-sh/src/cmd.h create mode 100644 p1-sh/src/hash.c create mode 100644 p1-sh/src/hash.h create mode 100644 p1-sh/src/main.c create mode 100644 p1-sh/src/process.c create mode 100644 p1-sh/src/process.h create mode 100644 p1-sh/src/shell.c create mode 100644 p1-sh/src/shell.h create mode 100644 p1-sh/tests/Makefile create mode 100755 p1-sh/tests/bin/cat create mode 100755 p1-sh/tests/bin/chmod create mode 100755 p1-sh/tests/bin/cut create mode 100755 p1-sh/tests/bin/env create mode 100755 p1-sh/tests/bin/head create mode 100755 p1-sh/tests/bin/ls create mode 100755 p1-sh/tests/bin/repeat create mode 100644 p1-sh/tests/cut_data/data.csv create mode 100644 p1-sh/tests/cut_data/data.spaces create mode 100755 p1-sh/tests/data/.hidden.txt create mode 100644 p1-sh/tests/data/FIRST.txt create mode 100644 p1-sh/tests/data/empty.txt create mode 100644 p1-sh/tests/data/pwd.txt create mode 100755 p1-sh/tests/data/yat.txt create mode 100644 p1-sh/tests/expected/A_cat_tail.txt create mode 100644 p1-sh/tests/expected/A_env.txt create mode 100644 p1-sh/tests/expected/A_pipe.txt create mode 100644 p1-sh/tests/expected/A_which.txt create mode 100644 p1-sh/tests/expected/B_env.txt create mode 100644 p1-sh/tests/expected/B_export_unset.txt create mode 100644 p1-sh/tests/expected/B_repeat.txt create mode 100644 p1-sh/tests/expected/B_return_code.txt create mode 100644 p1-sh/tests/expected/B_setenv.txt create mode 100644 p1-sh/tests/expected/C_binaries.txt create mode 100644 p1-sh/tests/expected/C_binaries_bad-2.txt create mode 100644 p1-sh/tests/expected/C_binaries_bad.txt create mode 100644 p1-sh/tests/expected/C_binaries_flags.txt create mode 100644 p1-sh/tests/expected/C_cd_pwd.txt create mode 100644 p1-sh/tests/expected/C_chmod.txt create mode 100644 p1-sh/tests/expected/C_cut.txt create mode 100644 p1-sh/tests/expected/C_cut_bad.txt create mode 100644 p1-sh/tests/expected/C_echo.txt create mode 100644 p1-sh/tests/expected/C_echo_space.txt create mode 100644 p1-sh/tests/expected/C_which.txt create mode 100644 p1-sh/tests/expected/D_echo.txt create mode 100644 p1-sh/tests/expected/D_quit.txt create mode 100755 p1-sh/tests/integration.sh create mode 100644 p1-sh/tests/itests.include create mode 100644 p1-sh/tests/public.c create mode 100644 p1-sh/tests/scripts/bins.txt create mode 100644 p1-sh/tests/scripts/bins_bad.txt create mode 100644 p1-sh/tests/scripts/bins_chmod.txt create mode 100644 p1-sh/tests/scripts/bins_flags.txt create mode 100644 p1-sh/tests/scripts/cut.txt create mode 100644 p1-sh/tests/scripts/cut_bad.txt create mode 100644 p1-sh/tests/scripts/dirs.txt create mode 100644 p1-sh/tests/scripts/echo.txt create mode 100644 p1-sh/tests/scripts/echo_min.txt create mode 100644 p1-sh/tests/scripts/echo_space.txt create mode 100644 p1-sh/tests/scripts/env.txt create mode 100644 p1-sh/tests/scripts/env_pipe.txt create mode 100644 p1-sh/tests/scripts/export.txt create mode 100644 p1-sh/tests/scripts/pipe.txt create mode 100644 p1-sh/tests/scripts/quit.txt create mode 100644 p1-sh/tests/scripts/rc.txt create mode 100644 p1-sh/tests/scripts/repeat.txt create mode 100644 p1-sh/tests/scripts/setenv.txt create mode 100644 p1-sh/tests/scripts/tail.txt create mode 100644 p1-sh/tests/scripts/unset.txt create mode 100644 p1-sh/tests/scripts/which.txt create mode 100644 p1-sh/tests/scripts/which_exec.txt create mode 100755 p1-sh/tests/style.sh create mode 100644 p1-sh/tests/testsuite.c create mode 100644 p1-sh/utils/Makefile create mode 100644 p1-sh/utils/cat.c create mode 100644 p1-sh/utils/chmod.c create mode 100644 p1-sh/utils/cut.c create mode 100644 p1-sh/utils/env.c create mode 100644 p1-sh/utils/head.c create mode 100644 p1-sh/utils/ls.c create mode 100644 p1-sh/utils/repeat.c create mode 100644 p2-dns/.gitignore create mode 100644 p2-dns/Makefile create mode 100644 p2-dns/PHASE-1.txt create mode 100644 p2-dns/PHASE-2.txt create mode 100644 p2-dns/PHASE-3.txt create mode 100644 p2-dns/PHASE-4.txt create mode 100644 p2-dns/port_utils.o create mode 100644 p2-dns/src/client.c create mode 100644 p2-dns/src/dns.c create mode 100644 p2-dns/src/dns.h create mode 100644 p2-dns/src/port_utils.h create mode 100644 p2-dns/src/utils.c create mode 100644 p2-dns/src/utils.h create mode 100644 p2-dns/tests/Makefile create mode 100755 p2-dns/tests/dukens create mode 100644 p2-dns/tests/expected/A_cname_a.txt create mode 100644 p2-dns/tests/expected/A_cname_additional.txt create mode 100644 p2-dns/tests/expected/A_cname_basic.txt create mode 100644 p2-dns/tests/expected/A_example_soa.txt create mode 100644 p2-dns/tests/expected/A_jmu_soa.txt create mode 100644 p2-dns/tests/expected/A_no_ipv6_authority.txt create mode 100644 p2-dns/tests/expected/A_no_ipv6_subauth.txt create mode 100644 p2-dns/tests/expected/A_no_ptr.txt create mode 100644 p2-dns/tests/expected/A_ptr_basic.txt create mode 100644 p2-dns/tests/expected/A_ptr_mixed.txt create mode 100644 p2-dns/tests/expected/A_ptr_multiple.txt create mode 100644 p2-dns/tests/expected/B_a_multiple.txt create mode 100644 p2-dns/tests/expected/B_aaaa.txt create mode 100644 p2-dns/tests/expected/B_double_compress.txt create mode 100644 p2-dns/tests/expected/B_empty_domain.txt create mode 100644 p2-dns/tests/expected/B_jmu_mx.txt create mode 100644 p2-dns/tests/expected/B_jmu_ns.txt create mode 100644 p2-dns/tests/expected/B_no_ipv6.txt create mode 100644 p2-dns/tests/expected/C_a_basic.txt create mode 100644 p2-dns/tests/expected/C_a_compressed.txt create mode 100644 p2-dns/tests/expected/C_a_localhost.txt create mode 100644 p2-dns/tests/expected/C_bad_domain.txt create mode 100644 p2-dns/tests/expected/D_ping-2.txt create mode 100644 p2-dns/tests/expected/D_ping.txt create mode 100755 p2-dns/tests/integration.sh create mode 100644 p2-dns/tests/itests.include create mode 100644 p2-dns/tests/mappings.bin create mode 100755 p2-dns/tests/style.sh create mode 100644 p2-dns/tests/testsuite.c create mode 100644 p3-dhcps/.gitignore create mode 100644 p3-dhcps/Makefile create mode 100644 p3-dhcps/port_utils.o create mode 100644 p3-dhcps/src/dhcp.c create mode 100644 p3-dhcps/src/dhcp.h create mode 100644 p3-dhcps/src/format.c create mode 100644 p3-dhcps/src/format.h create mode 100644 p3-dhcps/src/main.c create mode 100644 p3-dhcps/src/port_utils.h create mode 100644 p3-dhcps/src/server.c create mode 100644 p3-dhcps/src/server.h create mode 100644 p3-dhcps/tests/Makefile create mode 100755 p3-dhcps/tests/client create mode 100644 p3-dhcps/tests/data/arc-req-bad create mode 100644 p3-dhcps/tests/data/arc-two create mode 100644 p3-dhcps/tests/data/arc-two-rel create mode 100644 p3-dhcps/tests/data/eth-dhcp create mode 100644 p3-dhcps/tests/data/eth-disc create mode 100644 p3-dhcps/tests/data/fibre-repeat create mode 100644 p3-dhcps/tests/data/frame-inter create mode 100644 p3-dhcps/tests/data/ieee-release create mode 100644 p3-dhcps/tests/data/ieee-req create mode 100644 p3-dhcps/tests/data/multi-dhcp create mode 100644 p3-dhcps/tests/data/multi-release create mode 100644 p3-dhcps/tests/data/multi-reuse create mode 100644 p3-dhcps/tests/data/multi-threads create mode 100644 p3-dhcps/tests/data/multi-threads.old create mode 100644 p3-dhcps/tests/expected/A_threads.txt create mode 100644 p3-dhcps/tests/expected/B_arc_two.txt create mode 100644 p3-dhcps/tests/expected/B_fibre_repeat.txt create mode 100644 p3-dhcps/tests/expected/B_frame_interleave.txt create mode 100644 p3-dhcps/tests/expected/B_ieee_release.txt create mode 100644 p3-dhcps/tests/expected/B_multi_release.txt create mode 100644 p3-dhcps/tests/expected/B_multi_reuse.txt create mode 100644 p3-dhcps/tests/expected/C_arc_bad.txt create mode 100644 p3-dhcps/tests/expected/C_arc_two.txt create mode 100644 p3-dhcps/tests/expected/C_eth_dhcp.txt create mode 100644 p3-dhcps/tests/expected/C_multi.txt create mode 100644 p3-dhcps/tests/expected/D_eth_disc.txt create mode 100644 p3-dhcps/tests/expected/D_ieee_req.txt create mode 100755 p3-dhcps/tests/integration.sh create mode 100644 p3-dhcps/tests/itests.include create mode 100644 p3-dhcps/tests/public.c create mode 100755 p3-dhcps/tests/style.sh create mode 100644 p3-dhcps/tests/testsuite.c create mode 100755 p3-dhcps/tests/threads diff --git a/lab1-ptrs/.gitignore b/lab1-ptrs/.gitignore new file mode 100644 index 0000000..a44fc93 --- /dev/null +++ b/lab1-ptrs/.gitignore @@ -0,0 +1,22 @@ +# Ignore all compiled files regardless of location +*.o +tests/public.o +tests/testsuite.o + +# But not the private.o +!tests/private.o + +# Ignore executables for this project +ptrs +testsuite + +# Ignore test outputs +tests/ckstyle +tests/itests.txt +tests/outputs +tests/style.txt +tests/valgrind +tests/utests.txt + +**/.nfs* +**/.vscode diff --git a/lab1-ptrs/Makefile b/lab1-ptrs/Makefile new file mode 100644 index 0000000..4ed1c9e --- /dev/null +++ b/lab1-ptrs/Makefile @@ -0,0 +1,51 @@ +# +# Simple Makefile +# Mike Lam, James Madison University, August 2016 +# +# This makefile builds a simple application that contains a main module +# (specified by the EXE variable) and a predefined list of additional modules +# (specified by the MODS variable). If there are any external library +# dependencies (e.g., the math library, "-lm"), list them in the LIBS variable. +# If there are any precompiled object files, list them in the OBJS variable. +# +# By default, this makefile will build the project with debugging symbols and +# without optimization. To change this, edit or remove the "-g" and "-O0" +# options in CFLAGS and LDFLAGS accordingly. +# +# By default, this makefile build the application using the GNU C compiler, +# adhering to the C99 standard with all warnings enabled. + + +# application-specific settings and run target + +EXE=ptrs +MODS=movies.o +OBJS= +LIBS= + +default: $(EXE) + +test: $(EXE) + make -C tests test + +# compiler/linker settings + +CC=gcc +CFLAGS=-g -O0 -Wall -Werror --std=c99 +LDFLAGS=-g -O0 + + +# build targets + +$(EXE): main.o $(MODS) $(OBJS) + $(CC) $(LDFLAGS) -o $(EXE) $^ $(LIBS) + +%.o: %.c + $(CC) -c $(CFLAGS) $< + +clean: + rm -f $(EXE) main.o $(MODS) + make -C tests clean + +.PHONY: default clean + diff --git a/lab1-ptrs/main.c b/lab1-ptrs/main.c new file mode 100644 index 0000000..c592c27 --- /dev/null +++ b/lab1-ptrs/main.c @@ -0,0 +1,80 @@ +#include +#include +#include +#include + +#include "movies.h" + +int cmdline (int, char **, bool *); + +void +usage (void) +{ + printf ("Usage: ptrs [-hm]\n"); + printf (" Options are:\n"); + printf (" -m Parse the CSV data as a movie\n"); + printf (" -h Print this help message\n"); + printf ("Only one option is allowable\n"); +} + +int +main (int argc, char **argv) +{ + bool run_movie = false; + + if (cmdline (argc, argv, &run_movie) != 0) + return EXIT_FAILURE; + + if (!run_movie) + { + usage (); + return EXIT_FAILURE; + } + + // Split the following comma-separated value (CSV) data into fields + char *data = "The Shawshank Redemption,1994,Drama"; + movie_t movie = split_data (data); + printf ("Title: %s\n", movie.title); + printf ("Year: %d\n", movie.year); + printf ("Genre: %s\n", movie.genre); + + char *rebuilt = merge_data (movie); + printf ("%s\n", rebuilt); + + free (rebuilt); + free (movie.title); + free (movie.genre); + + return EXIT_SUCCESS; +} + +/* DO NOT MODIFY THIS FUNCTION. Parses the command line arguments to get + the signal numbers to override, the number of overridden signals, and + the order of signals to raise in the full implementation. */ +int +cmdline (int argc, char **argv, bool *movies) +{ + int option; + + if ((option = getopt (argc, argv, "mh")) == -1) + { + usage (); + return -1; + } + + switch (option) + { + case 'h': + *movies = false; + return -1; + case 'm': + *movies = true; + break; + default: + printf ("Invalid command-line option: %c\n", optopt); + usage (); + return -1; + } + + return 0; +} diff --git a/lab1-ptrs/movies.c b/lab1-ptrs/movies.c new file mode 100644 index 0000000..3a14bb5 --- /dev/null +++ b/lab1-ptrs/movies.c @@ -0,0 +1,97 @@ +#define _POSIX_C_SOURCE 200809L // needed for strdup extension + +#include +#include +#include + +#include "movies.h" + +/* Given a string (possibly read-only) of IMDB movie data + (formatted as "Title,Year,Genre"), create a movie_t object + based on the appropriate fields. */ +movie_t +split_data (char *csv) +{ + movie_t movie; + char *temp = strdup (csv); + char *token = strtok (temp, ","); + movie.title = strdup (token); // make a copy the first token as the title + + char *year_str = strtok (NULL, ","); + char *endptr; + movie.year = strtol (year_str, &endptr, 10); + + char *genre = strtok (NULL, ","); + movie.genre = strdup (genre); + + free (temp); + + return movie; +} + +/* Build a dynamically allocated string from a movie_t object as follows: + "Title [Year] - Genre" + */ +char * +merge_data (movie_t movie) +{ + // There are a few ways to merge strings in C. One approach is to start with + // a base string on the heap and repeatedly grow and append the string. In + // pseudocode, it might look like this: + // + // str = strdup ("Hello"); + // str = realloc (str, ...length...); // grow + // strncat (str, " World", ...length...); // concatenate + // str = realloc (str, ...length...); // grow + // strncat (str, " Again", ...length...); // concatenate + // + // This is the C equivalent of something like this in Java: + // + // str = "Hello"; + // str += " World"; + // str += " Again"; + // + // Another (generally more efficient) way to do this is to use snprintf(), + // for formatting a string. If you are not familiar with snprintf(), you + // should be familiar with its cousin, printf(). Consider the following + // line of code, which prints three string variables to the screen: + // + // printf ("%s %s %s\n", str1, str2, str3); + // + // You do the exact same thing with snprintf(), but add two variables at + // the beginning: the destination buffer (i.e., where do you want the + // final string in memory) and the length: + // + // snprintf (buffer, length, "%s %s %s\n", str1, str2, str3); + // + // The original strings str1, str2, and str3 are unmodified, because their + // contents are copied into the buffer. snprintf() also has a great built-in + // feature that strncat() doesn't: it works with more than just strings! + // + // int num = 42; + // char *str = "My favorite number is "; + // snprintf (message, length, "%s%d\n", str, num); + // + // Just remember that the message/buffer variable must be a pointer to a + // writable portion of memory where you have allocated enough space to store + // the combined string. + + // HINT: When doing string manipulation, you should use a variable to keep + // explicit track of the string length. C string lengths are not inherently + // stored anywhere. + size_t len = 1; // always keep track of the null byte + + // +10 byte space for yeaar, spaces, brackets, and dash + len += strlen (movie.title) + 10 + strlen (movie.genre); + + // Use one of the two techniques above to allocated enough space for the + // merged string. You should NOT rely on any length unless its exact value + // is known. For instance, you can assume that " [Year] - " adds 10 bytes, + // but you cannot assume anything about the length of the genre or title. + // As such, you need to compute those lengths using strlen(). Do not forget + // to account for the null byte. + char *result = calloc (len, sizeof (char)); + snprintf (result, len, "%s [%d] - %s", movie.title, movie.year, movie.genre); + + return result; +} diff --git a/lab1-ptrs/movies.h b/lab1-ptrs/movies.h new file mode 100644 index 0000000..7e289ef --- /dev/null +++ b/lab1-ptrs/movies.h @@ -0,0 +1,16 @@ +#ifndef __CS361_MOVIES_H__ +#define __CS361_MOVIES_H__ + +#include + +typedef struct movie +{ + char *title; + int year; + char *genre; +} movie_t; + +movie_t split_data (char *); +char *merge_data (movie_t); + +#endif diff --git a/lab1-ptrs/tests/Makefile b/lab1-ptrs/tests/Makefile new file mode 100644 index 0000000..b4bda4d --- /dev/null +++ b/lab1-ptrs/tests/Makefile @@ -0,0 +1,89 @@ +# +# Simple Test Makefile +# Mike Lam, James Madison University, August 2016 +# +# This version of the Makefile includes support for building a test suite. The +# recommended framework is Check (http://check.sourceforge.net/). To build and +# run the test suite, execute the "test" target. The test suite must be located +# in a module called "testsuite". The MODS, LIBS, and OBJS variables work as +# they do in the main Makefile. +# +# To change the default build target (which executes when you just type +# "make"), change the right-hand side of the definition of the "default" +# target. +# +# By default, this makefile will build the project with debugging symbols and +# without optimization. To change this, edit or remove the "-g" and "-O0" +# options in CFLAGS and LDFLAGS accordingly. +# +# By default, this makefile build the application using the GNU C compiler, +# adhering to the C99 standard with all warnings enabled. + + +# application-specific settings and run target + +EXE=../ptrs +TEST=testsuite +MODS=public.o +OBJS=../movies.o +LIBS= + +UTESTOUT=utests.txt +ITESTOUT=itests.txt +SCHECKOUT=style.txt + +default: $(TEST) + +$(EXE): + make -C ../ + +test: utest itest style + @echo "========================================" + +utest: $(EXE) $(TEST) + @echo "========================================" + @echo " UNIT TESTS" + @./$(TEST) 2>/dev/null >$(UTESTOUT) + @cat $(UTESTOUT) | sed -n -e '/Checks/,$$p' | sed -e 's/^private.*:[EF]://g' + +itest: $(EXE) + @echo "========================================" + @echo " INTEGRATION TESTS" + @./integration.sh | tee $(ITESTOUT) + +style: $(EXE) + @echo "========================================" + @echo " CODING STYLE CHECK" + @./style.sh 2>/dev/null >$(SCHECKOUT) + @cat $(SCHECKOUT) + +# compiler/linker settings + +CC=gcc +CFLAGS=-g -O0 -Wall --std=c99 -pedantic +LDFLAGS=-g -O0 + +#CFLAGS+=-I/opt/local/include -Wno-gnu-zero-variadic-macro-arguments +CFLAGS+=-Wno-gnu-zero-variadic-macro-arguments +#LDFLAGS+=-L/opt/local/lib +LDFLAGS= +LIBS+=-lcheck -lm -lpthread + +ifeq ($(shell uname -s),Linux) + LIBS+=-lrt -lsubunit +endif + + +# build targets + +$(TEST): $(TEST).o $(MODS) $(OBJS) + $(CC) $(LDFLAGS) -o $(TEST) $^ $(LIBS) + +%.o: %.c + $(CC) -c $(CFLAGS) $< + +clean: + rm -rf $(TEST) $(TEST).o $(MODS) $(UTESTOUT) $(ITESTOUT) $(SCHECKOUT) outputs valgrind ckstyle + +.PHONY: default clean test unittest inttest + diff --git a/lab1-ptrs/tests/expected/.INTEG_movies.sha b/lab1-ptrs/tests/expected/.INTEG_movies.sha new file mode 100644 index 0000000..0f71a4d --- /dev/null +++ b/lab1-ptrs/tests/expected/.INTEG_movies.sha @@ -0,0 +1 @@ +514af63cc957d92dab366709ad95b76ad030e9d9 diff --git a/lab1-ptrs/tests/expected/.INTEG_movies_inc.sha b/lab1-ptrs/tests/expected/.INTEG_movies_inc.sha new file mode 100644 index 0000000..0f71a4d --- /dev/null +++ b/lab1-ptrs/tests/expected/.INTEG_movies_inc.sha @@ -0,0 +1 @@ +514af63cc957d92dab366709ad95b76ad030e9d9 diff --git a/lab1-ptrs/tests/expected/INTEG_movies.txt b/lab1-ptrs/tests/expected/INTEG_movies.txt new file mode 100644 index 0000000..69a154c --- /dev/null +++ b/lab1-ptrs/tests/expected/INTEG_movies.txt @@ -0,0 +1,4 @@ +Title: The Shawshank Redemption +Year: 1994 +Genre: Drama +The Shawshank Redemption [1994] - Drama diff --git a/lab1-ptrs/tests/expected/INTEG_movies_inc.txt b/lab1-ptrs/tests/expected/INTEG_movies_inc.txt new file mode 100644 index 0000000..69a154c --- /dev/null +++ b/lab1-ptrs/tests/expected/INTEG_movies_inc.txt @@ -0,0 +1,4 @@ +Title: The Shawshank Redemption +Year: 1994 +Genre: Drama +The Shawshank Redemption [1994] - Drama diff --git a/lab1-ptrs/tests/integration.sh b/lab1-ptrs/tests/integration.sh new file mode 100755 index 0000000..de719a9 --- /dev/null +++ b/lab1-ptrs/tests/integration.sh @@ -0,0 +1,75 @@ +#!/bin/bash + +EXE="../ptrs" + +function run_test { + + # parameters + TAG=$1 + ARGS=$2 + + # file paths + OUTPUT=outputs/$TAG.txt + DIFF=outputs/$TAG.diff + EXPECT=expected/$TAG.txt + VALGRND=valgrind/$TAG.txt + + # print tag format + PTAG=$(printf '%-30s' "$TAG") + + # check for expected text that needs to be fixed + if [ ! -z "$(egrep "<<<<.*>>>>" "$EXPECT")" ] ; then + echo "$PTAG FAIL ($EXPECT not correct)" + return + fi + + # compare the file + SHAOUT=$(shasum "$EXPECT" | awk '{print $1}') + SHAEXP=$(cat "expected/.$TAG.sha") + if [ "$SHAOUT" != "$SHAEXP" ] ; then + echo "$PTAG FAIL ($EXPECT not correct)" + return + fi + + # run test and compare output to the expected version + $EXE "$ARGS" 2>/dev/null >"$OUTPUT" + diff -u "$OUTPUT" "$EXPECT" >"$DIFF" + if [ -s "$DIFF" ]; then + + # try alternative solution (if it exists) + EXPECT=expected/$TAG-2.txt + if [ -e "$EXPECT" ]; then + diff -u "$OUTPUT" "$EXPECT" >"$DIFF" + if [ -s "$DIFF" ]; then + echo "$PTAG FAIL (see $DIFF for details)" + else + echo "$PTAG pass" + fi + else + echo "$PTAG FAIL (see $DIFF for details)" + fi + else + echo "$PTAG pass" + fi + + # run valgrind + valgrind $EXE $ARGS &>$VALGRND +} + +# initialize output folders +mkdir -p outputs +mkdir -p valgrind +rm -f outputs/* valgrind/* + +# run individual tests +source itests.include + +# check for memory leaks +LEAK=`cat valgrind/*.txt | grep 'definitely lost' | grep -v ' 0 bytes in 0 blocks'` +if [ -z "$LEAK" ]; then + echo "No memory leak found." +else + echo "Memory leak(s) found. See files listed below for details." + grep 'definitely lost' valgrind/*.txt | grep -v ' 0 bytes in 0 blocks' | sed -e 's/:.*$//g' | sed -e 's/^/ - /g' +fi + diff --git a/lab1-ptrs/tests/itests.include b/lab1-ptrs/tests/itests.include new file mode 100644 index 0000000..dfaf482 --- /dev/null +++ b/lab1-ptrs/tests/itests.include @@ -0,0 +1,7 @@ +# list of integration tests +# format: run_test +# used as the root for all filenames (i.e., "expected/$TAG.txt") +# command-line arguments to test + +run_test INTEG_movies "-m" +run_test INTEG_movies_inc "-m" diff --git a/lab1-ptrs/tests/public.c b/lab1-ptrs/tests/public.c new file mode 100644 index 0000000..b517d80 --- /dev/null +++ b/lab1-ptrs/tests/public.c @@ -0,0 +1,95 @@ +#include +#include +#include + +#include + +#include "../movies.h" + +// Test that split_data gets the title copy +START_TEST (UNIT_split_title) +{ + char *csv = "Casablanca,1942,Romance"; + movie_t movie = split_data (csv); + ck_assert_str_eq (csv, "Casablanca,1942,Romance"); + ck_assert_str_eq (movie.title, "Casablanca"); +} +END_TEST + +// Test that split_data parses numeric values +START_TEST (UNIT_split_nums) +{ + char *csv = "Casablanca,1942,Romance"; + movie_t movie = split_data (csv); + ck_assert_str_eq (csv, "Casablanca,1942,Romance"); + ck_assert_int_eq (movie.year, 1942); +} +END_TEST + +// Test that split_data gets the genre copy +START_TEST (UNIT_split_genre) +{ + char *csv = "Casablanca,1942,Romance"; + movie_t movie = split_data (csv); + ck_assert_str_eq (csv, "Casablanca,1942,Romance"); + ck_assert_str_eq (movie.genre, "Romance"); +} +END_TEST + +// Test that split_data operates correctly on stack-based strings +START_TEST (UNIT_split_stack) +{ + + char csv[] = "Casablanca,1942,Romance"; + movie_t movie = split_data (csv); + ck_assert_str_eq (movie.title, "Casablanca"); + ck_assert_int_eq (movie.year, 1942); + ck_assert_str_eq (movie.genre, "Romance"); + ck_assert_str_eq (csv, "Casablanca,1942,Romance"); +} +END_TEST + +// Test that merge_data combines the fields into a string +START_TEST (UNIT_merge_movie) +{ + movie_t movie; + movie.title = "The Lord of the Rings: The Fellowship of the Ring"; + movie.year = 2001; + movie.genre = "Fantasy"; + char *result = merge_data (movie); + ck_assert_str_eq (result, "The Lord of the Rings: The Fellowship of the Ring [2001] - Fantasy"); + free (result); +} +END_TEST + + +// Test that merge_data combines the fields from the heap into a string +START_TEST (UNIT_merge_heap) +{ + movie_t *movie = calloc (1, sizeof (movie_t)); + movie->title = "The Lord of the Rings: The Fellowship of the Ring"; + movie->year = 2001; + movie->genre = "Fantasy"; + char *result = merge_data (*movie); + ck_assert_str_eq (result, "The Lord of the Rings: The Fellowship of the Ring [2001] - Fantasy"); + free (result); + result = NULL; + ck_assert_str_eq (movie->title, "The Lord of the Rings: The Fellowship of the Ring"); + ck_assert_int_eq (movie->year, 2001); + ck_assert_str_eq (movie->genre, "Fantasy"); + free (movie); +} +END_TEST + +void public_tests (Suite *s) +{ + TCase *tc_public = tcase_create ("Public"); + tcase_add_test (tc_public, UNIT_split_title); + tcase_add_test (tc_public, UNIT_split_nums); + tcase_add_test (tc_public, UNIT_split_genre); + tcase_add_test (tc_public, UNIT_split_stack); + tcase_add_test (tc_public, UNIT_merge_movie); + tcase_add_test (tc_public, UNIT_merge_heap); + suite_add_tcase (s, tc_public); +} + diff --git a/lab1-ptrs/tests/style.sh b/lab1-ptrs/tests/style.sh new file mode 100755 index 0000000..ba2210a --- /dev/null +++ b/lab1-ptrs/tests/style.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +STYLE="gnu" + +IGNORE=() +FAIL=0 + +function comp_file { + + SRC=$1 + SRC_NAME=$2 + + # file paths + FORMAT=ckstyle/${SRC_NAME}.$STYLE + DIFF=ckstyle/${SRC_NAME}.diff + + # run clang-format and compare results + clang-format --style=$STYLE $source > $FORMAT + diff -u $SRC $FORMAT >$DIFF + + PTAG=$(printf '%-30s' "$SRC_NAME") + if [ -s $DIFF ]; then + echo "$PTAG FAIL (see $DIFF for details)" + FAIL=1 + else + echo "$PTAG pass" + rm $DIFF + fi + rm $FORMAT +} + +mkdir -p ckstyle +rm -f ckstyle/* + +for source in $(ls ../*.c ../*.h) ; do + SKIP=0 + src=$(basename $source) + for ignore in ${IGNORE[*]} ; do + if [ "$src" = "$ignore" ] ; then + SKIP=1 + fi + done + if [ $SKIP = 0 ] ; then + comp_file $source $src + fi +done + +if [ $FAIL != 0 ] ; then + echo "Code that does not adhere to GNU standards will not be accepted." + echo "You must fix these files before submission." +else + echo "Code correctly adheres to required style." +fi diff --git a/lab1-ptrs/tests/testsuite.c b/lab1-ptrs/tests/testsuite.c new file mode 100644 index 0000000..a938490 --- /dev/null +++ b/lab1-ptrs/tests/testsuite.c @@ -0,0 +1,36 @@ +#include +#include +#include +#include +#include +#include + +#include + +extern void public_tests (Suite *s); +void run_testsuite (void); + +Suite * +test_suite (void) +{ + Suite *s = suite_create ("Default"); + public_tests (s); + return s; +} + +void +run_testsuite (void) +{ + Suite *s = test_suite (); + SRunner *sr = srunner_create (s); + srunner_run_all (sr, CK_NORMAL); + srunner_free (sr); +} + +int +main (void) +{ + srand((unsigned)time(NULL)); + run_testsuite (); + return EXIT_SUCCESS; +} diff --git a/lab2-cmd/.gitignore b/lab2-cmd/.gitignore new file mode 100644 index 0000000..d70f6f2 --- /dev/null +++ b/lab2-cmd/.gitignore @@ -0,0 +1,20 @@ +# Ignore all compiled files regardless of location +**/*.o + +# But not the private.o +!tests/private.o + +# Ignore executables for this project +shell +testsuite + +# Ignore test outputs +tests/ckstyle +tests/itests.txt +tests/outputs +tests/style.txt +tests/valgrind +tests/utests.txt + +**/.nfs* +**/.vscode diff --git a/lab2-cmd/Makefile b/lab2-cmd/Makefile new file mode 100644 index 0000000..3998d5a --- /dev/null +++ b/lab2-cmd/Makefile @@ -0,0 +1,51 @@ +# +# Simple Makefile +# Mike Lam, James Madison University, August 2016 +# +# This makefile builds a simple application that contains a main module +# (specified by the EXE variable) and a predefined list of additional modules +# (specified by the MODS variable). If there are any external library +# dependencies (e.g., the math library, "-lm"), list them in the LIBS variable. +# If there are any precompiled object files, list them in the OBJS variable. +# +# By default, this makefile will build the project with debugging symbols and +# without optimization. To change this, edit or remove the "-g" and "-O0" +# options in CFLAGS and LDFLAGS accordingly. +# +# By default, this makefile build the application using the GNU C compiler, +# adhering to the C99 standard with all warnings enabled. + + +# application-specific settings and run target + +EXE=shell +MODS=model.o effects.o +OBJS= +LIBS= + +default: $(EXE) + +test: $(EXE) + make -C tests test + +# compiler/linker settings + +CC=gcc +CFLAGS=-g -O0 -Wall -Werror --std=c99 -pedantic +LDFLAGS=-g -O0 + + +# build targets + +$(EXE): main.o $(MODS) $(OBJS) + $(CC) $(LDFLAGS) -o $(EXE) $^ $(LIBS) + +%.o: %.c + $(CC) -c $(CFLAGS) $< + +clean: + rm -f $(EXE) main.o $(MODS) + make -C tests clean + +.PHONY: default clean + diff --git a/lab2-cmd/effects.c b/lab2-cmd/effects.c new file mode 100644 index 0000000..e0947ed --- /dev/null +++ b/lab2-cmd/effects.c @@ -0,0 +1,88 @@ +#define _POSIX_C_SOURCE 200809L // needed for strdup extension + +#include +#include +#include +#include +#include +#include + +#include "effects.h" +#include "model.h" + +/* Executed when starting to process a new command line. The fsm_t + should have been updated to include a pointer to the current token. + For instance, if the command line was "ls -l data NL", the fsm_t + has a field that points to "ls". */ +void +start_command (fsm_t *cmdmodel) +{ + printf ("Starting new command: %s\n", cmdmodel->current_token); + // TODO: Copy the current token to store it in the FSM's command + // field. Next, create the FSM's args array (length MAX_ARGUMENTS) + // set the current token as args[0], and initialize nargs to be + // the number of arguments (1 at this point). + // Allocate args array (NULL-initialized) if not already + cmdmodel->args = calloc (MAX_ARGUMENTS, sizeof (char *)); + cmdmodel->command = cmdmodel->current_token; + cmdmodel->args[0] = cmdmodel->current_token; + cmdmodel->nargs = 1; +} + +/* Executed when processing a token after the command name. For instance, + if the command line was "ls -l data NL", this function will be called + when the current token is "-l" and again when it is "data". */ +void +append (fsm_t *cmdmodel) +{ + if (cmdmodel->nargs >= MAX_ARGUMENTS) + return; + + printf ("Appending %s to the argument list\n", cmdmodel->current_token); + assert (cmdmodel->args != NULL); + + // TODO: Store the current token into the args array and increment nargs + cmdmodel->args[cmdmodel->nargs++] = cmdmodel->current_token; +} + +/* Executed when either a NL or | (pipe) is encountered. For instance, if + the command line is "ls -l data NL", the current token will be "NL"; also, + the FSM's args array should be complete, containing "ls", "-l", and "data", + followed by several NULL pointers. */ +void +execute (fsm_t *cmdmodel) +{ + assert (cmdmodel->args != NULL); + + // TODO: Print out the argument list similar to the format shown and free + // the args array. + printf ("Execute %s with arguments { ", cmdmodel->args[0]); + for (uint8_t i = 0; i < cmdmodel->nargs; i++) + { + printf ("%s, ", cmdmodel->args[i]); + } + printf ("(null) }\n"); +} + +// No changes are needed to the effects below + +void +link_commands (fsm_t *cmdmodel) +{ + printf ("Set up pipe\n"); + execute (cmdmodel); +} + +void +error_pipe (fsm_t *cmdmodel) +{ + printf ("ERROR: Received token %s while in state %s\n", + cmdmodel->current_token, state_name (cmdmodel->state)); +} + +void +error_newline (fsm_t *cmdmodel) +{ + printf ("ERROR: Received token %s while in state %s\n", + cmdmodel->current_token, state_name (cmdmodel->state)); +} diff --git a/lab2-cmd/effects.h b/lab2-cmd/effects.h new file mode 100644 index 0000000..18ca2e8 --- /dev/null +++ b/lab2-cmd/effects.h @@ -0,0 +1,13 @@ +#ifndef __effects_h__ +#define __effects_h__ + +#include "model.h" + +void start_command (fsm_t *); +void append (fsm_t *); +void execute (fsm_t *); +void link_commands (fsm_t *); +void error_pipe (fsm_t *); +void error_newline (fsm_t *); + +#endif diff --git a/lab2-cmd/main.c b/lab2-cmd/main.c new file mode 100644 index 0000000..437fb40 --- /dev/null +++ b/lab2-cmd/main.c @@ -0,0 +1,61 @@ +/* + * CS 361: Template lab driver + * + * Name: + */ + +#include +#include +#include +#include +#include +#include + +#include "model.h" + +void +usage (void) +{ + printf ("shell, a command line state model simulator\n\n"); + printf ("usage: shell cmd [arg ...] [ | cmd [arg ...] ... ]\n"); + printf ("Each cmd can contain an argument list of variable length\n"); + printf ("Each cmd after the first must be preceded by a pipe | character\n"); +} + +int +main (int argc, char **argv) +{ + // With no arguments, show usage and exit + if (argc != 2) + { + usage (); + return EXIT_FAILURE; + } + + // Declare and initialize a FSM + fsm_t *cmdmodel = cmdline_init (); + if (cmdmodel == NULL) + return EXIT_FAILURE; + + // TODO: Change this to split the string into tokens, where + // each token is an event that needs to be handled. After + // looking up the event number, store the token in the FSM + // and call handle_event(). + char *token = strtok (argv[1], " "); + while (token != NULL) + { + cmdmodel->current_token = token; + event_t event = lookup (token); + if (!handle_event (cmdmodel, event)) + break; + + token = strtok (NULL, " "); + } + + // Free remaining allocated data + if (cmdmodel->args != NULL) + free (cmdmodel->args); + free (cmdmodel); + + return EXIT_SUCCESS; +} diff --git a/lab2-cmd/model.c b/lab2-cmd/model.c new file mode 100644 index 0000000..0840814 --- /dev/null +++ b/lab2-cmd/model.c @@ -0,0 +1,130 @@ +#include +#include +#include +#include +#include + +#include "effects.h" +#include "model.h" + +// TODO: Complete this table mapping state/events to the target state. Each +// row corresponds to the current state. Each column within that row indicates +// the next state for a given event. If there is no transition defined, that +// entry should be NST ("no state"). +static state_t const _transitions[NUM_STATES][NUM_EVENTS] + = { { Command, Term, NST }, + { Arguments, Make_Pipe, Term }, + { Arguments, Make_Pipe, Term }, + { Command, Term, Term }, + { NST, NST, NST } }; + +// TODO: Create a table mapping states/events to the effect functions. If +// there is no valid transition, the entry here would be NULL because actions +// are function pointers. +static action_t const _effects[NUM_STATES][NUM_EVENTS] + = { { start_command, error_pipe, NULL }, + { append, link_commands, execute }, + { append, link_commands, execute }, + { start_command, error_pipe, error_newline }, + { NULL, NULL, NULL } }; + +/* Create an instance of an FSM and initialize its fields as appropriate. + Some fields are common to most FSMs (such as an initial state or a + pointer to a transition function). Other fields will be specific to + this fsm_t declaration. Return NULL if any part of the initialization + fails. */ +fsm_t * +cmdline_init (void) +{ + fsm_t *fsm = calloc (1, sizeof (fsm_t)); + fsm->state = Init; + fsm->transition = transition; + + return fsm; +} + +// TODO: Create a transition function that is specific to this type +// of FSM. This function needs to take an fsm_t* and an event, returning +// both the new state and the effect to perform (the latter is returned +// using a call-by-reference parameter. This function should NOT contain +// any "if" types of statements based on the state or event; it should +// simply lookup these values in the tables defined above. +state_t +transition (struct fsm *fsm, event_t event, action_t *effect) +{ + assert (fsm->state < NST); + assert (event < NIL); + + *effect = _effects[fsm->state][event]; + return _transitions[fsm->state][event]; +} + +/* Helper function for providing a printable string name for an event */ +const char * +event_name (event_t evt) +{ + assert (evt <= NIL); + + // Event names for printing out + const char *names[] = { "TOKEN", "PIPE", "NEWLINE", "NIL" }; + return names[evt]; +} + +/* Helper function for providing a printable string name for an state */ +const char * +state_name (state_t st) +{ + assert (st <= NST); + + // State names for printing out + const char *names[] + = { "Init", "Command", "Arguments", "Make_Pipe", "Term", "NST" }; + return names[st]; +} + +/* Generic front-end for handling events. Should do nothing more + than calling the FSM's transition function, performing an effect + (if appropriate) and updating the state. Return false if the new + state is the terminal state. */ +bool +handle_event (fsm_t *fsm, event_t event) +{ + assert (fsm != NULL); + // TODO: Look up the current state/event combination in the + // transition table. Print the following line for debugging + // purposes just for this lab. This should be printed even + // if there is no transition. + action_t effect; + state_t new_state = fsm->transition (fsm, event, &effect); + printf ("[%s.%s -> %s]\n", state_name (fsm->state), event_name (event), + state_name (new_state)); + + // TODO: If the state/event combination is valid, execute + // the transition and effect function (if there is one). + // If the next state is Term (terminated), return false. + // Otherwise return true. + if (new_state != NST) + { + if (effect != NULL) + { + effect (fsm); + } + + fsm->state = new_state; + } + + return fsm->state != Term; +} + +/* Given a string, return the event type. Do not modify this function. */ +event_t +lookup (char *token) +{ + if (!strcmp (token, "|")) + return PIPE; + + if (!strcmp (token, "NL")) + return NEWLINE; + + return TOKEN; +} diff --git a/lab2-cmd/model.h b/lab2-cmd/model.h new file mode 100644 index 0000000..cb2a7ee --- /dev/null +++ b/lab2-cmd/model.h @@ -0,0 +1,75 @@ +#ifndef __model_h__ +#define __model_h__ + +#include +#include +#include +#include + +// Generic definitions for any type of statemodel + +// States and events should just be integers +typedef int state_t; +typedef int event_t; + +// Needed for circular typedef. This lets action_t use fsm_t in its parameter +// list, while the struct fsm can use action_t as a field. +typedef struct fsm fsm_t; + +// All entry, exit, and effect instances use the action type +typedef void (*action_t) (fsm_t *); + +// Each FSM instance contains a current state +struct fsm +{ + state_t state; // current state + + // pointer to the FSM's transition function + state_t (*transition) (struct fsm *, event_t, action_t *); + + // Additional data fields specific to this FSM + char *command; // the name of the command to run + size_t nargs; // the number of command-line arguments + char **args; // the command-line arguments + char *current_token; // current token being processed +}; + +// Generic entry point for handling events +bool handle_event (fsm_t *, event_t); + +// Additional definitions specific to an FSM for command line processing + +#define MAX_ARGUMENTS 10 + +// Events +typedef enum +{ + TOKEN, // normal command-line token + PIPE, // vertical bar character + NEWLINE, // newline at the end of the command + NIL // invalid non-event +} cmdevt_t; +#define NUM_EVENTS NIL + +// States +typedef enum +{ + Init, // initial state + Command, // establishing the command name + Arguments, // building the argument list + Make_Pipe, // linking the commands together for a pipe + Term, // terminal state (execute program or error) + NST // invalid non-state +} cmdst_t; +#define NUM_STATES NST + +// Helper functions +fsm_t *cmdline_init (void); // initialize the FSM +state_t transition (struct fsm *fsm, event_t event, action_t *effect); +event_t lookup (char *); // convert an event string to its numeric value + +// Translate event/state numbers to their string equivalent +const char *event_name (event_t); +const char *state_name (state_t); + +#endif diff --git a/lab2-cmd/tests/Makefile b/lab2-cmd/tests/Makefile new file mode 100644 index 0000000..0f5df03 --- /dev/null +++ b/lab2-cmd/tests/Makefile @@ -0,0 +1,88 @@ +# +# Simple Test Makefile +# Mike Lam, James Madison University, August 2016 +# +# This version of the Makefile includes support for building a test suite. The +# recommended framework is Check (http://check.sourceforge.net/). To build and +# run the test suite, execute the "test" target. The test suite must be located +# in a module called "testsuite". The MODS, LIBS, and OBJS variables work as +# they do in the main Makefile. +# +# To change the default build target (which executes when you just type +# "make"), change the right-hand side of the definition of the "default" +# target. +# +# By default, this makefile will build the project with debugging symbols and +# without optimization. To change this, edit or remove the "-g" and "-O0" +# options in CFLAGS and LDFLAGS accordingly. +# +# By default, this makefile build the application using the GNU C compiler, +# adhering to the C99 standard with all warnings enabled. + + +# application-specific settings and run target + +EXE=../fsm +TEST=testsuite +MODS=public.o +OBJS=../model.o ../effects.o private.o +LIBS= + +UTESTOUT=utests.txt +ITESTOUT=itests.txt +SCHECKOUT=style.txt + +default: $(TEST) + +$(EXE): + make -C ../ + +test: utest itest style + @echo "========================================" + +utest: $(EXE) $(TEST) + @echo "========================================" + @echo " UNIT TESTS" + @./utests.sh | tee $(UTESTOUT) + +itest: $(EXE) + @echo "========================================" + @echo " INTEGRATION TESTS" + @./integration.sh | tee $(ITESTOUT) + +style: $(EXE) + @echo "========================================" + @echo " CODING STYLE CHECK" + @./style.sh 2>/dev/null >$(SCHECKOUT) + @cat $(SCHECKOUT) + +# compiler/linker settings + +CC=gcc +CFLAGS=-g -O0 -Wall --std=c99 +LDFLAGS=-g -O0 + +#CFLAGS+=-I/opt/local/include -Wno-gnu-zero-variadic-macro-arguments +#CFLAGS+=-Wno-gnu-zero-variadic-macro-arguments +#LDFLAGS+=-L/opt/local/lib +#LDFLAGS= +LIBS+=-lcheck -lm -lpthread + +ifeq ($(shell uname -s),Linux) + LIBS+=-lrt -lsubunit +endif + + +# build targets + +$(TEST): $(TEST).o $(MODS) $(OBJS) + $(CC) $(LDFLAGS) -o $(TEST) $^ $(LIBS) + +%.o: %.c + $(CC) -c $(CFLAGS) $< + +clean: + rm -rf $(TEST) $(TEST).o $(MODS) $(UTESTOUT) $(ITESTOUT) $(SCHECKOUT) outputs valgrind ckstyle + +.PHONY: default clean test unittest inttest + diff --git a/lab2-cmd/tests/expected/.INTEG_command.sha b/lab2-cmd/tests/expected/.INTEG_command.sha new file mode 100644 index 0000000..2e74f11 --- /dev/null +++ b/lab2-cmd/tests/expected/.INTEG_command.sha @@ -0,0 +1 @@ +015ebe95460b1d6fbf79df280ec3ae5fab362ae8 diff --git a/lab2-cmd/tests/expected/.INTEG_execute.sha b/lab2-cmd/tests/expected/.INTEG_execute.sha new file mode 100644 index 0000000..32a3cce --- /dev/null +++ b/lab2-cmd/tests/expected/.INTEG_execute.sha @@ -0,0 +1 @@ +42ae8f1bdf52b57ad234791690bf611379fc8249 diff --git a/lab2-cmd/tests/expected/.INTEG_execute_flags.sha b/lab2-cmd/tests/expected/.INTEG_execute_flags.sha new file mode 100644 index 0000000..40e3ea6 --- /dev/null +++ b/lab2-cmd/tests/expected/.INTEG_execute_flags.sha @@ -0,0 +1 @@ +ebc846cda2557a05e691ef4d7dc7de4ee312cbd1 diff --git a/lab2-cmd/tests/expected/.INTEG_execute_name.sha b/lab2-cmd/tests/expected/.INTEG_execute_name.sha new file mode 100644 index 0000000..b1e3ee8 --- /dev/null +++ b/lab2-cmd/tests/expected/.INTEG_execute_name.sha @@ -0,0 +1 @@ +2fd3754d04e83c387269e26d81186b290d299f82 diff --git a/lab2-cmd/tests/expected/.INTEG_pipe.sha b/lab2-cmd/tests/expected/.INTEG_pipe.sha new file mode 100644 index 0000000..7bfe9bc --- /dev/null +++ b/lab2-cmd/tests/expected/.INTEG_pipe.sha @@ -0,0 +1 @@ +17611b03d098cb2d095ddac6dc39c08f66514f4e diff --git a/lab2-cmd/tests/expected/.INTEG_pipe_flags.sha b/lab2-cmd/tests/expected/.INTEG_pipe_flags.sha new file mode 100644 index 0000000..02017ee --- /dev/null +++ b/lab2-cmd/tests/expected/.INTEG_pipe_flags.sha @@ -0,0 +1 @@ +10b932ebcb7ca5600c9b81652547ea3721854698 diff --git a/lab2-cmd/tests/expected/INTEG_command.txt b/lab2-cmd/tests/expected/INTEG_command.txt new file mode 100644 index 0000000..756ac9a --- /dev/null +++ b/lab2-cmd/tests/expected/INTEG_command.txt @@ -0,0 +1,2 @@ +[Init.TOKEN -> Command] +Starting new command: ls diff --git a/lab2-cmd/tests/expected/INTEG_execute.txt b/lab2-cmd/tests/expected/INTEG_execute.txt new file mode 100644 index 0000000..d0bba3a --- /dev/null +++ b/lab2-cmd/tests/expected/INTEG_execute.txt @@ -0,0 +1,4 @@ +[Init.TOKEN -> Command] +Starting new command: ls +[Command.NEWLINE -> Term] +Execute ls with arguments { ls, (null) } diff --git a/lab2-cmd/tests/expected/INTEG_execute_flags.txt b/lab2-cmd/tests/expected/INTEG_execute_flags.txt new file mode 100644 index 0000000..294ffe9 --- /dev/null +++ b/lab2-cmd/tests/expected/INTEG_execute_flags.txt @@ -0,0 +1,8 @@ +[Init.TOKEN -> Command] +Starting new command: ls +[Command.TOKEN -> Arguments] +Appending -a to the argument list +[Arguments.TOKEN -> Arguments] +Appending -l to the argument list +[Arguments.NEWLINE -> Term] +Execute ls with arguments { ls, -a, -l, (null) } diff --git a/lab2-cmd/tests/expected/INTEG_execute_name.txt b/lab2-cmd/tests/expected/INTEG_execute_name.txt new file mode 100644 index 0000000..1dc586e --- /dev/null +++ b/lab2-cmd/tests/expected/INTEG_execute_name.txt @@ -0,0 +1,6 @@ +[Init.TOKEN -> Command] +Starting new command: ls +[Command.TOKEN -> Arguments] +Appending data to the argument list +[Arguments.NEWLINE -> Term] +Execute ls with arguments { ls, data, (null) } diff --git a/lab2-cmd/tests/expected/INTEG_pipe.txt b/lab2-cmd/tests/expected/INTEG_pipe.txt new file mode 100644 index 0000000..71e3b1c --- /dev/null +++ b/lab2-cmd/tests/expected/INTEG_pipe.txt @@ -0,0 +1,9 @@ +[Init.TOKEN -> Command] +Starting new command: ls +[Command.PIPE -> Make_Pipe] +Set up pipe +Execute ls with arguments { ls, (null) } +[Make_Pipe.TOKEN -> Command] +Starting new command: head +[Command.NEWLINE -> Term] +Execute head with arguments { head, (null) } diff --git a/lab2-cmd/tests/expected/INTEG_pipe_flags.txt b/lab2-cmd/tests/expected/INTEG_pipe_flags.txt new file mode 100644 index 0000000..8ac5f3d --- /dev/null +++ b/lab2-cmd/tests/expected/INTEG_pipe_flags.txt @@ -0,0 +1,15 @@ +[Init.TOKEN -> Command] +Starting new command: ls +[Command.TOKEN -> Arguments] +Appending -l to the argument list +[Arguments.PIPE -> Make_Pipe] +Set up pipe +Execute ls with arguments { ls, -l, (null) } +[Make_Pipe.TOKEN -> Command] +Starting new command: head +[Command.TOKEN -> Arguments] +Appending -n to the argument list +[Arguments.TOKEN -> Arguments] +Appending 1 to the argument list +[Arguments.NEWLINE -> Term] +Execute head with arguments { head, -n, 1, (null) } diff --git a/lab2-cmd/tests/integration.sh b/lab2-cmd/tests/integration.sh new file mode 100755 index 0000000..777b769 --- /dev/null +++ b/lab2-cmd/tests/integration.sh @@ -0,0 +1,75 @@ +#!/bin/bash + +EXE="../shell" + +function run_test { + + # parameters + TAG=$1 + ARGS=$2 + + # file paths + OUTPUT=outputs/$TAG.txt + DIFF=outputs/$TAG.diff + EXPECT=expected/$TAG.txt + VALGRND=valgrind/$TAG.txt + + # print tag format + PTAG=$(printf '%-30s' "$TAG") + + # check for expected text that needs to be fixed + if [ ! -z "$(egrep "<<<<.*>>>>" "$EXPECT")" ] ; then + echo "$PTAG FAIL ($EXPECT not correct)" + return + fi + + # compare the file + SHAOUT=$(shasum "$EXPECT" | awk '{print $1}') + SHAEXP=$(cat "expected/.$TAG.sha") + if [ "$SHAOUT" != "$SHAEXP" ] ; then + echo "$PTAG FAIL ($EXPECT not correct)" + return + fi + + # run test and compare output to the expected version + $EXE "$ARGS" 2>/dev/null >"$OUTPUT" + diff -u "$OUTPUT" "$EXPECT" >"$DIFF" + if [ -s "$DIFF" ]; then + + # try alternative solution (if it exists) + EXPECT=expected/$TAG-2.txt + if [ -e "$EXPECT" ]; then + diff -u "$OUTPUT" "$EXPECT" >"$DIFF" + if [ -s "$DIFF" ]; then + echo "$PTAG FAIL (see $DIFF for details)" + else + echo "$PTAG pass" + fi + else + echo "$PTAG FAIL (see $DIFF for details)" + fi + else + echo "$PTAG pass" + fi + + # run valgrind + valgrind $EXE $ARGS &>$VALGRND +} + +# initialize output folders +mkdir -p outputs +mkdir -p valgrind +rm -f outputs/* valgrind/* + +# run individual tests +source itests.include + +# check for memory leaks +LEAK=`cat valgrind/*.txt | grep 'definitely lost' | grep -v ' 0 bytes in 0 blocks'` +if [ -z "$LEAK" ]; then + echo "No memory leak found." +else + echo "Memory leak(s) found. See files listed below for details." + grep 'definitely lost' valgrind/*.txt | grep -v ' 0 bytes in 0 blocks' | sed -e 's/:.*$//g' | sed -e 's/^/ - /g' +fi + diff --git a/lab2-cmd/tests/itests.include b/lab2-cmd/tests/itests.include new file mode 100644 index 0000000..2c9c862 --- /dev/null +++ b/lab2-cmd/tests/itests.include @@ -0,0 +1,11 @@ +# list of integration tests +# format: run_test +# used as the root for all filenames (i.e., "expected/$TAG.txt") +# command-line arguments to test + +run_test INTEG_command "ls" +run_test INTEG_execute "ls NL" +run_test INTEG_execute_name "ls data NL" +run_test INTEG_execute_flags "ls -a -l NL" +run_test INTEG_pipe "ls | head NL" +run_test INTEG_pipe_flags "ls -l | head -n 1 NL" diff --git a/lab2-cmd/tests/private.o b/lab2-cmd/tests/private.o new file mode 100644 index 0000000000000000000000000000000000000000..9f5baf0e04b89437aa2d9e88e912087b311d83a7 GIT binary patch literal 26024 zcmdUXeSDPFmH(Yr28NhdUJ?){Kp==oc+(G3z#t4vl(%Y9QN$rkCL|@9gn6;h1x#VyKZrR)}_|1v{lP~_ScrSO4HU#+`1NbU4OW`m2SnwRa@59EpA)cbM8IonL8&l zF8lJ&UYO_J`@P?L?z!ild!PHmUJ)3US$woC*O;Nup zp7w7ok%6<31Mh4a*!NWAULO`l22P&S@qZ>e5Pp6z{CwnaWFUMhax#1x)rbs2`eWhK znYAa%G5eA7ECFXgz&W=Oa0UdNEAGD^a&NB(Jp&grr%BMJi*xojMOXUC6{)pL1D#&Rl{%xy@SoRF2k%o4Al| z;`}8vkt-?N#B)F75x*(&hwmJQ_6DBrx_I$oTV&8%Q;W?HgwHJ){%E6!QsXKE;e(L_ z`wps1tyBE{@65J4^sG~0OnowZ1byUW_))Kpj-v0K3_p(JelmQ_qoe<4%8ncGDFY50 z@EKEm#N?hg(J3S4B@>;-2nMbXB6YGq4PC|5TMMD;$^PA(Er#r%&a&qZ{BTgSo&4WN z2CNQbEehUnaPR>XJWzt^b%Tw8$h|FngZH9zL z@bof}@ju{i{hL8f+R_V!(d~{DB4CopAcBPH;eNY!cy%Ck3kS)g!j)3sKw

lR|@x;mOf!BZj21J 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 PtACjE=W~3nBkTV=HV`3G literal 0 HcmV?d00001 diff --git a/lab2-cmd/tests/public.c b/lab2-cmd/tests/public.c new file mode 100644 index 0000000..9e92131 --- /dev/null +++ b/lab2-cmd/tests/public.c @@ -0,0 +1,53 @@ +#include +#include +#include +#include + +#include + +#include "../effects.h" +#include "../model.h" + +/* nothing but starting a new cmdline */ +START_TEST (UNIT_new_cmdline) +{ + printf ("\n======================================\n"); + printf ("UNIT TEST: new_cmdline\n\n"); + + fsm_t *fsm = cmdline_init (); + assert (fsm != NULL); + + handle_event (fsm, TOKEN); + ck_assert_int_eq (fsm->state, Command); + + free (fsm); +} +END_TEST + +/* a single cmdline from NEW to TRM */ +START_TEST (UNIT_exec_cmd) +{ + printf ("\n======================================\n"); + printf ("UNIT TEST: exec_cmd\n\n"); + + fsm_t *fsm = cmdline_init (); + assert (fsm != NULL); + + handle_event (fsm, TOKEN); + ck_assert_int_eq (fsm->state, Command); + + handle_event (fsm, NEWLINE); + ck_assert_int_eq (fsm->state, Term); + + free (fsm); +} +END_TEST + +void public_tests (Suite *s) +{ + TCase *tc_public = tcase_create ("Public"); + tcase_add_test (tc_public, UNIT_new_cmdline); + tcase_add_test (tc_public, UNIT_exec_cmd); + suite_add_tcase (s, tc_public); +} + diff --git a/lab2-cmd/tests/style.sh b/lab2-cmd/tests/style.sh new file mode 100755 index 0000000..ba2210a --- /dev/null +++ b/lab2-cmd/tests/style.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +STYLE="gnu" + +IGNORE=() +FAIL=0 + +function comp_file { + + SRC=$1 + SRC_NAME=$2 + + # file paths + FORMAT=ckstyle/${SRC_NAME}.$STYLE + DIFF=ckstyle/${SRC_NAME}.diff + + # run clang-format and compare results + clang-format --style=$STYLE $source > $FORMAT + diff -u $SRC $FORMAT >$DIFF + + PTAG=$(printf '%-30s' "$SRC_NAME") + if [ -s $DIFF ]; then + echo "$PTAG FAIL (see $DIFF for details)" + FAIL=1 + else + echo "$PTAG pass" + rm $DIFF + fi + rm $FORMAT +} + +mkdir -p ckstyle +rm -f ckstyle/* + +for source in $(ls ../*.c ../*.h) ; do + SKIP=0 + src=$(basename $source) + for ignore in ${IGNORE[*]} ; do + if [ "$src" = "$ignore" ] ; then + SKIP=1 + fi + done + if [ $SKIP = 0 ] ; then + comp_file $source $src + fi +done + +if [ $FAIL != 0 ] ; then + echo "Code that does not adhere to GNU standards will not be accepted." + echo "You must fix these files before submission." +else + echo "Code correctly adheres to required style." +fi diff --git a/lab2-cmd/tests/testsuite.c b/lab2-cmd/tests/testsuite.c new file mode 100644 index 0000000..145faa1 --- /dev/null +++ b/lab2-cmd/tests/testsuite.c @@ -0,0 +1,34 @@ +#include +#include +#include +#include +#include +#include + +#include + +extern void public_tests (Suite *s); +extern void private_tests (Suite *s); + +Suite * test_suite (void) +{ + Suite *s = suite_create ("Default"); + public_tests (s); + private_tests (s); + return s; +} + +void run_testsuite () +{ + Suite *s = test_suite (); + SRunner *sr = srunner_create (s); + srunner_run_all (sr, CK_NORMAL); + srunner_free (sr); +} + +int main (void) +{ + srand((unsigned)time(NULL)); + run_testsuite (); + return EXIT_SUCCESS; +} diff --git a/lab2-cmd/tests/utests.sh b/lab2-cmd/tests/utests.sh new file mode 100755 index 0000000..3f133d6 --- /dev/null +++ b/lab2-cmd/tests/utests.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +OUTPUT=utests.output + +./testsuite > "$OUTPUT" 2>/dev/null + +if [ ! -s "$OUTPUT" ] ; then + echo "UNIT FAIL (testsuite produced no output)" + rm -f "$OUTPUT" + exit 1 +fi + +cat "$OUTPUT" | awk '/Failures/,0' + +percent=$(cat "$OUTPUT" | grep Failures | cut -d':' -f1) +rm -f "$OUTPUT" +[ "$percent" = "100%" ] && exit 0 + +echo "(run ./testsuite in tests directory for more information)" +exit 1 + diff --git a/lab3-proc/.gitignore b/lab3-proc/.gitignore new file mode 100644 index 0000000..783d568 --- /dev/null +++ b/lab3-proc/.gitignore @@ -0,0 +1,20 @@ +# Ignore all compiled files regardless of location +**/*.o + +# But not the private.o +!tests/private.o + +# Ignore executables for this project +procs +testsuite + +# Ignore test outputs +tests/ckstyle +tests/itests.txt +tests/outputs +tests/style.txt +tests/valgrind +tests/utests.txt + +**/.nfs* +**/.vscode diff --git a/lab3-proc/Makefile b/lab3-proc/Makefile new file mode 100644 index 0000000..5560846 --- /dev/null +++ b/lab3-proc/Makefile @@ -0,0 +1,51 @@ +# +# Simple Makefile +# Mike Lam, James Madison University, August 2016 +# +# This makefile builds a simple application that contains a main module +# (specified by the EXE variable) and a predefined list of additional modules +# (specified by the MODS variable). If there are any external library +# dependencies (e.g., the math library, "-lm"), list them in the LIBS variable. +# If there are any precompiled object files, list them in the OBJS variable. +# +# By default, this makefile will build the project with debugging symbols and +# without optimization. To change this, edit or remove the "-g" and "-O0" +# options in CFLAGS and LDFLAGS accordingly. +# +# By default, this makefile build the application using the GNU C compiler, +# adhering to the C99 standard with all warnings enabled. + + +# application-specific settings and run target + +EXE=procs +MODS=signals.o child.o +OBJS= +LIBS= + +default: $(EXE) + +test: $(EXE) + make -C tests test + +# compiler/linker settings + +CC=gcc +CFLAGS=-g -O0 -Wall -Werror --std=c99 -pedantic -D_POSIX_SOURCE +LDFLAGS=-g -O0 -pthread + + +# build targets + +$(EXE): main.o $(MODS) $(OBJS) + $(CC) $(LDFLAGS) -o $(EXE) $^ $(LIBS) + +%.o: %.c + $(CC) -c $(CFLAGS) $< + +clean: + rm -f $(EXE) main.o $(MODS) + make -C tests clean + +.PHONY: default clean + diff --git a/lab3-proc/child.c b/lab3-proc/child.c new file mode 100644 index 0000000..2d329c8 --- /dev/null +++ b/lab3-proc/child.c @@ -0,0 +1,39 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "child.h" + +pid_t +run_child (int rc) +{ + char *str = "I am the parent"; + + // TODO: Add code here to create a child process that prints + // "I am the child" and exits with return code passed as the rc + // parameter to this function. The parent must wait for the child + // to finish before printing the child's exit status (consult the + // textbook for this as needed) before printing "I am the parent". + // The parent should return the child's PID from this function. + int status = 0; + pid_t pid = fork (); + if (pid == 0) + { + printf ("I am the child\n"); + exit (rc); + } + else + { + waitpid (pid, &status, 0); + } + + printf ("Child exited with status %d\n", WEXITSTATUS (status)); + + printf ("%s\n", str); + return pid; +} diff --git a/lab3-proc/child.h b/lab3-proc/child.h new file mode 100644 index 0000000..fc7023a --- /dev/null +++ b/lab3-proc/child.h @@ -0,0 +1,6 @@ +#ifndef __CS361_CHILD_H__ +#define __CS361_CHILD_H__ + +pid_t run_child (int); + +#endif diff --git a/lab3-proc/main.c b/lab3-proc/main.c new file mode 100644 index 0000000..dc431a3 --- /dev/null +++ b/lab3-proc/main.c @@ -0,0 +1,87 @@ +/* + * CS 361: Process management lab + * + * Name: + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "child.h" +#include "signals.h" + +#define SIGNAL_MAX 20 + +int cmdline (int, char **, int *, char **); + +void +usage (void) +{ + printf ("Usage: procs [option]\n"); + printf (" Options are:\n"); + printf (" -e N Create a child that exits with status N\n"); + printf (" -s N Override signal number N and run signal handler\n"); + printf ("\n Default is to create a child process with normal exit\n"); +} + +int +main (int argc, char **argv) +{ + char *signal_override = NULL; + int rc = 42; + if (cmdline (argc, argv, &rc, &signal_override) < 0) + { + usage (); + return EXIT_FAILURE; + } + + // Set up a timeout to kill the process after 5 seconds + struct itimerval timeout; + memset (&timeout, 0, sizeof (timeout)); + timeout.it_value.tv_sec = 5; + timeout.it_value.tv_usec = 0; + setitimer (ITIMER_REAL, &timeout, NULL); + + if (signal_override == NULL) + run_child (rc); + else + override (signal_override); + + return EXIT_SUCCESS; +} + +/* DO NOT MODIFY THIS FUNCTION. Parses the command line arguments to get + the portion of the lab to run. */ +int +cmdline (int argc, char **argv, int *rc, char **sig) +{ + int option; + + while ((option = getopt (argc, argv, "e:s:")) != -1) + { + switch (option) + { + case 'e': + *rc = (int)strtol (optarg, NULL, 10); + break; + case 's': + *sig = optarg; + break; + default: + return -1; + } + } + + return 0; +} diff --git a/lab3-proc/signals.c b/lab3-proc/signals.c new file mode 100644 index 0000000..b7870e3 --- /dev/null +++ b/lab3-proc/signals.c @@ -0,0 +1,110 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "signals.h" + +static void catch_loop (int); + +/* Convert the short signal name (NNN part of SIGNNN) to its number. + Consult /usr/include/asm/signal.h for the number mappings. For + this lab, hard-code the numbers; do not use the numeric constants. + Return -1 if the name isn't one you need to look up. */ +int +signal_lookup (char *name) +{ + // TODO: Look up the numbers for INT, SEGV, KILL, and TSTP + if (strcmp (name, "INT") == 0) + return 2; + if (strcmp (name, "SEGV") == 0) + return 11; + if (strcmp (name, "KILL") == 0) + return 9; + if (strcmp (name, "TSTP") == 0) + return 20; + return -1; +} + +// Entry point for overriding a signal handler in this lab. +void +override (char *signal) +{ + int sig = signal_lookup (signal); + if (sig == SIGKILL) + printf ("Received bad signal name: %s\n", signal); + else + catch_loop (sig); +} + +// Generic signal handler. This function doesn't use the signal number +// in the provided distribution. +void +sig_handler (int signum __attribute__ ((unused))) +{ + // TODO: Complete this signal handler so that it writes the message + // "Breaking out with signal %d\n" with the signal number that was + // passed. Note that you cannot use printf() in a signal handler. + // You must use write(). After writing the message, exit with return + // code 37. + // + // HINT: If you fail integration tests and get a message that the + // binary files are different, you may be writing an extra byte (the + // null byte). You can make sure you avoid this by using strlen() + // instead of trying to hard-code a length. + char buf[128]; + snprintf (buf, 128, "Breaking out with signal %d\n", signum); + write (STDOUT_FILENO, buf, strlen (buf)); + exit (37); +} + +void +catch_loop (int signum) +{ + // sigaction(signum) + // TODO: Override the signal handler for the signal number passed. + // Then create a child that runs the following code: + // prctl (PR_SET_PDEATHSIG, SIGKILL); + // printf ("Child has started\n"); + // fflush (stdout); + // while (1) ; + + struct sigaction sa = { 0 }; + sa.sa_handler = sig_handler; + + sigaction (signum, &sa, NULL); + + pid_t pid = fork (); + + if (pid == 0) + { + prctl (PR_SET_PDEATHSIG, SIGKILL); + printf ("Child has started\n"); + fflush (stdout); + while (1) + pause (); + } + + // HINT: You will NOT call sig_handler() directly here. Instead, + // you must use sigaction() to modify the signal's interrupt handler + // so that sig_handler() will be called later when the signal occurs. + + // After starting the child, the parent will call sleep (1) to wait + // for 1 second before sending the signal to the child. The parent + // must wait for the child to run (and exit), then print + // "Child was stopped with status %d\n" with the child process's exit + // status code, retrieved with waitpid(). Note that you will need to + // call WEXITSTATUS() on the retrieved status code. + + sleep (1); + + int status = 0; + kill (pid, signum); + waitpid (pid, &status, 0); + printf ("Child was stopped with status %d\n", WEXITSTATUS (status)); +} diff --git a/lab3-proc/signals.h b/lab3-proc/signals.h new file mode 100644 index 0000000..c6fc1a2 --- /dev/null +++ b/lab3-proc/signals.h @@ -0,0 +1,7 @@ +#ifndef __CS361_SIGNALS_H__ +#define __CS361_SIGNALS_H__ + +void override (char *); +int signal_lookup (char *); + +#endif diff --git a/lab3-proc/tests/Makefile b/lab3-proc/tests/Makefile new file mode 100644 index 0000000..ab16746 --- /dev/null +++ b/lab3-proc/tests/Makefile @@ -0,0 +1,87 @@ +# +# Simple Test Makefile +# Mike Lam, James Madison University, August 2016 +# +# This version of the Makefile includes support for building a test suite. The +# recommended framework is Check (http://check.sourceforge.net/). To build and +# run the test suite, execute the "test" target. The test suite must be located +# in a module called "testsuite". The MODS, LIBS, and OBJS variables work as +# they do in the main Makefile. +# +# To change the default build target (which executes when you just type +# "make"), change the right-hand side of the definition of the "default" +# target. +# +# By default, this makefile will build the project with debugging symbols and +# without optimization. To change this, edit or remove the "-g" and "-O0" +# options in CFLAGS and LDFLAGS accordingly. +# +# By default, this makefile build the application using the GNU C compiler, +# adhering to the C99 standard with all warnings enabled. + + +# application-specific settings and run target + +EXE=../sigs +TEST=testsuite +MODS=public.o +OBJS=../signals.o ../child.o +LIBS= + +UTESTOUT=utests.txt +ITESTOUT=itests.txt +SCHECKOUT=style.txt + +default: $(TEST) + +$(EXE): + make -C ../ + +test: utest itest style + @echo "========================================" + +utest: $(EXE) $(TEST) + @echo "========================================" + @echo " UNIT TESTS" + @./$(TEST) 2>/dev/null >$(UTESTOUT) + @cat $(UTESTOUT) | sed -n -e '/Checks/,$$p' | sed -e 's/^private.*:[EF]://g' + +itest: $(EXE) + @echo "========================================" + @echo " INTEGRATION TESTS" + @./integration.sh | tee $(ITESTOUT) + +style: $(EXE) + @echo "========================================" + @echo " CODING STYLE CHECK" + @./style.sh 2>/dev/null >$(SCHECKOUT) + @cat $(SCHECKOUT) + +# compiler/linker settings + +CC=gcc +CFLAGS=-g -O0 -Wall --std=c99 -pedantic +LDFLAGS=-g -O0 + +CFLAGS+=-I/opt/local/include -Wno-gnu-zero-variadic-macro-arguments +LDFLAGS+=-L/opt/local/lib +LIBS+=-lcheck -lm -lpthread + +ifeq ($(shell uname -s),Linux) + LIBS+=-lrt -lsubunit +endif + + +# build targets + +$(TEST): $(TEST).o $(MODS) $(OBJS) + $(CC) $(LDFLAGS) -o $(TEST) $^ $(LIBS) + +%.o: %.c + $(CC) -c $(CFLAGS) $< + +clean: + rm -rf $(TEST) $(TEST).o $(MODS) $(UTESTOUT) $(ITESTOUT) $(SCHECKOUT) outputs valgrind + +.PHONY: default clean test unittest inttest + diff --git a/lab3-proc/tests/expected/.INTEG_child.sha b/lab3-proc/tests/expected/.INTEG_child.sha new file mode 100644 index 0000000..d768e3a --- /dev/null +++ b/lab3-proc/tests/expected/.INTEG_child.sha @@ -0,0 +1 @@ +6037b06b79a54f57617de151d505444f78a23489 diff --git a/lab3-proc/tests/expected/.INTEG_kill.sha b/lab3-proc/tests/expected/.INTEG_kill.sha new file mode 100644 index 0000000..0dae746 --- /dev/null +++ b/lab3-proc/tests/expected/.INTEG_kill.sha @@ -0,0 +1 @@ +ebef9658a6b46bd9cfcfeaf732a6d2444fe0b98a diff --git a/lab3-proc/tests/expected/.INTEG_segfault.sha b/lab3-proc/tests/expected/.INTEG_segfault.sha new file mode 100644 index 0000000..5f51de9 --- /dev/null +++ b/lab3-proc/tests/expected/.INTEG_segfault.sha @@ -0,0 +1 @@ +145cbab85e3834cf39635245272d5f80e6bd66f2 diff --git a/lab3-proc/tests/expected/.INTEG_sigint.sha b/lab3-proc/tests/expected/.INTEG_sigint.sha new file mode 100644 index 0000000..1568243 --- /dev/null +++ b/lab3-proc/tests/expected/.INTEG_sigint.sha @@ -0,0 +1 @@ +b4ec94fbc4feb8f7ce089341bd6c51e775fce9e1 diff --git a/lab3-proc/tests/expected/.INTEG_status.sha b/lab3-proc/tests/expected/.INTEG_status.sha new file mode 100644 index 0000000..8f94e2d --- /dev/null +++ b/lab3-proc/tests/expected/.INTEG_status.sha @@ -0,0 +1 @@ +fb52530bd8b6756b4acec57a42391f8b7c9c0e83 diff --git a/lab3-proc/tests/expected/INTEG_child.txt b/lab3-proc/tests/expected/INTEG_child.txt new file mode 100644 index 0000000..b2d306f --- /dev/null +++ b/lab3-proc/tests/expected/INTEG_child.txt @@ -0,0 +1,3 @@ +I am the child +Child exited with status 42 +I am the parent diff --git a/lab3-proc/tests/expected/INTEG_kill.txt b/lab3-proc/tests/expected/INTEG_kill.txt new file mode 100644 index 0000000..f110c52 --- /dev/null +++ b/lab3-proc/tests/expected/INTEG_kill.txt @@ -0,0 +1 @@ +Received bad signal name: KILL diff --git a/lab3-proc/tests/expected/INTEG_segfault.txt b/lab3-proc/tests/expected/INTEG_segfault.txt new file mode 100644 index 0000000..97847e2 --- /dev/null +++ b/lab3-proc/tests/expected/INTEG_segfault.txt @@ -0,0 +1,3 @@ +Child has started +Breaking out with signal 11 +Child was stopped with status 37 diff --git a/lab3-proc/tests/expected/INTEG_sigint.txt b/lab3-proc/tests/expected/INTEG_sigint.txt new file mode 100644 index 0000000..5c96cd8 --- /dev/null +++ b/lab3-proc/tests/expected/INTEG_sigint.txt @@ -0,0 +1,3 @@ +Child has started +Breaking out with signal 2 +Child was stopped with status 37 diff --git a/lab3-proc/tests/expected/INTEG_status.txt b/lab3-proc/tests/expected/INTEG_status.txt new file mode 100644 index 0000000..99187e4 --- /dev/null +++ b/lab3-proc/tests/expected/INTEG_status.txt @@ -0,0 +1,3 @@ +I am the child +Child exited with status 54 +I am the parent diff --git a/lab3-proc/tests/integration.sh b/lab3-proc/tests/integration.sh new file mode 100755 index 0000000..beb5b79 --- /dev/null +++ b/lab3-proc/tests/integration.sh @@ -0,0 +1,75 @@ +#!/bin/bash + +EXE="../procs" + +function run_test { + + # parameters + TAG=$1 + ARGS=$2 + + # file paths + OUTPUT=outputs/$TAG.txt + DIFF=outputs/$TAG.diff + EXPECT=expected/$TAG.txt + VALGRND=valgrind/$TAG.txt + + # print tag format + PTAG=$(printf '%-30s' "$TAG") + + # check for expected text that needs to be fixed + if [ ! -z "$(egrep "<<<<.*>>>>" "$EXPECT")" ] ; then + echo "$PTAG FAIL ($EXPECT not correct)" + return + fi + + # compare the file + SHAOUT=$(shasum "$EXPECT" | awk '{print $1}') + SHAEXP=$(cat "expected/.$TAG.sha") + if [ "$SHAOUT" != "$SHAEXP" ] ; then + echo "$PTAG FAIL (expected file is not correct)" + return + fi + + # run test and compare output to the expected version + $EXE $ARGS 2>/dev/null >"$OUTPUT" + diff -u "$OUTPUT" "$EXPECT" >"$DIFF" + if [ -s "$DIFF" ]; then + + # try alternative solution (if it exists) + EXPECT=expected/$TAG-2.txt + if [ -e "$EXPECT" ]; then + diff -u "$OUTPUT" "$EXPECT" >"$DIFF" + if [ -s "$DIFF" ]; then + echo "$PTAG FAIL (see $DIFF for details)" + else + echo "$PTAG pass" + fi + else + echo "$PTAG FAIL (see $DIFF for details)" + fi + else + echo "$PTAG pass" + fi + + # run valgrind + valgrind $EXE $ARGS &>$VALGRND +} + +# initialize output folders +mkdir -p outputs +mkdir -p valgrind +rm -f outputs/* valgrind/* + +# run individual tests +source itests.include + +# check for memory leaks +LEAK=`cat valgrind/*.txt | grep 'definitely lost' | grep -v ' 0 bytes in 0 blocks'` +if [ -z "$LEAK" ]; then + echo "No memory leak found." +else + echo "Memory leak(s) found. See files listed below for details." + grep 'definitely lost' valgrind/*.txt | grep -v ' 0 bytes in 0 blocks' | sed -e 's/:.*$//g' | sed -e 's/^/ - /g' +fi + diff --git a/lab3-proc/tests/itests.include b/lab3-proc/tests/itests.include new file mode 100644 index 0000000..0838538 --- /dev/null +++ b/lab3-proc/tests/itests.include @@ -0,0 +1,10 @@ +# list of integration tests +# format: run_test +# used as the root for all filenames (i.e., "expected/$TAG.txt") +# command-line arguments to test + +run_test INTEG_child "" +run_test INTEG_status "-e 54" +run_test INTEG_sigint "-s INT" +run_test INTEG_segfault "-s SEGV" +run_test INTEG_kill "-s KILL" diff --git a/lab3-proc/tests/private.o b/lab3-proc/tests/private.o new file mode 100644 index 0000000000000000000000000000000000000000..20f56ee515e4d3e3c1cfa82ce46e1484b5ad2970 GIT binary patch literal 1200 zcmbtTOG*Pl5Uq*-Z=$#mA~LR2FwGdFhzkjXd={c01l$Cf#5NJlhnbE<#m`D`=Mg-D zM-aS(7w`mb#FbT2t23OjS2J5s=_Uz7hd2iDiXXUfE~EP1y=NZV5NGs%4^8g z>Vh-8cFY%U=~(9FT9Y-rdV_o744B7HT*fQ=hn`#IwDbQEMLFHI-x!mWdFS3Tr(PX% zm=LGlN7Z&zV0cqMk9B&Vzv`DzOEVob9cq87>rneEzluc_>wOn`qQz!!8fyb+^!_?- zWMbw)X6rhX4A^nWBf9YNs8gHB6+2tA!s=@s8?Z<4&wg2)99 hhSAd*O+C#u-;N}<-2b=lXW|=uPU_1vOoOC5%qMv9O=tiB literal 0 HcmV?d00001 diff --git a/lab3-proc/tests/public.c b/lab3-proc/tests/public.c new file mode 100644 index 0000000..a5ea1f6 --- /dev/null +++ b/lab3-proc/tests/public.c @@ -0,0 +1,46 @@ +#include +#include +#include +#include + +#include + +#include "../signals.h" + +START_TEST (UNIT_good_signals) +{ + printf ("\n======================================\n"); + printf ("UNIT good signals\n"); + printf ("This test checks for signal name lookups\n"); + char *names[] = { "INT", "KILL", "SEGV", "TSTP" }; + int signals[] = { SIGINT, SIGKILL, SIGSEGV, SIGTSTP }; + for (int i = 0; i < 4; i++) + { + int sig = signal_lookup (names[i]); + ck_assert_int_eq (signals[i], sig); + } +} +END_TEST + +START_TEST (UNIT_bad_signals) +{ + printf ("\n======================================\n"); + printf ("UNIT bad signals\n"); + printf ("This test checks for other signal name lookups\n"); + char *names[] = { "HUP", "ALRM", "ILL", "KIL" }; + for (int i = 0; i < 4; i++) + { + int sig = signal_lookup (names[i]); + ck_assert_int_eq (sig, -1); + } +} +END_TEST + +void public_tests (Suite *s) +{ + TCase *tc_public = tcase_create ("Public"); + tcase_add_test (tc_public, UNIT_good_signals); + tcase_add_test (tc_public, UNIT_bad_signals); + suite_add_tcase (s, tc_public); +} + diff --git a/lab3-proc/tests/style.sh b/lab3-proc/tests/style.sh new file mode 100755 index 0000000..ba2210a --- /dev/null +++ b/lab3-proc/tests/style.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +STYLE="gnu" + +IGNORE=() +FAIL=0 + +function comp_file { + + SRC=$1 + SRC_NAME=$2 + + # file paths + FORMAT=ckstyle/${SRC_NAME}.$STYLE + DIFF=ckstyle/${SRC_NAME}.diff + + # run clang-format and compare results + clang-format --style=$STYLE $source > $FORMAT + diff -u $SRC $FORMAT >$DIFF + + PTAG=$(printf '%-30s' "$SRC_NAME") + if [ -s $DIFF ]; then + echo "$PTAG FAIL (see $DIFF for details)" + FAIL=1 + else + echo "$PTAG pass" + rm $DIFF + fi + rm $FORMAT +} + +mkdir -p ckstyle +rm -f ckstyle/* + +for source in $(ls ../*.c ../*.h) ; do + SKIP=0 + src=$(basename $source) + for ignore in ${IGNORE[*]} ; do + if [ "$src" = "$ignore" ] ; then + SKIP=1 + fi + done + if [ $SKIP = 0 ] ; then + comp_file $source $src + fi +done + +if [ $FAIL != 0 ] ; then + echo "Code that does not adhere to GNU standards will not be accepted." + echo "You must fix these files before submission." +else + echo "Code correctly adheres to required style." +fi diff --git a/lab3-proc/tests/testsuite.c b/lab3-proc/tests/testsuite.c new file mode 100644 index 0000000..3aa216b --- /dev/null +++ b/lab3-proc/tests/testsuite.c @@ -0,0 +1,34 @@ +#include +#include +#include +#include +#include +#include + +#include + +extern void public_tests (Suite *s); +//extern void private_tests (Suite *s); + +Suite * test_suite (void) +{ + Suite *s = suite_create ("Default"); + public_tests (s); + //private_tests (s); + return s; +} + +void run_testsuite () +{ + Suite *s = test_suite (); + SRunner *sr = srunner_create (s); + srunner_run_all (sr, CK_NORMAL); + srunner_free (sr); +} + +int main (void) +{ + srand((unsigned)time(NULL)); + run_testsuite (); + return EXIT_SUCCESS; +} diff --git a/lab4-ipc/.gitignore b/lab4-ipc/.gitignore new file mode 100644 index 0000000..bc55d8b --- /dev/null +++ b/lab4-ipc/.gitignore @@ -0,0 +1,20 @@ +# Ignore all compiled files regardless of location +**/*.o + +# But not the private.o +!tests/private.o + +# Ignore executables for this project +ipc +testsuite + +# Ignore test outputs +tests/ckstyle +tests/itests.txt +tests/outputs +tests/style.txt +tests/valgrind +tests/utests.txt + +**/.nfs* +**/.vscode diff --git a/lab4-ipc/Makefile b/lab4-ipc/Makefile new file mode 100644 index 0000000..84bb14d --- /dev/null +++ b/lab4-ipc/Makefile @@ -0,0 +1,52 @@ +# +# Simple Makefile +# Mike Lam, James Madison University, August 2016 +# +# This makefile builds a simple application that contains a main module +# (specified by the EXE variable) and a predefined list of additional modules +# (specified by the MODS variable). If there are any external library +# dependencies (e.g., the math library, "-lm"), list them in the LIBS variable. +# If there are any precompiled object files, list them in the OBJS variable. +# +# By default, this makefile will build the project with debugging symbols and +# without optimization. To change this, edit or remove the "-g" and "-O0" +# options in CFLAGS and LDFLAGS accordingly. +# +# By default, this makefile build the application using the GNU C compiler, +# adhering to the C99 standard with all warnings enabled. + + +# application-specific settings and run target + +EXE=ipc +MODS=pipe.o +OBJS= +LIBS=-lm + +default: $(EXE) + +test: $(EXE) + make -C tests test + +# compiler/linker settings + +CC=gcc +#CFLAGS=-g -O0 -Wall -Werror --std=gnu99 -pedantic +CFLAGS=-g -O0 -Wall -Werror --std=gnu99 -pedantic +LDFLAGS=-g -O0 -pthread + + +# build targets + +$(EXE): main.o $(MODS) $(OBJS) + $(CC) $(LDFLAGS) -o $(EXE) $^ $(LIBS) + +%.o: %.c + $(CC) -c $(CFLAGS) $< + +clean: + rm -f $(EXE) main.o $(MODS) + make -C tests clean + +.PHONY: default clean + diff --git a/lab4-ipc/data/f1.txt b/lab4-ipc/data/f1.txt new file mode 100644 index 0000000..ce01362 --- /dev/null +++ b/lab4-ipc/data/f1.txt @@ -0,0 +1 @@ +hello diff --git a/lab4-ipc/data/f2.txt b/lab4-ipc/data/f2.txt new file mode 100644 index 0000000..dd7e1c6 --- /dev/null +++ b/lab4-ipc/data/f2.txt @@ -0,0 +1 @@ +goodbye diff --git a/lab4-ipc/data/f3.txt b/lab4-ipc/data/f3.txt new file mode 100644 index 0000000..72704b2 --- /dev/null +++ b/lab4-ipc/data/f3.txt @@ -0,0 +1 @@ +alohomora diff --git a/lab4-ipc/data/index b/lab4-ipc/data/index new file mode 100644 index 0000000000000000000000000000000000000000..51aad9d33cceb9fa223aa887bcc7a0e4b0f3dfbb GIT binary patch literal 36 bcmYdEEJ@T)Gt?`oC}BtevyG70#$Yx8-)IZY literal 0 HcmV?d00001 diff --git a/lab4-ipc/main.c b/lab4-ipc/main.c new file mode 100644 index 0000000..be9d471 --- /dev/null +++ b/lab4-ipc/main.c @@ -0,0 +1,87 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pipe.h" + +int cmdline (int, char **, bool *, char **, char **, size_t *); + +void +usage (void) +{ + printf ("Usage: ipc [options] EXEC ARG\n"); + printf (" Send ARG through a pipe to child process that runs EXEC.\n"); + printf (" Option is:\n"); + printf (" -s Use posix_spawn() instead of fork() and exec().\n"); +} + +int +main (int argc, char **argv) +{ + bool use_spawn = false; + char *exec = NULL; + char *fname = NULL; + size_t lineno = -1; + + if (cmdline (argc, argv, &use_spawn, &exec, &fname, &lineno) < 0) + { + usage (); + return EXIT_FAILURE; + } + + // Use fork/exec or spawn to run the executable exec + char *sum = NULL; + if (!use_spawn) + sum = get_result (exec, fname); + else + sum = spawn_result (exec, fname); + + if (sum == NULL) + return EXIT_FAILURE; + + printf ("%s(%s) = '%s'\n", exec, fname, sum); + + // If you get an error here, it's because sum is not dynamically + // allocated correctly. + free (sum); + return EXIT_SUCCESS; +} + +/* Helper for parsing the command line arguments. Sets spawn, + filename, and lineno as call-by-reference parameters. Return 0 + on success, -1 otherwise. */ +int +cmdline (int argc, char **argv, bool *spawn, char **exec, char **filename, + size_t *lineno) +{ + int option; + + while ((option = getopt (argc, argv, "sh")) != -1) + { + switch (option) + { + case 's': + *spawn = true; + break; + case 'h': + return -1; + break; + default: + return -1; + } + } + + if (optind + 2 != argc) + return -1; + + *exec = argv[optind]; + *filename = argv[optind + 1]; + + return 0; +} diff --git a/lab4-ipc/pipe.c b/lab4-ipc/pipe.c new file mode 100644 index 0000000..6b1c8a9 --- /dev/null +++ b/lab4-ipc/pipe.c @@ -0,0 +1,118 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "pipe.h" + +/* Creates a child process. If this fails, close the pipe and return -1. + If the child is created, use it to run the executable program on the + filename passed. Send the result back through the pipe. The parent process + should just return the child's PID. Sample call: + + int fd[2]; + pipe (fd); + pid_t child_pid = create_child (exec, fd, "foo.txt"); + */ +pid_t +create_child (char *const exec, int *pipe, char *const filename) +{ + char *args[3] = { exec, filename, NULL }; + pid_t pid = fork (); + + if (pid == 0) + { + close (pipe[0]); + dup2 (pipe[1], STDOUT_FILENO); + close (pipe[1]); + execvp (exec, args); + exit (-1); + } + + close (pipe[1]); + return pid; +} + +/* Runs the executable program on the input filename. Start by creating a + pipe and calling create_child() to create the child process. If that + fails, return NULL. Otherwise, implement the parent here so that it + reads the result of the process from the pipe and returns the result + without a newline. The returned string must be a dynamically allocated + copy; you can use the constant BUFFER_LENGTH for its size. Be sure to + close the ends of the pipe at appropriate times. Sample call: + + char *sum = get_result ("cksum", "data/f1.txt"); + // sum is "3015617425 6 data/f1.txt" [without the newline] + */ +char * +get_result (char *const exec, char *const filename) +{ + int fd[2]; + if (pipe (fd) == -1) + { + return NULL; + } + + pid_t pid = create_child (exec, fd, filename); + + char *buffer = calloc (BUFFER_LENGTH, sizeof (char)); + ssize_t n = read (fd[0], buffer, BUFFER_LENGTH); + buffer[n - 1] = '\0'; + + close (fd[0]); + waitpid (pid, NULL, 0); + + return buffer; +} + +/* Re-implementation of the create_child() in pipe.c. Instead of + using the calls to pipe/fork/dup2/exec, combine the latter three + into a call to posix_spawn(). Sample call: + + char *sum = spawn_result ("cksum", "data/f1.txt"); + // sum is "3015617425 6 data/f1.txt\n" [with the newline] + */ +char * +spawn_result (char *const exec, char *const filename) +{ + // Instead of using fork() and exec(), use the posix_spawn functions. + // Note that you will need to create a pipe, initialize a + // posix_spawn_file_actions_t instance, and add close and dup2 actions + // to it so that the child process writes its STDOUT to the pipe. + // After spawning the new process, make sure to call + // posix_spawn_file_actions_destroy() on the actions to prevent + // memory leaks. To avoid looking up the executable in the $PATH, + // you can use posix_spawnp(). + + // Parent code: read the value back from the pipe into a dynamically + // allocated buffer. Wait for the child to exit, then return the + // buffer. Make sure to close the ends of the pipe in appropriate + // places. + int fd[2]; + pipe (fd); + + posix_spawn_file_actions_t actions; + posix_spawn_file_actions_init (&actions); + posix_spawn_file_actions_addclose (&actions, fd[0]); + posix_spawn_file_actions_adddup2 (&actions, fd[1], STDOUT_FILENO); + posix_spawn_file_actions_addclose (&actions, fd[1]); + + pid_t pid = -1; + char *args[3] = { exec, filename, NULL }; + + posix_spawnp (&pid, exec, &actions, NULL, args, NULL); + posix_spawn_file_actions_destroy (&actions); + close (fd[1]); + + char *buffer = calloc (BUFFER_LENGTH, sizeof (char)); + ssize_t n = read (fd[0], buffer, BUFFER_LENGTH); + buffer[n - 1] = '\0'; + + close (fd[0]); + waitpid (pid, NULL, 0); + + return buffer; +} diff --git a/lab4-ipc/pipe.h b/lab4-ipc/pipe.h new file mode 100644 index 0000000..81bc545 --- /dev/null +++ b/lab4-ipc/pipe.h @@ -0,0 +1,12 @@ +#ifndef __pipe_h__ +#define __pipe_h__ + +#include + +#define BUFFER_LENGTH 100 + +pid_t create_child (char *const, int *, char *const); +char *get_result (char *const, char *const); +char *spawn_result (char *const, char *const); + +#endif diff --git a/lab4-ipc/tests/Makefile b/lab4-ipc/tests/Makefile new file mode 100644 index 0000000..b830cde --- /dev/null +++ b/lab4-ipc/tests/Makefile @@ -0,0 +1,86 @@ +# +# Simple Test Makefile +# Mike Lam, James Madison University, August 2016 +# +# This version of the Makefile includes support for building a test suite. The +# recommended framework is Check (http://check.sourceforge.net/). To build and +# run the test suite, execute the "test" target. The test suite must be located +# in a module called "testsuite". The MODS, LIBS, and OBJS variables work as +# they do in the main Makefile. +# +# To change the default build target (which executes when you just type +# "make"), change the right-hand side of the definition of the "default" +# target. +# +# By default, this makefile will build the project with debugging symbols and +# without optimization. To change this, edit or remove the "-g" and "-O0" +# options in CFLAGS and LDFLAGS accordingly. +# +# By default, this makefile build the application using the GNU C compiler, +# adhering to the C99 standard with all warnings enabled. + + +# application-specific settings and run target + +EXE=../ipc +TEST=testsuite +MODS=public.o +OBJS=../pipe.o private.o +LIBS= + +UTESTOUT=utests.txt +ITESTOUT=itests.txt +SCHECKOUT=style.txt + +default: $(TEST) + +$(EXE): + make -C ../ + +test: utest itest style + @echo "========================================" + +utest: $(EXE) $(TEST) + @echo "========================================" + @echo " UNIT TESTS" + @./$(TEST) 2>/dev/null >$(UTESTOUT) + @cat $(UTESTOUT) | sed -n -e '/Checks/,$$p' | sed -e 's/^private.*:[EF]://g' + +itest: $(EXE) + @echo "========================================" + @echo " INTEGRATION TESTS" + @./integration.sh | tee $(ITESTOUT) + +style: $(EXE) + @echo "========================================" + @echo " CODING STYLE CHECK" + @./style.sh 2>/dev/null >$(SCHECKOUT) + @cat $(SCHECKOUT) + +# compiler/linker settings + +CC=gcc +CFLAGS=-g -O0 -Wall --std=gnu99 -pedantic +LDFLAGS=-g -O0 + +CFLAGS= +LIBS+=-lcheck -lm -lpthread + +ifeq ($(shell uname -s),Linux) + LIBS+=-lrt -lsubunit +endif + + +# build targets + +$(TEST): $(TEST).o $(MODS) $(OBJS) + $(CC) $(LDFLAGS) -o $(TEST) $^ $(LIBS) + +%.o: %.c + $(CC) -c $(CFLAGS) $< + +clean: + rm -rf $(TEST) $(TEST).o $(MODS) $(UTESTOUT) $(ITESTOUT) $(SCHECKOUT) outputs valgrind ckstyle + +.PHONY: default clean test unittest inttest + diff --git a/lab4-ipc/tests/data/f1.txt b/lab4-ipc/tests/data/f1.txt new file mode 100644 index 0000000..ce01362 --- /dev/null +++ b/lab4-ipc/tests/data/f1.txt @@ -0,0 +1 @@ +hello diff --git a/lab4-ipc/tests/data/f2.txt b/lab4-ipc/tests/data/f2.txt new file mode 100644 index 0000000..dd7e1c6 --- /dev/null +++ b/lab4-ipc/tests/data/f2.txt @@ -0,0 +1 @@ +goodbye diff --git a/lab4-ipc/tests/data/f3.txt b/lab4-ipc/tests/data/f3.txt new file mode 100644 index 0000000..72704b2 --- /dev/null +++ b/lab4-ipc/tests/data/f3.txt @@ -0,0 +1 @@ +alohomora diff --git a/lab4-ipc/tests/data/index b/lab4-ipc/tests/data/index new file mode 100644 index 0000000000000000000000000000000000000000..51aad9d33cceb9fa223aa887bcc7a0e4b0f3dfbb GIT binary patch literal 36 bcmYdEEJ@T)Gt?`oC}BtevyG70#$Yx8-)IZY literal 0 HcmV?d00001 diff --git a/lab4-ipc/tests/expected/.INTEG_fork.sha b/lab4-ipc/tests/expected/.INTEG_fork.sha new file mode 100644 index 0000000..124407d --- /dev/null +++ b/lab4-ipc/tests/expected/.INTEG_fork.sha @@ -0,0 +1 @@ +85cdeaba8a3d4b24f3471d8c91724de8fc9eb6de diff --git a/lab4-ipc/tests/expected/.INTEG_fork_no_exec.sha b/lab4-ipc/tests/expected/.INTEG_fork_no_exec.sha new file mode 100644 index 0000000..5949756 --- /dev/null +++ b/lab4-ipc/tests/expected/.INTEG_fork_no_exec.sha @@ -0,0 +1 @@ +e69f8afeb4d967606c11ee01002df44e6c01b910 diff --git a/lab4-ipc/tests/expected/.INTEG_fork_no_file.sha b/lab4-ipc/tests/expected/.INTEG_fork_no_file.sha new file mode 100644 index 0000000..1d96cd1 --- /dev/null +++ b/lab4-ipc/tests/expected/.INTEG_fork_no_file.sha @@ -0,0 +1 @@ +22cdb52c3ef40e84d4213809c7dcd1747efe8ff0 diff --git a/lab4-ipc/tests/expected/.INTEG_spawn.sha b/lab4-ipc/tests/expected/.INTEG_spawn.sha new file mode 100644 index 0000000..7a9e87b --- /dev/null +++ b/lab4-ipc/tests/expected/.INTEG_spawn.sha @@ -0,0 +1 @@ +3881b07e1676a7fbe53089dfe7b3add8ffed7fb0 diff --git a/lab4-ipc/tests/expected/.INTEG_spawn_no_exec.sha b/lab4-ipc/tests/expected/.INTEG_spawn_no_exec.sha new file mode 100644 index 0000000..5949756 --- /dev/null +++ b/lab4-ipc/tests/expected/.INTEG_spawn_no_exec.sha @@ -0,0 +1 @@ +e69f8afeb4d967606c11ee01002df44e6c01b910 diff --git a/lab4-ipc/tests/expected/.INTEG_spawn_no_file.sha b/lab4-ipc/tests/expected/.INTEG_spawn_no_file.sha new file mode 100644 index 0000000..cc16d5d --- /dev/null +++ b/lab4-ipc/tests/expected/.INTEG_spawn_no_file.sha @@ -0,0 +1 @@ +e609d97c46fa6bb297cb26adc575edf33978ca5a diff --git a/lab4-ipc/tests/expected/INTEG_fork.txt b/lab4-ipc/tests/expected/INTEG_fork.txt new file mode 100644 index 0000000..e4d62af --- /dev/null +++ b/lab4-ipc/tests/expected/INTEG_fork.txt @@ -0,0 +1 @@ +cksum(../data/f1.txt) = '3015617425 6 ../data/f1.txt' diff --git a/lab4-ipc/tests/expected/INTEG_fork_no_exec.txt b/lab4-ipc/tests/expected/INTEG_fork_no_exec.txt new file mode 100644 index 0000000..1059168 --- /dev/null +++ b/lab4-ipc/tests/expected/INTEG_fork_no_exec.txt @@ -0,0 +1 @@ +asldkfja(../data/f1.txt) = '' diff --git a/lab4-ipc/tests/expected/INTEG_fork_no_file.txt b/lab4-ipc/tests/expected/INTEG_fork_no_file.txt new file mode 100644 index 0000000..3bed00a --- /dev/null +++ b/lab4-ipc/tests/expected/INTEG_fork_no_file.txt @@ -0,0 +1 @@ +cksum(../data/f4.txt) = '' diff --git a/lab4-ipc/tests/expected/INTEG_spawn.txt b/lab4-ipc/tests/expected/INTEG_spawn.txt new file mode 100644 index 0000000..ce89512 --- /dev/null +++ b/lab4-ipc/tests/expected/INTEG_spawn.txt @@ -0,0 +1 @@ +shasum(../data/f2.txt) = 'e7d9b82b45d5833c9dada13f2379e7b66c823434 ../data/f2.txt' diff --git a/lab4-ipc/tests/expected/INTEG_spawn_no_exec.txt b/lab4-ipc/tests/expected/INTEG_spawn_no_exec.txt new file mode 100644 index 0000000..1059168 --- /dev/null +++ b/lab4-ipc/tests/expected/INTEG_spawn_no_exec.txt @@ -0,0 +1 @@ +asldkfja(../data/f1.txt) = '' diff --git a/lab4-ipc/tests/expected/INTEG_spawn_no_file.txt b/lab4-ipc/tests/expected/INTEG_spawn_no_file.txt new file mode 100644 index 0000000..14b9b61 --- /dev/null +++ b/lab4-ipc/tests/expected/INTEG_spawn_no_file.txt @@ -0,0 +1 @@ +shasum(../data/f5.txt) = '' diff --git a/lab4-ipc/tests/integration.sh b/lab4-ipc/tests/integration.sh new file mode 100755 index 0000000..e959edb --- /dev/null +++ b/lab4-ipc/tests/integration.sh @@ -0,0 +1,75 @@ +#!/bin/bash + +EXE="../ipc" + +function run_test { + + # parameters + TAG=$1 + ARGS=$2 + + # file paths + OUTPUT=outputs/$TAG.txt + DIFF=outputs/$TAG.diff + EXPECT=expected/$TAG.txt + VALGRND=valgrind/$TAG.txt + + # print tag format + PTAG=$(printf '%-30s' "$TAG") + + # check for expected text that needs to be fixed + if [ ! -z "$(egrep "<<<<.*>>>>" "$EXPECT")" ] ; then + echo "$PTAG FAIL ($EXPECT not correct)" + return + fi + + # compare the file + SHAOUT=$(shasum "$EXPECT" | awk '{print $1}') + SHAEXP=$(cat "expected/.$TAG.sha") + if [ "$SHAOUT" != "$SHAEXP" ] ; then + echo "$PTAG FAIL (expected file is not correct)" + return + fi + + # run test and compare output to the expected version + $EXE $ARGS 2>/dev/null >"$OUTPUT" + diff -u "$OUTPUT" "$EXPECT" >"$DIFF" + if [ -s "$DIFF" ]; then + + # try alternative solution (if it exists) + EXPECT=expected/$TAG-2.txt + if [ -e "$EXPECT" ]; then + diff -u "$OUTPUT" "$EXPECT" >"$DIFF" + if [ -s "$DIFF" ]; then + echo "$PTAG FAIL (see $DIFF for details)" + else + echo "$PTAG pass" + fi + else + echo "$PTAG FAIL (see $DIFF for details)" + fi + else + echo "$PTAG pass" + fi + + # run valgrind + valgrind $EXE $ARGS &>$VALGRND +} + +# initialize output folders +mkdir -p outputs +mkdir -p valgrind +rm -f outputs/* valgrind/* + +# run individual tests +source itests.include + +# check for memory leaks +LEAK=`cat valgrind/*.txt | grep 'definitely lost' | grep -v ' 0 bytes in 0 blocks'` +if [ -z "$LEAK" ]; then + echo "No memory leak found." +else + echo "Memory leak(s) found. See files listed below for details." + grep 'definitely lost' valgrind/*.txt | grep -v ' 0 bytes in 0 blocks' | sed -e 's/:.*$//g' | sed -e 's/^/ - /g' +fi + diff --git a/lab4-ipc/tests/itests.include b/lab4-ipc/tests/itests.include new file mode 100644 index 0000000..8a1dc5b --- /dev/null +++ b/lab4-ipc/tests/itests.include @@ -0,0 +1,11 @@ +# list of integration tests +# format: run_test +# used as the root for all filenames (i.e., "expected/$TAG.txt") +# command-line arguments to test + +run_test INTEG_fork "cksum ../data/f1.txt" +run_test INTEG_fork_no_exec "asldkfja ../data/f1.txt" +run_test INTEG_fork_no_file "cksum ../data/f4.txt" +run_test INTEG_spawn "-s shasum ../data/f2.txt" +run_test INTEG_spawn_no_exec "-s asldkfja ../data/f1.txt" +run_test INTEG_spawn_no_file "-s shasum ../data/f5.txt" diff --git a/lab4-ipc/tests/private.o b/lab4-ipc/tests/private.o new file mode 100644 index 0000000000000000000000000000000000000000..89b106d1931de2399f649ec20ff09e0da0d87d41 GIT binary patch literal 6216 zcmbtYYiv|S6rODrunLx!q9_?LKpS9b%fm=)ErFHQRIJJxALz2(y`>HN%HDgi7!b8S zx-=vk9}yExY77bf(2(d4AV7*5#b}gBVleSXV>BTtKZy8)sL6WH%sIQ$>0WH~H22QT zH{bbY?wRLyU!?24x=={4g+zy_mKYV{%-m|eQ)N5F95Gv5zH!l?v1swP=!+Li02fd0 zDVNKs=Y%sLx1wwDyDD1gV`LOMf4CwWIT9V*dsuk)jyD}s zlHA^BR`W-(cH(zN4n~JTsu&p>zo{yCu$mnL?IXgQUqwd1;|O?^OplRjeM$FblXh&6 z4#y5_j~_X9uZ;RjPc6pJ}OUh)c~j{eEoT4iVR-tF77>1ah%$0ps8bzKE&n%e8~k!FI5yH zS0EmjCIs?4kS+n~j2X!DRgW{^ab<$X7zPMD&YB)$Rgbf}jWybFC4~Nx_Ao<#)2Z;} z&~vEA%Kq81&yo=8^JV`Gyr&TAiK^95mnMW+MvHqdRC-KthNT!CUW;Ri-Q92uSNR5| zpeFQjRm0PN>ckkR;xJUc9=ZZju`Ty+jVJs1(9hyc*4vlce;K?Yyh2t*PqMEW%>S; z?2s+XWN!leQet&W>)Lg#cdcn#E7zGS4Fc9pQ`S$Xn?=EK{j?`2mF?|vagf`#EVvoX zEpfS}^>T@eu1j3}TQQ-8K>;)Tnp))2rLrlW+!-H0uPw2zuI37~y8<=KJ$odPZ8p}R zfXS|k-q9{qWwVwbXF%R@~s!sCSlm6kbz5LVBG5ref4crS1%@a4E;g|&Wz;>pC+&#O{%vL7!%ZIT@@R^ z9)JgR*q_rF;5a_EJqbW9JRX3j1Mq2s0(V9{dT1!+o@`VZjw$2I_2WncUO4H zgbRn2I+t+xo$17T!{E;{ z_#YbhjRyXSf#Z5){jUvtwt=5BaFhQF;oP4QD*t8Sxc<-@a03HlKKK7-!g0KCFXR3! zviRz{1@)^f`~+ZV9Tt94!=l^5S;w((*2!Bq>%2uc_H!;g+|NT6pTFxPgKv(@Ig5Xp z%D-Cp7u5f83;&kz-!1$P!vD7L6;!?+0}p1$e*xik{BJjKTwDCTYb_mKckKq>Tz8u+ zej}B;0{D+w{DoA0DuCZ(@m=+mLZmI6*OzDL;16Z~uGcI+f9E?E&hzJE3+MTB%))v8 zd}HCPGiKqeGj8Fm(@5uuJr5<}=!Or^!zP1o&chCiznj{7$ijIZK4#%tiT|{P^E}*P z;XDs_SvdD|kZ^k*zGv~@q;?M({F{w&JYw+i7RBTHgTY^5;6GV>Y#;5f0RAi@U~&sQ ztdBluxZJ3Vs?7Hy#AzuiobN)+$2Ef{)w`-QkHne`9DVpc)?(mDWgUW5oAfCPGW&zL zBqFD%f%P_qeVI7jn9w zYB3vz9H8NJE}2L}J$hAe0+YE+#)0cdIGclC%J4&ArMhcSc}La{=L@+!+(~wmfYTT6 zEhIAf@<4t6KZyiqjFWv+?*t5OK2IcFoDfrl7Q%y+PIyckq5&+CFrMAm4v!noU9_d} zAa0H=;xVgT#L&z!#qtVxQ1id~<39pX6X%6#k5l_2Rt5K;xp?oY)qmc&I+;kRw90ow zxmNrZ&=1iP@iBfFN^YOy_ZVQc+V7+diPtB|RQ$R8Fl1`Af0)|mb&2g`+jjfCfMNU0 zgRHV{Y9GhPG~}XjnltjMf?j)gLp8w~Ts#kvY7LWkf&3-Zna2HR>}|+kUu^#o^2gt4 zrrG|lLuIY_ou~Lk$r +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../pipe.h" + +// Change this to true if you want to inspect the files produced in +// tests/inputs for debugging +#define DEBUG false + +START_TEST (UNIT_result) +{ + printf ("\n======================================\n"); + printf ("UNIT_result\n"); + printf ("Gets cksum of data/f1.txt\n"); + + char *result = get_result ("cksum", "data/f1.txt"); + ck_assert_str_eq (result, "3015617425 6 data/f1.txt"); +} +END_TEST + +void public_tests (Suite *s) +{ + TCase *tc_public = tcase_create ("Public"); + tcase_add_test (tc_public, UNIT_result); + suite_add_tcase (s, tc_public); +} + diff --git a/lab4-ipc/tests/style.sh b/lab4-ipc/tests/style.sh new file mode 100755 index 0000000..ba2210a --- /dev/null +++ b/lab4-ipc/tests/style.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +STYLE="gnu" + +IGNORE=() +FAIL=0 + +function comp_file { + + SRC=$1 + SRC_NAME=$2 + + # file paths + FORMAT=ckstyle/${SRC_NAME}.$STYLE + DIFF=ckstyle/${SRC_NAME}.diff + + # run clang-format and compare results + clang-format --style=$STYLE $source > $FORMAT + diff -u $SRC $FORMAT >$DIFF + + PTAG=$(printf '%-30s' "$SRC_NAME") + if [ -s $DIFF ]; then + echo "$PTAG FAIL (see $DIFF for details)" + FAIL=1 + else + echo "$PTAG pass" + rm $DIFF + fi + rm $FORMAT +} + +mkdir -p ckstyle +rm -f ckstyle/* + +for source in $(ls ../*.c ../*.h) ; do + SKIP=0 + src=$(basename $source) + for ignore in ${IGNORE[*]} ; do + if [ "$src" = "$ignore" ] ; then + SKIP=1 + fi + done + if [ $SKIP = 0 ] ; then + comp_file $source $src + fi +done + +if [ $FAIL != 0 ] ; then + echo "Code that does not adhere to GNU standards will not be accepted." + echo "You must fix these files before submission." +else + echo "Code correctly adheres to required style." +fi diff --git a/lab4-ipc/tests/testsuite.c b/lab4-ipc/tests/testsuite.c new file mode 100644 index 0000000..145faa1 --- /dev/null +++ b/lab4-ipc/tests/testsuite.c @@ -0,0 +1,34 @@ +#include +#include +#include +#include +#include +#include + +#include + +extern void public_tests (Suite *s); +extern void private_tests (Suite *s); + +Suite * test_suite (void) +{ + Suite *s = suite_create ("Default"); + public_tests (s); + private_tests (s); + return s; +} + +void run_testsuite () +{ + Suite *s = test_suite (); + SRunner *sr = srunner_create (s); + srunner_run_all (sr, CK_NORMAL); + srunner_free (sr); +} + +int main (void) +{ + srand((unsigned)time(NULL)); + run_testsuite (); + return EXIT_SUCCESS; +} diff --git a/lab5-net/.gitignore b/lab5-net/.gitignore new file mode 100644 index 0000000..53df104 --- /dev/null +++ b/lab5-net/.gitignore @@ -0,0 +1,20 @@ +# Ignore all compiled files regardless of location +**/*.o + +# But not the private.o +!tests/private.o + +# Ignore executables for this project +net +testsuite + +# Ignore test outputs +tests/ckstyle +tests/itests.txt +tests/outputs +tests/style.txt +tests/valgrind +tests/utests.txt + +**/.nfs* +**/.vscode diff --git a/lab5-net/Makefile b/lab5-net/Makefile new file mode 100644 index 0000000..5fcb4b4 --- /dev/null +++ b/lab5-net/Makefile @@ -0,0 +1,52 @@ +# +# Simple Makefile +# Mike Lam, James Madison University, August 2016 +# +# This makefile builds a simple application that contains a main module +# (specified by the EXE variable) and a predefined list of additional modules +# (specified by the MODS variable). If there are any external library +# dependencies (e.g., the math library, "-lm"), list them in the LIBS variable. +# If there are any precompiled object files, list them in the OBJS variable. +# +# By default, this makefile will build the project with debugging symbols and +# without optimization. To change this, edit or remove the "-g" and "-O0" +# options in CFLAGS and LDFLAGS accordingly. +# +# By default, this makefile build the application using the GNU C compiler, +# adhering to the C99 standard with all warnings enabled. + + +# application-specific settings and run target + +EXE=net +MODS=client.o +OBJS= +LIBS=-lm + +default: $(EXE) + +test: $(EXE) + make -C tests test + +# compiler/linker settings + +CC=gcc +#CFLAGS=-g -O0 -Wall --std=gnu99 -pedantic +CFLAGS=-g -O0 -Wall --std=gnu99 -pedantic +LDFLAGS=-g -O0 -pthread + + +# build targets + +$(EXE): main.o $(MODS) $(OBJS) + $(CC) $(LDFLAGS) -o $(EXE) $^ $(LIBS) + +%.o: %.c + $(CC) -c $(CFLAGS) $< + +clean: + rm -f $(EXE) main.o $(MODS) + make -C tests clean + +.PHONY: default clean + diff --git a/lab5-net/client.c b/lab5-net/client.c new file mode 100644 index 0000000..5ab2d1a --- /dev/null +++ b/lab5-net/client.c @@ -0,0 +1,108 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "client.h" + +#define BUFFER_MAX_SIZE 4096 + +/* Given a struct with server information, return a dynamically allocated + string of the IP address in dotted decimal or colon hexadecimal notation. + Consult Chapter 4, as well as /usr/include/netdb.h and + /usr/include/arpa/inet.h as needed. Use inet_ntop() to get the formatted + string based on the address's ai_addr field. + */ +char * +addr_string (struct addrinfo *server) +{ + // Return safely in case server is NULL: + if (server == NULL) + return strdup ("no address information"); + + size_t addrlen + = server->ai_family == AF_INET6 ? INET6_ADDRSTRLEN : INET_ADDRSTRLEN; + char *buffer = calloc (addrlen, sizeof (char)); + + void *addr; + if (server->ai_family == AF_INET) + addr = &((struct sockaddr_in *)server->ai_addr)->sin_addr; + else + addr = &((struct sockaddr_in6 *)server->ai_addr)->sin6_addr; + + inet_ntop (server->ai_family, addr, buffer, addrlen); + return buffer; +} + +/* Given the server address info, return a dynamically allocated string with + its transport-layer protocol, IP version, and IP address. For instance, + querying jmu.edu over TCP/IPv4 should return: + "TCP IPv4 134.126.10.50" + Use addr_string() to get the formatted address string, concatenate it to + the string to return, then free the result from addr_string(). If the + passed server parameter is NULL, use strdup() to return a dynamically + allocated copy of "no address information". + + NOTE: When distinguishing TCP and UDP, you should be checking the + server->ai_socktype field, not server->ai_protocol. The test cases do not + set the server->ai_protocol, which is sometimes not used in practice. + */ +char * +serv_string (struct addrinfo *server) +{ + // Return safely in case server is NULL: + if (server == NULL) + return strdup ("no address information"); + + size_t addrlen + = server->ai_family == AF_INET6 ? INET6_ADDRSTRLEN : INET_ADDRSTRLEN; + + char *buffer = calloc (10 + addrlen, sizeof (char)); + + char *proto = server->ai_socktype == SOCK_STREAM ? "TCP" : "UDP"; + char *ipver = server->ai_family == AF_INET ? "IPv4" : "IPv6"; + + char *addr = addr_string (server); + + snprintf (buffer, 10 + addrlen, "%s %s %s", proto, ipver, addr); + + free (addr); + + return buffer; +} + +/* Given a hostname string, use getaddrinfo() to query DNS for the specified + protocol parameters. Boolean values indicate whether or not to use IPv6 + (as opposed to IPv4) or TCP (as opposed to UDP). For this lab, the protocol + will only be "http", though it could be others in a more complete + implementation. Return the pointer to the linked list of server results. + */ +struct addrinfo * +get_server_list (const char *hostname, const char *proto, bool tcp, bool ipv6) +{ + struct addrinfo hints = { 0 }; + struct addrinfo *server_list = NULL; + + hints.ai_family = ipv6 ? AF_INET6 : AF_INET; + + if (tcp) + { + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + } + else + { + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + } + + getaddrinfo (hostname, proto, &hints, &server_list); + + return server_list; +} diff --git a/lab5-net/client.h b/lab5-net/client.h new file mode 100644 index 0000000..82b3582 --- /dev/null +++ b/lab5-net/client.h @@ -0,0 +1,12 @@ +#ifndef __client_h__ +#define __client_h__ + +#include +#include + +char *addr_string (struct addrinfo *); +char *serv_string (struct addrinfo *); +struct addrinfo *get_server_list (const char *, const char *, bool, bool); +char *web (const char *, char *, const char *, char **, bool); + +#endif diff --git a/lab5-net/main.c b/lab5-net/main.c new file mode 100644 index 0000000..6a5bc69 --- /dev/null +++ b/lab5-net/main.c @@ -0,0 +1,106 @@ +/* + * CS 361: Template project driver + * + * Name: + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "client.h" + +int cmdline (int, char **, char **, bool *, short int *); + +void +usage (void) +{ + printf ("Usage: net [options] domain\n"); + printf (" Options are:\n"); + printf (" -6 Use IPv6 addresses\n"); + printf (" -p P Lookup address for application protocol P\n"); +} + +int +main (int argc, char **argv) +{ + char *protocol = "http"; + bool ipv6 = false; + short int port = 0; + if (cmdline (argc, argv, &protocol, &ipv6, &port) < 0) + { + usage (); + return EXIT_FAILURE; + } + char *domain = argv[optind]; + + // MINIMUM requirements: + // Get the server list for the specified domain and protocol + // Given this command-line: + // ./net -p http www.jmu.edu + // Print the following output: + // www.jmu.edu http: TCP IPv4 134.126.10.50 + // The requested transport-layer protocol should be based on the + // requested application-layer protocol. TCP is default, but use + // UDP for protocols "53", "67", "dns", or "dhcp". + + bool tcp = true; + char *udpProtocols[] = { "53", "67", "dns", "dhcp" }; + + for (size_t i = 0; i < 4; i++) + { + if (strcmp (protocol, udpProtocols[i]) == 0) + { + tcp = false; + break; + } + } + + struct addrinfo *server = get_server_list (domain, protocol, tcp, ipv6); + char *server_string = serv_string (server); + + printf ("%s %s: %s\n", domain, protocol, server_string); + free (server); + free (server_string); + + return EXIT_SUCCESS; +} + +/* DO NOT MODIFY THIS FUNCTION */ + +int +cmdline (int argc, char **argv, char **protocol, bool *ipv6, short int *port) +{ + int option; + + while ((option = getopt (argc, argv, "p:6sh")) != -1) + { + switch (option) + { + // Change this to merge -p and -o as same flag + case 'p': + *protocol = optarg; + break; + case '6': + *ipv6 = true; + break; + case 'h': + return -1; + break; + default: + return -1; + } + } + + if (optind != argc - 1) + return -1; + + return 0; +} diff --git a/lab5-net/tests/Makefile b/lab5-net/tests/Makefile new file mode 100644 index 0000000..f4577ab --- /dev/null +++ b/lab5-net/tests/Makefile @@ -0,0 +1,86 @@ +# +# Simple Test Makefile +# Mike Lam, James Madison University, August 2016 +# +# This version of the Makefile includes support for building a test suite. The +# recommended framework is Check (http://check.sourceforge.net/). To build and +# run the test suite, execute the "test" target. The test suite must be located +# in a module called "testsuite". The MODS, LIBS, and OBJS variables work as +# they do in the main Makefile. +# +# To change the default build target (which executes when you just type +# "make"), change the right-hand side of the definition of the "default" +# target. +# +# By default, this makefile will build the project with debugging symbols and +# without optimization. To change this, edit or remove the "-g" and "-O0" +# options in CFLAGS and LDFLAGS accordingly. +# +# By default, this makefile build the application using the GNU C compiler, +# adhering to the C99 standard with all warnings enabled. + + +# application-specific settings and run target + +EXE=../net +TEST=testsuite +MODS=public.o +OBJS=../client.o private.o +LIBS= + +UTESTOUT=utests.txt +ITESTOUT=itests.txt +SCHECKOUT=style.txt + +default: $(TEST) + +$(EXE): + make -C ../ + +test: utest itest style + @echo "========================================" + +utest: $(EXE) $(TEST) + @echo "========================================" + @echo " UNIT TESTS" + @./$(TEST) 2>/dev/null >$(UTESTOUT) + @cat $(UTESTOUT) | sed -n -e '/Checks/,$$p' | sed -e 's/^private.*:[EF]://g' + +itest: $(EXE) + @echo "========================================" + @echo " INTEGRATION TESTS" + @./integration.sh | tee $(ITESTOUT) + +style: $(EXE) + @echo "========================================" + @echo " CODING STYLE CHECK" + @./style.sh 2>/dev/null >$(SCHECKOUT) + @cat $(SCHECKOUT) + +# compiler/linker settings + +CC=gcc +CFLAGS=-g -O0 -Wall --std=gnu99 -pedantic +LDFLAGS=-g -O0 + +CFLAGS= +LIBS+=-lcheck -lm -lpthread + +ifeq ($(shell uname -s),Linux) + LIBS+=-lrt -lsubunit +endif + + +# build targets + +$(TEST): $(TEST).o $(MODS) $(OBJS) + $(CC) $(LDFLAGS) -o $(TEST) $^ $(LIBS) + +%.o: %.c + $(CC) -c $(CFLAGS) $< + +clean: + rm -rf $(TEST) $(TEST).o $(MODS) $(UTESTOUT) $(ITESTOUT) $(SCHECKOUT) outputs valgrind ckstyle + +.PHONY: default clean test unittest inttest + diff --git a/lab5-net/tests/expected/.INTEG_53_ns3.sha b/lab5-net/tests/expected/.INTEG_53_ns3.sha new file mode 100644 index 0000000..a7574b9 --- /dev/null +++ b/lab5-net/tests/expected/.INTEG_53_ns3.sha @@ -0,0 +1 @@ +6aa4050ebd7cf3e2973480a61c8f3e10abae0b8b diff --git a/lab5-net/tests/expected/.INTEG_53_ns3_ipv6.sha b/lab5-net/tests/expected/.INTEG_53_ns3_ipv6.sha new file mode 100644 index 0000000..3689671 --- /dev/null +++ b/lab5-net/tests/expected/.INTEG_53_ns3_ipv6.sha @@ -0,0 +1 @@ +1954727e95f02e160a1ca5747b42de74ba7a73bb diff --git a/lab5-net/tests/expected/.INTEG_localhost_ipv4.sha b/lab5-net/tests/expected/.INTEG_localhost_ipv4.sha new file mode 100644 index 0000000..9075df2 --- /dev/null +++ b/lab5-net/tests/expected/.INTEG_localhost_ipv4.sha @@ -0,0 +1 @@ +9e5b32bfdef634ce9687b592ef6ab66dd2359d3e diff --git a/lab5-net/tests/expected/.INTEG_localhost_ipv6.sha b/lab5-net/tests/expected/.INTEG_localhost_ipv6.sha new file mode 100644 index 0000000..6eedaa4 --- /dev/null +++ b/lab5-net/tests/expected/.INTEG_localhost_ipv6.sha @@ -0,0 +1 @@ +16f74b0708234d24ac814851071de4a4f8d3893a diff --git a/lab5-net/tests/expected/.INTEG_w3_ipv4.sha b/lab5-net/tests/expected/.INTEG_w3_ipv4.sha new file mode 100644 index 0000000..2284927 --- /dev/null +++ b/lab5-net/tests/expected/.INTEG_w3_ipv4.sha @@ -0,0 +1 @@ +c0a296ad55ce3db887a3fbfa529ec4b3dc407e4a diff --git a/lab5-net/tests/expected/.INTEG_w3_ipv6.sha b/lab5-net/tests/expected/.INTEG_w3_ipv6.sha new file mode 100644 index 0000000..dc0cf3a --- /dev/null +++ b/lab5-net/tests/expected/.INTEG_w3_ipv6.sha @@ -0,0 +1 @@ +fb304af0dc2a1fd9029541d8e653995755bc8b2c diff --git a/lab5-net/tests/expected/INTEG_53_ns3.txt b/lab5-net/tests/expected/INTEG_53_ns3.txt new file mode 100644 index 0000000..4948f60 --- /dev/null +++ b/lab5-net/tests/expected/INTEG_53_ns3.txt @@ -0,0 +1 @@ +ns3.jmu.edu 53: UDP IPv4 134.126.115.46 diff --git a/lab5-net/tests/expected/INTEG_53_ns3_ipv6-2.txt b/lab5-net/tests/expected/INTEG_53_ns3_ipv6-2.txt new file mode 100644 index 0000000..b654fd1 --- /dev/null +++ b/lab5-net/tests/expected/INTEG_53_ns3_ipv6-2.txt @@ -0,0 +1 @@ +ns3.jmu.edu 53: UDP IPv6 ::ffff:134.126.115.46 diff --git a/lab5-net/tests/expected/INTEG_53_ns3_ipv6.txt b/lab5-net/tests/expected/INTEG_53_ns3_ipv6.txt new file mode 100644 index 0000000..48af529 --- /dev/null +++ b/lab5-net/tests/expected/INTEG_53_ns3_ipv6.txt @@ -0,0 +1 @@ +ns3.jmu.edu 53: no address information diff --git a/lab5-net/tests/expected/INTEG_localhost_ipv4.txt b/lab5-net/tests/expected/INTEG_localhost_ipv4.txt new file mode 100644 index 0000000..5f23b5b --- /dev/null +++ b/lab5-net/tests/expected/INTEG_localhost_ipv4.txt @@ -0,0 +1 @@ +localhost http: TCP IPv4 127.0.0.1 diff --git a/lab5-net/tests/expected/INTEG_localhost_ipv6.txt b/lab5-net/tests/expected/INTEG_localhost_ipv6.txt new file mode 100644 index 0000000..46915f2 --- /dev/null +++ b/lab5-net/tests/expected/INTEG_localhost_ipv6.txt @@ -0,0 +1 @@ +localhost http: TCP IPv6 ::1 diff --git a/lab5-net/tests/expected/INTEG_w3_ipv4.txt b/lab5-net/tests/expected/INTEG_w3_ipv4.txt new file mode 100644 index 0000000..ab67f7e --- /dev/null +++ b/lab5-net/tests/expected/INTEG_w3_ipv4.txt @@ -0,0 +1 @@ +w3.cs.jmu.edu http: TCP IPv4 134.126.20.33 diff --git a/lab5-net/tests/expected/INTEG_w3_ipv6-2.txt b/lab5-net/tests/expected/INTEG_w3_ipv6-2.txt new file mode 100644 index 0000000..78985cc --- /dev/null +++ b/lab5-net/tests/expected/INTEG_w3_ipv6-2.txt @@ -0,0 +1 @@ +w3.cs.jmu.edu http: TCP IPv6 ::ffff:134.126.20.33 diff --git a/lab5-net/tests/expected/INTEG_w3_ipv6.txt b/lab5-net/tests/expected/INTEG_w3_ipv6.txt new file mode 100644 index 0000000..50b786f --- /dev/null +++ b/lab5-net/tests/expected/INTEG_w3_ipv6.txt @@ -0,0 +1 @@ +w3.cs.jmu.edu http: no address information diff --git a/lab5-net/tests/integration.sh b/lab5-net/tests/integration.sh new file mode 100755 index 0000000..43ad640 --- /dev/null +++ b/lab5-net/tests/integration.sh @@ -0,0 +1,75 @@ +#!/bin/bash + +EXE="../net" + +function run_test { + + # parameters + TAG=$1 + ARGS=$2 + + # file paths + OUTPUT=outputs/$TAG.txt + DIFF=outputs/$TAG.diff + EXPECT=expected/$TAG.txt + VALGRND=valgrind/$TAG.txt + + # print tag format + PTAG=$(printf '%-30s' "$TAG") + + # check for expected text that needs to be fixed + if [ ! -z "$(egrep "<<<<.*>>>>" "$EXPECT")" ] ; then + echo "$PTAG FAIL ($EXPECT not correct)" + return + fi + + # compare the file + SHAOUT=$(shasum "$EXPECT" | awk '{print $1}') + SHAEXP=$(cat "expected/.$TAG.sha") + if [ "$SHAOUT" != "$SHAEXP" ] ; then + echo "$PTAG FAIL (expected file is not correct)" + return + fi + + # run test and compare output to the expected version + $EXE $ARGS 2>/dev/null >"$OUTPUT" + diff -u "$OUTPUT" "$EXPECT" >"$DIFF" + if [ -s "$DIFF" ]; then + + # try alternative solution (if it exists) + EXPECT=expected/$TAG-2.txt + if [ -e "$EXPECT" ]; then + diff -u "$OUTPUT" "$EXPECT" >"$DIFF" + if [ -s "$DIFF" ]; then + echo "$PTAG FAIL (see $DIFF for details)" + else + echo "$PTAG pass" + fi + else + echo "$PTAG FAIL (see $DIFF for details)" + fi + else + echo "$PTAG pass" + fi + + # run valgrind + valgrind $EXE $ARGS &>$VALGRND +} + +# initialize output folders +mkdir -p outputs +mkdir -p valgrind +rm -f outputs/* valgrind/* + +# run individual tests +source itests.include + +# check for memory leaks +LEAK=`cat valgrind/*.txt | grep 'definitely lost' | grep -v ' 0 bytes in 0 blocks'` +if [ -z "$LEAK" ]; then + echo "No memory leak found." +else + echo "Memory leak(s) found. See files listed below for details." + grep 'definitely lost' valgrind/*.txt | grep -v ' 0 bytes in 0 blocks' | sed -e 's/:.*$//g' | sed -e 's/^/ - /g' +fi + diff --git a/lab5-net/tests/itests.include b/lab5-net/tests/itests.include new file mode 100644 index 0000000..446ee74 --- /dev/null +++ b/lab5-net/tests/itests.include @@ -0,0 +1,14 @@ +# list of integration tests +# format: run_test +# used as the root for all filenames (i.e., "expected/$TAG.txt") +# command-line arguments to test + +run_test INTEG_localhost_ipv4 "localhost" +run_test INTEG_localhost_ipv6 "-6 localhost" +run_test INTEG_w3_ipv6 "-6 w3.cs.jmu.edu" +run_test INTEG_w3_ipv4 "w3.cs.jmu.edu" +run_test INTEG_53_ns3 "-p 53 ns3.jmu.edu" +run_test INTEG_53_ns3_ipv6 "-6 -p 53 ns3.jmu.edu" +#run_test EXTRA_youtube_H "-w / youtube.com" +#run_test EXTRA_w3_index_H "-w /index.html w3.cs.jmu.edu" +#run_test EXTRA_nope_H "-w /nope.html example.com" diff --git a/lab5-net/tests/private.o b/lab5-net/tests/private.o new file mode 100644 index 0000000000000000000000000000000000000000..cb4d6f8506f71bee8bab641d0f1aaeb1ae581564 GIT binary patch literal 15520 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 Ni!t|mC;wB+{~Ip2hh_i( literal 0 HcmV?d00001 diff --git a/lab5-net/tests/public.c b/lab5-net/tests/public.c new file mode 100644 index 0000000..30cc5bf --- /dev/null +++ b/lab5-net/tests/public.c @@ -0,0 +1,100 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../client.h" + +START_TEST (UNIT_addr_string_localhost_v4) +{ + printf ("======================================\n"); + printf ("PUBLIC\n"); + printf (" UNIT addr string localhost v4\n"); + printf (" Convert 32-bit integer address for localhost\n"); + printf (" into dotted-decimal 127.0.0.1 string format.\n\n"); + + struct sockaddr_in address; + memset (&address, 0, sizeof (address)); + address.sin_addr.s_addr = htonl (0x7f000001); + + struct addrinfo server; + server.ai_family = AF_INET; + server.ai_addr = (struct sockaddr *) &address; + + char *ipv4 = addr_string (&server); + printf (" IPv4 address for localhost is '%s'\n", ipv4); + printf ("\n"); + + ck_assert_str_eq (ipv4, "127.0.0.1"); + free (ipv4); + +} +END_TEST + +START_TEST (UNIT_get_server_list_localhost) +{ + printf ("======================================\n"); + printf ("PUBLIC\n"); + printf (" UNIT get server list localhost\n"); + printf (" Use getaddrinfo() to gather information about localhost\n"); + printf (" for the HTTP protocol with IPv4. Requires network access\n"); + printf (" to get information from DNS.\n\n"); + + struct addrinfo *server = + get_server_list ("localhost", "http", true, false); + struct sockaddr_in *addr4 = (struct sockaddr_in *) server->ai_addr; + uint32_t address = ntohl (addr4->sin_addr.s_addr); + + printf (" IPv4 address for localhost is 0x%08" PRIx32 "\n", address); + printf ("\n"); + ck_assert_int_eq (address, 0x7f000001); + + freeaddrinfo (server); +} +END_TEST + +START_TEST (UNIT_serv_string_localhost) +{ + printf ("======================================\n"); + printf ("PUBLIC\n"); + printf (" UNIT serv string localhost\n"); + printf (" Converts a struct addrinfo into a string that contains\n"); + printf (" the transport-layer protocol, IP version, and IP address\n"); + printf (" in the appropriate format.\n\n"); + + struct sockaddr_in address; + memset (&address, 0, sizeof (address)); + address.sin_addr.s_addr = htonl (0x7f000001); + + struct addrinfo server; + server.ai_family = AF_INET; + server.ai_addr = (struct sockaddr *) &address; + server.ai_socktype = SOCK_STREAM; + + char *serv_info = serv_string (&server); + printf (" Server information for localhost: '%s'\n", serv_info); + printf ("\n"); + + ck_assert_str_eq (serv_info, "TCP IPv4 127.0.0.1"); + free (serv_info); +} +END_TEST + +void public_tests (Suite *s) +{ + TCase *tc_public = tcase_create ("Public"); + tcase_add_test (tc_public, UNIT_addr_string_localhost_v4); + tcase_add_test (tc_public, UNIT_get_server_list_localhost); + tcase_add_test (tc_public, UNIT_serv_string_localhost); + suite_add_tcase (s, tc_public); +} + diff --git a/lab5-net/tests/style.sh b/lab5-net/tests/style.sh new file mode 100755 index 0000000..ba2210a --- /dev/null +++ b/lab5-net/tests/style.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +STYLE="gnu" + +IGNORE=() +FAIL=0 + +function comp_file { + + SRC=$1 + SRC_NAME=$2 + + # file paths + FORMAT=ckstyle/${SRC_NAME}.$STYLE + DIFF=ckstyle/${SRC_NAME}.diff + + # run clang-format and compare results + clang-format --style=$STYLE $source > $FORMAT + diff -u $SRC $FORMAT >$DIFF + + PTAG=$(printf '%-30s' "$SRC_NAME") + if [ -s $DIFF ]; then + echo "$PTAG FAIL (see $DIFF for details)" + FAIL=1 + else + echo "$PTAG pass" + rm $DIFF + fi + rm $FORMAT +} + +mkdir -p ckstyle +rm -f ckstyle/* + +for source in $(ls ../*.c ../*.h) ; do + SKIP=0 + src=$(basename $source) + for ignore in ${IGNORE[*]} ; do + if [ "$src" = "$ignore" ] ; then + SKIP=1 + fi + done + if [ $SKIP = 0 ] ; then + comp_file $source $src + fi +done + +if [ $FAIL != 0 ] ; then + echo "Code that does not adhere to GNU standards will not be accepted." + echo "You must fix these files before submission." +else + echo "Code correctly adheres to required style." +fi diff --git a/lab5-net/tests/testsuite.c b/lab5-net/tests/testsuite.c new file mode 100644 index 0000000..145faa1 --- /dev/null +++ b/lab5-net/tests/testsuite.c @@ -0,0 +1,34 @@ +#include +#include +#include +#include +#include +#include + +#include + +extern void public_tests (Suite *s); +extern void private_tests (Suite *s); + +Suite * test_suite (void) +{ + Suite *s = suite_create ("Default"); + public_tests (s); + private_tests (s); + return s; +} + +void run_testsuite () +{ + Suite *s = test_suite (); + SRunner *sr = srunner_create (s); + srunner_run_all (sr, CK_NORMAL); + srunner_free (sr); +} + +int main (void) +{ + srand((unsigned)time(NULL)); + run_testsuite (); + return EXIT_SUCCESS; +} diff --git a/lab7-threads/.gitignore b/lab7-threads/.gitignore new file mode 100644 index 0000000..51c74a7 --- /dev/null +++ b/lab7-threads/.gitignore @@ -0,0 +1,24 @@ +# Ignore all compiled files regardless of location +discrete.o +main.o +pthread.o +tests/public.o +tests/testsuite.o + +# But not the private.o +!tests/private.o + +# Ignore executables for this project +threads +testsuite + +# Ignore test outputs +tests/ckstyle +tests/itests.txt +tests/outputs +tests/style.txt +tests/valgrind +tests/utests.txt + +**/.nfs* +**/.vscode diff --git a/lab7-threads/Makefile b/lab7-threads/Makefile new file mode 100644 index 0000000..12efc5d --- /dev/null +++ b/lab7-threads/Makefile @@ -0,0 +1,52 @@ +# +# Simple Makefile +# Mike Lam, James Madison University, August 2016 +# +# This makefile builds a simple application that contains a main module +# (specified by the EXE variable) and a predefined list of additional modules +# (specified by the MODS variable). If there are any external library +# dependencies (e.g., the math library, "-lm"), list them in the LIBS variable. +# If there are any precompiled object files, list them in the OBJS variable. +# +# By default, this makefile will build the project with debugging symbols and +# without optimization. To change this, edit or remove the "-g" and "-O0" +# options in CFLAGS and LDFLAGS accordingly. +# +# By default, this makefile build the application using the GNU C compiler, +# adhering to the C99 standard with all warnings enabled. + + +# application-specific settings and run target + +EXE=threads +MODS=pthread.o discrete.o +OBJS= +LIBS=-lm + +default: $(EXE) + +test: $(EXE) + make -C tests test + +# compiler/linker settings + +CC=gcc +#CFLAGS=-g -O0 -Wall --std=gnu99 -pedantic +CFLAGS=-g -O0 -Wall --std=gnu99 -pedantic +LDFLAGS=-g -O0 -pthread + + +# build targets + +$(EXE): main.o $(MODS) $(OBJS) + $(CC) $(LDFLAGS) -o $(EXE) $^ $(LIBS) + +%.o: %.c + $(CC) -c $(CFLAGS) $< + +clean: + rm -f $(EXE) main.o $(MODS) + make -C tests clean + +.PHONY: default clean + diff --git a/lab7-threads/discrete.c b/lab7-threads/discrete.c new file mode 100644 index 0000000..49818cf --- /dev/null +++ b/lab7-threads/discrete.c @@ -0,0 +1,53 @@ +#include +#include +#include +#include + +#include "discrete.h" + +/* Find a generator for Z_prime* */ +uint64_t +find_generator (uint64_t prime) +{ + uint64_t g = prime/2; // candidate generator + uint64_t n; // the nth power of g + uint64_t product = g; // product = g^n mod p + bool searching = true; // still searching for g + + while (searching) + { + product = g; + searching = false; // optimistically guess g is good + // stop at prime - 2, because g^(prime-1) = 1 for Z_p + for (n = 1; n < prime - 2; n++) + { + product = (product * g) % prime; + if (product == 1) + { + // FAIL: g is not a generator + g++; + if (g == prime) return 0; // should never get here + searching = true; + n = prime - 2; + } + } + } + + return g; +} + +/* Given g^n mod p, g, and p, compute n */ +uint64_t +discrete_log (uint64_t g2n, uint64_t g, uint64_t p) +{ + uint64_t value = g; + uint64_t n = 1; + while (value != g2n) + { + value = (value * g) % p; + n++; + } + + return n; +} + diff --git a/lab7-threads/discrete.h b/lab7-threads/discrete.h new file mode 100644 index 0000000..595be99 --- /dev/null +++ b/lab7-threads/discrete.h @@ -0,0 +1,9 @@ +#ifndef __discrete_h__ +#define __discrete_h__ + +#include + +uint64_t find_generator (uint64_t); +uint64_t discrete_log (uint64_t, uint64_t, uint64_t); + +#endif diff --git a/lab7-threads/main.c b/lab7-threads/main.c new file mode 100644 index 0000000..9b8e98d --- /dev/null +++ b/lab7-threads/main.c @@ -0,0 +1,200 @@ +/* + * CS 361: Template project driver + * + * Name: Nicholas Tamassia + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "discrete.h" +#include "pthread.h" + +int cmdline (int, char **, size_t *, uint64_t **, uint64_t **, uint64_t **, + size_t *); + +void +usage (void) +{ + printf ("Usage: threads [options] (g:p:g2n ... )\n"); + printf (" Required arguments must be an array of g:p:g2n values\n"); + printf (" Options are:\n"); + printf (" -n N Create N concurrent threads\n"); + printf (" g:p:g2n is a colon-delimited list of 3 uint64_t values\n"); + printf (" g A group generator\n"); + printf (" p A prime number\n"); + printf (" g2n g^n mod p for some unknown n\n"); +} + +int +main (int argc, char **argv) +{ + uint64_t *primes = NULL; + uint64_t *gens = NULL; + uint64_t *powers = NULL; + size_t num_threads = 1; + size_t num_args = 0; + + if (cmdline (argc, argv, &num_args, &primes, &gens, &powers, &num_threads) + < 0) + { + usage (); + return EXIT_FAILURE; + } + + if (num_args < 1 || num_args > 20) + { + printf ("There must be between 1 and 20 arguments\n"); + return EXIT_FAILURE; + } + + // Unithreaded version to serve as a template + if (num_threads == 1) + { + for (int i = 0; i < num_args; i++) + { + uint64_t result = discrete_log (powers[i], gens[i], primes[i]); + printf ("Given g = %" PRId64 ", p = %" PRId64 ", g2n = %" PRId64 + " => n = %" PRId64 "\n", + gens[i], primes[i], powers[i], result); + } + } + else + { + uint64_t *logs = calloc (num_args, sizeof (uint64_t)); + if (logs == NULL) + { + fprintf (stderr, "Failed to allocate logs array\n"); + return EXIT_FAILURE; + } + + // Adjust number of threads if there are fewer arguments than threads + if (num_threads > num_args) + num_threads = num_args; + + size_t base + = (num_args + num_threads - 1) / num_threads; // Ceiling division + size_t rem = num_args % num_threads; + + pthread_t *threads = calloc (num_threads, sizeof (pthread_t)); + struct time_args *targs + = calloc (num_threads, sizeof (struct time_args)); + if (threads == NULL || targs == NULL) + { + free (logs); + free (threads); + free (targs); + fprintf (stderr, "Failed to allocate thread structures\n"); + return EXIT_FAILURE; + } + + size_t created = 0; + for (size_t t = 0; t < num_threads; t++) + { + size_t start = t * base; + size_t count = (t + 1 == num_threads && rem > 0) ? rem : base; + + targs[created].start_index = start; + targs[created].number = count; + targs[created].generators = gens; + targs[created].primes = primes; + targs[created].mod_powers = powers; + targs[created].results = logs; + + if (pthread_create (&threads[created], NULL, time_log_thread, + (void *)&targs[created]) + != 0) + { + for (size_t j = 0; j < created; j++) + pthread_join (threads[j], NULL); + free (logs); + free (threads); + free (targs); + fprintf (stderr, "Failed to create worker thread\n"); + return EXIT_FAILURE; + } + + created++; + } + + for (size_t j = 0; j < created; j++) + pthread_join (threads[j], NULL); + + for (size_t i = 0; i < num_args; i++) + { + printf ("Given g = %lu, p = %lu, g2n = %lu => n = %lu\n", gens[i], + primes[i], powers[i], logs[i]); + } + + free (logs); + free (threads); + free (targs); + } + + if (primes != NULL) + free (primes); + if (gens != NULL) + free (gens); + if (powers != NULL) + free (powers); + + return EXIT_SUCCESS; +} + +/***************************************************************************** + ****************** DO NOT MODIFY FUNCTIONS IN THIS SECTION ****************** + *****************************************************************************/ + +int +cmdline (int argc, char **argv, size_t *length, uint64_t **primes, + uint64_t **gens, uint64_t **powers, size_t *num_threads) +{ + int option; + + while ((option = getopt (argc, argv, "n:h")) != -1) + { + switch (option) + { + case 'n': + *num_threads = (size_t)strtol (optarg, NULL, 10); + break; + case 'h': + return -1; + break; + default: + return -1; + } + } + + if (optind == argc) + return -1; + *length = (size_t)(argc - optind); + *primes = calloc (*length, sizeof (uint64_t)); + + size_t index; + // These must both have g:p:g2n values + *gens = calloc (*length, sizeof (uint64_t)); + *powers = calloc (*length, sizeof (uint64_t)); + for (index = optind; index < argc; index++) + { + char *token = strtok (argv[index], ":"); + uint64_t value = strtol (token, NULL, 10); + (*gens)[index - optind] = value; + token = strtok (NULL, ":"); + assert (token != NULL); + value = strtol (token, NULL, 10); + (*primes)[index - optind] = value; + token = strtok (NULL, ":"); + assert (token != NULL); + value = strtol (token, NULL, 10); + (*powers)[index - optind] = value; + } + + return 0; +} diff --git a/lab7-threads/private.h b/lab7-threads/private.h new file mode 100644 index 0000000..87cc60b --- /dev/null +++ b/lab7-threads/private.h @@ -0,0 +1,6 @@ +#ifndef __private_h__ +#define __private_h__ + +void * generator (void *); + +#endif diff --git a/lab7-threads/pthread.c b/lab7-threads/pthread.c new file mode 100644 index 0000000..83980ac --- /dev/null +++ b/lab7-threads/pthread.c @@ -0,0 +1,152 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "discrete.h" +#include "private.h" +#include "pthread.h" + +/* Create a thread that will run generator(prime). Use pthread_join() to + retrieve the result from the thread and return that value. */ +uint64_t +find_gen (uint64_t prime) +{ + pthread_t thread; + int rc = pthread_create (&thread, NULL, generator, (void *)prime); + if (rc != 0) + { + return 0; + } + + void *result = NULL; + rc = pthread_join (thread, &result); + if (rc != 0) + { + return 0; + } + + return (uint64_t)result; +} + +/* Helper function that is a wrapper for calling find_generator() in a + separate thread. Do not modify. */ +void * +generator (void *args) +{ + uint64_t prime = (uint64_t)args; + uint64_t gen = find_generator (prime); + pthread_exit ((void *)gen); +} + +/* Create multiple threads, each of which makes a single call to generator(). + Return a dynamically allocated array structured like the following: + results[0] = generator (0); + results[1] = generator (1); + ... + The arguments are the number of threads to create and the array of primes + to distribute between the threads (one prime per thread). */ +uint64_t * +find_gens (size_t num_threads, uint64_t *primes) +{ + if (num_threads == 0 || primes == NULL) + return NULL; + + pthread_t *threads = calloc (num_threads, sizeof (pthread_t)); + uint64_t *results = calloc (num_threads, sizeof (uint64_t)); + + for (size_t i = 0; i < num_threads; i++) + { + int rc + = pthread_create (&threads[i], NULL, generator, (void *)primes[i]); + if (rc != 0) + { + for (size_t j = 0; j < i; j++) + { + pthread_join (threads[j], NULL); + } + free (threads); + free (results); + return NULL; + } + } + + for (size_t i = 0; i < num_threads; i++) + { + void *retval = NULL; + int rc = pthread_join (threads[i], &retval); + if (rc != 0) + { + results[i] = 0; + } + else + { + results[i] = (uint64_t)retval; + } + } + + free (threads); + return results; +} + +/* Helper function to calculate the difference between a start and end time. */ +double +time_diff (struct timeval start, struct timeval end) +{ + double ending = end.tv_sec + (end.tv_usec * 0.000001); + double starting = start.tv_sec + (start.tv_usec * 0.000001); + return ending - starting; +} + +/* Calculate the discrete logarithm for several values. That is, given the + values g, p, and g^n mod p, determine n. (Note that this is an intentionally + SLOW operation!) The parameters are stored in three global arrays: + gens[] - this contains the generator values (g) + primes[] - this contains the prime numbers (p) + mod_powers[] - this contains the values g^n mod p for unknown n values + In pseudocode, you should be doing: + for i in start_index .. end_index-1 + logs [i] = discrete_log (parameters[i]) + The return value is the real time it takes to compute these values. Use + the C standard library function gettimeofday() to get a start and end time, + then use the helper function time_diff() to compute the difference. + This function will be called by time_log_thread() below. */ +double +time_log (size_t start_index, size_t end_index, uint64_t *gens, + uint64_t *primes, uint64_t *mod_powers, uint64_t *logs) +{ + struct timeval start, end; + gettimeofday (&start, NULL); + + for (size_t i = start_index; i < end_index; i++) + { + logs[i] = discrete_log (mod_powers[i], gens[i], primes[i]); + } + + gettimeofday (&end, NULL); + return time_diff (start, end); +} + +/* Wrapper function to call time_log() from within a thread. Given the + arguments passed, store the return value from time_log() into the + time_taken field. */ +void * +time_log_thread (void *_args) +{ + struct time_args *args = (struct time_args *)_args; + if (args == NULL) + pthread_exit (NULL); + + size_t start = args->start_index; + size_t end = start + args->number; + + args->time_taken = time_log (start, end, args->generators, args->primes, + args->mod_powers, args->results); + + pthread_exit (NULL); +} diff --git a/lab7-threads/pthread.h b/lab7-threads/pthread.h new file mode 100644 index 0000000..e262f43 --- /dev/null +++ b/lab7-threads/pthread.h @@ -0,0 +1,25 @@ +#ifndef __pthread_h__ +#define __pthread_h__ + +#include +#include +#include + +uint64_t find_gen (uint64_t); +uint64_t * find_gens (size_t, uint64_t *); + +// struct used for passing arguments to time_log_thread () +struct time_args { + size_t start_index; // starting index in the array of 20 items + size_t number; // how many items to compute + double time_taken; // how long total to compute all of the items + uint64_t * generators; // pointer to array of group generators + uint64_t * primes; // pointer to array of primes to use + uint64_t * mod_powers; // pointer to array of g^n mod p values + uint64_t * results; // pointer to array to capture the results +}; + +double time_diff (struct timeval, struct timeval); +void * time_log_thread (void *); + +#endif diff --git a/lab7-threads/tests/Makefile b/lab7-threads/tests/Makefile new file mode 100644 index 0000000..2af6694 --- /dev/null +++ b/lab7-threads/tests/Makefile @@ -0,0 +1,86 @@ +# +# Simple Test Makefile +# Mike Lam, James Madison University, August 2016 +# +# This version of the Makefile includes support for building a test suite. The +# recommended framework is Check (http://check.sourceforge.net/). To build and +# run the test suite, execute the "test" target. The test suite must be located +# in a module called "testsuite". The MODS, LIBS, and OBJS variables work as +# they do in the main Makefile. +# +# To change the default build target (which executes when you just type +# "make"), change the right-hand side of the definition of the "default" +# target. +# +# By default, this makefile will build the project with debugging symbols and +# without optimization. To change this, edit or remove the "-g" and "-O0" +# options in CFLAGS and LDFLAGS accordingly. +# +# By default, this makefile build the application using the GNU C compiler, +# adhering to the C99 standard with all warnings enabled. + + +# application-specific settings and run target + +EXE=../threads +TEST=testsuite +MODS=public.o +OBJS=../pthread.o ../discrete.o +LIBS= + +UTESTOUT=utests.txt +ITESTOUT=itests.txt + +default: $(TEST) + +$(EXE): + make -C ../ + +test: utest itest + @echo "========================================" + +utest: $(EXE) $(TEST) + @echo "========================================" + @echo " UNIT TESTS" + @echo "----------------------------------------" + @echo "Have patience:" + @echo "This should take several seconds" + @echo "You can see how it works by running:" + @echo " ./tests/testsuite" + @echo "----------------------------------------" + @./$(TEST) 2>/dev/null >$(UTESTOUT) + @cat $(UTESTOUT) | sed -n -e '/Checks/,$$p' | sed -e 's/^private.*:[EF]://g' + +itest: $(EXE) + @echo "========================================" + @echo " INTEGRATION TESTS" + @./integration.sh | tee $(ITESTOUT) + + +# compiler/linker settings + +CC=gcc +CFLAGS=-g -O0 -Wall --std=gnu99 -pedantic +LDFLAGS=-g -O0 + +CFLAGS= +LIBS+=-lcheck -lm -lpthread + +ifeq ($(shell uname -s),Linux) + LIBS+=-lrt -lsubunit +endif + + +# build targets + +$(TEST): $(TEST).o $(MODS) $(OBJS) + $(CC) $(LDFLAGS) -o $(TEST) $^ $(LIBS) + +%.o: %.c + $(CC) -c $(CFLAGS) $< + +clean: + rm -rf $(TEST) $(TEST).o $(MODS) $(UTESTOUT) $(ITESTOUT) outputs valgrind + +.PHONY: default clean test unittest inttest + diff --git a/lab7-threads/tests/expected/INTEG_logs_large.txt b/lab7-threads/tests/expected/INTEG_logs_large.txt new file mode 100644 index 0000000..3380a03 --- /dev/null +++ b/lab7-threads/tests/expected/INTEG_logs_large.txt @@ -0,0 +1,4 @@ +Given g = 43013398, p = 86026793, g2n = 64520094 => n = 62484235 +Given g = 35739627, p = 71479241, g2n = 53609430 => n = 37573249 +Given g = 47594171, p = 95188343, g2n = 71391255 => n = 83365257 +Given g = 32133930, p = 64267859, g2n = 48200892 => n = 42413257 diff --git a/lab7-threads/tests/expected/INTEG_logs_large_thr.txt b/lab7-threads/tests/expected/INTEG_logs_large_thr.txt new file mode 100644 index 0000000..3380a03 --- /dev/null +++ b/lab7-threads/tests/expected/INTEG_logs_large_thr.txt @@ -0,0 +1,4 @@ +Given g = 43013398, p = 86026793, g2n = 64520094 => n = 62484235 +Given g = 35739627, p = 71479241, g2n = 53609430 => n = 37573249 +Given g = 47594171, p = 95188343, g2n = 71391255 => n = 83365257 +Given g = 32133930, p = 64267859, g2n = 48200892 => n = 42413257 diff --git a/lab7-threads/tests/expected/INTEG_logs_medium.txt b/lab7-threads/tests/expected/INTEG_logs_medium.txt new file mode 100644 index 0000000..d6a0116 --- /dev/null +++ b/lab7-threads/tests/expected/INTEG_logs_medium.txt @@ -0,0 +1,9 @@ +Given g = 1374234, p = 2748467, g2n = 2061348 => n = 1014681 +Given g = 3290982, p = 6581963, g2n = 4936470 => n = 1229705 +Given g = 1449191, p = 2898383, g2n = 2173785 => n = 2576065 +Given g = 2511322, p = 5022643, g2n = 3766980 => n = 4460251 +Given g = 1523458, p = 3046903, g2n = 2285175 => n = 285081 +Given g = 935190, p = 1870369, g2n = 1402776 => n = 593354 +Given g = 1598454, p = 3196909, g2n = 2397681 => n = 1024170 +Given g = 2051286, p = 4102573, g2n = 3076929 => n = 2765678 +Given g = 1748890, p = 3497779, g2n = 2623332 => n = 1722817 diff --git a/lab7-threads/tests/expected/INTEG_logs_medium_thr.txt b/lab7-threads/tests/expected/INTEG_logs_medium_thr.txt new file mode 100644 index 0000000..d6a0116 --- /dev/null +++ b/lab7-threads/tests/expected/INTEG_logs_medium_thr.txt @@ -0,0 +1,9 @@ +Given g = 1374234, p = 2748467, g2n = 2061348 => n = 1014681 +Given g = 3290982, p = 6581963, g2n = 4936470 => n = 1229705 +Given g = 1449191, p = 2898383, g2n = 2173785 => n = 2576065 +Given g = 2511322, p = 5022643, g2n = 3766980 => n = 4460251 +Given g = 1523458, p = 3046903, g2n = 2285175 => n = 285081 +Given g = 935190, p = 1870369, g2n = 1402776 => n = 593354 +Given g = 1598454, p = 3196909, g2n = 2397681 => n = 1024170 +Given g = 2051286, p = 4102573, g2n = 3076929 => n = 2765678 +Given g = 1748890, p = 3497779, g2n = 2623332 => n = 1722817 diff --git a/lab7-threads/tests/expected/INTEG_logs_small.txt b/lab7-threads/tests/expected/INTEG_logs_small.txt new file mode 100644 index 0000000..a5327a8 --- /dev/null +++ b/lab7-threads/tests/expected/INTEG_logs_small.txt @@ -0,0 +1,12 @@ +Given g = 131, p = 263, g2n = 195 => n = 229 +Given g = 227, p = 439, g2n = 327 => n = 429 +Given g = 262, p = 523, g2n = 390 => n = 191 +Given g = 414, p = 829, g2n = 521 => n = 757 +Given g = 442, p = 883, g2n = 660 => n = 723 +Given g = 474, p = 947, g2n = 708 => n = 539 +Given g = 511, p = 1009, g2n = 756 => n = 974 +Given g = 526, p = 1049, g2n = 786 => n = 681 +Given g = 398, p = 797, g2n = 597 => n = 235 +Given g = 274, p = 547, g2n = 408 => n = 509 +Given g = 606, p = 1213, g2n = 909 => n = 892 +Given g = 11, p = 23, g2n = 15 => n = 19 diff --git a/lab7-threads/tests/expected/INTEG_logs_small_thr.txt b/lab7-threads/tests/expected/INTEG_logs_small_thr.txt new file mode 100644 index 0000000..a5327a8 --- /dev/null +++ b/lab7-threads/tests/expected/INTEG_logs_small_thr.txt @@ -0,0 +1,12 @@ +Given g = 131, p = 263, g2n = 195 => n = 229 +Given g = 227, p = 439, g2n = 327 => n = 429 +Given g = 262, p = 523, g2n = 390 => n = 191 +Given g = 414, p = 829, g2n = 521 => n = 757 +Given g = 442, p = 883, g2n = 660 => n = 723 +Given g = 474, p = 947, g2n = 708 => n = 539 +Given g = 511, p = 1009, g2n = 756 => n = 974 +Given g = 526, p = 1049, g2n = 786 => n = 681 +Given g = 398, p = 797, g2n = 597 => n = 235 +Given g = 274, p = 547, g2n = 408 => n = 509 +Given g = 606, p = 1213, g2n = 909 => n = 892 +Given g = 11, p = 23, g2n = 15 => n = 19 diff --git a/lab7-threads/tests/integration.sh b/lab7-threads/tests/integration.sh new file mode 100755 index 0000000..002eecb --- /dev/null +++ b/lab7-threads/tests/integration.sh @@ -0,0 +1,59 @@ +#!/bin/bash + +EXE="../threads" + +function run_test { + + # parameters + TAG=$1 + ARGS=$2 + + # file paths + OUTPUT=outputs/$TAG.txt + DIFF=outputs/$TAG.diff + EXPECT=expected/$TAG.txt + VALGRND=valgrind/$TAG.txt + + # run test and compare output to the expected version + $EXE $ARGS 2>/dev/null >"$OUTPUT" + diff -u "$OUTPUT" "$EXPECT" >"$DIFF" + PTAG=$(printf '%-30s' "$TAG") + if [ -s "$DIFF" ]; then + + # try alternative solution (if it exists) + EXPECT=expected/$TAG-2.txt + if [ -e "$EXPECT" ]; then + diff -u "$OUTPUT" "$EXPECT" >"$DIFF" + if [ -s "$DIFF" ]; then + echo "$PTAG FAIL (see $DIFF for details)" + else + echo "$PTAG pass" + fi + else + echo "$PTAG FAIL (see $DIFF for details)" + fi + else + echo "$PTAG pass" + fi + + # run valgrind +#valgrind $EXE $ARGS &>$VALGRND +} + +# initialize output folders +mkdir -p outputs +mkdir -p valgrind +rm -f outputs/* valgrind/* + +# run individual tests +source itests.include + +# check for memory leaks +#LEAK=`cat valgrind/*.txt | grep 'definitely lost' | grep -v ' 0 bytes in 0 blocks'` +#if [ -z "$LEAK" ]; then +# echo "No memory leak found." +#else +# echo "Memory leak(s) found. See files listed below for details." +# grep 'definitely lost' valgrind/*.txt | grep -v ' 0 bytes in 0 blocks' | sed -e 's/:.*$//g' | sed -e 's/^/ - /g' +#fi + diff --git a/lab7-threads/tests/itests.include b/lab7-threads/tests/itests.include new file mode 100644 index 0000000..0aa7120 --- /dev/null +++ b/lab7-threads/tests/itests.include @@ -0,0 +1,11 @@ +# list of integration tests +# format: run_test +# used as the root for all filenames (i.e., "expected/$TAG.txt") +# command-line arguments to test + +run_test INTEG_logs_small "131:263:195 227:439:327 262:523:390 414:829:521 442:883:660 474:947:708 511:1009:756 526:1049:786 398:797:597 274:547:408 606:1213:909 11:23:15" +run_test INTEG_logs_small_thr "-n 6 131:263:195 227:439:327 262:523:390 414:829:521 442:883:660 474:947:708 511:1009:756 526:1049:786 398:797:597 274:547:408 606:1213:909 11:23:15" +run_test INTEG_logs_medium "1374234:2748467:2061348 3290982:6581963:4936470 1449191:2898383:2173785 2511322:5022643:3766980 1523458:3046903:2285175 935190:1870369:1402776 1598454:3196909:2397681 2051286:4102573:3076929 1748890:3497779:2623332" +run_test INTEG_logs_medium_thr "-n 3 1374234:2748467:2061348 3290982:6581963:4936470 1449191:2898383:2173785 2511322:5022643:3766980 1523458:3046903:2285175 935190:1870369:1402776 1598454:3196909:2397681 2051286:4102573:3076929 1748890:3497779:2623332" +run_test INTEG_logs_large "43013398:86026793:64520094 35739627:71479241:53609430 47594171:95188343:71391255 32133930:64267859:48200892" +run_test INTEG_logs_large_thr "-n 4 43013398:86026793:64520094 35739627:71479241:53609430 47594171:95188343:71391255 32133930:64267859:48200892" diff --git a/lab7-threads/tests/public.c b/lab7-threads/tests/public.c new file mode 100644 index 0000000..9eef6cd --- /dev/null +++ b/lab7-threads/tests/public.c @@ -0,0 +1,217 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../discrete.h" +#include "../pthread.h" + +uint64_t large_primes[] = { + 22181233, 30722903, 39410039, 48210479, 23878483, 32452597, 41161361, + 49978519, 25582069, 34185953, 42918971, 51754777, 27290279, 35925737, + 44679787, 53532901, 29005421, 37666553, 46439677, 55316903 +}; + +uint64_t large_gens[] = { + 11090619, 15361451, 19705019, 24105239, 11939242, 16226298, 20580682, + 24989259, 12791034, 17092978, 21459486, 25877391, 13645139, 17962870, + 22339897, 26766452, 14502710, 18833278, 23219838, 27658451 +}; + +uint64_t mod_powers[] = { + 10722318, 10718795, 16846904, 28514080, 13198105, 21563282, 16451148, + 31188765, 8174568, 24132030, 5838284, 8726777, 10697873, 30365043, + 40656885, 6262555, 301068, 34096955, 29442656, 36415956 +}; + +// Move these to tests/private.c +// These are p / 4 * 3 +uint64_t expected[] = { + 16635924, 23042175, 29557527, 36157857, 17908860, 24339447, 30871020, + 37483887, 19186551, 25639464, 32189226, 38816082, 20467707, 26944302, + 33509838, 40149675, 21754065, 28249914, 34829757, 41487675 +}; + + +START_TEST (MIN_find_one_generator) +{ + printf ("======================================\n"); + printf ("PUBLIC\n"); + printf (" MIN find one generator\n"); + printf (" Calls find generator(%" PRId64 ") in a separate thread\n", large_primes[10]); + printf (" Should compute %" PRId64 "\n\n", large_gens[10]); + + uint64_t single = find_gen (large_primes[10]); + ck_assert_int_eq (single, large_gens[10]); +} +END_TEST + +START_TEST (MIN_find_two_generators) +{ + printf ("======================================\n"); + printf ("PUBLIC\n"); + printf (" MIN find two generators\n"); + printf (" Calls find_generator(%" PRId64 ") in a separate thread\n", large_primes[0]); + printf (" Should compute %" PRId64 "\n", large_gens[0]); + printf (" Calls find_generator(%" PRId64 ") in a separate thread\n", large_primes[1]); + printf (" Should compute %" PRId64 "\n\n", large_gens[1]); + + uint64_t * pair = find_gens (2, large_primes); + ck_assert (pair != NULL); + ck_assert_int_eq (pair[0], large_gens[0]); + ck_assert_int_eq (pair[1], large_gens[1]); + free (pair); +} +END_TEST + +START_TEST (MIN_find_5_generators) +{ + printf ("======================================\n"); + printf ("PRIVATE\n"); + printf (" MIN find one generator\n"); + printf (" Calls find generator() for the following values:\n "); + + size_t i; + for (i = 0; i < 5; i++) + printf (" %" PRId64, large_primes[i]); + printf ("\n"); + uint64_t * gens = find_gens (5, large_primes); + + printf (" Computed the following generators:\n "); + for (i = 0; i < 5; i++) + printf (" %" PRId64, gens[i]); + printf ("\n"); + + printf (" Expected the following generators:\n "); + for (i = 0; i < 5; i++) + printf (" %" PRId64, large_gens[i]); + printf ("\n\n"); + + ck_assert (gens != NULL); + for (i = 0; i < 5; i++) + ck_assert_int_eq (gens[i], large_gens[i]); + +} +END_TEST + +START_TEST (MIN_last_5_discrete_logs) +{ + printf ("======================================\n"); + printf ("PUBLIC\n"); + printf (" MIN last 5 discrete logs\n"); + printf (" Uses threads to compute the discrete logarithms for:\n"); + size_t i; + for (i = 0; i < 5; i++) + { + printf (" g = %8"PRId64 "; ", large_gens[15 + i]); + printf (" p = %"PRId64 "; ", large_primes[15 + i]); + printf (" g^n = %"PRId64 "\n", mod_powers[15 + i]); + } + + struct time_args args; + memset (&args, 0, sizeof (args)); + args.results = calloc (20, sizeof (uint64_t)); + args.start_index = 15; + args.number = 5; + args.generators = large_gens; + args.primes = large_primes; + args.mod_powers = mod_powers; + + pthread_t thread; + pthread_create (&thread, NULL, time_log_thread, (void *) &args); + pthread_join (thread, NULL); + printf (" Thread took %lf seconds\n", args.time_taken); + for (i = 0; i < 5; i++) + printf (" Computed n = %"PRId64 " [expected %"PRId64"]\n", + args.results[15 + i], expected[15 + i]); + printf ("\n"); + for (i = 0; i < 5; i++) + ck_assert_int_eq (args.results[15 + i], expected[15 + i]); + free (args.results); + +} +END_TEST + +START_TEST (MIN_parallel_discrete_logs) +{ + printf ("======================================\n"); + printf ("PUBLIC\n"); + printf (" MIN parallel discrete logs\n"); + printf (" Uses 4 threads to compute the discrete logarithms for:\n"); + size_t i; + for (i = 0; i < 20; i++) + { + printf (" g = %8"PRId64 "; ", large_gens[15 + i]); + printf (" p = %"PRId64 "; ", large_primes[15 + i]); + printf (" g^n = %"PRId64 "\n", mod_powers[15 + i]); + } + + struct timeval start, end; + gettimeofday (&start, NULL); + for (i = 0; i < 20; i++) + discrete_log (mod_powers[i], large_gens[i], large_primes[i]); + gettimeofday (&end, NULL); + double sequential_time = time_diff (start, end); + printf (" Sequential real-time took %lf seconds\n", sequential_time); + + uint64_t * logs = calloc (20, sizeof (uint64_t)); + + gettimeofday (&start, NULL); + pthread_t thread[4]; + struct time_args args[4]; + for (i = 0; i < 4; i++) + { + memset (&args[i], 0, sizeof (args[i])); + args[i].results = logs; + args[i].start_index = i * 5; + args[i].number = 5; + args[i].generators = large_gens; + args[i].primes = large_primes; + args[i].mod_powers = mod_powers; + pthread_create (&thread[i], NULL, time_log_thread, (void *) &args[i]); + } + + for (i = 0; i < 4; i++) + pthread_join (thread[i], NULL); + gettimeofday (&end, NULL); + + double parallel_time = time_diff (start, end); + printf (" Parallel real-time took %lf seconds\n", parallel_time); + for (i = 0; i < 4; i++) + printf (" Thread %zd took %lf seconds\n", i, args[i].time_taken); + + size_t j; + for (i = 0; i < 4; i++) + { + for (j = 0; j < 5; j++) + printf (" Thread %zd computed n = %"PRId64 " [expected %"PRId64"]\n", + i, logs[i * 4 + j], expected[i * 4 + j]); + } + printf ("\n"); + for (i = 0; i < 20; i++) + ck_assert_int_eq (logs[i], expected[i]); + free (logs); + + + ck_assert (parallel_time * 2 < sequential_time); + ck_assert (parallel_time * 4 > sequential_time); + +} +END_TEST + +void public_tests (Suite *s) +{ + TCase *tc_public = tcase_create ("Public"); + tcase_set_timeout (tc_public, 20.0); + tcase_add_test (tc_public, MIN_find_one_generator); + tcase_add_test (tc_public, MIN_find_two_generators); + tcase_add_test (tc_public, MIN_last_5_discrete_logs); + tcase_add_test (tc_public, MIN_parallel_discrete_logs); + suite_add_tcase (s, tc_public); +} + diff --git a/lab7-threads/tests/testsuite.c b/lab7-threads/tests/testsuite.c new file mode 100644 index 0000000..79db20b --- /dev/null +++ b/lab7-threads/tests/testsuite.c @@ -0,0 +1,32 @@ +#include +#include +#include +#include +#include +#include + +#include + +extern void public_tests (Suite *s); + +Suite * test_suite (void) +{ + Suite *s = suite_create ("Default"); + public_tests (s); + return s; +} + +void run_testsuite () +{ + Suite *s = test_suite (); + SRunner *sr = srunner_create (s); + srunner_run_all (sr, CK_NORMAL); + srunner_free (sr); +} + +int main (void) +{ + srand((unsigned)time(NULL)); + run_testsuite (); + return EXIT_SUCCESS; +} diff --git a/lab8-synch/.gitignore b/lab8-synch/.gitignore new file mode 100644 index 0000000..793f13b --- /dev/null +++ b/lab8-synch/.gitignore @@ -0,0 +1,24 @@ +# Ignore all compiled files regardless of location +main.o +mutex.o +pingpong.o +tests/public.o +tests/testsuite.o + +# But not the private.o +!tests/private.o + +# Ignore executables for this project +synch +testsuite + +# Ignore test outputs +tests/ckstyle +tests/itests.txt +tests/outputs +tests/style.txt +tests/valgrind +tests/utests.txt + +**/.nfs* +**/.vscode diff --git a/lab8-synch/Makefile b/lab8-synch/Makefile new file mode 100644 index 0000000..b9883d2 --- /dev/null +++ b/lab8-synch/Makefile @@ -0,0 +1,52 @@ +# +# Simple Makefile +# Mike Lam, James Madison University, August 2016 +# +# This makefile builds a simple application that contains a main module +# (specified by the EXE variable) and a predefined list of additional modules +# (specified by the MODS variable). If there are any external library +# dependencies (e.g., the math library, "-lm"), list them in the LIBS variable. +# If there are any precompiled object files, list them in the OBJS variable. +# +# By default, this makefile will build the project with debugging symbols and +# without optimization. To change this, edit or remove the "-g" and "-O0" +# options in CFLAGS and LDFLAGS accordingly. +# +# By default, this makefile build the application using the GNU C compiler, +# adhering to the C99 standard with all warnings enabled. + + +# application-specific settings and run target + +EXE=synch +MODS=mutex.o pingpong.o +OBJS= +LIBS=-lm + +default: $(EXE) + +test: $(EXE) + make -C tests test + +# compiler/linker settings + +CC=gcc +#CFLAGS=-g -O0 -Wall --std=gnu99 -pedantic +CFLAGS=-g -O0 -Wall --std=gnu99 -pedantic +LDFLAGS=-g -O0 -pthread + + +# build targets + +$(EXE): main.o $(MODS) $(OBJS) + $(CC) $(LDFLAGS) -o $(EXE) $^ $(LIBS) + +%.o: %.c + $(CC) -c $(CFLAGS) $< + +clean: + rm -f $(EXE) main.o $(MODS) + make -C tests clean + +.PHONY: default clean + diff --git a/lab8-synch/main.c b/lab8-synch/main.c new file mode 100644 index 0000000..7640b2d --- /dev/null +++ b/lab8-synch/main.c @@ -0,0 +1,93 @@ +/* + * CS 361: Template project driver + * + * Name: Nicholas Tamassia + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mutex.h" +#include "pingpong.h" + +int cmdline (int, char**, bool*, bool*, size_t *); + +void +usage (void) +{ + printf ("Usage: synch option [option...]\n"); + printf (" At least one argument must be passed\n"); + printf (" Options are:\n"); + printf (" -m Execute the runner() threads to demonstrate locks\n"); + printf (" -p N Run pingong() with N iterations to demonstrate " + "semaphore-based timing\n"); +} + +int +main (int argc, char **argv) +{ + bool run_mutex = false; + bool run_pingpong = false; + size_t count = 0; + if (cmdline (argc, argv, &run_mutex, &run_pingpong, &count) < 0) + { + usage (); + return EXIT_FAILURE; + } + + // Use mutexes for mutual exclusion + if (run_mutex) + { + printf ("Running mutual exclusion test\n"); + int64_t shared = run (); + printf ("Value of shared: %" PRId64 "\n", shared); + } + + // Use semaphores to demonstrate signaling + if (run_pingpong) + { + printf ("Running %zd iteration(s) of ping-pong\n", count); + ping (&count); + printf ("There should be 0 iterations left: %zd\n", count); + } + + pthread_exit (NULL); +} + +/***************************************************************************** + ****************** DO NOT MODIFY FUNCTIONS IN THIS SECTION ****************** + *****************************************************************************/ + +int +cmdline (int argc, char **argv, bool *mutex, bool *pingpong, size_t *count) +{ + int option; + + while ((option = getopt (argc, argv, "mp:h")) != -1) + { + switch (option) + { + case 'm': *mutex = true; + break; + case 'p': *pingpong = true; + *count = strtol (optarg, NULL, 10); + break; + case 'h': return -1; + break; + default: return -1; + } + } + + if (!*mutex && !*pingpong) + { + printf ("You must pass at least one argument\n"); + return -1; + } + return 0; +} diff --git a/lab8-synch/mutex.c b/lab8-synch/mutex.c new file mode 100644 index 0000000..e3183cc --- /dev/null +++ b/lab8-synch/mutex.c @@ -0,0 +1,89 @@ +#include +#include +#include +#include +#include +#include +#include + +// Use the struct here for passing arguments to the runner() threads. +// All instances of this struct should point to the same pthread_mutex_t +// and shared variables. +typedef struct args { + pthread_mutex_t *lock; + int64_t *shared; +} arg_t; + +/* Function to run in concurrent threads. The argument passed should be a + pointer to a pthread_mutex_t to be used for mutual exclusion. Within the + nested for-loop structure, use the mutex to protect the the increments + and decrements of the shared variable. To get the timing right for the + unit tests, experiment with placing the lock/unlock calls in different + places (e.g., around both for-loops, inside the outer one, inside the + inner one, etc.) */ +void * +runner (void *arg) +{ + // Retrieve the shared pointer and mutex from the passed struct. + arg_t *args = (arg_t *) arg; + pthread_mutex_t *lock = args->lock; + int64_t *shared = args->shared; + + for (int j = 0; j < 1000000; j++) + { + pthread_mutex_lock (lock); + for (int i = 0; i < 100; i++) + { + *shared += 1; + *shared -= 1; + } + pthread_mutex_unlock (lock); + } + + pthread_exit (NULL); +} + +/* Simple fork-join routine that creates two threads running the runner() + function above. Pass the lock and a pointer to the shared variable to + both threads. When both threads complete, return the shared variable, + which should have a value of 0. + */ +int64_t +run (void) +{ + int64_t shared = 0; + + pthread_mutex_t lock; + if (pthread_mutex_init (&lock, NULL) != 0) + { + fprintf (stderr, "Failed to initialize mutex\n"); + return shared; + } + + arg_t args; + args.lock = &lock; + args.shared = &shared; + + pthread_t t1, t2; + if (pthread_create (&t1, NULL, runner, &args) != 0) + { + fprintf (stderr, "Failed to create thread 1\n"); + pthread_mutex_destroy (&lock); + return shared; + } + if (pthread_create (&t2, NULL, runner, &args) != 0) + { + fprintf (stderr, "Failed to create thread 2\n"); + pthread_cancel (t1); + pthread_join (t1, NULL); + pthread_mutex_destroy (&lock); + return shared; + } + + pthread_join (t1, NULL); + pthread_join (t2, NULL); + + pthread_mutex_destroy (&lock); + + return shared; +} diff --git a/lab8-synch/mutex.h b/lab8-synch/mutex.h new file mode 100644 index 0000000..e882619 --- /dev/null +++ b/lab8-synch/mutex.h @@ -0,0 +1,10 @@ +#ifndef __mutex_h__ +#define __mutex_h__ + +#include +#include + +void * runner (void *); +int64_t run (void); + +#endif diff --git a/lab8-synch/pingpong.c b/lab8-synch/pingpong.c new file mode 100644 index 0000000..6e29653 --- /dev/null +++ b/lab8-synch/pingpong.c @@ -0,0 +1,94 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pingpong.h" + +typedef struct pparg { + sem_t *sem_ping; // Tell ping to wake up + sem_t *sem_pong; // Tell pong to wake up + size_t *shared; +} ppargs_t; + +void * +pong (void * _args) +{ + ppargs_t *args = (ppargs_t *) _args; + + while (1) + { + sem_wait (args->sem_pong); + + printf ("PONG!\n"); + + if (*(args->shared) == 0) + { + break; + } + else + { + sem_post (args->sem_ping); + } + } + + printf ("PONG: Game over\n"); + pthread_exit (NULL); +} + +void +ping (size_t *count) +{ + pthread_t pong_thread; + sem_t sem_ping; + sem_t sem_pong; + + if (sem_init (&sem_ping, 0, 1) != 0) + { + perror ("sem_init sem_ping"); + return; + } + if (sem_init (&sem_pong, 0, 0) != 0) + { + perror ("sem_init sem_pong"); + sem_destroy (&sem_ping); + return; + } + + ppargs_t args; + args.sem_ping = &sem_ping; + args.sem_pong = &sem_pong; + args.shared = count; + + if (pthread_create (&pong_thread, NULL, pong, (void *)&args) != 0) + { + perror ("pthread_create"); + sem_destroy (&sem_ping); + sem_destroy (&sem_pong); + return; + } + + while (1) + { + sem_wait (&sem_ping); + + (*count)--; + printf ("PING!\n"); + + sem_post (&sem_pong); + + if (*count == 0) + break; + } + + // After PONG prints game over and exits, do the same for PING + pthread_join (pong_thread, NULL); + printf ("PING: Game over\n"); + + sem_destroy (&sem_ping); + sem_destroy (&sem_pong); +} diff --git a/lab8-synch/pingpong.h b/lab8-synch/pingpong.h new file mode 100644 index 0000000..6a86ff9 --- /dev/null +++ b/lab8-synch/pingpong.h @@ -0,0 +1,6 @@ +#ifndef __CS361_PINGPONG__ +#define __CS361_PINGPONG__ + +void ping (size_t *); + +#endif diff --git a/lab8-synch/tests/Makefile b/lab8-synch/tests/Makefile new file mode 100644 index 0000000..54a4bf8 --- /dev/null +++ b/lab8-synch/tests/Makefile @@ -0,0 +1,88 @@ +# +# Simple Test Makefile +# Mike Lam, James Madison University, August 2016 +# +# This version of the Makefile includes support for building a test suite. The +# recommended framework is Check (http://check.sourceforge.net/). To build and +# run the test suite, execute the "test" target. The test suite must be located +# in a module called "testsuite". The MODS, LIBS, and OBJS variables work as +# they do in the main Makefile. +# +# To change the default build target (which executes when you just type +# "make"), change the right-hand side of the definition of the "default" +# target. +# +# By default, this makefile will build the project with debugging symbols and +# without optimization. To change this, edit or remove the "-g" and "-O0" +# options in CFLAGS and LDFLAGS accordingly. +# +# By default, this makefile build the application using the GNU C compiler, +# adhering to the C99 standard with all warnings enabled. + + +# application-specific settings and run target + +EXE=../synch +TEST=testsuite +MODS=public.o +OBJS=../mutex.o ../pingpong.o +LIBS= + +UTESTOUT=utests.txt +ITESTOUT=itests.txt + +default: $(TEST) + +$(EXE): + make -C ../ + +test: utest itest + @echo "========================================" + +utest: $(EXE) $(TEST) + @echo "==============================================" + @echo " UNIT TESTS" + @echo "This should take about 5 seconds" + @echo "If it is much slower or faster, your calls to" + @echo "pthread_mutex_lock and pthread_mutex_unlock" + @echo "are probably in the wrong place." + @echo "" + @echo "You can see how it works by running:" + @echo " ./tests/testsuite" + @echo "----------------------------------------------" + @./$(TEST) 2>/dev/null >$(UTESTOUT) + @cat $(UTESTOUT) | sed -n -e '/Checks/,$$p' | sed -e 's/^private.*:[EF]://g' + +itest: $(EXE) + @echo "==============================================" + @echo " INTEGRATION TESTS" + @./integration.sh | tee $(ITESTOUT) + + +# compiler/linker settings + +CC=gcc +CFLAGS=-g -O0 -Wall --std=gnu99 -pedantic +LDFLAGS=-g -O0 + +CFLAGS= +LIBS+=-lcheck -lm -lpthread + +ifeq ($(shell uname -s),Linux) + LIBS+=-lrt -lsubunit +endif + + +# build targets + +$(TEST): $(TEST).o $(MODS) $(OBJS) + $(CC) $(LDFLAGS) -o $(TEST) $^ $(LIBS) + +%.o: %.c + $(CC) -c $(CFLAGS) $< + +clean: + rm -rf $(TEST) $(TEST).o $(MODS) $(UTESTOUT) $(ITESTOUT) outputs valgrind + +.PHONY: default clean test unittest inttest + diff --git a/lab8-synch/tests/expected/.INTEG_run_10.sha b/lab8-synch/tests/expected/.INTEG_run_10.sha new file mode 100644 index 0000000..e69de29 diff --git a/lab8-synch/tests/expected/.INTEG_run_5.sha b/lab8-synch/tests/expected/.INTEG_run_5.sha new file mode 100644 index 0000000..e69de29 diff --git a/lab8-synch/tests/expected/.INTEG_run_mutex.sha b/lab8-synch/tests/expected/.INTEG_run_mutex.sha new file mode 100644 index 0000000..46da628 --- /dev/null +++ b/lab8-synch/tests/expected/.INTEG_run_mutex.sha @@ -0,0 +1 @@ +2c9be6afe105589c6486941c344fa8af47c4f96b diff --git a/lab8-synch/tests/expected/.INTEG_run_pingpong.sha b/lab8-synch/tests/expected/.INTEG_run_pingpong.sha new file mode 100644 index 0000000..51094fc --- /dev/null +++ b/lab8-synch/tests/expected/.INTEG_run_pingpong.sha @@ -0,0 +1 @@ +258a022a43b06ef7cd1187fb3445760809d5af0c diff --git a/lab8-synch/tests/expected/.INTEG_run_pingpong_10.sha b/lab8-synch/tests/expected/.INTEG_run_pingpong_10.sha new file mode 100644 index 0000000..5ea2f64 --- /dev/null +++ b/lab8-synch/tests/expected/.INTEG_run_pingpong_10.sha @@ -0,0 +1 @@ +8f29a21768d1d35aead683f394e6435fcae1edaf diff --git a/lab8-synch/tests/expected/.INTEG_run_pingpong_5.sha b/lab8-synch/tests/expected/.INTEG_run_pingpong_5.sha new file mode 100644 index 0000000..926889d --- /dev/null +++ b/lab8-synch/tests/expected/.INTEG_run_pingpong_5.sha @@ -0,0 +1 @@ +016cd27526965203f7be5709148c6ba67a2a8cec diff --git a/lab8-synch/tests/expected/INTEG_run_mutex.txt b/lab8-synch/tests/expected/INTEG_run_mutex.txt new file mode 100644 index 0000000..bc3530d --- /dev/null +++ b/lab8-synch/tests/expected/INTEG_run_mutex.txt @@ -0,0 +1,2 @@ +Running mutual exclusion test +Value of shared: 0 diff --git a/lab8-synch/tests/expected/INTEG_run_pingpong.txt b/lab8-synch/tests/expected/INTEG_run_pingpong.txt new file mode 100644 index 0000000..b382e04 --- /dev/null +++ b/lab8-synch/tests/expected/INTEG_run_pingpong.txt @@ -0,0 +1,6 @@ +Running 1 iteration(s) of ping-pong +PING! +PONG! +PONG: Game over +PING: Game over +There should be 0 iterations left: 0 diff --git a/lab8-synch/tests/expected/INTEG_run_pingpong_10.txt b/lab8-synch/tests/expected/INTEG_run_pingpong_10.txt new file mode 100644 index 0000000..be28abb --- /dev/null +++ b/lab8-synch/tests/expected/INTEG_run_pingpong_10.txt @@ -0,0 +1,24 @@ +Running 10 iteration(s) of ping-pong +PING! +PONG! +PING! +PONG! +PING! +PONG! +PING! +PONG! +PING! +PONG! +PING! +PONG! +PING! +PONG! +PING! +PONG! +PING! +PONG! +PING! +PONG! +PONG: Game over +PING: Game over +There should be 0 iterations left: 0 diff --git a/lab8-synch/tests/expected/INTEG_run_pingpong_5.txt b/lab8-synch/tests/expected/INTEG_run_pingpong_5.txt new file mode 100644 index 0000000..a669556 --- /dev/null +++ b/lab8-synch/tests/expected/INTEG_run_pingpong_5.txt @@ -0,0 +1,14 @@ +Running 5 iteration(s) of ping-pong +PING! +PONG! +PING! +PONG! +PING! +PONG! +PING! +PONG! +PING! +PONG! +PONG: Game over +PING: Game over +There should be 0 iterations left: 0 diff --git a/lab8-synch/tests/expected/MIN_run_pingpong.txt b/lab8-synch/tests/expected/MIN_run_pingpong.txt new file mode 100644 index 0000000..0d5aeaf --- /dev/null +++ b/lab8-synch/tests/expected/MIN_run_pingpong.txt @@ -0,0 +1,24 @@ +Running a controlled timing exercise +PING! +PONG! +PING! +PONG! +PING! +PONG! +PING! +PONG! +PING! +PONG! +PING! +PONG! +PING! +PONG! +PING! +PONG! +PING! +PONG! +PING! +PONG! +PONG: Game over +PING: Game over +Value of result should be 0: 0 diff --git a/lab8-synch/tests/integration.sh b/lab8-synch/tests/integration.sh new file mode 100755 index 0000000..3138272 --- /dev/null +++ b/lab8-synch/tests/integration.sh @@ -0,0 +1,75 @@ +#!/bin/bash + +EXE="../synch" + +function run_test { + + # parameters + TAG=$1 + ARGS=$2 + + # file paths + OUTPUT=outputs/$TAG.txt + DIFF=outputs/$TAG.diff + EXPECT=expected/$TAG.txt + VALGRND=valgrind/$TAG.txt + + # print tag format + PTAG=$(printf '%-30s' "$TAG") + + # check for expected text that needs to be fixed + if [ ! -z "$(egrep "<<<<.*>>>>" "$EXPECT")" ] ; then + echo "$PTAG FAIL ($EXPECT not correct)" + return + fi + + # compare the file + SHAOUT=$(shasum "$EXPECT" | awk '{print $1}') + SHAEXP=$(cat "expected/.$TAG.sha") + if [ "$SHAOUT" != "$SHAEXP" ] ; then + echo "$PTAG FAIL (expected file is not correct)" + return + fi + + # run test and compare output to the expected version + $EXE $ARGS 2>/dev/null >"$OUTPUT" + diff -u "$OUTPUT" "$EXPECT" >"$DIFF" + if [ -s "$DIFF" ]; then + + # try alternative solution (if it exists) + EXPECT=expected/$TAG-2.txt + if [ -e "$EXPECT" ]; then + diff -u "$OUTPUT" "$EXPECT" >"$DIFF" + if [ -s "$DIFF" ]; then + echo "$PTAG FAIL (see $DIFF for details)" + else + echo "$PTAG pass" + fi + else + echo "$PTAG FAIL (see $DIFF for details)" + fi + else + echo "$PTAG pass" + fi + + # run valgrind + valgrind $EXE $ARGS &>$VALGRND +} + +# initialize output folders +mkdir -p outputs +mkdir -p valgrind +rm -f outputs/* valgrind/* + +# run individual tests +source itests.include + +# check for memory leaks +LEAK=`cat valgrind/*.txt | grep 'definitely lost' | grep -v ' 0 bytes in 0 blocks'` +if [ -z "$LEAK" ]; then + echo "No memory leak found." +else + echo "Memory leak(s) found. See files listed below for details." + grep 'definitely lost' valgrind/*.txt | grep -v ' 0 bytes in 0 blocks' | sed -e 's/:.*$//g' | sed -e 's/^/ - /g' +fi + diff --git a/lab8-synch/tests/itests.include b/lab8-synch/tests/itests.include new file mode 100644 index 0000000..e79a0e8 --- /dev/null +++ b/lab8-synch/tests/itests.include @@ -0,0 +1,9 @@ +# list of integration tests +# format: run_test +# used as the root for all filenames (i.e., "expected/$TAG.txt") +# command-line arguments to test + +run_test INTEG_run_mutex "-m" +run_test INTEG_run_pingpong "-p 1" +run_test INTEG_run_pingpong_5 "-p 5" +run_test INTEG_run_pingpong_10 "-p 10" diff --git a/lab8-synch/tests/public.c b/lab8-synch/tests/public.c new file mode 100644 index 0000000..ec523d9 --- /dev/null +++ b/lab8-synch/tests/public.c @@ -0,0 +1,98 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../mutex.h" + +double time_diff (struct timeval start, struct timeval end) +{ + double ending = end.tv_sec + (end.tv_usec * 0.000001); + double starting = start.tv_sec + (start.tv_usec * 0.000001); + return ending - starting; +} + +START_TEST (MIN_mutex_run_two_threads) +{ + printf ("======================================\n"); + printf ("PUBLIC\n"); + printf (" MIN mutex run two threads\n"); + printf (" Call runner in two threads and ensure result is 0\n"); + + pthread_mutex_t lock; + pthread_mutex_init (&lock, NULL); + int64_t shared = run (); + pthread_mutex_destroy (&lock); + ck_assert_int_eq (shared, 0); + printf ("\n"); +} +END_TEST + +START_TEST (MIN_mutex_run_two_threads_time) +{ + printf ("======================================\n"); + printf ("PUBLIC\n"); + printf (" MIN mutex runner two threads time\n"); + printf (" Call runner in two threads and measure time\n"); + printf (" Multithreaded should take more than 3 times and\n"); + printf (" less than 6 times the unithreaded time.\n"); + + int64_t num = 0; + int64_t *shared = # + + struct timeval start, end; + gettimeofday (&start, NULL); + for (int j = 0; j < 1000000; j++) + { + for (int i = 0; i < 100; i++) + { + *shared += 1; + *shared -= 1; + } + } + gettimeofday (&end, NULL); + double unidiff = time_diff (start, end); + + pthread_mutex_t lock; + pthread_mutex_init (&lock, NULL); + gettimeofday (&start, NULL); + num = run (); + gettimeofday (&end, NULL); + double multidiff = time_diff (start, end); + + printf ("\n Unithreaded time: %f\n Multithreaded time: %f\n\n", + unidiff, multidiff); + + ck_assert_int_eq (num, 0); + ck_assert (unidiff * 6 > multidiff); + ck_assert (unidiff * 2.3 < multidiff); + printf ("\n"); + +} +END_TEST + +/* + 1 - Create a queue of size 1 and put things in one at a time, pausing + for 50 ms in between. + 2 - Create a queue of size 8, fill it, then start the worker. + 3 - Create a queue of size 15, start the listener, then pull one item + at a time. + 4 - Just use fifo_queue for size 10, 20, and 30. +*/ + +void public_tests (Suite *s) +{ + TCase *tc_public = tcase_create ("Public"); + tcase_set_timeout (tc_public, 30.0); + tcase_add_test (tc_public, MIN_mutex_run_two_threads); + tcase_add_test (tc_public, MIN_mutex_run_two_threads_time); + suite_add_tcase (s, tc_public); +} diff --git a/lab8-synch/tests/testsuite.c b/lab8-synch/tests/testsuite.c new file mode 100644 index 0000000..79db20b --- /dev/null +++ b/lab8-synch/tests/testsuite.c @@ -0,0 +1,32 @@ +#include +#include +#include +#include +#include +#include + +#include + +extern void public_tests (Suite *s); + +Suite * test_suite (void) +{ + Suite *s = suite_create ("Default"); + public_tests (s); + return s; +} + +void run_testsuite () +{ + Suite *s = test_suite (); + SRunner *sr = srunner_create (s); + srunner_run_all (sr, CK_NORMAL); + srunner_free (sr); +} + +int main (void) +{ + srand((unsigned)time(NULL)); + run_testsuite (); + return EXIT_SUCCESS; +} diff --git a/p1-sh/.gitignore b/p1-sh/.gitignore new file mode 100644 index 0000000..1a509ab --- /dev/null +++ b/p1-sh/.gitignore @@ -0,0 +1,24 @@ +*.swp +*.swo + +# Ignore all compiled files regardless of location +build/*.o +tests/public.o +tests/testsuite.o + +# Ignore executables for this project +dukesh +testsuite +bin/* + +# Ignore test outputs +tests/ckstyle +tests/itests.txt +tests/outputs +tests/style.txt +tests/valgrind +tests/utests.txt + +# Ignore all vscode stuff and NFS files +**/.nfs* +**/.vscode diff --git a/p1-sh/Makefile b/p1-sh/Makefile new file mode 100644 index 0000000..4b86919 --- /dev/null +++ b/p1-sh/Makefile @@ -0,0 +1,61 @@ +# +# Simple Makefile +# Mike Lam, James Madison University, August 2016 +# +# This makefile builds a simple application that contains a main module +# (specified by the EXE variable) and a predefined list of additional modules +# (specified by the MODS variable). If there are any external library +# dependencies (e.g., the math library, "-lm"), list them in the LIBS variable. +# If there are any precompiled object files, list them in the OBJS variable. +# +# By default, this makefile will build the project with debugging symbols and +# without optimization. To change this, edit or remove the "-g" and "-O0" +# options in CFLAGS and LDFLAGS accordingly. +# +# By default, this makefile build the application using the GNU C compiler, +# adhering to the C99 standard with all warnings enabled. + + +# application-specific settings and run target + +EXE=dukesh +MODS=main.o process.o shell.o builtins.o cmd.o hash.o +OBJS= +LIBS= + +default: build $(EXE) + +build: + mkdir build + +test: build $(EXE) + make -C tests test + +style: $(EXE) + make -C tests style + +# compiler/linker settings + +CC=gcc +CFLAGS=-g -O0 -Wall -Werror -std=c99 -pedantic -D_POSIX_C_SOURCE=200809L +LDFLAGS=-g -O0 + + +# build targets + +BUILD=$(addprefix build/, $(MODS)) + +$(EXE): build/main.o $(BUILD) $(OBJS) + $(CC) $(LDFLAGS) -o $(EXE) $^ $(LIBS) + make -C utils + +build/%.o: src/%.c + $(CC) -c $(CFLAGS) -o $@ $< + +clean: + rm -rf $(EXE) build + make -C utils clean + make -C tests clean + +.PHONY: default clean + diff --git a/p1-sh/PHASE-1.txt b/p1-sh/PHASE-1.txt new file mode 100644 index 0000000..ccc5c1c --- /dev/null +++ b/p1-sh/PHASE-1.txt @@ -0,0 +1,15 @@ +Answer the following questions to describe your code submission. Please keep +all lines to a maximum of 80 characters wide. + +1 - From the main() function, how did you distinguish if the shell was using + a script or an interactive prompt? + + If the -b flag was passed we read in whatever file was passed, printing + an error message if the input file was invalid. Otherwise, use stdin. + + +2 - How does the quit command exit the shell? + + It calls exit(0) + + diff --git a/p1-sh/PHASE-2.txt b/p1-sh/PHASE-2.txt new file mode 100644 index 0000000..775b125 --- /dev/null +++ b/p1-sh/PHASE-2.txt @@ -0,0 +1,26 @@ +Answer the following questions to describe your code submission. Please keep +all lines to a maximum of 80 characters wide. + +1 - When implementing the built-ins, which ones essentially only required + making a call to an existing C function? What function(s)? + + The pwd, cd, and quit are essentially just calls to c functions. + +2 - Briefly describe how you sorted the files in the ls program. If you used + an existing C function to sort, explain the arguments you passed. + + We created a custom, leading dot ignoring, case-insensitive comparison + function that can be used by scandir to sort the files before they are + returned to us. The two arguments we two dirents, the names of which + were compared, and an integer returned to tell scandir which should be + sorted first. + +3 - If you used your lab 2 code to parse the command line, briefly describe + any changes or adaptations you made. If you didn't use lab 2, briefly + describe how you built the array of arguments to pass when executing the + program. + + Most of the code from lab 2 was fairly plug in play. The only adaptation + necessary was turning the parse_buffer and functions process.c to use the + fsm from lab2 instead of just strings. + \ No newline at end of file diff --git a/p1-sh/PHASE-3.txt b/p1-sh/PHASE-3.txt new file mode 100644 index 0000000..44557b2 --- /dev/null +++ b/p1-sh/PHASE-3.txt @@ -0,0 +1,27 @@ +Answer the following questions to describe your code submission. Please keep +all lines to a maximum of 80 characters wide. + +1 - Explain how you are keeping track of the return code for use in echo $?. + + After each process is executed in the shell's main loop, the "?" key is + set to the return code of that process. + +2 - Briefly describe how you are storing or deleting environment variales. + + Environment variables are stored directly in the hashmap. Export sets + the value to the corresponding key in the hashmap. Unset removes the + key from the hasmap. Echo finds the value in the hashmap based on the key. + +3 - Consider the following three command lines: + $ echo ${VAR} + $ echo {VAR} + $ echo ${VAR + Briefly explain how your code processes each. Does your code handle bad + input strings? + + In the first case, the correct environment variable is printed. + In the second case the literal string {VAR} is printed. + In the third case nothig is printed. + In the first two cases the exit code of echo is 0 because they are both + valid inputs to echo, but the third exits with a code 1 because the + syntax is invalid. \ No newline at end of file diff --git a/p1-sh/PHASE-4.txt b/p1-sh/PHASE-4.txt new file mode 100644 index 0000000..604baba --- /dev/null +++ b/p1-sh/PHASE-4.txt @@ -0,0 +1,73 @@ +Answer the following questions to describe your code submission. Please keep +all lines to a maximum of 80 characters wide. + +1 - In pseudo-code, show your general algorithm for setting up two processes + that are connected by a pipe. You need to show all calls to relevant + functions (e.g., pipe(), fork(), etc.) but you do not need to show error + checking or precise syntax. + + pipes[# of processes][2] + for process in processes: + pipe(pipes[process #]) + + for process in processes: + if not first process: + add_dup2(pipes[process #][0], stdin) + + if not last process: + add_dup2(pipes[process #][1], stdout) + + for pipe in pipes: + add_close(pipe[0]) + add_close(pipe[1]) + + if process is util: + posix_spawn(process) + else: + posix_spawnp(process) + + +2 - The project does not ask you to implement pipes with built-ins, but show + a pseudo-code approach to modifying echo for this purpose. For simplicity, + ignore the issues of escape characters and environment variables, and + assume that echo only writes into the pipe. Specifically, what would be + the flow of relevant functions (pipe(), fork(), etc.) for the following + command line: + + $ echo hello world | cut -f2 + + (HINT: Be careful that you do NOT close or redirect the shell's STDOUT, + which would prevent it from displaying any more prompts!) + + pipefd[2] + pipe(fd) + + echo(pipefd[1], "Message") // Modify echo to take in a file descriptor to write to + close(pipefd[1]) + + add_dup2(pipefd[0], stdin) + add_close(pipefd[0]); + + posix_spawnp("cut", argv) + + close(pipefd[0]) + + +3 - The project does not ask you to implement file redirection, but show a + general algorithm in pseudo-code (like above) for the following command + line (note that 2>&1 says to send STDERR to the same file as STDOUT): + + $ ls > data.txt 2>&1 + + fd = open("data.txt") + + add_dup2(fd, stdout) + add_dup2(fd, stderr) + + add_close(fd) + + posix_spawnp("ls", argv) + + close(fd) + + diff --git a/p1-sh/cut_data/data.csv b/p1-sh/cut_data/data.csv new file mode 100644 index 0000000..8c9b2a6 --- /dev/null +++ b/p1-sh/cut_data/data.csv @@ -0,0 +1,2 @@ +hello,world,me +later,gator diff --git a/p1-sh/cut_data/data.spaces b/p1-sh/cut_data/data.spaces new file mode 100644 index 0000000..80ea279 --- /dev/null +++ b/p1-sh/cut_data/data.spaces @@ -0,0 +1,2 @@ +hello world me +later gator diff --git a/p1-sh/data/.hidden.txt b/p1-sh/data/.hidden.txt new file mode 100755 index 0000000..75891bc --- /dev/null +++ b/p1-sh/data/.hidden.txt @@ -0,0 +1 @@ +oops diff --git a/p1-sh/data/FIRST.txt b/p1-sh/data/FIRST.txt new file mode 100644 index 0000000..4856fe2 --- /dev/null +++ b/p1-sh/data/FIRST.txt @@ -0,0 +1 @@ +First diff --git a/p1-sh/data/empty.txt b/p1-sh/data/empty.txt new file mode 100644 index 0000000..e69de29 diff --git a/p1-sh/data/pwd.txt b/p1-sh/data/pwd.txt new file mode 100644 index 0000000..c992476 --- /dev/null +++ b/p1-sh/data/pwd.txt @@ -0,0 +1,2 @@ +pwd +quit diff --git a/p1-sh/data/yat.txt b/p1-sh/data/yat.txt new file mode 100755 index 0000000..7fb00f9 --- /dev/null +++ b/p1-sh/data/yat.txt @@ -0,0 +1 @@ +yet another test file diff --git a/p1-sh/src/builtins.c b/p1-sh/src/builtins.c new file mode 100644 index 0000000..f1d8682 --- /dev/null +++ b/p1-sh/src/builtins.c @@ -0,0 +1,281 @@ +#include +#include +#include +#include +#include +#include + +#include "hash.h" + +// Given a message as input, print it to the screen followed by a +// newline ('\n'). If the message contains the two-byte escape sequence +// "\\n", print a newline '\n' instead. No other escape sequence is +// allowed. If the sequence contains a '$', it must be an environment +// variable or the return code variable ("$?"). Environment variable +// names must be wrapped in curly braces (e.g., ${PATH}). +// +// Returns 0 for success, 1 for errors (invalid escape sequence or no +// curly braces around environment variables). +int +echo (char **args) +{ + // Loop through arguments skipping echo + for (int i = 1; args[i] != NULL; i++) + { + char *msg = args[i]; + + // Loop over the arg string + for (int j = 0; msg[j] != '\0'; j++) + { + // Check for escape key + if (msg[j] == '\\') + { + + if (msg[j + 1] == 'n') + { + putchar ('\n'); + j++; + } + else + { + putchar ('\n'); + return 1; + } + } + else if (msg[j] == '$') + { + if (msg[j + 1] == '?') + { + printf ("%s", hash_find ("?")); + j++; + } + else if (msg[j + 1] == '{') + { + j += 2; + char varname[256]; + int z = 0; + + while (msg[j] != '\0' && msg[j] != '}') + { + if (z >= 255) + return 1; + varname[z++] = msg[j++]; + } + + if (msg[j] != '}') + { + putchar ('\n'); + return 1; + } + + varname[z] = '\0'; + + char *val = hash_find (varname); + if (val != NULL) + { + printf ("%s", val); + } + } + else + { + putchar ('\n'); + return 1; + } + } + else + { + putchar (msg[j]); + } + } + if (args[i + 1] != NULL) + putchar (' '); + } + putchar ('\n'); + return 0; +} + +// Given a key-value pair string (e.g., "alpha=beta"), insert the mapping +// into the global hash table (hash_insert ("alpha", "beta")). +// +// Returns 0 on success, 1 for an invalid pair string (kvpair is NULL or +// there is no '=' in the string). +// +// NOTE: For some strange reason, clang-format (used for checking the style) +// expects export to be initially formatted this way... +int export (char **args) +{ + if (args[1] == NULL) + { + return 1; + } + + char buffer[256]; + snprintf (buffer, sizeof (buffer), "%s", args[1]); + + char *name = strtok (buffer, "="); + char *set = strtok (NULL, "="); + + if (name == NULL || set == NULL) + { + return 1; + } + + hash_insert (name, set); + + return 0; +} + +// Prints the current working directory (see getcwd()). Returns 0. +int +pwd (void) +{ + char cwd[1024]; + + if (getcwd (cwd, sizeof (cwd)) != NULL) + { + printf ("%s\n", cwd); + } + else + { + return 1; + } + return 0; +} + +// Removes a key-value pair from the global hash table. +// Returns 0 on success, 1 if the key does not exist. +int +unset (char **args) +{ + if (args[1] == NULL) + { + return 1; + } + char *value = hash_find (args[1]); + + if (value == NULL) + { + return 1; + } + + hash_remove (args[1]); + return 0; +} + +// Given a string of commands, find their location(s) in the $PATH global +// variable. If the string begins with "-a", print all locations, not just +// the first one. +// +// Returns 0 if at least one location is found, 1 if no commands were +// passed or no locations found. +int +which (char **args) +{ + const char *builtins[] + = { "cd", "echo", "pwd", "which", "export", "unset", "quit", NULL }; + if (args[1] == NULL) + { + return 1; + } + + int print_all = 0; + int index = 1; + + if (strcmp (args[index], "-a") == 0) + { + print_all = 1; + index = 2; + + if (args[index] == NULL) + { + return 1; + } + } + + int found_any = 0; + int if_builtin = 0; + + for (int a = index; args[a] != NULL; a++) + { + char *cmd = args[a]; + + // Check if its a built-in + for (int i = 0; builtins[i] != NULL; i++) + { + if (strcmp (cmd, builtins[i]) == 0) + { + printf ("%s: dukesh built-in command\n", cmd); + found_any = 1; + if_builtin = 1; + } + } + if (if_builtin) + { + continue; + } + + // Check if its an executable + if (strncmp (cmd, "./", 2) == 0) + { + printf ("%s\n", cmd); + found_any = 1; + continue; + } + + // Search through the path + char *path_env = getenv ("PATH"); + + char *path_copy = strdup (path_env); + + char *dir = strtok (path_copy, ":"); + + while (dir != NULL) + { + char fullpath[1024]; + snprintf (fullpath, sizeof (fullpath), "%s/%s", dir, cmd); + + if (access (fullpath, X_OK) == 0) + { + printf ("%s\n", fullpath); + found_any = 1; + if (!print_all) + break; + } + dir = strtok (NULL, ":"); + } + + free (path_copy); + } + + if (found_any) + { + return 0; + } + else + { + return 1; + } +} + +int +cd (char **args) +{ + if (args[1] == NULL) + { + return 1; + } + if (chdir (args[1]) != 0) + { + return 1; + } + return 0; +} + +// Immediately exits the shell +int +quit (void) +{ + printf ("\n"); + exit (0); + return 0; +} diff --git a/p1-sh/src/builtins.h b/p1-sh/src/builtins.h new file mode 100644 index 0000000..feae4f7 --- /dev/null +++ b/p1-sh/src/builtins.h @@ -0,0 +1,14 @@ +#ifndef __cs361_builtins__ +#define __cs361_builtins__ + +#include + +int echo (char **); +int export (char **); +int pwd (void); +int unset (char **); +int which (char **); +int cd (char **); +int quit (void); + +#endif diff --git a/p1-sh/src/cmd.c b/p1-sh/src/cmd.c new file mode 100644 index 0000000..4f8753b --- /dev/null +++ b/p1-sh/src/cmd.c @@ -0,0 +1,202 @@ +#include "cmd.h" +#include +#include +#include +#include +#include +#include + +// Integrate the FSM command-line parser from lab 2 here. Note that the FSM +// effects will be vastly different from that of lab 2. Instead of implementing +// the effects here, this file should focus on the model and the parsing. You +// should modify the model's effects table to call functions in process.c or +// builtins.c as appropriate. (Or you can have a function here to call those +// functions indirectly.) + +static state_t const _transitions[NUM_STATES][NUM_EVENTS] + = { { Command, Term, NST }, + { Arguments, Make_Pipe, Term }, + { Arguments, Make_Pipe, Term }, + { Command, Term, Term }, + { NST, NST, NST } }; + +static action_t const _effects[NUM_STATES][NUM_EVENTS] + = { { start_command, error_pipe, NULL }, + { append, NULL, NULL }, + { append, NULL, NULL }, + { start_command, error_pipe, error_newline }, + { NULL, NULL, NULL } }; + +/* Helper function for providing a printable string name for an event */ +const char * +event_name (event_t evt) +{ + assert (evt <= NIL); + + // Event names for printing out + const char *names[] = { "TOKEN", "PIPE", "NEWLINE", "NIL" }; + return names[evt]; +} + +/* Helper function for providing a printable string name for an state */ +const char * +state_name (state_t st) +{ + assert (st <= NST); + + // State names for printing out + const char *names[] + = { "Init", "Command", "Arguments", "Make_Pipe", "Term", "NST" }; + return names[st]; +} + +/* Executed when starting to process a new command line. The fsm_t + should have been updated to include a pointer to the current token. + For instance, if the command line was "ls -l data NL", the fsm_t + has a field that points to "ls". */ +void +start_command (fsm_t *cmdmodel) +{ + // printf ("Starting new command: %s\n", cmdmodel->current_token); + // TODO: Copy the current token to store it in the FSM's command + // field. Next, create the FSM's args array (length MAX_ARGUMENTS) + // set the current token as args[0], and initialize nargs to be + // the number of arguments (1 at this point). + // Allocate args array (NULL-initialized) if not already + cmdmodel->args = calloc (MAX_ARGUMENTS, sizeof (char *)); + cmdmodel->command = cmdmodel->current_token; + cmdmodel->args[0] = cmdmodel->current_token; + cmdmodel->nargs = 1; +} + +/* Executed when processing a token after the command name. For instance, + if the command line was "ls -l data NL", this function will be called + when the current token is "-l" and again when it is "data". */ +void +append (fsm_t *cmdmodel) +{ + if (cmdmodel->nargs >= MAX_ARGUMENTS) + return; + + // printf ("Appending %s to the argument list\n", cmdmodel->current_token); + assert (cmdmodel->args != NULL); + + // TODO: Store the current token into the args array and increment nargs + cmdmodel->args[cmdmodel->nargs++] = cmdmodel->current_token; +} + +void +error_pipe (fsm_t *cmdmodel) +{ + printf ("ERROR: Received token %s while in state %s\n", + cmdmodel->current_token, state_name (cmdmodel->state)); +} + +void +error_newline (fsm_t *cmdmodel) +{ + printf ("ERROR: Received token %s while in state %s\n", + cmdmodel->current_token, state_name (cmdmodel->state)); +} + +state_t +transition (struct fsm *fsm, event_t event, action_t *effect) +{ + assert (fsm->state < NST); + assert (event < NIL); + + *effect = _effects[fsm->state][event]; + return _transitions[fsm->state][event]; +} + +/* Generic front-end for handling events. Should do nothing more + than calling the FSM's transition function, performing an effect + (if appropriate) and updating the state. Return false if the new + state is the terminal state. */ +bool +handle_event (fsm_t *fsm, event_t event) +{ + assert (fsm != NULL); + // Look up the current state/event combination in the + // transition table. Print the following line for debugging + // purposes just for this lab. This should be printed even + // if there is no transition. + action_t effect; + state_t new_state = fsm->transition (fsm, event, &effect); + // printf("[%s.%s -> %s]\n", state_name(fsm->state), event_name(event), + // state_name(new_state)); + + // If the state/event combination is valid, execute + // the transition and effect function (if there is one). + // If the next state is Term (terminated), return false. + // Otherwise return true. + if (new_state != NST) + { + if (effect != NULL) + { + effect (fsm); + } + + fsm->state = new_state; + } + + return fsm->state != Term; +} + +/* Given a string, return the event type. Do not modify this function. */ +event_t +lookup (char *token) +{ + if (!strcmp (token, "|")) + return PIPE; + + if (!strcmp (token, "NL")) + return NEWLINE; + + return TOKEN; +} + +char *** +split_commands (char **tokens) +{ + size_t ncmds = 0; + char ***cmds = calloc (MAX_ARGUMENTS, sizeof (*cmds)); + + size_t start = 0; + size_t i; + for (i = 0; tokens[i]; i++) + { + if (strcmp (tokens[i], "|") == 0) + { + tokens[i] = NULL; + cmds[ncmds++] = &tokens[start]; + start = i + 1; + } + } + + cmds[ncmds++] = &tokens[start]; + cmds[ncmds] = NULL; + return cmds; +} + +// You should provide some function like this to serve as the interface to +// parsing the command line. This function is just a placeholder and you +// should define your own. +char *** +parse_buffer (char *buffer) +{ + buffer[strcspn (buffer, "\n")] + = '\0'; // Replace newline with null terminator + + char **argv = calloc (MAX_ARGUMENTS, sizeof (*argv)); + + size_t i = 0; + char *token = strtok (buffer, " "); + while (token != NULL) + { + argv[i++] = token; + token = strtok (NULL, " "); + } + + return split_commands (argv); +} diff --git a/p1-sh/src/cmd.h b/p1-sh/src/cmd.h new file mode 100644 index 0000000..98e7209 --- /dev/null +++ b/p1-sh/src/cmd.h @@ -0,0 +1,74 @@ +#include + +#ifndef __cs361_cmd_h__ +#define __cs361_cmd_h__ + +// Generic definitions for any type of statemodel + +// States and events should just be integers +typedef int state_t; +typedef int event_t; + +// Needed for circular typedef. This lets action_t use fsm_t in its parameter +// list, while the struct fsm can use action_t as a field. +typedef struct fsm fsm_t; + +// All entry, exit, and effect instances use the action type +typedef void (*action_t) (fsm_t *); + +// Each FSM instance contains a current state +struct fsm +{ + state_t state; // current state + + // pointer to the FSM's transition function + state_t (*transition) (struct fsm *, event_t, action_t *); + + // Additional data fields specific to this FSM + char *command; // the name of the command to run + size_t nargs; // the number of command-line arguments + char **args; // the command-line arguments + char *current_token; // current token being processed +}; + +#define MAX_ARGUMENTS 32 + +// Events +typedef enum +{ + TOKEN, // normal command-line token + PIPE, // vertical bar character + NEWLINE, // newline at the end of the command + NIL // invalid non-event +} cmdevt_t; +#define NUM_EVENTS NIL + +// States +typedef enum +{ + Init, // initial state + Command, // establishing the command name + Arguments, // building the argument list + Make_Pipe, // linking the commands together for a pipe + Term, // terminal state (execute program or error) + NST // invalid non-state +} cmdst_t; +#define NUM_STATES NST + +state_t transition (struct fsm *fsm, event_t event, action_t *effect); +event_t lookup (char *); // convert an event string to its numeric value + +// Translate event/state numbers to their string equivalent +const char *event_name (event_t); +const char *state_name (state_t); + +void start_command (fsm_t *); +void append (fsm_t *); +void execute (fsm_t *); +void link_commands (fsm_t *); +void error_pipe (fsm_t *); +void error_newline (fsm_t *); + +char ***parse_buffer (char *buffer); + +#endif diff --git a/p1-sh/src/hash.c b/p1-sh/src/hash.c new file mode 100644 index 0000000..c573c0c --- /dev/null +++ b/p1-sh/src/hash.c @@ -0,0 +1,284 @@ +#include +#include +#include +#include +#include +#include + +#include "hash.h" + +static ssize_t find_index (char *); +static unsigned long hash (unsigned char *); +static bool insert_help (char *, char *, bool); +static void rehash (size_t); + +#define MINSIZE 100 +#define PRIME 97 + +typedef struct kvpair +{ + char *key; + char *value; + bool alive; +} kvpair_t; + +static kvpair_t *table = NULL; +static size_t capacity = 0; +static size_t entries = 0; + +void +hash_destroy (void) +{ + if (table == NULL) + return; + + for (size_t i = 0; i < capacity; i++) + { + if (table[i].key != NULL) + { + free (table[i].key); + table[i].key = NULL; + } + if (table[i].value != NULL) + { + free (table[i].value); + table[i].value = NULL; + } + } + free (table); +} + +/* Dumps the table contents to STDOUT (useful for debugging) */ +void +hash_dump (void) +{ + printf ("TABLE:\n"); + for (size_t i = 0; i < capacity; i++) + if (table[i].key != NULL) + printf (" [%zd].%s = %s%s\n", i, table[i].key, table[i].value, + (!table[i].alive ? " [deleted]" : "")); +} + +/* Initializes the hash table to a given size (minimum 100) */ +void +hash_init (size_t size) +{ + if (table != NULL) + free (table); + + // Minimum of 100 entries to start + if (size < MINSIZE) + size = MINSIZE; + + table = calloc (size, sizeof (kvpair_t)); + capacity = size; + entries = 0; +} + +/* Find the value for a given key. Returns NULL if there is no entry for + the given key. */ +char * +hash_find (char *key) +{ + if (table == NULL) // uninitialized table + return NULL; + + ssize_t index = find_index (key); + assert (index >= 0); + + if (table[index].key == NULL) // key not found + return NULL; + + // If .key is not NULL, find_index found a key match + assert (!strcmp (table[index].key, key)); + if (table[index].alive) // not marked for deletion + return table[index].value; + + return NULL; +} + +/* Inserts a new entry into the hash table. If there is already an entry + for the given key, replace the value (freeing the old one). */ +bool +hash_insert (char *key, char *value) +{ + if (table == NULL) // uninitialized table + return false; + + return insert_help (key, value, true); +} + +/* Gets a list of pointers to the keys in the hash table. */ +char ** +hash_keys (void) +{ + if (table == NULL) // uninitialized table + return NULL; + + char **keys = calloc (entries + 1, sizeof (char *)); + size_t next = 0; + for (size_t i = 0; i < capacity; i++) + if (table[i].key != NULL && table[i].alive) + keys[next++] = table[i].key; + return keys; +} + +/* Removes a key-value pair from the hash table. Marks the entry as + deleted. Can be overwritten later. */ +bool +hash_remove (char *key) +{ + if (table == NULL) // uninitialized table + return false; + + ssize_t index = find_index (key); + assert (index >= 0); + + if (table[index].key == NULL) // key not found + return true; + + // If .key is not NULL, find_index found a key match + assert (!strcmp (table[index].key, key)); + table[index].alive = false; + entries--; + + if ((entries < capacity / 4) && (capacity / 2 >= MINSIZE)) + rehash (capacity / 2); + + return true; +} + +/* ********************************************************************** + * Helper functions only below this point * + * ********************************************************************** */ + +static bool +insert_help (char *key, char *value, bool dup) +{ + ssize_t index = find_index (key); + assert (index >= 0); // failed to find an open slot; should never happen + + if (table[index].key == NULL) + { + // New entry for this key. Check if rehashing is needed. + if (entries + 1 > capacity / 2) + { + rehash (capacity * 2); + index = find_index (key); + } + + // Set the entries in the table (duplicating if requested) + if (dup) + { + table[index].key = strdup (key); + table[index].value = strdup (value); + } + else + { + table[index].key = key; + table[index].value = value; + } + entries++; + table[index].alive = true; + + return true; + } + + // If table[index].key is not NULL, it must match the existing key OR + // the existing entry has been deleted. Otherwise, the double hashing + // should have found a different location. If the key does not match + // the current key, replace it. + if (strcmp (table[index].key, key)) + { + assert (!table[index].alive); + free (table[index].key); + if (dup) + table[index].key = strdup (key); + else + table[index].key = key; + } + + // Free the old value and replace it + free (table[index].value); + if (dup) + table[index].value = strdup (value); + else + table[index].value = value; + table[index].alive = true; + + return true; +} + +static ssize_t +find_index (char *str) +{ + if (table == NULL) + return -1; + + unsigned long keyhash = hash ((unsigned char *)str); + // probe is 1 .. PRIME, guaranteed non-zero + unsigned long probe = PRIME - (keyhash % PRIME); + + // Use double hashing to resolve collisions + size_t index = (size_t)(keyhash % capacity); + ssize_t first_open = -1; + for (size_t i = 0; i < capacity; i++) + { + size_t trial = (index + (size_t)(i * probe)) % capacity; + // If the key is NULL, we have not encountered the string. If there + // was an earlier open spot, use that one. Otherwise, use the spot + // with the empty key. + if (table[trial].key == NULL) + { + if (first_open < 0) + return (ssize_t)trial; + else + return first_open; + } + + // Keep track of the first open index. This will be returned if + // the key has not been previously entered and deleted. + if (first_open < 0 && !table[trial].alive) + first_open = trial; + + // If the key has been previously used, re-use this position + if (!strcmp (table[trial].key, str)) + return (ssize_t)trial; + } + + // Due to rehashing, there should always be at least 50% of the table + // entries free. So there should always be a free space. + abort (); +} + +static unsigned long +hash (unsigned char *string) +{ + if (string == NULL) + return 0; + + unsigned long hash = 5381; + + // Derived from djb2 by Dan Bernstein + // hash = hash * 33 + ch + for (unsigned char *ptr = string; *ptr != '\0'; ptr++) + hash = ((hash << 5) + hash) + *ptr; + + return hash; +} + +static void +rehash (size_t newcap) +{ + size_t oldcap = capacity; + capacity = newcap; + kvpair_t *oldtable = table; + table = calloc (capacity, sizeof (kvpair_t)); + entries = 0; + + for (int i = 0; i < oldcap; i++) + { + if (oldtable[i].key != NULL && oldtable[i].alive) + insert_help (oldtable[i].key, oldtable[i].value, false); + } +} diff --git a/p1-sh/src/hash.h b/p1-sh/src/hash.h new file mode 100644 index 0000000..22a1b9c --- /dev/null +++ b/p1-sh/src/hash.h @@ -0,0 +1,15 @@ +#ifndef __cs361_hash__ +#define __cs361_hash__ + +#include + +void hash_dump (void); // for debugging if needed + +void hash_destroy (void); +void hash_init (size_t); +char *hash_find (char *); +bool hash_insert (char *, char *); +char **hash_keys (void); +bool hash_remove (char *); + +#endif diff --git a/p1-sh/src/main.c b/p1-sh/src/main.c new file mode 100644 index 0000000..0ae0063 --- /dev/null +++ b/p1-sh/src/main.c @@ -0,0 +1,65 @@ +#include +#include +#include +#include +#include + +#include "hash.h" +#include "shell.h" + +static bool get_args (int, char **, FILE **); +static void usage (void); + +int +main (int argc, char *argv[]) +{ + FILE *input = stdin; + + if (!get_args (argc, argv, &input)) + { + usage (); + } + + if (input == NULL) + { + printf ("Invalid input file\n"); + return EXIT_FAILURE; + } + + shell (input); + + return EXIT_SUCCESS; +} + +/* Parse the command-line arguments. Sets the client/server variables to + point to a file name (typically in the data/ directory). Can also set + the bot variable if a second file is used to interact with the + client/server. If -d was passed, turn on debugging mode to print + information about state transitions. */ +static bool +get_args (int argc, char **argv, FILE **script) +{ + int ch = 0; + while ((ch = getopt (argc, argv, "b:h")) != -1) + { + switch (ch) + { + case 'b': + *script = fopen (optarg, "r"); + break; + default: + return false; + } + } + return true; +} + +static void +usage (void) +{ + printf ("dukesh, a simple command shell\n"); + printf ("usage: dukesh [-b FILE]\n"); + printf (" -b FILE use FILE as a shell script to execute\n"); + printf ("If no script is passed, then the shell should be interactive,\n"); + printf ("processing one command at a time from STDIN.\n"); +} diff --git a/p1-sh/src/process.c b/p1-sh/src/process.c new file mode 100644 index 0000000..04f59e5 --- /dev/null +++ b/p1-sh/src/process.c @@ -0,0 +1,152 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "builtins.h" +#include "cmd.h" +#include "hash.h" + +// The contents of this file are up to you, but they should be related to +// running separate processes. It is recommended that you have functions +// for: +// - performing a $PATH lookup +// - determining if a command is a built-in or executable +// - running a single command in a second process +// - running a pair of commands that are connected with a pipe + +// You should provide some function like this to serve as the interface to +// parsing the command line. This function is just a placeholder and you +// should define your own. +bool +is_util (char *str) +{ + char *utils[] = { "./bin/cat", "./bin/chmod", "./bin/cut", "./bin/env", + "./bin/head", "./bin/ls", "./bin/repeat" }; + + for (size_t i = 0; i < 7; i++) + { + if (strcmp (str, utils[i]) == 0) + { + return true; + } + } + + return false; +} + +char ** +hash_to_envp () +{ + char **keys = hash_keys (); + + size_t key_count = 0; + while (keys[key_count] != NULL) + key_count++; + + char **envp = calloc (key_count + 1, sizeof (char *)); + + for (size_t i = 0; i < key_count; i++) + { + char *key = keys[i]; // get the key + char *value + = hash_find (key); // entry should be found since this is a key + + size_t len = strlen (key) + strlen (value) + 2; + envp[i] = calloc (1, len); + snprintf (envp[i], len, "%s=%s", key, value); + } + + return envp; +} + +int +run_process (char ***cmds) +{ + if (strcmp (cmds[0][0], "quit") == 0) + return quit (); + if (strcmp (cmds[0][0], "echo") == 0) + return echo (cmds[0]); + if (strcmp (cmds[0][0], "cd") == 0) + return cd (cmds[0]); + if (strcmp (cmds[0][0], "pwd") == 0) + return pwd (); + if (strcmp (cmds[0][0], "which") == 0) + return which (cmds[0]); + if (strcmp (cmds[0][0], "export") == 0) + return export (cmds[0]); + if (strcmp (cmds[0][0], "unset") == 0) + return unset (cmds[0]); + + int ncmds = 0; + while (cmds[ncmds]) + ncmds++; + + int pipes[ncmds - 1][2]; + for (int i = 0; i < ncmds - 1; i++) + { + if (pipe (pipes[i]) < 0) + { + perror ("pipe"); + return -1; + } + } + + pid_t pids[ncmds]; + for (int i = 0; i < ncmds; i++) + { + posix_spawn_file_actions_t actions; + posix_spawn_file_actions_init (&actions); + + if (i > 0) + { + posix_spawn_file_actions_adddup2 (&actions, pipes[i - 1][0], + STDIN_FILENO); + } + if (i < ncmds - 1) + { + posix_spawn_file_actions_adddup2 (&actions, pipes[i][1], + STDOUT_FILENO); + } + + for (int j = 0; j < ncmds - 1; j++) + { + posix_spawn_file_actions_addclose (&actions, pipes[j][0]); + posix_spawn_file_actions_addclose (&actions, pipes[j][1]); + } + + if (is_util (cmds[i][0])) + { + posix_spawn (&pids[i], cmds[i][0], &actions, NULL, cmds[i], + hash_to_envp ()); + } + else + { + posix_spawnp (&pids[i], cmds[i][0], &actions, NULL, cmds[i], + hash_to_envp ()); + } + + posix_spawn_file_actions_destroy (&actions); + } + + // parent closes all pipe fds + for (int i = 0; i < ncmds - 1; i++) + { + close (pipes[i][0]); + close (pipes[i][1]); + } + + // wait for all children + int status; + for (int i = 0; i < ncmds; i++) + { + waitpid (pids[i], &status, 0); + } + + free (cmds); + + return WEXITSTATUS (status); +} diff --git a/p1-sh/src/process.h b/p1-sh/src/process.h new file mode 100644 index 0000000..43e21b6 --- /dev/null +++ b/p1-sh/src/process.h @@ -0,0 +1,25 @@ +#include "cmd.h" +#include + +#ifndef __cs361_process__ +#define __cs361_process__ + +// The contents of this file are up to you, but they should be related to +// running separate processes. It is recommended that you have functions +// for: +// - performing a $PATH lookup +// - determining if a command is a built-in or executable +// - running a single command in a second process +// - running a pair of commands that are connected with a pipe + +// You should provide some function like this to serve as the interface to +// parsing the command line. This function is just a placeholder and you +// should define your own. + +bool is_util (char *str); + +char **hash_to_envp (); + +int run_process (char ***cmd); + +#endif diff --git a/p1-sh/src/shell.c b/p1-sh/src/shell.c new file mode 100644 index 0000000..3402cec --- /dev/null +++ b/p1-sh/src/shell.c @@ -0,0 +1,43 @@ +#include +#include +#include + +#include "builtins.h" +#include "cmd.h" +#include "hash.h" +#include "process.h" + +// No command line can be more than 100 characters +#define MAXLENGTH 100 + +void +shell (FILE *input) +{ + hash_init (100); + hash_insert ("?", "0"); + hash_insert ("PATH", getenv ("PATH")); + char buffer[MAXLENGTH]; + while (1) + { + // Print the cursor and get the next command entered + printf ("$ "); + memset (buffer, 0, sizeof (buffer)); + if (fgets (buffer, MAXLENGTH, input) == NULL) + break; + + if (input != stdin) + printf ("%s", buffer); + + // Keep this here to avoid weird line interleavings + fflush (stdout); + + char ***cmds = parse_buffer (buffer); + int rc = run_process (cmds); + char buffer[20]; + sprintf (buffer, "%d", rc); + char *str = buffer; + hash_insert ("?", str); + } + printf ("\n"); + hash_destroy (); +} diff --git a/p1-sh/src/shell.h b/p1-sh/src/shell.h new file mode 100644 index 0000000..15aae1f --- /dev/null +++ b/p1-sh/src/shell.h @@ -0,0 +1,6 @@ +#ifndef __cs361_shell__ +#define __cs361_shell__ + +void shell (FILE *); + +#endif diff --git a/p1-sh/tests/Makefile b/p1-sh/tests/Makefile new file mode 100644 index 0000000..a6d8d32 --- /dev/null +++ b/p1-sh/tests/Makefile @@ -0,0 +1,89 @@ +# +# Simple Test Makefile +# Mike Lam, James Madison University, August 2016 +# +# This version of the Makefile includes support for building a test suite. The +# recommended framework is Check (http://check.sourceforge.net/). To build and +# run the test suite, execute the "test" target. The test suite must be located +# in a module called "testsuite". The MODS, LIBS, and OBJS variables work as +# they do in the main Makefile. +# +# To change the default build target (which executes when you just type +# "make"), change the right-hand side of the definition of the "default" +# target. +# +# By default, this makefile will build the project with debugging symbols and +# without optimization. To change this, edit or remove the "-g" and "-O0" +# options in CFLAGS and LDFLAGS accordingly. +# +# By default, this makefile build the application using the GNU C compiler, +# adhering to the C99 standard with all warnings enabled. + + +# application-specific settings and run target + +EXE=../dukesh +TEST=testsuite +MODS=public.o +OBJS=../build/process.o ../build/shell.o ../build/builtins.o ../build/cmd.o ../build/hash.o +LIBS= + +UTESTOUT=utests.txt +ITESTOUT=itests.txt +SCHECKOUT=style.txt + +default: $(TEST) + +$(EXE): + make -C ../ + +test: utest itest style + @echo "========================================" + +utest: $(EXE) $(TEST) + @echo "========================================" + @echo " UNIT TESTS" + @./$(TEST) 2>/dev/null >$(UTESTOUT) + @cat $(UTESTOUT) | sed -n -e '/Checks/,$$p' | sed -e 's/^private.*:[EF]://g' + +itest: $(EXE) + @echo "========================================" + @echo " INTEGRATION TESTS" + @./integration.sh | tee $(ITESTOUT) + +style: $(EXE) + @echo "========================================" + @echo " CODING STYLE CHECK" + @./style.sh 2>/dev/null >$(SCHECKOUT) + @cat $(SCHECKOUT) + +# compiler/linker settings + +CC=gcc +CFLAGS=-g -O0 -Wall --std=c99 -pedantic -D_POSIX_C_SOURCE=200809L +LDFLAGS=-g -O0 + +#CFLAGS+=-I/opt/local/include -Wno-gnu-zero-variadic-macro-arguments +CFLAGS+=-Wno-gnu-zero-variadic-macro-arguments +#LDFLAGS+=-L/opt/local/lib +LDFLAGS= +LIBS+=-lcheck -lm -lpthread + +ifeq ($(shell uname -s),Linux) + LIBS+=-lrt -lsubunit +endif + + +# build targets + +$(TEST): $(TEST).o $(MODS) $(OBJS) + $(CC) $(LDFLAGS) -o $(TEST) $^ $(LIBS) + +%.o: %.c + $(CC) -c $(CFLAGS) $< + +clean: + rm -rf $(TEST) $(TEST).o $(MODS) $(UTESTOUT) $(ITESTOUT) $(SCHECKOUT) outputs valgrind ckstyle + +.PHONY: default clean test unittest inttest + diff --git a/p1-sh/tests/bin/cat b/p1-sh/tests/bin/cat new file mode 100755 index 0000000000000000000000000000000000000000..d33a6453681a7d8e1be33fe5425d54e8ba4b0975 GIT binary patch literal 18992 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 literal 0 HcmV?d00001 diff --git a/p1-sh/tests/bin/chmod b/p1-sh/tests/bin/chmod new file mode 100755 index 0000000000000000000000000000000000000000..6d533361c411ba569cbc0c465547654cdb09fbc0 GIT binary patch literal 18472 zcmeHPdvF`adEWzgBt%erSr1BfrK2MyQZ0fH(TXL77WDuHCXyw~QewG|Patq4VB^8y z4tkC4s;2W`73P?;#?IXR{F$ooiuP1;U`uy-d8jKX)u<=G#}mMJ;uf9m(GPi(&!+PLzQXMY^c-Zl{2zM+|P zsNQ5lI+VyCcM;+#AH-Jz~ZZ zkC=neWJ*{kla8>RT*}Hohz=1Wjhthqqe((yC!^+IG80WDPg{b-)IGL{z5Dui?KZnY z-Njs2=+0t(bGI=25AQeQR?Zqq+K!bwynlBpo3Rc@2T~Ti4yCghRgUSZE~tnm_|Oad z_^VRXFR9yDlH7|dUkA&Q$)$9M_#K7iS0|TH@3IvCt#jwmoqXamu6gl(#rKJ6^*ne= z-H#Z*pzuJiYsfhBQ#)Lo`YN4kPSYiLY5$%n!QKAtTFjN;wd@3Rz67W1g43TWocb?c zlO%h-1gGnb)5Q{8J|~o5q6Alu7AbkT1m`xBV6p^9fGepIfl34_5vW9<5`jttDiQdN zi@<+1tod1F}G#>lcxdQ>`OO!8i37y^CnLNui3BOK*GHrTLskC5dR*53$~ z^Y453cRc*Rd-#9#@K?+D&Y$#;zWPvP^o_{Kn{x*bhdU=aUx}RU`5Ky4SpFAKy*?Of zSoVZS{p<+{DRTsNIN2SM(Z{CZk+G`v8>FxBeCNcK@b%n@lOi$} zo{EfKnfXKbjEv0toral*5QcBl8J?K{cqKfItcY9*&k%hp35%K0moCyN-#Gaq>5X3c z$2UeNN5gMM#*R#pIHFB9ywDgqyZ^e&z1;AExa*~SU@ZLRlJN9ftz~-RV{IsO7Jl49 zKOt)pp3x#BkIndwkB^0?p+6sw+_6kn-_d90e^DsBa=D~|#^_^jM@IA0BagkU!`aht zc9Ci}JB|bDFdZ40K#xZlugts&|D)kKg*Gbm2NI?Fl5UGaFG_TNEIfBSz4(aex#8~8+)PTF=do3V{-#)uk&P$v(0 zJ33@-bB&B6_Z>3!9y(|oI2YXbTj)87`w@{b_t$nXhm;?O|Xd}HBBGI>5O4{i|LOa>0-C9=@ z7>A5-T7xhJUmqrgJvH)^+B#&r@tc09P#7k>zOk>q<>L*(6M-{g$Lg)Wy?Nd31e5)P z_-%l_G$as-a0fpF_%TpURMwa9yBBqSoxpH?C&i9jU+l?YTKP>Db#0+k3?M`@fO8%y(g3& zuesy$*C?FzeqM0fhqy`zU-V>qr&L4nrBw19cf5v4{zWDKZ^d&vl>7fT!t1=f4m;HO z?p5@lqQ?{+R`iUbUi)9g^Lov_yLWFhHXIqqXPmszxjD2s)X}yjFWIigyShUi-5V7w zHo>QR`rP2os->WBaO^x~hM=qqO)A&Yc7nPMN@F2Q+24FuS8juj7O1%mPPM9Mj>0^hlVKH4wPQTr7sJLY>0v{P|$-!bTPDK6m)f$LUWO6ZS6H9)r&3Gu0T z9RMZm>Ge2m)q}99{Vh`Uos0OIz}3~0z;`b0TLE;L@^^5_M^)8NlQmM~!$MmjL*P4i z|B{_bDgy+muM3A3)uhA7H__>6rjw2{!MlV0dMI`afBm*rZF_KQu>TexE^ogs z`Vqpa6(k6{I8H%>2(|`DP_26{YC_tMRZ>~s8vMwr{#!n_N`qoR!P0rFMuH}-xu&<} zQ7K*$)QCmubZ(U13BW_JWRYCOWx)00hEG;j?Eup>~{Jgfx#Ss>+`$txB znFtx$8sH{TQ=2r6TGZT%BHC${Go1h(H?aGW*EH?t^-GBWq&3#atd)1G4)90udlbKC z@bhVFeGLJ5(aFQtfN9?%!QbHEzl=i_%^g+!6CHF-Eb(!7@YibZ>Gjw2n&W!4@KtFq z>NSa@dhN4%!*SjJguc2%55BB7|43hRRj<9Ouh^%rd{S??st5NS)z?7Z=Rf|8-a>}= z>$U!RN7s4+59-x{cDT{&kLq_kt2aHNZ~Bqm%*QoTdfOBFxtZ7^z| z%ES`6Y$ltx+wz$cn7?duXhFXXq1;x`Hk`{kR?NxfUV$O^ebftqIv>@65F%LDx&;!H#dK|sbg?@jC)#6ndm@{*+HEJ_?nKj3+s=!>1$CH z`&79G&Tu?#4TciJIW=tApg19pBym(oW^$Nmc9CQjbn!&jSe1CU;MJgUhX%=)aND9} z$fFRrY!}pD1Kfw*86E1Osnn$%;m17qW~I;LLe}p`MiZ+=W3P)){V37yAx*3hJkFCK z4suh^pEa!BFvbZ5e2a=`}9TEU=&l?=XP3`6; zF4IYyG1|suLo=Q=hf>*rXv&P^es7!6{7DhZriW9OgRfQ{3v19;vZNW!<)WudE92x& ziNRbnZJF_WI(-TzC7cOOr%Y9Tu0w_7=Qt?$9oo4+Y=-aIV`2~4o}>5d+~2<&FdlMd zIHIJHJ%@z3ci(|sJNKCf`ugq*A2tu~+_f(Z3-4yPUf$F8D|WBZcDkZ2?Rqn<*Zbc3{-A7<%p21dkERkP!=p@Y%3;0j&%|&bysL-0S_2KqZ*MjpXDwY}k6sbkezE=8q)RE_lHzd- za;5g(ecJ1anr?>g?+nv8q12woDNGGkRMcz#Dd6v8?t|;k^FvIzd^IieOZAV!p59#8 zp68F48Wdb4sv~8&{doJ4DTyNut=OLDqnKWD)rv>uuE>7|IhxyHd!El>%H?qVS&u2b z`qEqx^E_X~v{#knt-pfBS!5`tY|ryoOed5gm+x)=mzDi4rN{GOOvg*?sV!dnXMs^n z*njc)r=Ej6PpI6;x^kZA)A*Iz8_JI9aH%r#N?fGRdF;oP0#nv!H_S7A-eZ43378&Z zLq*w+>0f*7d0fNvdwgO%_U!iC9(x|wG39w!*7sgNf2Zt=#TZIc(Z5wJ6Fz^;{{u1< zf3}}6TnW=XE?Yc$%l|PXrS^eVSC#48MN5Tyc$#X%G>p6kNmTrQ7BugKm0i5mmJz>0Es*7Ql6pw7r zR8HEJ*o$7J+Tl_zN1?^?nWw2e^2O!zIJQyQb2+R}=c+W(j1OT-y(nV)JBo;#VSA>l zmAywzY*!Awe%X%oH!foTh%)Trj;Rd1_IEB~|Hoaf!77h|hZVaQv44SHjFFam3_Pr$ z30}gy?fhQ1G#43<0kcdWK!);O`>pg~MxyE9(!_S9)Dt49-a?baNC9=heh7Jbhvxpk m&n22);J$-wF&W$4>;oR>TrMlpYurNir+ZwDhdl-!R{Rf4oBt31 literal 0 HcmV?d00001 diff --git a/p1-sh/tests/bin/cut b/p1-sh/tests/bin/cut new file mode 100755 index 0000000000000000000000000000000000000000..f5c941fcca2f77f2b36f059a4745404844809162 GIT binary patch literal 20080 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

s#)!{qI)#jL!i+UVT>quS9vGO5gS-#H;XMjg({R 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%0A%r$M$~YQHwyVEVrx zZ2?zm@87pg@2N`@->(^?`9rBa&tI5Ytf;Bq{xiUTjWrRjKd&1xuSk(l=C!u|E)f?=h9jSF8-`zkX;firUZpFDx*WEO`{)QKWd} z{$t?pktwyGvXq4BZdTOPU;aM(Xc103xxT;^ukAe+vPfyWCCuyuklAW0o3+MAsiBw&Q+$0T`*UJ+DijjF7gF z``cAc9oe2KEy0!Ai*Bvmt|*tI(PH_`*TI;4arr!7ZPfN$4(n4{rKw4muhokpw!f>0 zs0`aPU8C)NYGS)~==aNZtWT>(evD*~X~WMJPi$r3*WJ8?{p%Z*J}ozvCLb$yEny$% zR90<110O36YI}b>BlM3UkdFEcm}UCN680x)U`9HrNb$&arPLQ9soVG|Wn$?Ic0E{n sw&wGJ$3I$k;B!Y=sAD@V(jk4`=bXz`QhJtK-2RWcl*Si)20m8&7a)`V5dZ)H literal 0 HcmV?d00001 diff --git a/p1-sh/tests/bin/head b/p1-sh/tests/bin/head new file mode 100755 index 0000000000000000000000000000000000000000..c2e8803fd4e174ac9b3a4f8fc4788e73f67790ff GIT binary patch literal 19776 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 Ubo)4w|5w*23%U<4E*??*7Z~4cTL1t6 literal 0 HcmV?d00001 diff --git a/p1-sh/tests/bin/ls b/p1-sh/tests/bin/ls new file mode 100755 index 0000000000000000000000000000000000000000..d95e5be356b2778d5ddaad2153f239a7bbd22542 GIT binary patch literal 21072 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 literal 0 HcmV?d00001 diff --git a/p1-sh/tests/bin/repeat b/p1-sh/tests/bin/repeat new file mode 100755 index 0000000000000000000000000000000000000000..360cf4c8c0567ef763006f8ff4c5b28da7971cd5 GIT binary patch literal 18096 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 NR~^Sw9)O1x{|U#(kWv5u literal 0 HcmV?d00001 diff --git a/p1-sh/tests/cut_data/data.csv b/p1-sh/tests/cut_data/data.csv new file mode 100644 index 0000000..8c9b2a6 --- /dev/null +++ b/p1-sh/tests/cut_data/data.csv @@ -0,0 +1,2 @@ +hello,world,me +later,gator diff --git a/p1-sh/tests/cut_data/data.spaces b/p1-sh/tests/cut_data/data.spaces new file mode 100644 index 0000000..80ea279 --- /dev/null +++ b/p1-sh/tests/cut_data/data.spaces @@ -0,0 +1,2 @@ +hello world me +later gator diff --git a/p1-sh/tests/data/.hidden.txt b/p1-sh/tests/data/.hidden.txt new file mode 100755 index 0000000..75891bc --- /dev/null +++ b/p1-sh/tests/data/.hidden.txt @@ -0,0 +1 @@ +oops diff --git a/p1-sh/tests/data/FIRST.txt b/p1-sh/tests/data/FIRST.txt new file mode 100644 index 0000000..4856fe2 --- /dev/null +++ b/p1-sh/tests/data/FIRST.txt @@ -0,0 +1 @@ +First diff --git a/p1-sh/tests/data/empty.txt b/p1-sh/tests/data/empty.txt new file mode 100644 index 0000000..e69de29 diff --git a/p1-sh/tests/data/pwd.txt b/p1-sh/tests/data/pwd.txt new file mode 100644 index 0000000..c992476 --- /dev/null +++ b/p1-sh/tests/data/pwd.txt @@ -0,0 +1,2 @@ +pwd +quit diff --git a/p1-sh/tests/data/yat.txt b/p1-sh/tests/data/yat.txt new file mode 100755 index 0000000..7fb00f9 --- /dev/null +++ b/p1-sh/tests/data/yat.txt @@ -0,0 +1 @@ +yet another test file diff --git a/p1-sh/tests/expected/A_cat_tail.txt b/p1-sh/tests/expected/A_cat_tail.txt new file mode 100644 index 0000000..01569d9 --- /dev/null +++ b/p1-sh/tests/expected/A_cat_tail.txt @@ -0,0 +1,10 @@ +$ ./bin/cat cut_data/data.csv +hello,world,me +later,gator +$ ./bin/cat cut_data/data.csv | ./bin/cut -d , -f 2 +world +gator +$ ./bin/cat cut_data/data.csv | tail -n 1 +later,gator +$ quit + diff --git a/p1-sh/tests/expected/A_env.txt b/p1-sh/tests/expected/A_env.txt new file mode 100644 index 0000000..3dd5441 --- /dev/null +++ b/p1-sh/tests/expected/A_env.txt @@ -0,0 +1,17 @@ +$ export A=5 +$ ./bin/env ./bin/repeat 1 A +A=5 +$ ./bin/env B=6 C=7 ./bin/repeat 1 A 2 B 3 C +A=5 +B=6 +B=6 +C=7 +C=7 +C=7 +$ ./bin/env C=7 ./bin/repeat 1 A 2 B 3 C | head -n 4 +A=5 +B= +B= +C=7 +$ quit + diff --git a/p1-sh/tests/expected/A_pipe.txt b/p1-sh/tests/expected/A_pipe.txt new file mode 100644 index 0000000..be4e7d2 --- /dev/null +++ b/p1-sh/tests/expected/A_pipe.txt @@ -0,0 +1,17 @@ +$ ./bin/ls -a data | ./bin/head -n 1 +empty.txt +$ ./bin/ls -a data | ./bin/head -n 2000 +empty.txt +FIRST.txt +.hidden.txt +pwd.txt +subdir +yat.txt +$ ./bin/head -n 1 cut_data/data.spaces | ./bin/cut -f 2 +world +$ ./bin/head -n 1 cut_data/data.csv | ./bin/cut -d , -f 1 +hello +$ ./bin/head -n 1 cut_data/data.csv | ./bin/cut -d , -f 3 +me +$ quit + diff --git a/p1-sh/tests/expected/A_which.txt b/p1-sh/tests/expected/A_which.txt new file mode 100644 index 0000000..2417237 --- /dev/null +++ b/p1-sh/tests/expected/A_which.txt @@ -0,0 +1,6 @@ +$ which ./bin/rm +./bin/rm +$ which ./bin/cat +./bin/cat +$ quit + diff --git a/p1-sh/tests/expected/B_env.txt b/p1-sh/tests/expected/B_env.txt new file mode 100644 index 0000000..12fda9e --- /dev/null +++ b/p1-sh/tests/expected/B_env.txt @@ -0,0 +1,18 @@ +$ export A=5 +$ ./bin/repeat 2 A +A=5 +A=5 +$ ./bin/env B=6 ./bin/repeat 1 A +A=5 +$ ./bin/env C=10 ./bin/repeat 2 A 3 B 4 C +A=5 +A=5 +B= +B= +B= +C=10 +C=10 +C=10 +C=10 +$ quit + diff --git a/p1-sh/tests/expected/B_export_unset.txt b/p1-sh/tests/expected/B_export_unset.txt new file mode 100644 index 0000000..4980278 --- /dev/null +++ b/p1-sh/tests/expected/B_export_unset.txt @@ -0,0 +1,9 @@ +$ which export +export: dukesh built-in command +$ echo N=${NUM} +N= +$ export NUM=5 +$ echo N=${NUM} +N=5 +$ quit + diff --git a/p1-sh/tests/expected/B_repeat.txt b/p1-sh/tests/expected/B_repeat.txt new file mode 100644 index 0000000..72843f0 --- /dev/null +++ b/p1-sh/tests/expected/B_repeat.txt @@ -0,0 +1,9 @@ +$ export SHELL=/bin/bash +$ export USER=me +$ ./bin/repeat 1 USER +USER=me +$ ./bin/repeat 2 SHELL +SHELL=/bin/bash +SHELL=/bin/bash +$ quit + diff --git a/p1-sh/tests/expected/B_return_code.txt b/p1-sh/tests/expected/B_return_code.txt new file mode 100644 index 0000000..dd38dc0 --- /dev/null +++ b/p1-sh/tests/expected/B_return_code.txt @@ -0,0 +1,13 @@ +$ ./bin/ls data +empty.txt +FIRST.txt +pwd.txt +subdir +yat.txt +$ echo $? +0 +$ ./bin/ls asldfkjasldfkj +$ echo $? +1 +$ quit + diff --git a/p1-sh/tests/expected/B_setenv.txt b/p1-sh/tests/expected/B_setenv.txt new file mode 100644 index 0000000..c869635 --- /dev/null +++ b/p1-sh/tests/expected/B_setenv.txt @@ -0,0 +1,14 @@ +$ ./bin/env A=5 ./bin/repeat 2 A +A=5 +A=5 +$ ./bin/env A=5 B=6 C=7 ./bin/repeat 1 A 2 B 3 C +A=5 +B=6 +B=6 +C=7 +C=7 +C=7 +$ ./bin/env A=10 ./bin/repeat 1 B +B= +$ quit + diff --git a/p1-sh/tests/expected/C_binaries.txt b/p1-sh/tests/expected/C_binaries.txt new file mode 100644 index 0000000..ce13b63 --- /dev/null +++ b/p1-sh/tests/expected/C_binaries.txt @@ -0,0 +1,14 @@ +$ ./bin/ls data +empty.txt +FIRST.txt +pwd.txt +subdir +yat.txt +$ ./bin/head Makefile +# +# Simple Test Makefile +# Mike Lam, James Madison University, August 2016 +# +# This version of the Makefile includes support for building a test suite. The +$ quit + diff --git a/p1-sh/tests/expected/C_binaries_bad-2.txt b/p1-sh/tests/expected/C_binaries_bad-2.txt new file mode 100644 index 0000000..167e091 --- /dev/null +++ b/p1-sh/tests/expected/C_binaries_bad-2.txt @@ -0,0 +1,4 @@ +$ ./bin/ls -l +$ ./bin/head -c 5 Makefile +$ quit + diff --git a/p1-sh/tests/expected/C_binaries_bad.txt b/p1-sh/tests/expected/C_binaries_bad.txt new file mode 100644 index 0000000..39147d3 --- /dev/null +++ b/p1-sh/tests/expected/C_binaries_bad.txt @@ -0,0 +1,6 @@ +$ ./bin/ls -l +./bin/ls: invalid option -- 'l' +$ ./bin/head -c 5 Makefile +./bin/head: invalid option -- 'c' +$ quit + diff --git a/p1-sh/tests/expected/C_binaries_flags.txt b/p1-sh/tests/expected/C_binaries_flags.txt new file mode 100644 index 0000000..bad3cbb --- /dev/null +++ b/p1-sh/tests/expected/C_binaries_flags.txt @@ -0,0 +1,35 @@ +$ /usr/bin/chmod 751 data/subdir +$ /usr/bin/chmod 640 data/empty.txt +$ /usr/bin/chmod 640 data/FIRST.txt +$ /usr/bin/chmod 400 data/.hidden.txt +$ /usr/bin/chmod 640 data/pwd.txt +$ /usr/bin/chmod 640 data/yat.txt +$ ./bin/ls -a data +empty.txt +FIRST.txt +.hidden.txt +pwd.txt +subdir +yat.txt +$ ./bin/ls -s data +0 empty.txt +6 FIRST.txt +9 pwd.txt +22 yat.txt +$ ./bin/ls -sa data +0 empty.txt +6 FIRST.txt +5 .hidden.txt +9 pwd.txt +22 yat.txt +$ ./bin/ls -ap data +-rw-r----- empty.txt +-rw-r----- FIRST.txt +-r-------- .hidden.txt +-rw-r----- pwd.txt +drwxr-x--x subdir +-rw-r----- yat.txt +$ ./bin/head -n 1 Makefile +# +$ quit + diff --git a/p1-sh/tests/expected/C_cd_pwd.txt b/p1-sh/tests/expected/C_cd_pwd.txt new file mode 100644 index 0000000..96b0e04 --- /dev/null +++ b/p1-sh/tests/expected/C_cd_pwd.txt @@ -0,0 +1,7 @@ +$ cd /usr/bin +$ pwd +/usr/bin +$ which ls +/usr/bin/ls +$ quit + diff --git a/p1-sh/tests/expected/C_chmod.txt b/p1-sh/tests/expected/C_chmod.txt new file mode 100644 index 0000000..ff09e4d --- /dev/null +++ b/p1-sh/tests/expected/C_chmod.txt @@ -0,0 +1,35 @@ +$ ./bin/ls -a data +empty.txt +FIRST.txt +.hidden.txt +pwd.txt +subdir +yat.txt +$ ./bin/ls -s data +0 empty.txt +6 FIRST.txt +9 pwd.txt +22 yat.txt +$ ./bin/ls -sa data +0 empty.txt +6 FIRST.txt +5 .hidden.txt +9 pwd.txt +22 yat.txt +$ ./bin/chmod rw- rw- --- data/empty.txt +$ ./bin/chmod r-- --- --x data/FIRST.txt +$ ./bin/chmod rw- rw- rw- data/pwd.txt +$ ./bin/chmod r-x r-x r-x data/yat.txt +$ ./bin/chmod rwx --- --- data/.hidden.txt +$ ./bin/chmod r-x --x r-- data/subdir +$ ./bin/ls -ap data +-rw-rw---- empty.txt +-r-------x FIRST.txt +-rwx------ .hidden.txt +-rw-rw-rw- pwd.txt +dr-x--xr-- subdir +-r-xr-xr-x yat.txt +$ ./bin/head -n 1 Makefile +# +$ quit + diff --git a/p1-sh/tests/expected/C_cut.txt b/p1-sh/tests/expected/C_cut.txt new file mode 100644 index 0000000..5328e11 --- /dev/null +++ b/p1-sh/tests/expected/C_cut.txt @@ -0,0 +1,17 @@ +$ ./bin/cut cut_data/data.spaces +hello +later +$ ./bin/cut -f 2 cut_data/data.spaces +world +gator +$ ./bin/cut cut_data/data.csv +hello,world,me +later,gator +$ ./bin/cut -d , cut_data/data.csv +hello +later +$ ./bin/cut -d , -f 2 cut_data/data.csv +world +gator +$ quit + diff --git a/p1-sh/tests/expected/C_cut_bad.txt b/p1-sh/tests/expected/C_cut_bad.txt new file mode 100644 index 0000000..92ae22d --- /dev/null +++ b/p1-sh/tests/expected/C_cut_bad.txt @@ -0,0 +1,15 @@ +$ ./bin/cut -f -1 cut_data/data.spaces +cut, splits each line based on a delimiter +usage: cut [FLAG] FILE +FLAG can be: + -d C split each line based on the character C (default ' ') + -f N print the Nth field (1 is first, default 1) +If no FILE specified, read from STDIN +$ ./bin/cut -f 3 cut_data/data.spaces +me + +$ ./bin/cut -d , -f 5 cut_data/data.csv + + +$ quit + diff --git a/p1-sh/tests/expected/C_echo.txt b/p1-sh/tests/expected/C_echo.txt new file mode 100644 index 0000000..3e7f1de --- /dev/null +++ b/p1-sh/tests/expected/C_echo.txt @@ -0,0 +1,7 @@ +$ echo goodbye +goodbye +$ echo hello\nworld +hello +world +$ quit + diff --git a/p1-sh/tests/expected/C_echo_space.txt b/p1-sh/tests/expected/C_echo_space.txt new file mode 100644 index 0000000..fc282e2 --- /dev/null +++ b/p1-sh/tests/expected/C_echo_space.txt @@ -0,0 +1,8 @@ +$ echo this has extra spaces +this has extra spaces +$ echo this\nhas\nnewlines +this +has +newlines +$ quit + diff --git a/p1-sh/tests/expected/C_which.txt b/p1-sh/tests/expected/C_which.txt new file mode 100644 index 0000000..91a8380 --- /dev/null +++ b/p1-sh/tests/expected/C_which.txt @@ -0,0 +1,18 @@ +$ which cd +cd: dukesh built-in command +$ which echo +echo: dukesh built-in command +$ which pwd +pwd: dukesh built-in command +$ which which +which: dukesh built-in command +$ which ./bin/ls +./bin/ls +$ which ./bin/head +./bin/head +$ which ls +/usr/bin/ls +$ which head +/usr/bin/head +$ quit + diff --git a/p1-sh/tests/expected/D_echo.txt b/p1-sh/tests/expected/D_echo.txt new file mode 100644 index 0000000..42f391a --- /dev/null +++ b/p1-sh/tests/expected/D_echo.txt @@ -0,0 +1,6 @@ +$ echo hello +hello +$ echo goodbye +goodbye +$ quit + diff --git a/p1-sh/tests/expected/D_quit.txt b/p1-sh/tests/expected/D_quit.txt new file mode 100644 index 0000000..8891f6b --- /dev/null +++ b/p1-sh/tests/expected/D_quit.txt @@ -0,0 +1,2 @@ +$ quit + diff --git a/p1-sh/tests/integration.sh b/p1-sh/tests/integration.sh new file mode 100755 index 0000000..db16deb --- /dev/null +++ b/p1-sh/tests/integration.sh @@ -0,0 +1,95 @@ +#!/bin/bash + +# Ensure the data directory has 750 permissions +chmod 750 data + +# Must run with env -i so that students cannot "accidentally" use the +# standard shell environment variables +EXE="env -i PATH=/usr/bin:. ../dukesh" + +SKIP_C="" +SKIP_B="" +SKIP_A="" + +function run_test { + + # parameters + TAG=$1 + ARGS=$2 + + PTAG=$(printf '%-30s' "$TAG") + if [[ $(echo $TAG | cut -d'_' -f1) == $SKIP_C || + $(echo $TAG | cut -d'_' -f1) == $SKIP_B || + $(echo $TAG | cut -d'_' -f1) == $SKIP_A ]] ; then + echo "$PTAG SKIPPED (previous phases not complete)" + return + fi + + # file paths + OUTPUT=outputs/$TAG.txt + DIFF=outputs/$TAG.diff + EXPECT=expected/$TAG.txt + VALGRND=valgrind/$TAG.txt + + # run test and compare output to the expected version + $EXE $ARGS 2>/dev/null >"$OUTPUT" + diff -u "$OUTPUT" "$EXPECT" >"$DIFF" + passed=false + EFILES=$(find expected -type f -name "$TAG-*.txt") + if [ ! -s "$DIFF" ]; then + passed=true + elif [ "$EFILES" != "" ]; then + for EF in $EFILES ; do + DF=$(echo $EF | sed 's/^expected/outputs/' | sed 's/txt$/diff/') + diff -u "$OUTPUT" "$EF" >"$DF" + if [ ! -s "$DF" ]; then + passed=true + fi + done + fi + if [[ $passed = true ]] ; then + echo "$PTAG pass" + rm outputs/$TAG*diff + else + echo "$PTAG FAIL - Command line: $EXE $ARGS" + if [ "$EFILES" != "" ] ; then + echo "$(printf '%30s' ' ') *WARNING* This test has more than one possible output file." + echo "$(printf '%30s' ' ') *WARNING* Please inspect each manually for comparison." + else + echo "$(printf '%30s' ' ') See $DIFF for diff with expected output" + fi + echo "" + + if [[ $(echo $PTAG | cut -d'_' -f1) == 'D' ]] ; then + SKIP_C="C" + SKIP_B="B" + SKIP_A="A" + elif [[ $(echo $PTAG | cut -d'_' -f1) == 'C' ]] ; then + SKIP_B="B" + SKIP_A="A" + elif [[ $(echo $PTAG | cut -d'_' -f1) == 'B' ]] ; then + SKIP_A="A" + fi + fi + + # run valgrind + valgrind $EXE $ARGS &>$VALGRND +} + +# initialize output folders +mkdir -p outputs +mkdir -p valgrind +rm -f outputs/* valgrind/* + +# run individual tests +source itests.include + +# check for memory leaks +LEAK=`cat valgrind/*.txt | grep 'definitely lost' | grep -v ' 0 bytes in 0 blocks'` +if [ -z "$LEAK" ]; then + echo "No memory leak found." +else + echo "Memory leak(s) found. See files listed below for details." + grep 'definitely lost' valgrind/*.txt | grep -v ' 0 bytes in 0 blocks' | sed -e 's/:.*$//g' | sed -e 's/^/ - /g' +fi + diff --git a/p1-sh/tests/itests.include b/p1-sh/tests/itests.include new file mode 100644 index 0000000..ba11ecb --- /dev/null +++ b/p1-sh/tests/itests.include @@ -0,0 +1,40 @@ +# list of integration tests +# format: run_test +# used as the root for all filenames (i.e., "expected/$TAG.txt") +# command-line arguments to test + +run_test D_quit "-b scripts/quit.txt" +run_test D_echo "-b scripts/echo_min.txt" + +run_test C_cd_pwd "-b scripts/dirs.txt" +run_test C_echo "-b scripts/echo.txt" +run_test C_echo_space "-b scripts/echo_space.txt" + +run_test C_binaries "-b scripts/bins.txt" + # Your ls implementation must list in alphabetical order +run_test C_binaries_flags "-b scripts/bins_flags.txt" + # Your ls must print directories (. and ..) first, then + # the rest in alphabetical order (ignoring leading . and case) + +run_test C_chmod "-b scripts/bins_chmod.txt" + # Your ls must work correctly before chmod will work + +run_test C_binaries_bad "-b scripts/bins_bad.txt" +run_test C_which "-b scripts/which.txt" +run_test C_cut "-b scripts/cut.txt" +run_test C_cut_bad "-b scripts/cut_bad.txt" + +run_test B_return_code "-b scripts/rc.txt" + # Your ls implementation must list in alphabetical order +run_test B_export_unset "-b scripts/export.txt" + # Previous test exports and uses echo, no binaries +run_test B_repeat "-b scripts/repeat.txt" + # Previous test exports and uses repeat binary +run_test B_setenv "-b scripts/setenv.txt" + # Previous test uses ./bin/env to set env vars +run_test B_env "-b scripts/env.txt" + # Previous combines export and ./bin/env with ./bin/repeat + +run_test A_cat_tail "-b scripts/tail.txt" +run_test A_pipe "-b scripts/pipe.txt" +run_test A_env "-b scripts/env_pipe.txt" diff --git a/p1-sh/tests/public.c b/p1-sh/tests/public.c new file mode 100644 index 0000000..b01c9bd --- /dev/null +++ b/p1-sh/tests/public.c @@ -0,0 +1,114 @@ +#include + +#include "../src/hash.h" + +START_TEST (C_hash_insert) +{ + char *strings[] = { "foo", "bar", "zoo", "yadda", "help", "goo", "hi", "boo", + "nine", "ten" }; + + hash_init (100); + + for (int i = 0; i < 10; i++) + ck_assert (hash_insert (strings[i], strings[(i + 1) % 10])); + + for (int i = 0; i < 10; i++) + { + char *value = hash_find (strings[i]); + ck_assert_str_eq (value, strings[(i + 1) % 10]); + } +} +END_TEST + +START_TEST (C_hash_find_missing) +{ + char *strings[] = { "foo", "bar", "zoo", "yadda", "help", "goo", "hi", "boo", + "nine", "ten" }; + + hash_init (100); + + for (int i = 0; i < 10; i++) + ck_assert (hash_insert (strings[i], strings[(i + 1) % 10])); + + ck_assert (hash_find ("gobble") == NULL); +} +END_TEST + +START_TEST (C_hash_remove) +{ + char *strings[] = { "foo", "bar", "zoo", "yadda", "help", "goo", "hi", "boo", + "nine", "ten" }; + + hash_init (100); + + for (int i = 0; i < 10; i++) + ck_assert (hash_insert (strings[i], strings[(i + 1) % 10])); + + for (int i = 0; i < 5; i++) + ck_assert (hash_remove (strings[i])); + + ck_assert_str_eq (hash_find ("goo"), "hi"); + + for (int i = 5; i < 10; i++) + ck_assert (hash_remove (strings[i])); + + ck_assert (hash_find ("goo") == NULL); +} +END_TEST + +START_TEST (C_hash_replace) +{ + char *strings[] = { "foo", "bar", "zoo", "yadda", "help", "goo", "hi", "boo", + "nine", "ten" }; + + hash_init (100); + + for (int i = 0; i < 10; i++) + ck_assert (hash_insert (strings[i], strings[(i + 1) % 10])); + + ck_assert (hash_insert ("yadda", "again?!")); + ck_assert_str_eq (hash_find ("yadda"), "again?!"); + + ck_assert (hash_insert ("yadda", "really?!")); + ck_assert_str_eq (hash_find ("yadda"), "really?!"); +} +END_TEST + +START_TEST (C_hash_collisions) +{ + char *strings[] = { "foo", "bar", "zoo", "yadda", "help", "goo", "hi", "boo", + "nine", "ten" }; + + hash_init (100); + + for (int i = 0; i < 10; i++) + ck_assert (hash_insert (strings[i], strings[(i + 1) % 10])); + + ck_assert (hash_insert ("bar", "a")); + ck_assert (hash_insert ("help", "b")); + ck_assert_str_eq (hash_find ("bar"), "a"); + ck_assert_str_eq (hash_find ("help"), "b"); + + ck_assert (hash_remove ("bar")); + ck_assert (hash_remove ("help")); + ck_assert (hash_find ("bar") == NULL); + ck_assert (hash_find ("help") == NULL); + + ck_assert (hash_insert ("help", "asldasdffkj")); + ck_assert (hash_insert ("ten", "asldfkj")); + ck_assert_str_eq (hash_find ("help"), "asldasdffkj"); + ck_assert_str_eq (hash_find ("ten"), "asldfkj"); +} +END_TEST + +void +public_tests (Suite *s) +{ + TCase *tc_public = tcase_create ("Public"); + tcase_add_test (tc_public, C_hash_insert); + tcase_add_test (tc_public, C_hash_find_missing); + tcase_add_test (tc_public, C_hash_remove); + tcase_add_test (tc_public, C_hash_replace); + tcase_add_test (tc_public, C_hash_collisions); + suite_add_tcase (s, tc_public); +} diff --git a/p1-sh/tests/scripts/bins.txt b/p1-sh/tests/scripts/bins.txt new file mode 100644 index 0000000..8f4b0ea --- /dev/null +++ b/p1-sh/tests/scripts/bins.txt @@ -0,0 +1,3 @@ +./bin/ls data +./bin/head Makefile +quit diff --git a/p1-sh/tests/scripts/bins_bad.txt b/p1-sh/tests/scripts/bins_bad.txt new file mode 100644 index 0000000..2f8c7d7 --- /dev/null +++ b/p1-sh/tests/scripts/bins_bad.txt @@ -0,0 +1,3 @@ +./bin/ls -l +./bin/head -c 5 Makefile +quit diff --git a/p1-sh/tests/scripts/bins_chmod.txt b/p1-sh/tests/scripts/bins_chmod.txt new file mode 100644 index 0000000..0130ae2 --- /dev/null +++ b/p1-sh/tests/scripts/bins_chmod.txt @@ -0,0 +1,12 @@ +./bin/ls -a data +./bin/ls -s data +./bin/ls -sa data +./bin/chmod rw- rw- --- data/empty.txt +./bin/chmod r-- --- --x data/FIRST.txt +./bin/chmod rw- rw- rw- data/pwd.txt +./bin/chmod r-x r-x r-x data/yat.txt +./bin/chmod rwx --- --- data/.hidden.txt +./bin/chmod r-x --x r-- data/subdir +./bin/ls -ap data +./bin/head -n 1 Makefile +quit diff --git a/p1-sh/tests/scripts/bins_flags.txt b/p1-sh/tests/scripts/bins_flags.txt new file mode 100644 index 0000000..dd52c3c --- /dev/null +++ b/p1-sh/tests/scripts/bins_flags.txt @@ -0,0 +1,12 @@ +/usr/bin/chmod 751 data/subdir +/usr/bin/chmod 640 data/empty.txt +/usr/bin/chmod 640 data/FIRST.txt +/usr/bin/chmod 400 data/.hidden.txt +/usr/bin/chmod 640 data/pwd.txt +/usr/bin/chmod 640 data/yat.txt +./bin/ls -a data +./bin/ls -s data +./bin/ls -sa data +./bin/ls -ap data +./bin/head -n 1 Makefile +quit diff --git a/p1-sh/tests/scripts/cut.txt b/p1-sh/tests/scripts/cut.txt new file mode 100644 index 0000000..85ca653 --- /dev/null +++ b/p1-sh/tests/scripts/cut.txt @@ -0,0 +1,6 @@ +./bin/cut cut_data/data.spaces +./bin/cut -f 2 cut_data/data.spaces +./bin/cut cut_data/data.csv +./bin/cut -d , cut_data/data.csv +./bin/cut -d , -f 2 cut_data/data.csv +quit diff --git a/p1-sh/tests/scripts/cut_bad.txt b/p1-sh/tests/scripts/cut_bad.txt new file mode 100644 index 0000000..aca5514 --- /dev/null +++ b/p1-sh/tests/scripts/cut_bad.txt @@ -0,0 +1,4 @@ +./bin/cut -f -1 cut_data/data.spaces +./bin/cut -f 3 cut_data/data.spaces +./bin/cut -d , -f 5 cut_data/data.csv +quit diff --git a/p1-sh/tests/scripts/dirs.txt b/p1-sh/tests/scripts/dirs.txt new file mode 100644 index 0000000..d95eb4e --- /dev/null +++ b/p1-sh/tests/scripts/dirs.txt @@ -0,0 +1,4 @@ +cd /usr/bin +pwd +which ls +quit diff --git a/p1-sh/tests/scripts/echo.txt b/p1-sh/tests/scripts/echo.txt new file mode 100644 index 0000000..055b554 --- /dev/null +++ b/p1-sh/tests/scripts/echo.txt @@ -0,0 +1,3 @@ +echo goodbye +echo hello\nworld +quit diff --git a/p1-sh/tests/scripts/echo_min.txt b/p1-sh/tests/scripts/echo_min.txt new file mode 100644 index 0000000..11a8cfe --- /dev/null +++ b/p1-sh/tests/scripts/echo_min.txt @@ -0,0 +1,3 @@ +echo hello +echo goodbye +quit diff --git a/p1-sh/tests/scripts/echo_space.txt b/p1-sh/tests/scripts/echo_space.txt new file mode 100644 index 0000000..0c94e67 --- /dev/null +++ b/p1-sh/tests/scripts/echo_space.txt @@ -0,0 +1,3 @@ +echo this has extra spaces +echo this\nhas\nnewlines +quit diff --git a/p1-sh/tests/scripts/env.txt b/p1-sh/tests/scripts/env.txt new file mode 100644 index 0000000..61e5774 --- /dev/null +++ b/p1-sh/tests/scripts/env.txt @@ -0,0 +1,5 @@ +export A=5 +./bin/repeat 2 A +./bin/env B=6 ./bin/repeat 1 A +./bin/env C=10 ./bin/repeat 2 A 3 B 4 C +quit diff --git a/p1-sh/tests/scripts/env_pipe.txt b/p1-sh/tests/scripts/env_pipe.txt new file mode 100644 index 0000000..4a633f9 --- /dev/null +++ b/p1-sh/tests/scripts/env_pipe.txt @@ -0,0 +1,5 @@ +export A=5 +./bin/env ./bin/repeat 1 A +./bin/env B=6 C=7 ./bin/repeat 1 A 2 B 3 C +./bin/env C=7 ./bin/repeat 1 A 2 B 3 C | head -n 4 +quit diff --git a/p1-sh/tests/scripts/export.txt b/p1-sh/tests/scripts/export.txt new file mode 100644 index 0000000..ac49b07 --- /dev/null +++ b/p1-sh/tests/scripts/export.txt @@ -0,0 +1,5 @@ +which export +echo N=${NUM} +export NUM=5 +echo N=${NUM} +quit diff --git a/p1-sh/tests/scripts/pipe.txt b/p1-sh/tests/scripts/pipe.txt new file mode 100644 index 0000000..9d91288 --- /dev/null +++ b/p1-sh/tests/scripts/pipe.txt @@ -0,0 +1,6 @@ +./bin/ls -a data | ./bin/head -n 1 +./bin/ls -a data | ./bin/head -n 2000 +./bin/head -n 1 cut_data/data.spaces | ./bin/cut -f 2 +./bin/head -n 1 cut_data/data.csv | ./bin/cut -d , -f 1 +./bin/head -n 1 cut_data/data.csv | ./bin/cut -d , -f 3 +quit diff --git a/p1-sh/tests/scripts/quit.txt b/p1-sh/tests/scripts/quit.txt new file mode 100644 index 0000000..ff60466 --- /dev/null +++ b/p1-sh/tests/scripts/quit.txt @@ -0,0 +1 @@ +quit diff --git a/p1-sh/tests/scripts/rc.txt b/p1-sh/tests/scripts/rc.txt new file mode 100644 index 0000000..45e31fb --- /dev/null +++ b/p1-sh/tests/scripts/rc.txt @@ -0,0 +1,5 @@ +./bin/ls data +echo $? +./bin/ls asldfkjasldfkj +echo $? +quit diff --git a/p1-sh/tests/scripts/repeat.txt b/p1-sh/tests/scripts/repeat.txt new file mode 100644 index 0000000..85c9b24 --- /dev/null +++ b/p1-sh/tests/scripts/repeat.txt @@ -0,0 +1,5 @@ +export SHELL=/bin/bash +export USER=me +./bin/repeat 1 USER +./bin/repeat 2 SHELL +quit diff --git a/p1-sh/tests/scripts/setenv.txt b/p1-sh/tests/scripts/setenv.txt new file mode 100644 index 0000000..f011f66 --- /dev/null +++ b/p1-sh/tests/scripts/setenv.txt @@ -0,0 +1,4 @@ +./bin/env A=5 ./bin/repeat 2 A +./bin/env A=5 B=6 C=7 ./bin/repeat 1 A 2 B 3 C +./bin/env A=10 ./bin/repeat 1 B +quit diff --git a/p1-sh/tests/scripts/tail.txt b/p1-sh/tests/scripts/tail.txt new file mode 100644 index 0000000..645e89f --- /dev/null +++ b/p1-sh/tests/scripts/tail.txt @@ -0,0 +1,4 @@ +./bin/cat cut_data/data.csv +./bin/cat cut_data/data.csv | ./bin/cut -d , -f 2 +./bin/cat cut_data/data.csv | tail -n 1 +quit diff --git a/p1-sh/tests/scripts/unset.txt b/p1-sh/tests/scripts/unset.txt new file mode 100644 index 0000000..028d04a --- /dev/null +++ b/p1-sh/tests/scripts/unset.txt @@ -0,0 +1,8 @@ +echo N=${NUM} +export NUM=5 +echo N=${NUM} +unset NUM +echo N=${NUM} +export NUM=10 +echo N=${NUM} +quit diff --git a/p1-sh/tests/scripts/which.txt b/p1-sh/tests/scripts/which.txt new file mode 100644 index 0000000..988634d --- /dev/null +++ b/p1-sh/tests/scripts/which.txt @@ -0,0 +1,9 @@ +which cd +which echo +which pwd +which which +which ./bin/ls +which ./bin/head +which ls +which head +quit diff --git a/p1-sh/tests/scripts/which_exec.txt b/p1-sh/tests/scripts/which_exec.txt new file mode 100644 index 0000000..8bbdf60 --- /dev/null +++ b/p1-sh/tests/scripts/which_exec.txt @@ -0,0 +1,3 @@ +which ./bin/rm +which ./bin/cat +quit diff --git a/p1-sh/tests/style.sh b/p1-sh/tests/style.sh new file mode 100755 index 0000000..0243934 --- /dev/null +++ b/p1-sh/tests/style.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +STYLE="gnu" + +IGNORE=() +FAIL=0 + +function comp_file { + + SRC=$1 + SRC_NAME=$2 + + # file paths + FORMAT=ckstyle/${SRC_NAME}.$STYLE + DIFF=ckstyle/${SRC_NAME}.diff + + # run clang-format and compare results + clang-format --style=$STYLE $source > $FORMAT + diff -u $SRC $FORMAT >$DIFF + + PTAG=$(printf '%-30s' "$SRC_NAME") + if [ -s $DIFF ]; then + echo "$PTAG FAIL (see $DIFF for details)" + FAIL=1 + else + echo "$PTAG pass" + rm $DIFF + fi + rm $FORMAT +} + +mkdir -p ckstyle +rm -f ckstyle/* + +for source in $(ls ../src/*.c ../src/*.h) ; do + SKIP=0 + src=$(basename $source) + for ignore in ${IGNORE[*]} ; do + if [ "$src" = "$ignore" ] ; then + SKIP=1 + fi + done + if [ $SKIP = 0 ] ; then + comp_file $source $src + fi +done + +if [ $FAIL != 0 ] ; then + echo "Code that does not adhere to GNU standards will not be accepted." + echo "You must fix these files before submission." +else + echo "Code correctly adheres to required style." +fi diff --git a/p1-sh/tests/testsuite.c b/p1-sh/tests/testsuite.c new file mode 100644 index 0000000..79db20b --- /dev/null +++ b/p1-sh/tests/testsuite.c @@ -0,0 +1,32 @@ +#include +#include +#include +#include +#include +#include + +#include + +extern void public_tests (Suite *s); + +Suite * test_suite (void) +{ + Suite *s = suite_create ("Default"); + public_tests (s); + return s; +} + +void run_testsuite () +{ + Suite *s = test_suite (); + SRunner *sr = srunner_create (s); + srunner_run_all (sr, CK_NORMAL); + srunner_free (sr); +} + +int main (void) +{ + srand((unsigned)time(NULL)); + run_testsuite (); + return EXIT_SUCCESS; +} diff --git a/p1-sh/utils/Makefile b/p1-sh/utils/Makefile new file mode 100644 index 0000000..3b66c73 --- /dev/null +++ b/p1-sh/utils/Makefile @@ -0,0 +1,41 @@ +# +# Simple Makefile +# Mike Lam, James Madison University, August 2016 +# +# This makefile builds a simple application that contains a main module +# (specified by the EXE variable) and a predefined list of additional modules +# (specified by the MODS variable). If there are any external library +# dependencies (e.g., the math library, "-lm"), list them in the LIBS variable. +# If there are any precompiled object files, list them in the OBJS variable. +# +# By default, this makefile will build the project with debugging symbols and +# without optimization. To change this, edit or remove the "-g" and "-O0" +# options in CFLAGS and LDFLAGS accordingly. +# +# By default, this makefile build the application using the GNU C compiler, +# adhering to the C99 standard with all warnings enabled. + +EXES=ls chmod head cut repeat env cat + +# compiler/linker settings + +CC=gcc +CFLAGS=-g -O0 -Wall -Werror -std=c99 -pedantic -D_POSIX_C_SOURCE=200809L +LDFLAGS=-O0 + +# build targets + +all: ../bin $(EXES) + +.c: + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< + mv $@ ../bin + +../bin: + mkdir ../bin + +clean: + rm -rf ../bin + +.PHONY: all clean + diff --git a/p1-sh/utils/cat.c b/p1-sh/utils/cat.c new file mode 100644 index 0000000..94eeee7 --- /dev/null +++ b/p1-sh/utils/cat.c @@ -0,0 +1,41 @@ +#include +#include + +static void usage (void) __attribute__ ((unused)); + +int +main (int argc, char *argv[]) +{ + if (argc < 2) + { + int c; + while ((c = getchar()) != EOF) + { + putchar(c); + } + return EXIT_SUCCESS; + } + + FILE *fp = fopen(argv[1], "r"); + if (fp == NULL) + { + return EXIT_FAILURE; + } + + int c; + while ((c = fgetc(fp)) != EOF) + { + putchar(c); + } + + fclose(fp); + + return EXIT_SUCCESS; +} + +static void +usage (void) +{ + printf ("cat, print the contents of a file\n"); + printf ("usage: cat FILE\n"); +} diff --git a/p1-sh/utils/chmod.c b/p1-sh/utils/chmod.c new file mode 100644 index 0000000..c7b23e8 --- /dev/null +++ b/p1-sh/utils/chmod.c @@ -0,0 +1,70 @@ +#include +#include +#include +#include +#include + +static void usage (void); + +mode_t +calculate_permission (char *arg_str) +{ + const char perms[4] = "rwx"; + + if (strlen (arg_str) != 3) + { + usage (); + exit (EXIT_FAILURE); + } + + mode_t permission = 0; + + for (size_t j = 0; j < 3; j++) + { + int perm = 1 << (2 - j); + if (arg_str[j] == perms[j]) + { + permission |= perm; + } + else if (arg_str[j] != '-') + { + usage (); + exit (EXIT_FAILURE); + } + } + + return permission; +} + +int +main (int argc, char **argv) +{ + if (argc != 5) + { + usage (); + return EXIT_FAILURE; + } + + mode_t user = calculate_permission (argv[1]); + mode_t group = calculate_permission (argv[2]); + mode_t other = calculate_permission (argv[3]); + + mode_t permissions = (user << 6) | (group << 3) | other; + + if (chmod (argv[4], permissions) == -1) + { + perror ("Failed to chmod"); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +static void +usage (void) +{ + printf ("chmod, changes permissions on a file\n"); + printf ("usage: chmod USR GRP OTH FILE\n\n"); + printf ("USR, GRP, and OTH must be of the rwx format,\n"); + printf ("with - indicating a permission is not allwed.\n"); +} diff --git a/p1-sh/utils/cut.c b/p1-sh/utils/cut.c new file mode 100644 index 0000000..42c2869 --- /dev/null +++ b/p1-sh/utils/cut.c @@ -0,0 +1,75 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include + +// You may assume that lines are no longer than 1024 bytes +#define LINELEN 1024 + +static void usage (void); + +int +main (int argc, char *argv[]) +{ + int opt; + char *delimitter = " "; + long field = 1; + + while ((opt = getopt (argc, argv, "d:f:")) != -1) + { + switch (opt) + { + case 'd': + delimitter = optarg; + break; + case 'f': + field = strtol (optarg, NULL, 10); + break; + default: + printf ("./bin/cut: invalid option -- \'%c\'\n", optopt); + exit (EXIT_FAILURE); + } + } + + if (field < 1) + { + usage (); + exit (EXIT_FAILURE); + } + + FILE *file = optind < argc ? fopen (argv[optind], "r") : stdin; + if (file == NULL) + { + perror ("Failed to open input file"); + exit (EXIT_FAILURE); + } + + char buffer[LINELEN]; + + while (fgets (buffer, sizeof (buffer), file) != NULL) + { + buffer[strcspn (buffer, "\n")] = '\0'; + + char *token = strtok (buffer, delimitter); + long i = 1; + while (i++ < field) token = strtok (NULL, delimitter); + + printf("%s\n", token != NULL ? token : ""); + } + + return EXIT_SUCCESS; +} + +static void +usage (void) +{ + printf ("cut, splits each line based on a delimiter\n"); + printf ("usage: cut [FLAG] FILE\n"); + printf ("FLAG can be:\n"); + printf ( + " -d C split each line based on the character C (default ' ')\n"); + printf (" -f N print the Nth field (1 is first, default 1)\n"); + printf ("If no FILE specified, read from STDIN\n"); +} diff --git a/p1-sh/utils/env.c b/p1-sh/utils/env.c new file mode 100644 index 0000000..ca6f07e --- /dev/null +++ b/p1-sh/utils/env.c @@ -0,0 +1,65 @@ +#include +#include +#include +#include +#include + +static void usage (void); + +bool +is_util (char *str) +{ + char *utils[] = { "./bin/cat", "./bin/chmod", "./bin/cut", "./bin/env", + "./bin/head", "./bin/ls", "./bin/repeat" }; + + for (size_t i = 0; i < 7; i++) + { + if (strcmp (str, utils[i]) == 0) + { + return true; + } + } + + return false; +} + +int +main (int argc, char *argv[], char *envp[]) +{ + size_t envp_count = 0; + while (envp[envp_count] != NULL) envp_count++; + + size_t env_args = 0; + char* util = NULL; + + for (size_t i = 1; i < argc; i++) + { + if (is_util (argv[i])) { + util = argv[i]; + break; + } + + env_args++; + } + + if (util == NULL) { + usage(); + exit(EXIT_FAILURE); + } + + char **combined_envp = calloc (envp_count + env_args + 1, sizeof (char *)); + memcpy (combined_envp, envp, envp_count * sizeof (char *)); + memcpy (combined_envp + envp_count, &argv[1], env_args * sizeof (char *)); + combined_envp[envp_count + env_args] = NULL; + + execve(util, &argv[env_args + 1], combined_envp); + + return EXIT_SUCCESS; +} + +static void +usage (void) +{ + printf ("env, set environment variables and execute program\n"); + printf ("usage: env [name=value ...] PROG ARGS\n"); +} diff --git a/p1-sh/utils/head.c b/p1-sh/utils/head.c new file mode 100644 index 0000000..982c6ab --- /dev/null +++ b/p1-sh/utils/head.c @@ -0,0 +1,58 @@ +#define _GNU_SOURCE +#include +#include +#include +#include + +// You may assume that lines are no longer than 1024 bytes +#define LINELEN 1024 + +static void usage (void) __attribute__ ((unused)); + +int +main (int argc, char *argv[]) +{ + int opt; + int n = 5; + + while ((opt = getopt (argc, argv, "n:")) != -1) + { + switch (opt) + { + case 'n': + n = strtol (optarg, NULL, 10); + break; + default: + printf ("./bin/head: invalid option -- \'%c\'\n", optopt); + exit (EXIT_FAILURE); + } + } + + FILE *file = optind < argc ? fopen (argv[optind], "r") : stdin; + if (file == NULL) + { + perror ("Failed to open input file"); + exit (EXIT_FAILURE); + } + + char buffer[LINELEN]; + int count = 0; + + while (count < n && fgets (buffer, sizeof (buffer), file) != NULL) + { + printf ("%s", buffer); + count++; + } + + return EXIT_SUCCESS; +} + +static void +usage (void) +{ + printf ("head, prints the first few lines of a file\n"); + printf ("usage: head [FLAG] FILE\n"); + printf ("FLAG can be:\n"); + printf (" -n N show the first N lines (default 5)\n"); + printf ("If no FILE specified, read from STDIN\n"); +} diff --git a/p1-sh/utils/ls.c b/p1-sh/utils/ls.c new file mode 100644 index 0000000..c354d43 --- /dev/null +++ b/p1-sh/utils/ls.c @@ -0,0 +1,148 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_PATH_LEN 4096 + +static void usage (void) __attribute__ ((unused)); + +// WARNING WARNING WARNING: +// When using opendir and readdir to read directory listings, the files +// are returned in the order they were created. There is randomness to that +// creation that can cause tests to fail. You MUST sort the file names as +// expected in the test case files. To avoid making your task harder than +// it needs to be, we strongly suggest you use scandir instead. + +int +filename_cmp (const struct dirent **a, const struct dirent **b) +{ + const char *name1 = (*a)->d_name; + const char *name2 = (*b)->d_name; + + while (*name1 == '.') + name1++; + while (*name2 == '.') + name2++; + + return strcasecmp (name1, name2); +} + +void +print_permissions (mode_t mode) +{ + char perms[11]; + + perms[0] = S_ISDIR (mode) ? 'd' : '-'; + perms[1] = (mode & S_IRUSR) ? 'r' : '-'; + perms[2] = (mode & S_IWUSR) ? 'w' : '-'; + perms[3] = (mode & S_IXUSR) ? 'x' : '-'; + perms[4] = (mode & S_IRGRP) ? 'r' : '-'; + perms[5] = (mode & S_IWGRP) ? 'w' : '-'; + perms[6] = (mode & S_IXGRP) ? 'x' : '-'; + perms[7] = (mode & S_IROTH) ? 'r' : '-'; + perms[8] = (mode & S_IWOTH) ? 'w' : '-'; + perms[9] = (mode & S_IXOTH) ? 'x' : '-'; + perms[10] = '\0'; + + printf ("%s", perms); +} + +int +main (int argc, char *argv[]) +{ + int opt; + int a_flag = false, p_flag = false, s_flag = false; + + while ((opt = getopt (argc, argv, "aps")) != -1) + { + switch (opt) + { + case 'a': + a_flag = true; + break; + case 'p': + p_flag = true; + break; + case 's': + s_flag = true; + break; + default: + printf ("./bin/ls: invalid option -- \'%c\'\n", optopt); + exit (EXIT_FAILURE); + } + } + + char *dirname = optind < argc ? argv[optind] : "."; + struct stat dirstat; + + if (stat(dirname, &dirstat) != 0) { + return EXIT_FAILURE; + } + + struct dirent **namelist; + int n = scandir (dirname, &namelist, NULL, filename_cmp); + + for (int i = 0; i < n; i++) + { + char path[MAX_PATH_LEN]; + snprintf (path, sizeof (path), "%s/%s", dirname, namelist[i]->d_name); + + struct stat st; + lstat (path, &st); + + bool dot_dir = strcmp (namelist[i]->d_name, ".") == 0 + || strcmp (namelist[i]->d_name, "..") == 0; + + bool hidden = namelist[i]->d_name[0] == '.'; + + if (dot_dir || (!a_flag && hidden) || (s_flag && S_ISDIR (st.st_mode))) + { + free (namelist[i]); + continue; + } + + if (s_flag) + { + printf ("%ld ", st.st_size); + } + + if (p_flag) + { + print_permissions (st.st_mode); + printf (" "); + } + + printf ("%s\n", namelist[i]->d_name); + + free (namelist[i]); + } + free (namelist); + + return EXIT_SUCCESS; +} + +static void +usage (void) +{ + printf ("ls, list directory contents\n"); + printf ("usage: ls [FLAG ...] [DIR]\n"); + printf ("FLAG is one or more of:\n"); + printf (" -a list all files (even hidden ones)\n"); + printf (" -p list permission bitmask\n"); + printf (" -s list file sizes\n"); + printf ("If no DIR specified, list current directory contents.\n\n"); + printf ("Files must be sorted alphabetically, case insensitive.\n"); + printf ("Leading dots should be ignored when sorting.\n\n"); + printf ("With the -s flag, do not show entries for subdirectories.\n"); + printf ("Permission bitmasks are 10-character strings such as:\n"); + printf (" -rwxr-x---\n\n"); + printf ( + "The first character is d for directories and - for regular files.\n\n"); + printf ("Do not show the \".\" or \"..\" directory entries.\n"); +} diff --git a/p1-sh/utils/repeat.c b/p1-sh/utils/repeat.c new file mode 100644 index 0000000..5c026ca --- /dev/null +++ b/p1-sh/utils/repeat.c @@ -0,0 +1,76 @@ +#include +#include +#include + +static void usage (void) __attribute__ ((unused)); + +int +main (int argc, char *argv[], char *envp[]) +{ + if (argc < 3) + { + usage(); + return EXIT_FAILURE; + } + + int i = 1; + while (i < argc) + { + char *endptr; + long count = strtol (argv[i], &endptr, 10); + if (*endptr != '\0' || count <= 0) + { + usage(); + return EXIT_FAILURE; + } + i++; + + if (i >= argc) + { + usage(); + return EXIT_FAILURE; + } + + char *var = argv[i]; + char *val = NULL; + int x = 0; + while (envp[x] != NULL) + { + if (strstr(envp[x], var) == envp[x]) + { + val = envp[x]; + break; + } + x++; + } + + if (val == NULL) + { + val = ""; + for (long j = 0; j < count; j++) + { + printf("%s=%s\n", var, val); + } + val = ""; + } + else + { + for (long j = 0; j < count; j++) + { + printf("%s\n", val); + } + } + i++; + } + return EXIT_SUCCESS; +} + +static void +usage (void) +{ + printf ("repeat, a tool for printing repeated environment variables\n"); + printf ("usage: repeat N VAR ...\n"); + printf ("each N must be a positive integer\n"); + printf ("N VAR can be repeated, but each repetition must have both\n"); +} + diff --git a/p2-dns/.gitignore b/p2-dns/.gitignore new file mode 100644 index 0000000..f6c4f88 --- /dev/null +++ b/p2-dns/.gitignore @@ -0,0 +1,22 @@ +*.swp +*.swo + +# Ignore all compiled files regardless of location +build/*.o +tests/*.o + +# Ignore executables for this project +digduke +testsuite + +# Ignore test outputs +tests/ckstyle +tests/itests.txt +tests/outputs +tests/style.txt +tests/valgrind +tests/utests.txt + +# Ignore all vscode stuff and NFS files +**/.nfs* +**/.vscode diff --git a/p2-dns/Makefile b/p2-dns/Makefile new file mode 100644 index 0000000..a596ece --- /dev/null +++ b/p2-dns/Makefile @@ -0,0 +1,59 @@ +# +# Simple Makefile +# Mike Lam, James Madison University, August 2016 +# +# This makefile builds a simple application that contains a main module +# (specified by the EXE variable) and a predefined list of additional modules +# (specified by the MODS variable). If there are any external library +# dependencies (e.g., the math library, "-lm"), list them in the LIBS variable. +# If there are any precompiled object files, list them in the OBJS variable. +# +# By default, this makefile will build the project with debugging symbols and +# without optimization. To change this, edit or remove the "-g" and "-O0" +# options in CFLAGS and LDFLAGS accordingly. +# +# By default, this makefile build the application using the GNU C compiler, +# adhering to the C99 standard with all warnings enabled. + + +# application-specific settings and run target + +EXE=digduke +MODS=client.o dns.o utils.o +OBJS=port_utils.o +LIBS=-lm + +default: build $(EXE) + +build: + mkdir build + +test: build $(EXE) + make -C tests test + +style: $(EXE) + make -C tests style + +# compiler/linker settings + +CC=gcc +CFLAGS=-g -O0 -Wall -Werror --std=gnu99 -pedantic +LDFLAGS=-g -O0 -pthread + + +# build targets + +BUILD=$(addprefix build/, $(MODS)) + +$(EXE): $(BUILD) $(OBJS) + $(CC) $(LDFLAGS) -o $(EXE) $^ $(LIBS) + +build/%.o: src/%.c + $(CC) -c $(CFLAGS) -o $@ $< + +clean: + rm -rf $(EXE) build + make -C tests clean + +.PHONY: default clean + diff --git a/p2-dns/PHASE-1.txt b/p2-dns/PHASE-1.txt new file mode 100644 index 0000000..56269b0 --- /dev/null +++ b/p2-dns/PHASE-1.txt @@ -0,0 +1,39 @@ +Answer the following questions to describe your code submission. Please keep +all lines to a maximum of 80 characters wide. + +1 - Your implementation must send an array of bytes to the server. Explain the + meaning of these bytes and how the server interprets them. + + The bytes we send form a complete DNS request, which is hardcoded. The first + 12 bytes make up the DNS header, which includes a transaction ID, flags, and + counts of questions, answers, authority, and additional records. After the + header, we include the question section, which specifies the domain name + (which is a 0 length label which mean root server), the record type (A = IPv4), + and the class (IN = Internet). The server interprets these bytes as a query + asking for the IPv4 address of the root domain, and responds with the A + record containing the IP address of one of the root servers a.root-servers.net. + or b.root-servers.net. + + +2 - Show (in pseudocode) how you set up your socket to send your message, + including a description of how you specified the address and port number. + What would you have to change to send an HTTP message (which uses TCP) to + a server? + + const char *hostname = "127.0.0.1"; + const char *port = get_port (); + + struct addrinfo *server_list + = get_server_list (hostname, port, false, false); + + int sockfd = socket (server_list->ai_family, server_list->ai_socktype, 0); + + We do this to specify the hostname and port. + To send an HTTP message to a server we would hardcode the port to be 80 + and create the socket using SOCK_STREAM. We would have to call connect() to + establish the TCP handshake and then use send() to send the query and recv() + to get the response. + + + + diff --git a/p2-dns/PHASE-2.txt b/p2-dns/PHASE-2.txt new file mode 100644 index 0000000..e14222c --- /dev/null +++ b/p2-dns/PHASE-2.txt @@ -0,0 +1,26 @@ +Answer the following questions to describe your code submission. Please keep +all lines to a maximum of 80 characters wide. + +1 - Explain the algorithm that your code uses to convert a domain name from its + DNS (binary) format to the standard dotted notation for humans to read. + (I.e., how do you convert the bytes "01 61 01 62 00" into "a.b"?) + + The algorithm we use reads from a pointer to the front of the domain name. The first + byte gives the length of the next label. The code copies that many bytes + as text, appends a period, and moves forward by that label length. It + repeats until it encounters a 0x00 byte using a while loop, which is the null byte and the end of the + domain name. + +2 - Consider the following data: + + 12 34 81 80 00 01 00 01 00 00 00 00 03 6e 6f 74 02 68 69 03 6d 6f + 6d 00 00 01 00 01 c0 10 00 01 00 01 00 00 03 84 04 01 02 03 04 + + What is the domain name in the response? What is the IP address? + + The domain name is not.hi.mom + The IP address is 1.2.3.4 + + + + diff --git a/p2-dns/PHASE-3.txt b/p2-dns/PHASE-3.txt new file mode 100644 index 0000000..5ba3dd0 --- /dev/null +++ b/p2-dns/PHASE-3.txt @@ -0,0 +1,24 @@ +Answer the following questions to describe your code submission. Please keep +all lines to a maximum of 80 characters wide. + +1 - If a record is sought by there are no answers, what status code(s) are + returned and what does the RFC indicate these mean? + + The status code would be NOERROR and this means the question went through and was + processed fully but there was no corresponding data to return. The status code could also be NXDOMAIN + which indicates a domain name that does not exist and is an error. + + +2 - Consider the following data: + + 12 34 81 80 00 01 00 02 00 00 00 00 03 6e 6f 74 02 68 69 03 6d 6f + 6d 00 00 01 00 01 05 68 6f 77 64 79 c0 13 00 01 00 01 00 00 12 34 + 04 0a 00 00 01 c0 1c 00 1c 00 01 00 00 12 34 10 10 00 00 00 00 00 + 00 01 + + What are the domains name in the response? What are the IP addresses? + + howdy.mom. 10.0.0.1 + howdy.mom. 1010::1 + + diff --git a/p2-dns/PHASE-4.txt b/p2-dns/PHASE-4.txt new file mode 100644 index 0000000..372adf4 --- /dev/null +++ b/p2-dns/PHASE-4.txt @@ -0,0 +1,30 @@ +Answer the following questions to describe your code submission. Please keep +all lines to a maximum of 80 characters wide. + +1 - Explain how you convert the IP address 1.2.3.4 into the correct format for + a PTR request. + + We reversed the IP address to 4.3.2.1 by tokenizing at the periods and + then putting them back in correct order. Then concatenated the string portion on to the end of that + in-addr.arpa. , to give 4.3.2.1.in-addr.arpa. + + +2 - Explain the fields of an SOA record. Which of these would be interpreted as + an email address and how so? + + There are 7 different fields: + MNAME (The primary master name), + RNAME (The email address), + SERIAL (The version number of the zone data), + REFRESH (How often secondary servers check for updates), + RETRY (How long to wait before retrying after a failed refresh), + EXPIRE (How long secondary servers keep data if updates fail), + MINIMUM (Default TTL for negative responses). + + The RNAME would be interpreted as the email address and you would replace + the first . with an @ to turn into the normal email address structure. + + + + + diff --git a/p2-dns/port_utils.o b/p2-dns/port_utils.o new file mode 100644 index 0000000000000000000000000000000000000000..a576d09c1e791a4b6b617d72b5418913b7fbb5c4 GIT binary patch literal 4560 zcmeH~U1%It6vxjtO&hgsVr?N%Df?hGTDzv(P@;kcOYDGBikR95A(`E|yED7<<(=7P zHxF7rA`LMRMWF~nUzGY}eX{nUMvJCJkYYiK`rw0wiXVstDI$&M%$}PjhvdmO@xa}C z|G#_Axp(H?nYsJI@RN_P$>qRU4jzG<%20rwO*h*^S#t=wVG~^I-u~B!HFd>$@fTPF z;8@dZcl2SuH8p&F_nb8|nZL2HV9ji$kdGhEI_9mZZ?0MIUL|BrP4<$*{A3SSM1FdM z@N}lF>DNBb;-@pOGhJE4SJu>g7CbrE7(25L*;y=L&2(lJcXEnK=ZWsh)Z`}2-u+2d zO?|rP`Po^DUX=5vDM5Q)?PNdiuwKq)l3Se7EA1Ig&UH^7T3A@fLagbL>yEW^OLpaH zXO`i@IZF1w{YUS>4z~feV+Tpig_2fb==lydqVdqHDJV4@M>!$Zx~_^r70R~Oksqf} zc9qgCaO}`ieyxg)$VtIby3`6aI8meO+qMJB4f0V zj8q5EYjL^JRB}uFjmNFXH!m4Cn=`UIu+a5Oqib7<-?a+gxe6axg=ZThj0*kuz@($J zOQ=EnEQBQ0VL6C#cLs?{ac9KZ3)2dWj^blfu8)@8`e?=Wd`t~}^n-@$mX*KpT~9`z zFfv-p^;isV5=0|Tt7P~LK{U!kt6=zB1kuR<7A%H;L=cVqsn75)2%^b6m)bba+T3U3 z{{ZuT;XlaQ)W-i&=0n2YVr^>Ue}MTx;eUwrVZldPj|uLwHnqwB9P?QCPq1zZ{u1k> zf={zPCit7IX9a(U^>M-9V|_yK4_KcR-0Z(og3qx&E%*i2rr+F$ubH10{@<~_DEN=8 zFAM%F>mLOFll4`>Z?OJd@J^Z-+Vys|xIZ^CH?J@=fAgGlalhf)S@#Hj59?mR`&s7& ze~@*b;E%C3aU|)vdxH6(@PC^1kl@doLKPvUUZZVjT8a9LVVX%qY`PNLoQ^m#|KCyLL^B&UA%THB%^LO~=M#TcMOBsH3(hM|gAz(IJdd zRltMCuI$ssx*9eLv5xSZm9{vCaz`r~&w}m2|NU_%qm`rIPbM%<*~S$hEnD21nSSoHD-2@q@;ZYZ+fdzS8 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dns.h" +#include "port_utils.h" +#include "utils.h" + +#define BUFFER_MAX_SIZE 4096 + +const char *HOSTNAME = "127.0.0.1"; + +ssize_t +query_dns (unsigned char *query, size_t size, unsigned char **response) +{ + + // hardcoded for dukens server + const char *port_str = get_port (); // same port dukens listens on + + int port = strtol (port_str, NULL, 10); + if (port <= 0 || port > 65535) + { + fprintf (stderr, "Invalid port: %s\n", port_str); + exit (EXIT_FAILURE); + } + + struct sockaddr_in addr = { 0 }; + addr.sin_family = AF_INET; + addr.sin_port = htons (port); + if (inet_aton (HOSTNAME, &addr.sin_addr) != 1) + { + fprintf (stderr, "Invalid IPv4 address: %s\n", HOSTNAME); + exit (EXIT_FAILURE); + } + + // create socket + int sockfd = socket (AF_INET, SOCK_DGRAM, 0); + if (sockfd < 0) + { + perror ("socket"); + exit (EXIT_FAILURE); + } + + ssize_t sent = sendto (sockfd, query, size, 0, (struct sockaddr *)&addr, + sizeof (addr)); + if (sent < 0) + { + perror ("sendto"); + close (sockfd); + exit (EXIT_FAILURE); + } + + // receive and print response + *response = calloc (BUFFER_MAX_SIZE, sizeof (unsigned char)); + ssize_t received + = recvfrom (sockfd, *response, BUFFER_MAX_SIZE, 0, NULL, NULL); + if (received < 0) + { + perror ("recvfrom"); + close (sockfd); + exit (EXIT_FAILURE); + } + + // cleanup + close (sockfd); + + return received; +} + +void +phase_one () +{ + // hard coded DNS request + unsigned char query[17] + = { 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01 }; + + unsigned char *res_bytes; + ssize_t received = query_dns (query, sizeof (query), &res_bytes); + + printf ("Sending %zu bytes to %s:\n", sizeof (query), HOSTNAME); + print_byte_block (query, sizeof (query)); + + printf ("\nReceived %zd bytes from %s:\n", received, HOSTNAME); + print_byte_block (res_bytes, received); + printf ("\n"); + + dns_response_t res = { 0 }; + parse_dns_response (res_bytes, sizeof (query), &res); + dns_answer_t answer = res.answers[0]; + + printf ("Domain name: %s\n", answer.domain); + printf ("Record type: %s %s\n", qclass_to_str (answer.qclass), + record_to_str (answer.qtype)); + printf ("TTL: %d\n", answer.ttl); + printf ("IPv4 address: %s\n", answer.rdata); + + free (res_bytes); +} + +void +not_phase_one (int argc, char *argv[]) +{ + // building query + uint16_t xid = strtol (argv[2], NULL, 10); + + uint16_t record = get_record_from_str (argv[4]); + + char domain_name[BUFFER_LENGTH]; + + if (record == PTR) + { + ptr_ip (domain_name, BUFFER_LENGTH, argv[3]); + } + else + { + strncpy (domain_name, argv[3], sizeof (domain_name) - 1); + domain_name[sizeof (domain_name) - 1] = '\0'; + } + + unsigned char query_bytes[512]; + size_t offset = 0; + + // DNS Header + query_bytes[offset++] = (xid >> 8) & 0xFF; + query_bytes[offset++] = xid & 0xFF; + query_bytes[offset++] = 0x01; // flags + query_bytes[offset++] = 0x00; + query_bytes[offset++] = 0x00; // QDCOUNT high + query_bytes[offset++] = 0x01; // QDCOUNT low + // rest zeros (ANCOUNT, NSCOUNT, ARCOUNT) + for (int i = 0; i < 6; i++) + query_bytes[offset++] = 0x00; + + // Question section: domain name + char *token = strtok (domain_name, "."); + while (token) + { + size_t len = strlen (token); + query_bytes[offset++] = (unsigned char)len; + memcpy (&query_bytes[offset], token, len); + offset += len; + token = strtok (NULL, "."); + } + query_bytes[offset++] = 0x00; // end of domain + + query_bytes[offset++] = record >> 8; + query_bytes[offset++] = record & 0xff; + query_bytes[offset++] = 0x00; // QCLASS = IN + query_bytes[offset++] = 0x01; + + unsigned char *res_bytes; + query_dns (query_bytes, offset, &res_bytes); + + dns_query_t query = { 0 }; + parse_dns_query (&query_bytes[0], &query); + dns_response_t response = { 0 }; + parse_dns_response (&res_bytes[0], offset, &response); + + // print header + print_dns_header (&response.query.header); + + // print question section + print_question_section (&query); + + uint8_t status = (response.query.header.flags >> 0) & 0xF; + + if (status == 0 && response.query.header.ancount > 0) + { + printf ("\n;; ANSWER SECTION:\n"); + + for (size_t i = 0; i < response.query.header.ancount; i++) + { + dns_answer_t answer = response.answers[i]; + print_answer_section (&answer); + } + } + + if (response.query.header.nscount > 0) + { + printf ("\n;; AUTHORITY SECTION:\n"); + + for (size_t i = 0; i < response.query.header.nscount; i++) + { + dns_answer_t authority = response.authorities[i]; + print_answer_section (&authority); + } + } + + if (response.query.header.arcount > 0) + { + printf ("\n;; ADDITIONAL SECTION:\n"); + + for (size_t i = 0; i < response.query.header.arcount; i++) + { + dns_answer_t additional = response.additionals[i]; + print_answer_section (&additional); + } + } + + free (res_bytes); +} + +static bool +get_args (int argc, char **argv, bool *opendns, bool *xflag) +{ + int ch = 0; + while ((ch = getopt (argc, argv, "ox")) != -1) + { + switch (ch) + { + case 'o': + *opendns = true; + break; + case 'x': + *xflag = true; + break; + default: + return false; + } + } + + return true; +} + +int +main (int argc, char *argv[]) +{ + bool opendns = false; + bool xflag = false; + + if (!get_args (argc, argv, &opendns, &xflag)) + { + fprintf (stderr, "ERROR: Show usage\n"); + return EXIT_FAILURE; + } + + if (!xflag) + { + phase_one (); + } + else + { + not_phase_one (argc, argv); + } + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/p2-dns/src/dns.c b/p2-dns/src/dns.c new file mode 100644 index 0000000..cbf4deb --- /dev/null +++ b/p2-dns/src/dns.c @@ -0,0 +1,393 @@ +#include +#include +#include +#include +#include +#include + +#include "dns.h" + +#define BUFFER_MAX_SIZE 4096 + +// SUGGESTION: Use this file for DNS-related functionality +const char * +opcode_to_str (uint8_t opcode) +{ + switch (opcode) + { + case 0: + return "QUERY"; + case 1: + return "IQUERY"; + case 2: + return "STATUS"; + default: + return "UNKNOWN"; + } +} +const char * +status_to_str (uint8_t status) +{ + switch (status) + { + case 0: + return "NOERROR"; + case 1: + return "FORMERR"; + case 2: + return "SERVFAIL"; + case 3: + return "NXDOMAIN"; + case 4: + return "NOTIMP"; + case 5: + return "REFUSED"; + default: + return "UNKNOWN"; + } +} + +const char * +record_to_str (uint16_t record) +{ + switch (record) + { + case 1: + return "A"; + case 2: + return "NS"; + case 5: + return "CNAME"; + case 6: + return "SOA"; + case 12: + return "PTR"; + case 15: + return "MX"; + case 28: + return "AAAA"; + default: + return "UNKNOWN"; + } +} + +const char * +qclass_to_str (uint16_t qclass_bytes) +{ + switch (qclass_bytes) + { + case 1: + return "IN"; + default: + return "UNKNOWN"; + } +} + +uint16_t +get_record_from_str (const char *record_str) +{ + if (strcasecmp (record_str, "A") == 0) + return A; + else if (strcasecmp (record_str, "AAAA") == 0) + return AAAA; + else if (strcasecmp (record_str, "CNAME") == 0) + return CNAME; + else if (strcasecmp (record_str, "MX") == 0) + return MX; + else if (strcasecmp (record_str, "NS") == 0) + return NS; + else if (strcasecmp (record_str, "SOA") == 0) + return SOA; + else if (strcasecmp (record_str, "PTR") == 0) + return PTR; + else + { + fprintf (stderr, "Unsupported record type: %s\n", record_str); + return 0; + } +} + +uint32_t +merge_32 (unsigned char *bytes) +{ + return ((uint32_t)bytes[0] << 24) | ((uint32_t)bytes[1] << 16) + | ((uint32_t)bytes[2] << 8) | ((uint32_t)bytes[3]); +} + +void +recursive_domain_parse (unsigned char *bytes, ssize_t *pointer, char **domain) +{ + if (bytes[*pointer] == 0x00) + { + (*pointer)++; + return; + } + + bool is_compressed = (bytes[*pointer] & 0xc0) == 0xc0; + + if (!is_compressed) + { + size_t len = (size_t)bytes[(*pointer)++]; + strncat (*domain, (char *)&bytes[*pointer], len); + strncat (*domain, ".", 2); + *pointer += len; + + recursive_domain_parse (bytes, pointer, domain); + } + else + { + ssize_t offset = (bytes[*pointer] << 8 | bytes[*pointer + 1]) & 0x3fff; + recursive_domain_parse (bytes, &offset, domain); + *pointer += 2; + } +} + +void +parse_domain (unsigned char *bytes, ssize_t *pointer, char **domain) +{ + recursive_domain_parse (bytes, pointer, domain); + + if (strlen (*domain) == 0) + { + strncat (*domain, ".", 2); + } +} + +char * +parse_ip_addr (unsigned char *bytes, uint16_t rdlen) +{ + char ip_addr[INET6_ADDRSTRLEN]; + uint8_t ip_ver = rdlen == 4 ? AF_INET : AF_INET6; + + if (inet_ntop (ip_ver, bytes, ip_addr, sizeof (ip_addr)) == NULL) + { + perror ("inet_ntop"); + exit (EXIT_FAILURE); + } + + return strdup (ip_addr); +} + +void +print_dns_header (dns_header_t *header) +{ + + // switch endianness + uint16_t xid = header->xid; + uint16_t flags = header->flags; + uint16_t qdcount = header->qdcount; + uint16_t ancount = header->ancount; + uint16_t nscount = header->nscount; + uint16_t arcount = header->arcount; + + // find flags + uint8_t qr = (flags >> 15) & 0x1; + uint8_t opcode = (flags >> 11) & 0xF; + uint8_t aa = (flags >> 10) & 0x1; + uint8_t tc = (flags >> 9) & 0x1; + uint8_t rd = (flags >> 8) & 0x1; + uint8_t ra = (flags >> 7) & 0x1; + uint8_t status = (flags >> 0) & 0xF; + + // get string equivalent for opcode and status + const char *opcode_str = opcode_to_str (opcode); + const char *status_str = status_to_str (status); + + // print first line of header + printf (";; ->>HEADER<<- opcode: %s, status: %s, id: %u\n", opcode_str, + status_str, xid); + + // print set flags + printf (";; flags:"); + if (qr) + printf (" qr"); + if (aa) + printf (" aa"); + if (tc) + printf (" tc"); + if (rd) + printf (" rd"); + if (ra) + printf (" ra"); + + // print the last line + printf ("; QUERY: %u, ANSWER: %u, AUTHORITY: %u, ADDITIONAL: %u\n\n", + qdcount, ancount, nscount, arcount); +} + +// print question section +void +print_question_section (dns_query_t *query) +{ + // print first line + printf (";; QUESTION SECTION:\n"); + + char *domain = calloc (BUFFER_MAX_SIZE, sizeof (char)); + ssize_t pointer = 0; + parse_domain (query->domain, &pointer, &domain); + + printf (";%s %s %s\n", domain, qclass_to_str (query->qclass), + record_to_str (query->qtype)); + + free (domain); +} + +// print the answer section +void +print_answer_section (dns_answer_t *answer) +{ + char buf[BUFFER_LENGTH] = { 0 }; + snprintf (buf, sizeof (buf), "%s %s %u.", qclass_to_str (answer->qclass), + record_to_str (answer->qtype), answer->ttl); + + printf ("%-30s %-16s %s\n", answer->domain, buf, answer->rdata); +} + +ssize_t +parse_dns_header (unsigned char *bytes, dns_header_t *header) +{ + memcpy (header, bytes, sizeof (dns_header_t)); + + header->xid = ntohs (header->xid); + header->flags = ntohs (header->flags); + header->qdcount = ntohs (header->qdcount); + header->ancount = ntohs (header->ancount); + header->nscount = ntohs (header->nscount); + header->arcount = ntohs (header->arcount); + + return sizeof (dns_header_t); +} + +ssize_t +parse_dns_query (unsigned char *bytes, dns_query_t *query) +{ + ssize_t pointer = parse_dns_header (bytes, &(query->header)); + + query->domain = &bytes[pointer]; + while (bytes[pointer++] != 0x00) + ; + + query->qtype = (bytes[pointer] << 8) | bytes[pointer + 1]; + pointer += 2; + + query->qclass = (bytes[pointer] << 8) | bytes[pointer + 1]; + pointer += 2; + + return pointer; +} + +void +parse_dns_answer (unsigned char *bytes, ssize_t *pointer, dns_answer_t *answer) +{ + answer->domain = calloc (BUFFER_MAX_SIZE, sizeof (char)); + parse_domain (bytes, pointer, &(answer->domain)); + + answer->qtype = (bytes[*pointer] << 8) | bytes[*pointer + 1]; + *pointer += 2; + + answer->qclass = (bytes[*pointer] << 8) | bytes[*pointer + 1]; + *pointer += 2; + + for (int i = 3; i >= 0; i--) + { + answer->ttl |= bytes[(*pointer)++] << (8 * i); + } + + answer->rdlen = (bytes[*pointer] << 8) | bytes[*pointer + 1]; + *pointer += 2; + + answer->rdata = calloc (BUFFER_MAX_SIZE, sizeof (char)); + + if (answer->qtype == A || answer->qtype == AAAA) + { + answer->rdata = parse_ip_addr (&bytes[*pointer], answer->rdlen); + *pointer += answer->rdlen; + return; + } + + if (answer->qtype == NS || answer->qtype == CNAME || answer->qtype == PTR) + { + parse_domain (bytes, pointer, &(answer->rdata)); + return; + } + + if (answer->qtype == MX) + { + uint16_t preference = (bytes[*pointer] << 8) | bytes[*pointer + 1]; + *pointer += 2; + + char *exchange = calloc (BUFFER_LENGTH, sizeof (char)); + parse_domain (bytes, pointer, &exchange); + + snprintf (answer->rdata, BUFFER_MAX_SIZE, "%u %s", preference, exchange); + + free (exchange); + + return; + } + + if (answer->qtype == SOA) + { + char *mname = calloc (BUFFER_LENGTH, sizeof (char)); + parse_domain (bytes, pointer, &mname); + + char *rname = calloc (BUFFER_LENGTH, sizeof (char)); + parse_domain (bytes, pointer, &rname); + + uint32_t serial = merge_32 (&bytes[*pointer]); + uint32_t refresh = merge_32 (&bytes[*pointer + 4]); + uint32_t retry = merge_32 (&bytes[*pointer + 8]); + uint32_t expire = merge_32 (&bytes[*pointer + 12]); + uint32_t minimum = merge_32 (&bytes[*pointer + 16]); + + *pointer += 20; + + snprintf (answer->rdata, BUFFER_MAX_SIZE, "%s %s %u %u %u %u %u", mname, + rname, serial, refresh, retry, expire, minimum); + + free (mname); + free (rname); + + return; + } +} + +ssize_t +parse_dns_response (unsigned char *bytes, ssize_t query_len, + dns_response_t *response) +{ + ssize_t pointer = parse_dns_query (bytes, &(response->query)); + + response->answers + = calloc (response->query.header.ancount, sizeof (dns_answer_t)); + + for (size_t i = 0; i < response->query.header.ancount; i++) + { + dns_answer_t answer = { 0 }; + parse_dns_answer (bytes, &pointer, &answer); + memcpy (&(response->answers[i]), &answer, sizeof (dns_answer_t)); + } + + response->authorities + = calloc (response->query.header.nscount, sizeof (dns_answer_t)); + + for (size_t i = 0; i < response->query.header.nscount; i++) + { + dns_answer_t authoritative = { 0 }; + parse_dns_answer (bytes, &pointer, &authoritative); + memcpy (&(response->authorities[i]), &authoritative, + sizeof (dns_answer_t)); + } + + response->additionals + = calloc (response->query.header.arcount, sizeof (dns_answer_t)); + + for (size_t i = 0; i < response->query.header.arcount; i++) + { + dns_answer_t additional = { 0 }; + parse_dns_answer (bytes, &pointer, &additional); + memcpy (&(response->additionals[i]), &additional, sizeof (dns_answer_t)); + } + + return pointer; +} \ No newline at end of file diff --git a/p2-dns/src/dns.h b/p2-dns/src/dns.h new file mode 100644 index 0000000..941c17c --- /dev/null +++ b/p2-dns/src/dns.h @@ -0,0 +1,62 @@ +#ifndef __cs361_dns_h__ +#define __cs361_dns_h__ + +#include + +// Maximum buffer lengths allows +#define BUFFER_LENGTH 512 + +// Structure of the bytes for a DNS header +typedef struct +{ + uint16_t xid; + uint16_t flags; + uint16_t qdcount; + uint16_t ancount; + uint16_t nscount; + uint16_t arcount; +} dns_header_t; + +typedef struct +{ + dns_header_t header; + unsigned char *domain; + uint16_t qtype; + uint16_t qclass; +} dns_query_t; + +typedef struct +{ + char *domain; + uint16_t qtype; + uint16_t qclass; + uint32_t ttl; + uint16_t rdlen; + char *rdata; +} dns_answer_t; + +typedef struct +{ + dns_query_t query; + dns_answer_t *answers; + dns_answer_t *authorities; + dns_answer_t *additionals; +} dns_response_t; + +// Supported record types +#define A 1 +#define NS 2 +#define CNAME 5 +#define SOA 6 +#define PTR 12 +#define MX 15 +#define AAAA 28 + +void print_dns_header (dns_header_t *); +void print_question_section (dns_query_t *query); +void print_answer_section (dns_answer_t *answer); +ssize_t parse_dns_query (unsigned char *bytes, dns_query_t *query); +ssize_t parse_dns_response (unsigned char *bytes, ssize_t query_len, + dns_response_t *response); + +#endif diff --git a/p2-dns/src/port_utils.h b/p2-dns/src/port_utils.h new file mode 100644 index 0000000..b1700c9 --- /dev/null +++ b/p2-dns/src/port_utils.h @@ -0,0 +1,6 @@ +#ifndef __cs361_port_utils_h__ +#define __cs361_port_utils_h__ + +char *get_port (void); + +#endif diff --git a/p2-dns/src/utils.c b/p2-dns/src/utils.c new file mode 100644 index 0000000..562c2d0 --- /dev/null +++ b/p2-dns/src/utils.c @@ -0,0 +1,53 @@ +#include +#include +#include +#include + +#include "dns.h" +#include "port_utils.h" +#include "utils.h" + +// SUGGESTION: Use this file for any network-related functionality, such as +// sending or receiving messages. + +void +ptr_ip (char *dst, size_t dst_size, const char *ip) +{ + char tmp[64]; + strncpy (tmp, ip, sizeof (tmp)); + tmp[sizeof (tmp) - 1] = '\0'; + + char *octets[4]; + char *token = strtok (tmp, "."); + int count = 0; + while (token && count < 4) + { + octets[count++] = token; + token = strtok (NULL, "."); + } + + if (count != 4) + { + fprintf (stderr, "Invalid PTR IP"); + exit (EXIT_FAILURE); + } + + snprintf (dst, dst_size, "%s.%s.%s.%s.in-addr.arpa.", octets[3], octets[2], + octets[1], octets[0]); +} + +void +print_byte_block (uint8_t *bytes, size_t length) +{ + printf (" "); + for (int i = 0; i < length; i++) + { + printf ("%02x", bytes[i]); + if (i == length - 1) + printf ("\n"); + else if ((i + 1) % 16 == 0) + printf ("\n "); + else if ((i % 2) != 0) + printf (" "); + } +} diff --git a/p2-dns/src/utils.h b/p2-dns/src/utils.h new file mode 100644 index 0000000..95ffda9 --- /dev/null +++ b/p2-dns/src/utils.h @@ -0,0 +1,9 @@ +#ifndef __cs361_utils_h__ +#define __cs361_utils_h__ + +#include + +void ptr_ip (char *dst, size_t dst_size, const char *ip); +void print_byte_block (uint8_t *, size_t); + +#endif diff --git a/p2-dns/tests/Makefile b/p2-dns/tests/Makefile new file mode 100644 index 0000000..2d28414 --- /dev/null +++ b/p2-dns/tests/Makefile @@ -0,0 +1,87 @@ +# +# Simple Test Makefile +# Mike Lam, James Madison University, August 2016 +# +# This version of the Makefile includes support for building a test suite. The +# recommended framework is Check (http://check.sourceforge.net/). To build and +# run the test suite, execute the "test" target. The test suite must be located +# in a module called "testsuite". The MODS, LIBS, and OBJS variables work as +# they do in the main Makefile. +# +# To change the default build target (which executes when you just type +# "make"), change the right-hand side of the definition of the "default" +# target. +# +# By default, this makefile will build the project with debugging symbols and +# without optimization. To change this, edit or remove the "-g" and "-O0" +# options in CFLAGS and LDFLAGS accordingly. +# +# By default, this makefile build the application using the GNU C compiler, +# adhering to the C99 standard with all warnings enabled. + + +# application-specific settings and run target + +EXE=../digduke +TEST=testsuite +MODS=public.o +OBJS=../port_utils.o +LIBS= + +UTESTOUT=utests.txt +ITESTOUT=itests.txt +SCHECKOUT=style.txt + +default: $(TEST) + + +$(EXE): + make -C ../ + +test: itest style + @echo "========================================" + +utest: $(EXE) $(TEST) + @echo "========================================" + @echo " UNIT TESTS" + @./$(TEST) 2>/dev/null >$(UTESTOUT) + @cat $(UTESTOUT) | sed -n -e '/Checks/,$$p' | sed -e 's/^private.*:[EF]://g' + +itest: $(EXE) + @echo "========================================" + @echo " INTEGRATION TESTS" + @./integration.sh | tee $(ITESTOUT) + +style: $(EXE) + @echo "========================================" + @echo " CODING STYLE CHECK" + @./style.sh 2>/dev/null >$(SCHECKOUT) + @cat $(SCHECKOUT) + +# compiler/linker settings + +CC=gcc +CFLAGS=-g -O0 -Wall --std=gnu99 -pedantic +LDFLAGS=-g -O0 + +CFLAGS= +LIBS+=-lcheck -lm -lpthread + +ifeq ($(shell uname -s),Linux) + LIBS+=-lrt -lsubunit +endif + + +# build targets + +$(TEST): $(TEST).o $(MODS) $(OBJS) + $(CC) $(LDFLAGS) -o $(TEST) $^ $(LIBS) + +%.o: %.c + $(CC) -c $(CFLAGS) $< + +clean: + rm -rf $(TEST) $(TEST).o $(MODS) $(UTESTOUT) $(ITESTOUT) $(SCHECKOUT) outputs valgrind ckstyle + +.PHONY: default clean test unittest inttest + diff --git a/p2-dns/tests/dukens b/p2-dns/tests/dukens new file mode 100755 index 0000000000000000000000000000000000000000..bd560657cbdde8a6cdac57fe7cab6cbc7e041d27 GIT binary patch literal 44512 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 F_;1};7k>Z% literal 0 HcmV?d00001 diff --git a/p2-dns/tests/expected/A_cname_a.txt b/p2-dns/tests/expected/A_cname_a.txt new file mode 100644 index 0000000..3ff6b17 --- /dev/null +++ b/p2-dns/tests/expected/A_cname_a.txt @@ -0,0 +1,9 @@ +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 32412 +;; flags: qr aa rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0 + +;; QUESTION SECTION: +;stu. IN A + +;; ANSWER SECTION: +stu. IN CNAME 0. stu.cs.jmu.edu. +stu.cs.jmu.edu. IN A 0. 127.0.1.1 diff --git a/p2-dns/tests/expected/A_cname_additional.txt b/p2-dns/tests/expected/A_cname_additional.txt new file mode 100644 index 0000000..e9ff258 --- /dev/null +++ b/p2-dns/tests/expected/A_cname_additional.txt @@ -0,0 +1,11 @@ +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 51120 +;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 + +;; QUESTION SECTION: +;www.jmu.edu. IN CNAME + +;; ANSWER SECTION: +www.jmu.edu. IN CNAME 592. it-www1.jmu.edu. + +;; ADDITIONAL SECTION: +it-www1.jmu.edu. IN A 900. 134.126.126.99 diff --git a/p2-dns/tests/expected/A_cname_basic.txt b/p2-dns/tests/expected/A_cname_basic.txt new file mode 100644 index 0000000..7433f5b --- /dev/null +++ b/p2-dns/tests/expected/A_cname_basic.txt @@ -0,0 +1,8 @@ +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 49602 +;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 + +;; QUESTION SECTION: +;stu.cs.jmu.edu. IN CNAME + +;; ANSWER SECTION: +stu.cs.jmu.edu. IN CNAME 1769. student.cs.jmu.edu. diff --git a/p2-dns/tests/expected/A_example_soa.txt b/p2-dns/tests/expected/A_example_soa.txt new file mode 100644 index 0000000..a6e7ae7 --- /dev/null +++ b/p2-dns/tests/expected/A_example_soa.txt @@ -0,0 +1,8 @@ +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 52173 +;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 + +;; QUESTION SECTION: +;example.com. IN SOA + +;; ANSWER SECTION: +example.com. IN SOA 1803. ns.icann.org. noc.dns.icann.org. 2025011715 7200 3600 1209600 3600 diff --git a/p2-dns/tests/expected/A_jmu_soa.txt b/p2-dns/tests/expected/A_jmu_soa.txt new file mode 100644 index 0000000..8ea04be --- /dev/null +++ b/p2-dns/tests/expected/A_jmu_soa.txt @@ -0,0 +1,8 @@ +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 62055 +;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 + +;; QUESTION SECTION: +;jmu.edu. IN SOA + +;; ANSWER SECTION: +jmu.edu. IN SOA 3600. it-ns.jmu.edu. network.jmu.edu. 735142168 86400 7200 3600000 3600 diff --git a/p2-dns/tests/expected/A_no_ipv6_authority.txt b/p2-dns/tests/expected/A_no_ipv6_authority.txt new file mode 100644 index 0000000..d4ab3d6 --- /dev/null +++ b/p2-dns/tests/expected/A_no_ipv6_authority.txt @@ -0,0 +1,11 @@ +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 61906 +;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 0 + +;; QUESTION SECTION: +;www.jmu.edu. IN AAAA + +;; ANSWER SECTION: +www.jmu.edu. IN CNAME 592. it-www1.jmu.edu. + +;; AUTHORITY SECTION: +jmu.edu. IN SOA 3600. it-ns.jmu.edu. network.jmu.edu. 735142168 86400 7200 3600000 3600 diff --git a/p2-dns/tests/expected/A_no_ipv6_subauth.txt b/p2-dns/tests/expected/A_no_ipv6_subauth.txt new file mode 100644 index 0000000..2abcdf8 --- /dev/null +++ b/p2-dns/tests/expected/A_no_ipv6_subauth.txt @@ -0,0 +1,8 @@ +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 10099 +;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 0 + +;; QUESTION SECTION: +;fac.cs.jmu.edu. IN AAAA + +;; AUTHORITY SECTION: +cs.jmu.edu. IN SOA 900. it-nios-gm.jmu.edu. network.jmu.edu. 14 10800 3600 2419200 900 diff --git a/p2-dns/tests/expected/A_no_ptr.txt b/p2-dns/tests/expected/A_no_ptr.txt new file mode 100644 index 0000000..8b85ea5 --- /dev/null +++ b/p2-dns/tests/expected/A_no_ptr.txt @@ -0,0 +1,8 @@ +;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 56769 +;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 0 + +;; QUESTION SECTION: +;5.67.101.151.in-addr.arpa. IN PTR + +;; AUTHORITY SECTION: +151.in-addr.arpa. IN SOA 3600. pri.authdns.ripe.net. dns.ripe.net. 1752675523 3600 600 864000 3600 diff --git a/p2-dns/tests/expected/A_ptr_basic.txt b/p2-dns/tests/expected/A_ptr_basic.txt new file mode 100644 index 0000000..42fea15 --- /dev/null +++ b/p2-dns/tests/expected/A_ptr_basic.txt @@ -0,0 +1,8 @@ +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 870 +;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 + +;; QUESTION SECTION: +;221.141.126.134.in-addr.arpa. IN PTR + +;; ANSWER SECTION: +221.141.126.134.in-addr.arpa. IN PTR 3542. student.cs.jmu.edu. diff --git a/p2-dns/tests/expected/A_ptr_mixed.txt b/p2-dns/tests/expected/A_ptr_mixed.txt new file mode 100644 index 0000000..fdab6d7 --- /dev/null +++ b/p2-dns/tests/expected/A_ptr_mixed.txt @@ -0,0 +1,12 @@ +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 45675 +;; flags: qr aa rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1 + +;; QUESTION SECTION: +;1.1.0.127.in-addr.arpa. IN PTR + +;; ANSWER SECTION: +1.1.0.127.in-addr.arpa. IN PTR 0. stu.cs.jmu.edu. +1.1.0.127.in-addr.arpa. IN PTR 0. stu. + +;; ADDITIONAL SECTION: +1.1.0.127.in-addr.arpa. IN CNAME 1769. student.cs.jmu.edu. diff --git a/p2-dns/tests/expected/A_ptr_multiple.txt b/p2-dns/tests/expected/A_ptr_multiple.txt new file mode 100644 index 0000000..db0daad --- /dev/null +++ b/p2-dns/tests/expected/A_ptr_multiple.txt @@ -0,0 +1,13 @@ +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 57548 +;; flags: qr rd ra; QUERY: 1, ANSWER: 6, AUTHORITY: 0, ADDITIONAL: 0 + +;; QUESTION SECTION: +;99.126.126.134.in-addr.arpa. IN PTR + +;; ANSWER SECTION: +99.126.126.134.in-addr.arpa. IN PTR 1483. it-www1.jmu.edu. +99.126.126.134.in-addr.arpa. IN PTR 1483. spcecoms.jmu.edu. +99.126.126.134.in-addr.arpa. IN PTR 1483. groups.jmu.edu. +99.126.126.134.in-addr.arpa. IN PTR 1483. groups.dukes.jmu.edu. +99.126.126.134.in-addr.arpa. IN PTR 1483. it-www2.jmu.edu. +99.126.126.134.in-addr.arpa. IN PTR 1483. athletics.jmu.edu. diff --git a/p2-dns/tests/expected/B_a_multiple.txt b/p2-dns/tests/expected/B_a_multiple.txt new file mode 100644 index 0000000..256a023 --- /dev/null +++ b/p2-dns/tests/expected/B_a_multiple.txt @@ -0,0 +1,13 @@ +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 7059 +;; flags: qr rd ra; QUERY: 1, ANSWER: 6, AUTHORITY: 0, ADDITIONAL: 0 + +;; QUESTION SECTION: +;example.com. IN A + +;; ANSWER SECTION: +example.com. IN A 300. 96.7.128.198 +example.com. IN A 300. 23.215.0.136 +example.com. IN A 300. 23.192.228.80 +example.com. IN A 300. 23.192.228.84 +example.com. IN A 300. 96.7.128.175 +example.com. IN A 300. 23.215.0.138 diff --git a/p2-dns/tests/expected/B_aaaa.txt b/p2-dns/tests/expected/B_aaaa.txt new file mode 100644 index 0000000..5616077 --- /dev/null +++ b/p2-dns/tests/expected/B_aaaa.txt @@ -0,0 +1,13 @@ +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 4801 +;; flags: qr rd ra; QUERY: 1, ANSWER: 6, AUTHORITY: 0, ADDITIONAL: 0 + +;; QUESTION SECTION: +;example.com. IN AAAA + +;; ANSWER SECTION: +example.com. IN AAAA 21. 2600:1406:3a00:21::173e:2e65 +example.com. IN AAAA 21. 2600:1406:bc00:53::b81e:94c8 +example.com. IN AAAA 21. 2600:1408:ec00:36::1736:7f24 +example.com. IN AAAA 21. 2600:1406:3a00:21::173e:2e66 +example.com. IN AAAA 21. 2600:1408:ec00:36::1736:7f31 +example.com. IN AAAA 21. 2600:1406:bc00:53::b81e:94ce diff --git a/p2-dns/tests/expected/B_double_compress.txt b/p2-dns/tests/expected/B_double_compress.txt new file mode 100644 index 0000000..d1a5faf --- /dev/null +++ b/p2-dns/tests/expected/B_double_compress.txt @@ -0,0 +1,9 @@ +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 42937 +;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0 + +;; QUESTION SECTION: +;example.com. IN NS + +;; ANSWER SECTION: +example.com. IN NS 84682. b.iana-servers.net. +example.com. IN NS 84682. a.iana-servers.net. diff --git a/p2-dns/tests/expected/B_empty_domain.txt b/p2-dns/tests/expected/B_empty_domain.txt new file mode 100644 index 0000000..6510c0b --- /dev/null +++ b/p2-dns/tests/expected/B_empty_domain.txt @@ -0,0 +1,8 @@ +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 57867 +;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 + +;; QUESTION SECTION: +;example.com. IN MX + +;; ANSWER SECTION: +example.com. IN MX 86400. 0 . diff --git a/p2-dns/tests/expected/B_jmu_mx.txt b/p2-dns/tests/expected/B_jmu_mx.txt new file mode 100644 index 0000000..b31325a --- /dev/null +++ b/p2-dns/tests/expected/B_jmu_mx.txt @@ -0,0 +1,8 @@ +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 13835 +;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 + +;; QUESTION SECTION: +;jmu.edu. IN MX + +;; ANSWER SECTION: +jmu.edu. IN MX 1538. 0 jmu-edu.mail.protection.outlook.com. diff --git a/p2-dns/tests/expected/B_jmu_ns.txt b/p2-dns/tests/expected/B_jmu_ns.txt new file mode 100644 index 0000000..4901d19 --- /dev/null +++ b/p2-dns/tests/expected/B_jmu_ns.txt @@ -0,0 +1,9 @@ +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 23379 +;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0 + +;; QUESTION SECTION: +;jmu.edu. IN NS + +;; ANSWER SECTION: +jmu.edu. IN NS 1798. it-ns2-19.jmu.edu. +jmu.edu. IN NS 1798. it-ns1-19.jmu.edu. diff --git a/p2-dns/tests/expected/B_no_ipv6.txt b/p2-dns/tests/expected/B_no_ipv6.txt new file mode 100644 index 0000000..48e4b0c --- /dev/null +++ b/p2-dns/tests/expected/B_no_ipv6.txt @@ -0,0 +1,5 @@ +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 54920 +;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0 + +;; QUESTION SECTION: +;localhost. IN AAAA diff --git a/p2-dns/tests/expected/C_a_basic.txt b/p2-dns/tests/expected/C_a_basic.txt new file mode 100644 index 0000000..261ac39 --- /dev/null +++ b/p2-dns/tests/expected/C_a_basic.txt @@ -0,0 +1,8 @@ +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 2 +;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 + +;; QUESTION SECTION: +;jmu.edu. IN A + +;; ANSWER SECTION: +jmu.edu. IN A 900. 134.126.126.99 diff --git a/p2-dns/tests/expected/C_a_compressed.txt b/p2-dns/tests/expected/C_a_compressed.txt new file mode 100644 index 0000000..c0ee7c9 --- /dev/null +++ b/p2-dns/tests/expected/C_a_compressed.txt @@ -0,0 +1,8 @@ +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 46412 +;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 + +;; QUESTION SECTION: +;google.com. IN A + +;; ANSWER SECTION: +google.com. IN A 194. 142.250.31.113 diff --git a/p2-dns/tests/expected/C_a_localhost.txt b/p2-dns/tests/expected/C_a_localhost.txt new file mode 100644 index 0000000..27b3583 --- /dev/null +++ b/p2-dns/tests/expected/C_a_localhost.txt @@ -0,0 +1,8 @@ +;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 39713 +;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 + +;; QUESTION SECTION: +;localhost. IN A + +;; ANSWER SECTION: +localhost. IN A 0. 127.0.0.1 diff --git a/p2-dns/tests/expected/C_bad_domain.txt b/p2-dns/tests/expected/C_bad_domain.txt new file mode 100644 index 0000000..8cbe4af --- /dev/null +++ b/p2-dns/tests/expected/C_bad_domain.txt @@ -0,0 +1,5 @@ +;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 22016 +;; flags: qr aa rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0 + +;; QUESTION SECTION: +;asdf. IN A diff --git a/p2-dns/tests/expected/D_ping-2.txt b/p2-dns/tests/expected/D_ping-2.txt new file mode 100644 index 0000000..e1c1651 --- /dev/null +++ b/p2-dns/tests/expected/D_ping-2.txt @@ -0,0 +1,14 @@ +Sending 17 bytes to 127.0.0.1: + 0001 0100 0001 0000 0000 0000 0000 0100 + 01 + +Received 51 bytes from 127.0.0.1: + 0001 8180 0001 0001 0000 0000 0000 0100 + 0101 620c 726f 6f74 2d73 6572 7665 7273 + 036e 6574 0000 0100 0100 008c a000 04aa + f7aa 02 + +Domain name: b.root-servers.net. +Record type: IN A +TTL: 36000 +IPv4 address: 170.247.170.2 diff --git a/p2-dns/tests/expected/D_ping.txt b/p2-dns/tests/expected/D_ping.txt new file mode 100644 index 0000000..05d93d1 --- /dev/null +++ b/p2-dns/tests/expected/D_ping.txt @@ -0,0 +1,14 @@ +Sending 17 bytes to 127.0.0.1: + 0001 0100 0001 0000 0000 0000 0000 0100 + 01 + +Received 51 bytes from 127.0.0.1: + 0001 8180 0001 0001 0000 0000 0000 0100 + 0101 610c 726f 6f74 2d73 6572 7665 7273 + 036e 6574 0000 0100 0100 0000 0a00 04c6 + 2900 04 + +Domain name: a.root-servers.net. +Record type: IN A +TTL: 10 +IPv4 address: 198.41.0.4 diff --git a/p2-dns/tests/integration.sh b/p2-dns/tests/integration.sh new file mode 100755 index 0000000..0739e3f --- /dev/null +++ b/p2-dns/tests/integration.sh @@ -0,0 +1,97 @@ +#!/bin/bash + +EXE="../digduke" + +function run_test { + + # parameters + TAG=$1 + shift + ARGS=$1 + shift + + PTAG=$(printf '%-30s' "$TAG") + if [[ $(echo $TAG | cut -d'_' -f1) == $SKIP_C || + $(echo $TAG | cut -d'_' -f1) == $SKIP_B || + $(echo $TAG | cut -d'_' -f1) == $SKIP_A ]] ; then + echo "$PTAG SKIPPED (previous phases not complete)" + return + fi + + # file paths + OUTPUT=outputs/$TAG.txt + DIFF=outputs/$TAG.diff + EXPECT=expected/$TAG.txt + VALGRND=valgrind/$TAG.txt + + # run test and compare output to the expected version + ./dukens -s 2 >/dev/null 2>&1 & + sleep 1 + $EXE $ARGS 2>/dev/null >"$OUTPUT" + for ARG in "$@" ; do + $EXE $ARG 2>/dev/null >>"$OUTPUT" + done + + diff -u "$OUTPUT" "$EXPECT" >"$DIFF" + passed=false + EFILES=$(find expected -type f -name "$TAG-*.txt") + if [ ! -s "$DIFF" ]; then + passed=true + elif [ "$EFILES" != "" ]; then + for EF in $EFILES ; do + DF=$(echo $EF | sed 's/^expected/outputs/' | sed 's/txt$/diff/') + diff -u "$OUTPUT" "$EF" >"$DF" + if [ ! -s "$DF" ]; then + passed=true + fi + done + fi + if [[ $passed = true ]] ; then + echo "$PTAG pass" + rm outputs/$TAG*diff + else + echo "$PTAG FAIL - Command line: $EXE $ARGS" + if [ "$EFILES" != "" ] ; then + echo "$(printf '%30s' ' ') *WARNING* This test has more than one possible output file." + echo "$(printf '%30s' ' ') *WARNING* Please inspect each manually for comparison." + else + echo "$(printf '%30s' ' ') See $DIFF for diff with expected output" + fi + echo "" + + if [[ $(echo $PTAG | cut -d'_' -f1) == 'D' ]] ; then + SKIP_C="C" + SKIP_B="B" + SKIP_A="A" + elif [[ $(echo $PTAG | cut -d'_' -f1) == 'C' ]] ; then + SKIP_B="B" + SKIP_A="A" + elif [[ $(echo $PTAG | cut -d'_' -f1) == 'B' ]] ; then + SKIP_A="A" + fi + fi + + # run valgrind +#./dukens -s 2 >/dev/null 2>&1 & +# sleep 1 +# valgrind $EXE $ARGS >"$VALGRND" 2>&1 +} + +# initialize output folders +mkdir -p outputs +mkdir -p valgrind +rm -f outputs/* valgrind/* + +# run individual tests +source itests.include + +# check for memory leaks +#LEAK=`cat valgrind/*.txt | grep 'definitely lost' | grep -v ' 0 bytes in 0 blocks'` +LEAK= +if [ -z "$LEAK" ]; then + echo "No memory leak found." +else + echo "Memory leak(s) found. See files listed below for details." + grep 'definitely lost' valgrind/*.txt | sed -e 's/:.*$//g' | sed -e 's/^/ - /g' +fi + diff --git a/p2-dns/tests/itests.include b/p2-dns/tests/itests.include new file mode 100644 index 0000000..c7551fe --- /dev/null +++ b/p2-dns/tests/itests.include @@ -0,0 +1,32 @@ +# list of integration tests +# format: run_test +# used as the root for all filenames (i.e., "expected/$TAG.txt") +# command-line arguments to test + +# sends a message to the server and prints the response in a formatted way +run_test D_ping "" + +run_test C_a_basic "-x 2 jmu.edu A" +run_test C_a_compressed "-x 46412 google.com A" +run_test C_a_localhost "-x 39713 localhost A" +run_test C_bad_domain "-x 22016 asdf A" + +run_test B_a_multiple "-x 7059 example.com A" +run_test B_aaaa "-x 4801 example.com AAAA" +run_test B_jmu_ns "-x 23379 jmu.edu NS" +run_test B_jmu_mx "-x 13835 jmu.edu MX" +run_test B_double_compress "-x 42937 example.com NS" +run_test B_empty_domain "-x 57867 example.com MX" +run_test B_no_ipv6 "-x 54920 localhost AAAA" + +run_test A_cname_basic "-x 49602 stu.cs.jmu.edu CNAME" +run_test A_cname_additional "-x 51120 www.jmu.edu CNAME" +run_test A_cname_a "-x 32412 stu A" +run_test A_jmu_soa "-x 62055 jmu.edu SOA" +run_test A_example_soa "-x 52173 example.com SOA" +run_test A_ptr_basic "-x 870 134.126.141.221 PTR" +run_test A_ptr_multiple "-x 57548 134.126.126.99 PTR" +run_test A_ptr_mixed "-x 45675 127.0.1.1 PTR" +run_test A_no_ptr "-x 56769 151.101.67.5 PTR" +run_test A_no_ipv6_authority "-x 61906 www.jmu.edu AAAA" +run_test A_no_ipv6_subauth "-x 10099 fac.cs.jmu.edu AAAA" diff --git a/p2-dns/tests/mappings.bin b/p2-dns/tests/mappings.bin new file mode 100644 index 0000000000000000000000000000000000000000..763bb7798083db68062feb13f7f23db25c5f31e1 GIT binary patch literal 1670 zcmb7FyKdA#6g}hdBMAu!ArOj)hZKPXEW}H)uqpTesvsyR8N9=;$*#xv!KO;KKnPJ# z@gEdWP*5O{5CRE_27Z7Bg!lj|uD!vr6FVf9EZM&2+_`h-++$`ln=Np=e%W%pnrO8G zT?D?sIFf4$Jd9$^3HiNV%w;PEp?AR>DZUb9;D-s!z@nrr=|%a(++MPaC775x2wVa8 z1J@F9)MA9_JIx)3uj4!$-~e2lxoQ} z(et~KQv)%FBdJ19W5!$tPSFcyuU?Rqpx3~S1oZ0ovkz6EkzYU~&D7YKuL9+lyITdy zXR@n6X0E2s1rWn|2I#nL4c?aqX20QadEm|Wm8!ta(g3aTyfi>-JShY;e7weJfZ3gK zS9qcp%HV+v!gL_Zf#N^FNer$tkSu^g0uu);Uv;O-#Qhy3%TihCc(lImWXhX_MlN+Gsfb*!My{v#gxekS z0~JXpa+PPPIO;03n(8p)X)vi|nu0{$VZCNIvrzU>#Mq^nq3C3?(P4GG^dUYFviIu Qvn?1{A%D)?Wy?DM0=S!rj{pDw literal 0 HcmV?d00001 diff --git a/p2-dns/tests/style.sh b/p2-dns/tests/style.sh new file mode 100755 index 0000000..0243934 --- /dev/null +++ b/p2-dns/tests/style.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +STYLE="gnu" + +IGNORE=() +FAIL=0 + +function comp_file { + + SRC=$1 + SRC_NAME=$2 + + # file paths + FORMAT=ckstyle/${SRC_NAME}.$STYLE + DIFF=ckstyle/${SRC_NAME}.diff + + # run clang-format and compare results + clang-format --style=$STYLE $source > $FORMAT + diff -u $SRC $FORMAT >$DIFF + + PTAG=$(printf '%-30s' "$SRC_NAME") + if [ -s $DIFF ]; then + echo "$PTAG FAIL (see $DIFF for details)" + FAIL=1 + else + echo "$PTAG pass" + rm $DIFF + fi + rm $FORMAT +} + +mkdir -p ckstyle +rm -f ckstyle/* + +for source in $(ls ../src/*.c ../src/*.h) ; do + SKIP=0 + src=$(basename $source) + for ignore in ${IGNORE[*]} ; do + if [ "$src" = "$ignore" ] ; then + SKIP=1 + fi + done + if [ $SKIP = 0 ] ; then + comp_file $source $src + fi +done + +if [ $FAIL != 0 ] ; then + echo "Code that does not adhere to GNU standards will not be accepted." + echo "You must fix these files before submission." +else + echo "Code correctly adheres to required style." +fi diff --git a/p2-dns/tests/testsuite.c b/p2-dns/tests/testsuite.c new file mode 100644 index 0000000..e69de29 diff --git a/p3-dhcps/.gitignore b/p3-dhcps/.gitignore new file mode 100644 index 0000000..d6a7df0 --- /dev/null +++ b/p3-dhcps/.gitignore @@ -0,0 +1,27 @@ +*.swp +*.swo + +# Ignore all compiled files regardless of location +build/*.o +tests/public.o +tests/testsuite.o + +# Ignore executables for this project +tests/testsuite +dhcps + +# But don't ignore prebuilt test versions +!tests/client +!tests/threads + +# Ignore test outputs +tests/ckstyle +tests/itests.txt +tests/outputs +tests/style.txt +tests/valgrind +tests/utests.txt + +# Ignore all vscode stuff and NFS files +**/.nfs* +**/.vscode diff --git a/p3-dhcps/Makefile b/p3-dhcps/Makefile new file mode 100644 index 0000000..52eb99c --- /dev/null +++ b/p3-dhcps/Makefile @@ -0,0 +1,59 @@ +# +# Simple Makefile +# Mike Lam, James Madison University, August 2016 +# +# This makefile builds a simple application that contains a main module +# (specified by the EXE variable) and a predefined list of additional modules +# (specified by the MODS variable). If there are any external library +# dependencies (e.g., the math library, "-lm"), list them in the LIBS variable. +# If there are any precompiled object files, list them in the OBJS variable. +# +# By default, this makefile will build the project with debugging symbols and +# without optimization. To change this, edit or remove the "-g" and "-O0" +# options in CFLAGS and LDFLAGS accordingly. +# +# By default, this makefile build the application using the GNU C compiler, +# adhering to the C99 standard with all warnings enabled. + + +# application-specific settings and run target + +EXE=dhcps +MODS=dhcp.o format.o main.o server.o +OBJS=port_utils.o +LIBS=-lm + +default: build $(EXE) + +build: + mkdir build + +test: build $(EXE) + make -C tests test + +style: $(EXE) + make -C tests style + +# compiler/linker settings + +CC=gcc +CFLAGS=-g -O0 -Wall --std=gnu99 -pedantic +LDFLAGS=-g -O0 -pthread + + +# build targets + +BUILD=$(addprefix build/, $(MODS)) + +$(EXE): build/main.o $(BUILD) $(OBJS) + $(CC) $(LDFLAGS) -o $(EXE) $^ $(LIBS) + +build/%.o: src/%.c + $(CC) -c $(CFLAGS) -o $@ $< + +clean: + rm -rf $(EXE) build + make -C tests clean + +.PHONY: default clean + diff --git a/p3-dhcps/port_utils.o b/p3-dhcps/port_utils.o new file mode 100644 index 0000000000000000000000000000000000000000..a576d09c1e791a4b6b617d72b5418913b7fbb5c4 GIT binary patch literal 4560 zcmeH~U1%It6vxjtO&hgsVr?N%Df?hGTDzv(P@;kcOYDGBikR95A(`E|yED7<<(=7P zHxF7rA`LMRMWF~nUzGY}eX{nUMvJCJkYYiK`rw0wiXVstDI$&M%$}PjhvdmO@xa}C z|G#_Axp(H?nYsJI@RN_P$>qRU4jzG<%20rwO*h*^S#t=wVG~^I-u~B!HFd>$@fTPF z;8@dZcl2SuH8p&F_nb8|nZL2HV9ji$kdGhEI_9mZZ?0MIUL|BrP4<$*{A3SSM1FdM z@N}lF>DNBb;-@pOGhJE4SJu>g7CbrE7(25L*;y=L&2(lJcXEnK=ZWsh)Z`}2-u+2d zO?|rP`Po^DUX=5vDM5Q)?PNdiuwKq)l3Se7EA1Ig&UH^7T3A@fLagbL>yEW^OLpaH zXO`i@IZF1w{YUS>4z~feV+Tpig_2fb==lydqVdqHDJV4@M>!$Zx~_^r70R~Oksqf} zc9qgCaO}`ieyxg)$VtIby3`6aI8meO+qMJB4f0V zj8q5EYjL^JRB}uFjmNFXH!m4Cn=`UIu+a5Oqib7<-?a+gxe6axg=ZThj0*kuz@($J zOQ=EnEQBQ0VL6C#cLs?{ac9KZ3)2dWj^blfu8)@8`e?=Wd`t~}^n-@$mX*KpT~9`z zFfv-p^;isV5=0|Tt7P~LK{U!kt6=zB1kuR<7A%H;L=cVqsn75)2%^b6m)bba+T3U3 z{{ZuT;XlaQ)W-i&=0n2YVr^>Ue}MTx;eUwrVZldPj|uLwHnqwB9P?QCPq1zZ{u1k> zf={zPCit7IX9a(U^>M-9V|_yK4_KcR-0Z(og3qx&E%*i2rr+F$ubH10{@<~_DEN=8 zFAM%F>mLOFll4`>Z?OJd@J^Z-+Vys|xIZ^CH?J@=fAgGlalhf)S@#Hj59?mR`&s7& ze~@*b;E%C3aU|)vdxH6(@PC^1kl@doLKPvUUZZVjT8a9LVVX%qY`PNLoQ^m#|KCyLL^B&UA%THB%^LO~=M#TcMOBsH3(hM|gAz(IJdd zRltMCuI$ssx*9eLv5xSZm9{vCaz`r~&w}m2|NU_%qm`rIPbM%<*~S$hEnD21nSSoHD-2@q@;ZYZ+fdzS8 +#include +#include +#include + +void +dump_packet (uint8_t *packet, size_t length) +{ + for (size_t i = 0; i < length; i++) + { + printf ("%02x ", packet[i]); + if ((i + 1) % 16 == 0) + { + printf ("\n"); + } + } + if (length % 16 != 0) + { + printf ("\n"); + } +} + +void +free_options (options_t *options) +{ + if (options->request) + { + free (options->request); + options->request = NULL; + } + if (options->lease) + { + free (options->lease); + options->lease = NULL; + } + if (options->type) + { + free (options->type); + options->type = NULL; + } + if (options->sid) + { + free (options->sid); + options->sid = NULL; + } +} + +bool +get_options (uint8_t *packet, uint8_t *end, options_t *options) +{ + memset (options, 0, sizeof (options_t)); + + const size_t DHCP_FIXED_SIZE = sizeof (msg_t); + uint8_t *ptr = packet + DHCP_FIXED_SIZE; + + if (ptr + 4 > end) + { + return false; + } + + uint32_t cookie; + memcpy (&cookie, ptr, 4); + if (ntohl (cookie) != MAGIC_COOKIE) + { + return false; + } + ptr += 4; + + // Parse options + while (ptr < end) + { + uint8_t code = *ptr++; + + if (code == DHCP_opt_end) + { + break; + } + + if (ptr >= end) + { + break; + } + + uint8_t len = *ptr++; + + if (ptr + len > end) + { + break; + } + + switch (code) + { + case DHCP_opt_msgtype: + if (len == 1) + { + options->type = malloc (sizeof (uint8_t)); + *options->type = *ptr; + } + break; + case DHCP_opt_reqip: + if (len == 4) + { + options->request = malloc (sizeof (struct in_addr)); + memcpy (options->request, ptr, 4); + } + break; + case DHCP_opt_lease: + if (len == 4) + { + options->lease = malloc (sizeof (uint32_t)); + memcpy (options->lease, ptr, 4); + } + break; + case DHCP_opt_sid: + if (len == 4) + { + options->sid = malloc (sizeof (struct in_addr)); + memcpy (options->sid, ptr, 4); + } + break; + } + + ptr += len; + } + + return true; +} + +uint8_t * +append_cookie (uint8_t *packet, size_t *packet_size) +{ + size_t new_size = *packet_size + 4; + uint8_t *new_packet = realloc (packet, new_size); + + if (!new_packet) + { + return packet; + } + + uint32_t cookie = htonl (MAGIC_COOKIE); + memcpy (new_packet + *packet_size, &cookie, 4); + + *packet_size = new_size; + return new_packet; +} + +uint8_t * +append_option (uint8_t *packet, size_t *packet_size, uint8_t option, + uint8_t option_size, uint8_t *option_value) +{ + size_t new_size; + + if (option == DHCP_opt_end) + { + new_size = *packet_size + 1; + uint8_t *new_packet = realloc (packet, new_size); + + if (!new_packet) + { + return packet; + } + + // Add the end option code + new_packet[*packet_size] = DHCP_opt_end; + + *packet_size = new_size; + return new_packet; + } + + new_size = *packet_size + 2 + option_size; + uint8_t *new_packet = realloc (packet, new_size); + + if (!new_packet) + { + return packet; + } + + // Add option code + new_packet[*packet_size] = option; + + // Add option length + new_packet[*packet_size + 1] = option_size; + + // Add option value + if (option_size > 0 && option_value) + { + memcpy (new_packet + *packet_size + 2, option_value, option_size); + } + + *packet_size = new_size; + return new_packet; +} diff --git a/p3-dhcps/src/dhcp.h b/p3-dhcps/src/dhcp.h new file mode 100644 index 0000000..663b801 --- /dev/null +++ b/p3-dhcps/src/dhcp.h @@ -0,0 +1,120 @@ +#ifndef __cs361_dhcp_h__ +#define __cs361_dhcp_h__ + +#include +#include +#include + +// For reference, see: +// https://www.iana.org/assignments/bootp-dhcp-parameters/bootp-dhcp-parameters.xhtml +// https://www.iana.org/assignments/arp-parameters/arp-parameters.xhtml + +// Rules for MUST and MUST NOT options, as well as values of BOOTP fields: +// https://www.rfc-editor.org/rfc/rfc2131 + +// Option interpretations: +// https://www.rfc-editor.org/rfc/rfc2132 + +#define MAX_DHCP_LENGTH 576 + +// Possible BOOTP message op codes: +#define BOOTREQUEST 1 +#define BOOTREPLY 2 + +// Hardware types (htype) per ARP specifications: +#define ETH 1 +#define IEEE802 6 +#define ARCNET 7 +#define FRAME_RELAY 15 +#define FIBRE 18 + +// Hardware address lengths (hlen) per ARP specifications: +#define ETH_LEN 6 +#define IEEE802_LEN 6 +#define ARCNET_LEN 1 +#define FRAME_LEN 2 +#define FIBRE_LEN 3 + +// For DHCP messages, options must begin with this value: +#define MAGIC_COOKIE 0x63825363 + +// DHCP Message types +// If client detects offered address in use, MUST send DHCP Decline +// If server detects requested address in use, SHOULD send DHCP NAK +// Client can release its own IP address with DHCP Release +#define DHCPDISCOVER 1 +#define DHCPOFFER 2 +#define DHCPREQUEST 3 +#define DHCPDECLINE 4 +#define DHCPACK 5 +#define DHCPNAK 6 +#define DHCPRELEASE 7 + +// DHCP Option types +// All messages will use only these message types +#define DHCP_opt_reqip 50 +#define DHCP_opt_lease 51 +#define DHCP_opt_msgtype 53 +#define DHCP_opt_sid 54 +#define DHCP_opt_end 255 + +// BOOTP message type struct. This has an exact size. Add other fields to +// match the structure as defined in RFC 1542 and RFC 2131. This struct +// should NOT include the BOOTP vend field or the DHCP options field. +// (DHCP options replaced BOOTP vend, but does not have a fixed size and +// cannot be declared in a fixed-size struct.) +typedef struct +{ + uint8_t op; + uint8_t htype; + uint8_t hlen; + uint8_t hops; // always 0 + uint32_t xid; // selected by client/server + uint16_t secs; // seconds since DHCP process started + uint16_t flags; // leading bit is for broadcast...all others must be zero + struct in_addr ciaddr; // Client IP address + struct in_addr yiaddr; // "Your" IP address + struct in_addr siaddr; // Server IP address + struct in_addr giaddr; // Gateway IP address + uint8_t chaddr[16]; // Client hardware address 08002B2ED85E + // uint8_t padding[192]; // Skip the next 192 bytes...legacy BOOTP support + uint8_t sname[64]; // Server sending DHCPOFFER or DHCPACK (optional) + uint8_t file[128]; // Boot file (used in BOOTP from client) +} msg_t; + +// Helper struct for gathering and setting DHCP options +// DHCP: Magic Cookie 0x63825363 +typedef struct +{ + struct in_addr *request; // DHCP: [50] Requested IP address + uint32_t *lease; // DHCP: [51] IP Address Lease Time = 16 Days, 0:00:00 + uint8_t *type; // DHCP: [53] DHCP Message Type = DHCP ACK, 1+1+1 + struct in_addr *sid; // DHCP: [54] Server Identifier = 157.54.48.151, 1+1+N +} options_t; +// DHCP: [255] End (no data) + +// Utility function for printing the raw bytes of a packet: +void dump_packet (uint8_t *, size_t); + +// Utility functions for getting and freeing the DHCP options +// If you have read in a message from a socket, you can get the +// DHCP options as follows: +// nbytes = recvfrom (socketfd, buffer, length, 0, &addr, &addrlen); +// get_options (buffer, buffer + nbytes - 1, &opts); +// If an option is set (e.g., server ID), then options.sid would not be +// NULL. +void free_options (options_t *options); +bool get_options (uint8_t *packet, uint8_t *end, options_t *options); + +// Utility functions to append a DHCP cookie and a single DHCP option to the +// end of the packet. In both cases, packet_size is the current length of +// the array and packet points at the start of it. To set an option (e.g., +// message type), you can call: +// packet = append_option (packet, &size, DHCP_opt_msgtype, 1, DHCPOFFER); +// The return value is the packet but resized to also store the option. Note +// that the size variable is updated to specify the new length. +uint8_t *append_cookie (uint8_t *packet, size_t *packet_size); +uint8_t *append_option (uint8_t *packet, size_t *packet_size, uint8_t option, + uint8_t option_size, uint8_t *option_value); + +#endif diff --git a/p3-dhcps/src/format.c b/p3-dhcps/src/format.c new file mode 100644 index 0000000..9ee07c4 --- /dev/null +++ b/p3-dhcps/src/format.c @@ -0,0 +1,207 @@ +#include "format.h" +#include "dhcp.h" +#include +#include +#include + +const char * +get_htype_name (uint8_t htype) +{ + switch (htype) + { + case ETH: + return "Ethernet (10Mb)"; + case IEEE802: + return "IEEE 802 Networks"; + case ARCNET: + return "ARCNET"; + case FRAME_RELAY: + return "Frame Relay"; + case FIBRE: + return "Fibre Channel"; + default: + return "Unknown"; + } +} + +const char * +get_dhcp_msg_type (uint8_t type) +{ + switch (type) + { + case DHCPDISCOVER: + return "DHCP Discover"; + case DHCPOFFER: + return "DHCP Offer"; + case DHCPREQUEST: + return "DHCP Request"; + case DHCPDECLINE: + return "DHCP Decline"; + case DHCPACK: + return "DHCP ACK"; + case DHCPNAK: + return "DHCP NAK"; + case DHCPRELEASE: + return "DHCP Release"; + default: + return "UNKNOWN"; + } +} + +void +format_time (uint32_t seconds, char *buffer) +{ + uint32_t days = seconds / 86400; + uint32_t hours = (seconds % 86400) / 3600; + uint32_t mins = (seconds % 3600) / 60; + uint32_t secs = seconds % 60; + sprintf (buffer, "%u Days, %u:%02u:%02u", days, hours, mins, secs); +} + +void +dump_msg (FILE *out, msg_t *msg, size_t nbytes) +{ + fprintf (out, "\n"); + fprintf (out, "------------------------------------------------------\n"); + fprintf (out, "BOOTP Options\n"); + fprintf (out, "------------------------------------------------------\n"); + + // Print op code + fprintf (out, "Op Code (op) = %d [%s]\n", msg->op, + msg->op == BOOTREQUEST ? "BOOTREQUEST" : "BOOTREPLY"); + + // Print hardware type and length + fprintf (out, "Hardware Type (htype) = %d [%s]\n", msg->htype, + get_htype_name (msg->htype)); + fprintf (out, "Hardware Address Length (hlen) = %d\n", msg->hlen); + fprintf (out, "Hops (hops) = %d\n", msg->hops); + + // Print XID + fprintf (out, "Transaction ID (xid) = %u (0x%x)\n", ntohl (msg->xid), + ntohl (msg->xid)); + + // Print SECS + char time_str[64]; + format_time (ntohs (msg->secs), time_str); + fprintf (out, "Seconds (secs) = %s\n", time_str); + + // Print FLAGS + fprintf (out, "Flags (flags) = %u\n", ntohs (msg->flags)); + + // Print IP addresses + char ip_str[INET_ADDRSTRLEN]; + + inet_ntop (AF_INET, &msg->ciaddr, ip_str, INET_ADDRSTRLEN); + fprintf (out, "Client IP Address (ciaddr) = %s\n", ip_str); + + inet_ntop (AF_INET, &msg->yiaddr, ip_str, INET_ADDRSTRLEN); + fprintf (out, "Your IP Address (yiaddr) = %s\n", ip_str); + + inet_ntop (AF_INET, &msg->siaddr, ip_str, INET_ADDRSTRLEN); + fprintf (out, "Server IP Address (siaddr) = %s\n", ip_str); + + inet_ntop (AF_INET, &msg->giaddr, ip_str, INET_ADDRSTRLEN); + fprintf (out, "Relay IP Address (giaddr) = %s\n", ip_str); + + // Print CHADDR (hardware address) + fprintf (out, "Client Ethernet Address (chaddr) = "); + for (int i = 0; i < msg->hlen && i < 16; i++) + { + fprintf (out, "%02x", msg->chaddr[i]); + } + fprintf (out, "\n"); + + // Parse DHCP options + fprintf (out, "------------------------------------------------------\n"); + fprintf (out, "DHCP Options\n"); + fprintf (out, "------------------------------------------------------\n"); + + const size_t DHCP_FIXED_SIZE = sizeof (msg_t); + uint8_t *ptr = (uint8_t *)msg + DHCP_FIXED_SIZE; + uint8_t *end = (uint8_t *)msg + nbytes; + + // Check for magic cookie + if (ptr + 4 > end) + { + fprintf (out, "\n"); + return; + } + + uint32_t cookie; + memcpy (&cookie, ptr, 4); + if (ntohl (cookie) != MAGIC_COOKIE) + { + fprintf (out, "\n"); + return; + } + + fprintf (out, "Magic Cookie = [OK]\n"); + ptr += 4; + + // Temporary variables to store options + uint8_t msg_type = 0; + struct in_addr request_ip = { 0 }; + struct in_addr server_id = { 0 }; + uint32_t lease_time = 0; + + // Parse options and store them + while (ptr < end) + { + uint8_t code = *ptr++; + if (code == DHCP_opt_end) + break; + if (ptr >= end) + break; + + uint8_t len = *ptr++; + if (ptr + len > end) + break; + + switch (code) + { + case DHCP_opt_msgtype: + if (len == 1) + msg_type = *ptr; + break; + case DHCP_opt_reqip: + if (len == 4) + memcpy (&request_ip, ptr, 4); + break; + case DHCP_opt_sid: + if (len == 4) + memcpy (&server_id, ptr, 4); + break; + case DHCP_opt_lease: + if (len == 4) + memcpy (&lease_time, ptr, 4); + break; + } + + ptr += len; + } + + // Print options in fixed order + if (msg_type) + { + fprintf (out, "Message Type = %s\n", get_dhcp_msg_type (msg_type)); + } + + if (request_ip.s_addr) + { + inet_ntop (AF_INET, &request_ip, ip_str, INET_ADDRSTRLEN); + fprintf (out, "Request = %s\n", ip_str); + } + + if (lease_time) + { + char lease_str[64]; + format_time (ntohl (lease_time), lease_str); + fprintf (out, "IP Address Lease Time = %s\n", lease_str); + } + + if (server_id.s_addr) + { + inet_ntop (AF_INET, &server_id, ip_str, INET_ADDRSTRLEN); + fprintf (out, "Server Identifier = %s\n", ip_str); + } +} diff --git a/p3-dhcps/src/format.h b/p3-dhcps/src/format.h new file mode 100644 index 0000000..5aa1e81 --- /dev/null +++ b/p3-dhcps/src/format.h @@ -0,0 +1,12 @@ +#ifndef __cs361_format__ +#define __cs361_format__ + +#include "dhcp.h" +#include + +// Print the DHCP message to the specified output file stream +void dump_msg (FILE *, msg_t *, size_t); + +extern bool debug; + +#endif diff --git a/p3-dhcps/src/main.c b/p3-dhcps/src/main.c new file mode 100644 index 0000000..133fece --- /dev/null +++ b/p3-dhcps/src/main.c @@ -0,0 +1,54 @@ +#include +#include +#include +#include + +#include "format.h" +#include "port_utils.h" +#include "server.h" + +static bool get_args (int, char **, long *); + +bool debug = false; + +int +main (int argc, char **argv) +{ + long to_seconds = 2; + bool success = get_args (argc, argv, &to_seconds); + if (!success) + return EXIT_FAILURE; + + char *protocol = get_port (); + int socketfd = setup_server (protocol, to_seconds); + if (socketfd < 0) + return EXIT_FAILURE; + + // Indicate (for debugging) that the server is running + fprintf (stderr, "Server is started on port %s\n", protocol); + + if (debug) + fprintf (stderr, "Shutting down\n"); + return EXIT_SUCCESS; +} + +static bool +get_args (int argc, char **argv, long *to_seconds) +{ + int ch = 0; + while ((ch = getopt (argc, argv, "dhs:")) != -1) + { + switch (ch) + { + case 'd': + debug = true; + break; + case 's': + *to_seconds = atol (optarg); + break; + default: + return false; + } + } + return true; +} diff --git a/p3-dhcps/src/port_utils.h b/p3-dhcps/src/port_utils.h new file mode 100644 index 0000000..b1700c9 --- /dev/null +++ b/p3-dhcps/src/port_utils.h @@ -0,0 +1,6 @@ +#ifndef __cs361_port_utils_h__ +#define __cs361_port_utils_h__ + +char *get_port (void); + +#endif diff --git a/p3-dhcps/src/server.c b/p3-dhcps/src/server.c new file mode 100644 index 0000000..dd364f6 --- /dev/null +++ b/p3-dhcps/src/server.c @@ -0,0 +1,519 @@ +#include "server.h" +#include "dhcp.h" +#include "format.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct in_addr THIS_SERVER; +client_lease_t client_leases[MAX_CLIENTS] = { 0 }; + +void +reset_server_state () +{ + memset (client_leases, 0, sizeof (client_leases)); + for (int i = 0; i < MAX_CLIENTS; i++) + { + client_leases[i].xid = 0; + client_leases[i].hlen = 0; + client_leases[i].has_offer = 0; + client_leases[i].has_lease = 0; + memset (client_leases[i].chaddr, 0, 16); + memset (&client_leases[i].offered_ip, 0, sizeof (struct in_addr)); + memset (&client_leases[i].assigned_ip, 0, sizeof (struct in_addr)); + } +} + +// Find a client by XID (either with pending offer or confirmed lease) +client_lease_t * +find_client_by_xid (uint32_t xid) +{ + for (int i = 0; i < MAX_CLIENTS; i++) + { + if (client_leases[i].xid == xid + && (client_leases[i].has_offer || client_leases[i].has_lease)) + { + return &client_leases[i]; + } + } + return NULL; +} + +struct in_addr +get_next_available_ip (uint8_t *chaddr, uint8_t hlen) +{ + struct in_addr ip = { 0 }; + char ip_str[16]; + + for (int i = 0; i < MAX_CLIENTS; i++) + { + if (client_leases[i].hlen == hlen + && memcmp (client_leases[i].chaddr, chaddr, hlen) == 0) + { + // Prefer the assigned IP if present, otherwise offered IP. + if (client_leases[i].assigned_ip.s_addr != 0) + return client_leases[i].assigned_ip; + if (client_leases[i].offered_ip.s_addr != 0) + return client_leases[i].offered_ip; + } + } + + for (int i = 1; i <= MAX_CLIENTS; i++) + { + snprintf (ip_str, sizeof (ip_str), "192.168.1.%d", i); + + memset (&ip, 0, sizeof (ip)); + if (inet_pton (AF_INET, ip_str, &ip) != 1) + continue; + + int in_use = 0; + int ever_assigned = 0; + for (int j = 0; j < MAX_CLIENTS; j++) + { + if (client_leases[j].assigned_ip.s_addr == ip.s_addr + && client_leases[j].assigned_ip.s_addr != 0) + ever_assigned = 1; + if ((client_leases[j].has_offer + && client_leases[j].offered_ip.s_addr == ip.s_addr) + || (client_leases[j].has_lease + && client_leases[j].assigned_ip.s_addr == ip.s_addr)) + { + in_use = 1; + break; + } + } + + if (!ever_assigned && !in_use) + { + return ip; + } + } + + for (int last = 1; last <= MAX_CLIENTS; last++) + { + snprintf (ip_str, sizeof (ip_str), "192.168.1.%d", last); + memset (&ip, 0, sizeof (ip)); + if (inet_pton (AF_INET, ip_str, &ip) != 1) + continue; + + int owner_idx = -1; + int currently_offered_or_leased = 0; + for (int j = 0; j < MAX_CLIENTS; j++) + { + if (client_leases[j].assigned_ip.s_addr == ip.s_addr + && client_leases[j].assigned_ip.s_addr != 0) + owner_idx = j; + if (client_leases[j].has_offer + && client_leases[j].offered_ip.s_addr == ip.s_addr) + currently_offered_or_leased = 1; + if (client_leases[j].has_lease + && client_leases[j].assigned_ip.s_addr == ip.s_addr) + currently_offered_or_leased = 1; // treat leased as in-use + if (currently_offered_or_leased) + break; + } + + if (owner_idx != -1 && !client_leases[owner_idx].has_lease + && !currently_offered_or_leased) + { + return ip; + } + } + + ip.s_addr = 0; + return ip; +} + +// Find first empty slot in client_leases array +int +find_empty_slot () +{ + for (int i = 0; i < MAX_CLIENTS; i++) + { + if (!client_leases[i].has_offer && !client_leases[i].has_lease) + { + return i; + } + } + return -1; +} + +uint8_t +phase1_handle_request (msg_t *request, options_t *req_opts, msg_t *response) +{ + uint8_t msg_type; + if (req_opts->type && *req_opts->type == DHCPREQUEST) + { + msg_type = DHCPACK; + if (req_opts->request) + { + memcpy (&response->yiaddr, req_opts->request, + sizeof (struct in_addr)); + } + else + { + memcpy (&response->yiaddr, &request->ciaddr, + sizeof (struct in_addr)); + } + } + else + { + msg_type = DHCPOFFER; + inet_pton (AF_INET, "192.168.1.1", &response->yiaddr); + } + + return msg_type; +} + +uint8_t +handle_dhcp_discover (msg_t *request, options_t *req_opts, msg_t *response) +{ + uint8_t msg_type; + uint32_t xid = ntohl (request->xid); + + client_lease_t *existing_lease = NULL; + for (int i = 0; i < MAX_CLIENTS; i++) + { + if (client_leases[i].has_lease && client_leases[i].hlen == request->hlen + && memcmp (client_leases[i].chaddr, request->chaddr, request->hlen) + == 0) + { + existing_lease = &client_leases[i]; + break; + } + } + + if (existing_lease) + { + msg_type = DHCPOFFER; + memcpy (&response->yiaddr, &existing_lease->assigned_ip, + sizeof (struct in_addr)); + existing_lease->xid = xid; + existing_lease->offered_ip = existing_lease->assigned_ip; + existing_lease->has_offer = 1; + return msg_type; + } + + client_lease_t *pending_offer = NULL; + for (int i = 0; i < MAX_CLIENTS; i++) + { + if (client_leases[i].has_offer && !client_leases[i].has_lease + && client_leases[i].hlen == request->hlen + && memcmp (client_leases[i].chaddr, request->chaddr, request->hlen) + == 0) + { + pending_offer = &client_leases[i]; + break; + } + } + + if (pending_offer) + { + msg_type = DHCPOFFER; + memcpy (&response->yiaddr, &pending_offer->offered_ip, + sizeof (struct in_addr)); + pending_offer->xid = xid; + return msg_type; + } + + struct in_addr new_ip + = get_next_available_ip (request->chaddr, request->hlen); + + if (debug) + { + char ip_str[INET_ADDRSTRLEN]; + inet_ntop (AF_INET, &new_ip, ip_str, INET_ADDRSTRLEN); + fprintf (stderr, "New client - offering IP: %s\n", ip_str); + } + + if (new_ip.s_addr == 0) + { + msg_type = DHCPNAK; + memset (&response->yiaddr, 0, sizeof (struct in_addr)); + return msg_type; + } + + msg_type = DHCPOFFER; + memcpy (&response->yiaddr, &new_ip, sizeof (struct in_addr)); + + int slot = find_empty_slot (); + if (slot >= 0) + { + memset (&client_leases[slot], 0, sizeof (client_lease_t)); + + client_leases[slot].xid = xid; + memcpy (client_leases[slot].chaddr, request->chaddr, request->hlen); + client_leases[slot].hlen = request->hlen; + client_leases[slot].offered_ip = new_ip; + client_leases[slot].has_offer = 1; + client_leases[slot].has_lease = 0; + } + + return msg_type; +} + +uint8_t +handle_dhcp_request (msg_t *request, options_t *req_opts, msg_t *response) +{ + uint8_t msg_type; + uint32_t xid = ntohl (request->xid); + + client_lease_t *client = find_client_by_xid (xid); + + if (!client) + { + msg_type = DHCPNAK; + memset (&response->yiaddr, 0, sizeof (struct in_addr)); + return msg_type; + } + + int ip_match = 0; + if (req_opts->request) + { + ip_match = (memcmp (req_opts->request, &client->offered_ip, + sizeof (struct in_addr)) + == 0); + } + int server_match = 0; + if (req_opts->sid) + { + server_match + = (memcmp (req_opts->sid, &THIS_SERVER, sizeof (struct in_addr)) + == 0); + } + + if (ip_match && server_match) + { + msg_type = DHCPACK; + memcpy (&response->yiaddr, &client->offered_ip, sizeof (struct in_addr)); + + client->assigned_ip = client->offered_ip; + client->has_lease = 1; + client->has_offer = 0; + } + else + { + msg_type = DHCPNAK; + memset (&response->yiaddr, 0, sizeof (struct in_addr)); + + memset (client, 0, sizeof (client_lease_t)); + } + + return msg_type; +} + +// Create DHCP response +uint8_t * +create_response (msg_t *request, size_t *response_size, options_t *req_opts) +{ + msg_t response = { 0 }; + memcpy (&response, request, sizeof (msg_t)); + + response.op = BOOTREPLY; + memset (&response.siaddr, 0, sizeof (struct in_addr)); + + uint8_t msg_type; + uint32_t xid = ntohl (request->xid); + + // Phase 1 if the xid is 0 + if (xid == 0) + { + msg_type = phase1_handle_request (request, req_opts, &response); + } + // Phase 2 and beyond + else + { + if (req_opts->type && *req_opts->type == DHCPDISCOVER) + { + msg_type = handle_dhcp_discover (request, req_opts, &response); + } + else if (req_opts->type && *req_opts->type == DHCPREQUEST) + { + msg_type = handle_dhcp_request (request, req_opts, &response); + } + else + { + msg_type = DHCPOFFER; + inet_pton (AF_INET, "192.168.1.1", &response.yiaddr); + } + } + + size_t buffer_size = sizeof (msg_t); + uint8_t *response_buffer = calloc (1, buffer_size); + if (!response_buffer) + return NULL; + + memcpy (response_buffer, &response, sizeof (msg_t)); + + response_buffer = append_cookie (response_buffer, &buffer_size); + response_buffer + = append_option (response_buffer, &buffer_size, DHCP_opt_msgtype, + sizeof (msg_type), &msg_type); + + if (msg_type == DHCPOFFER || msg_type == DHCPACK) + { + uint32_t lease_time = htonl (60 * 60 * 24 * 30); + response_buffer + = append_option (response_buffer, &buffer_size, DHCP_opt_lease, + sizeof (lease_time), (uint8_t *)&lease_time); + } + + response_buffer + = append_option (response_buffer, &buffer_size, DHCP_opt_sid, + sizeof (THIS_SERVER), (uint8_t *)&THIS_SERVER); + response_buffer + = append_option (response_buffer, &buffer_size, DHCP_opt_end, 0, NULL); + + *response_size = buffer_size; + + return response_buffer; +} + +int +setup_server (char *port_str, long timeout_seconds) +{ + reset_server_state (); + + int sockfd; + struct sockaddr_in server_addr; + int port = atoi (port_str); + + // Create UDP socket + sockfd = socket (AF_INET, SOCK_DGRAM, 0); + if (sockfd < 0) + { + perror ("socket"); + return -1; + } + + if (timeout_seconds > 0) + { + struct timeval tv; + tv.tv_sec = timeout_seconds; + tv.tv_usec = 0; + setsockopt (sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof (tv)); + } + + // Bind socket + memset (&server_addr, 0, sizeof (server_addr)); + server_addr.sin_family = AF_INET; + server_addr.sin_addr.s_addr = INADDR_ANY; + server_addr.sin_port = htons (port); + + if (bind (sockfd, (struct sockaddr *)&server_addr, sizeof (server_addr)) < 0) + { + perror ("bind"); + close (sockfd); + return -1; + } + + // Set THIS_SERVER to 192.168.1.0 + inet_pton (AF_INET, "192.168.1.0", &THIS_SERVER); + + uint8_t buffer[MAX_DHCP_LENGTH]; + struct sockaddr_in client_addr; + socklen_t client_len = sizeof (client_addr); + + // Main server loop + while (1) + { + ssize_t nbytes = recvfrom (sockfd, buffer, MAX_DHCP_LENGTH, 0, + (struct sockaddr *)&client_addr, &client_len); + + if (nbytes < 0) + { + break; + } + + msg_t *msg = (msg_t *)buffer; + + // Print received message + printf ("++++++++++++++++++++++++++\n"); + printf ("SERVER RECEIVED %zd BYTES:\n", nbytes); + printf ("++++++++++++++++++++++++++\n"); + dump_msg (stdout, msg, nbytes); + fflush (stdout); + + // Get DHCP options + options_t opts; + if (!get_options (buffer, buffer + nbytes, &opts)) + { + fprintf (stderr, "Failed to parse DHCP options\n"); + fflush (stderr); + free_options (&opts); + continue; + } + + if (*opts.type == DHCPRELEASE) + { + uint32_t xid = ntohl (msg->xid); + client_lease_t *client = find_client_by_xid (xid); + if (client) + { + if (debug) + { + char ip_str[INET_ADDRSTRLEN]; + inet_ntop (AF_INET, &client->assigned_ip, ip_str, + INET_ADDRSTRLEN); + fprintf (stderr, "Releasing IP %s for client\n", ip_str); + } + client->has_lease = 0; + client->has_offer = 0; + } + free_options (&opts); + continue; + } + + // Create and send response + size_t response_size; + uint8_t *response = create_response (msg, &response_size, &opts); + + if (!response) + { + fprintf (stderr, "ERROR: create_response returned NULL!\n"); + fflush (stderr); + free_options (&opts); + continue; + } + + if (response_size == 0) + { + fprintf (stderr, "ERROR: response_size is 0!\n"); + fflush (stderr); + free (response); + free_options (&opts); + continue; + } + + ssize_t sent = sendto (sockfd, response, response_size, 0, + (struct sockaddr *)&client_addr, client_len); + + if (sent < 0) + { + perror ("sendto failed"); + fprintf (stderr, "Failed to send %zu bytes\n", response_size); + fflush (stderr); + } + + // Print sent message + printf ("\n"); + printf ("+++++++++++++++++++++++++\n"); + printf ("SERVER SENDING %zu BYTES:\n", response_size); + printf ("+++++++++++++++++++++++++\n"); + dump_msg (stdout, (msg_t *)response, response_size); + fflush (stdout); + + free (response); + + free_options (&opts); + } + + close (sockfd); + return 0; +} diff --git a/p3-dhcps/src/server.h b/p3-dhcps/src/server.h new file mode 100644 index 0000000..7176313 --- /dev/null +++ b/p3-dhcps/src/server.h @@ -0,0 +1,30 @@ +#ifndef __cs361_dhcp_server_h__ +#define __cs361_dhcp_server_h__ + +#include +#include + +#include "dhcp.h" + +void reset_server_state (); +int setup_server (char *, long to_seconds); + +// Maximum number of clients to support +#define MAX_CLIENTS 4 + +// Client lease information +typedef struct +{ + uint32_t xid; + uint8_t chaddr[16]; + uint8_t hlen; + struct in_addr offered_ip; + struct in_addr assigned_ip; + int has_offer; + int has_lease; +} client_lease_t; + +extern bool debug; +extern struct in_addr THIS_SERVER; + +#endif diff --git a/p3-dhcps/tests/Makefile b/p3-dhcps/tests/Makefile new file mode 100644 index 0000000..50dd48d --- /dev/null +++ b/p3-dhcps/tests/Makefile @@ -0,0 +1,93 @@ +# +# Simple Test Makefile +# Mike Lam, James Madison University, August 2016 +# +# This version of the Makefile includes support for building a test suite. The +# recommended framework is Check (http://check.sourceforge.net/). To build and +# run the test suite, execute the "test" target. The test suite must be located +# in a module called "testsuite". The MODS, LIBS, and OBJS variables work as +# they do in the main Makefile. +# +# To change the default build target (which executes when you just type +# "make"), change the right-hand side of the definition of the "default" +# target. +# +# By default, this makefile will build the project with debugging symbols and +# without optimization. To change this, edit or remove the "-g" and "-O0" +# options in CFLAGS and LDFLAGS accordingly. +# +# By default, this makefile build the application using the GNU C compiler, +# adhering to the C99 standard with all warnings enabled. + + +# application-specific settings and run target + +EXE=../dhcps +TEST=testsuite +MODS=public.o +OBJS=../port_utils.o +LIBS= + +UTESTOUT=utests.txt +ITESTOUT=itests.txt +SCHECKOUT=style.txt + +default: $(TEST) + +TOBJS=dhcp.o format.o threads.o + + +threads: threads.c $(TOBJS) + gcc -O2 -o threads $(TOBJS) $(OBJS) + +$(EXE): + make -C ../ + +test: utest itest style + @echo "========================================" + +utest: $(EXE) $(TEST) + @echo "========================================" + @echo " UNIT TESTS" + @./$(TEST) 2>/dev/null >$(UTESTOUT) + @cat $(UTESTOUT) | sed -n -e '/Checks/,$$p' | sed -e 's/^private.*:[EF]://g' + +itest: $(EXE) + @echo "========================================" + @echo " INTEGRATION TESTS" + @./integration.sh | tee $(ITESTOUT) + +style: $(EXE) + @echo "========================================" + @echo " CODING STYLE CHECK" + @./style.sh 2>/dev/null >$(SCHECKOUT) + @cat $(SCHECKOUT) + +# compiler/linker settings + +CC=gcc +CFLAGS=-g -O0 -Wall --std=gnu99 -pedantic +LDFLAGS=-g -O0 + +#CKCFLAGS= +CKLIBS+=-lcheck -lm -lpthread + +ifeq ($(shell uname -s),Linux) + CKLIBS+=-lrt -lsubunit +endif + + +# build targets + +$(TEST): $(TEST).o $(MODS) $(OBJS) + $(CC) -c testsuite.c -o testsuite.o + $(CC) $(LDFLAGS) -o $(TEST) $^ $(CKLIBS) + +%.o: %.c + $(CC) -c $(CFLAGS) $< + +clean: + rm -rf $(TEST) $(TEST).o $(MODS) $(UTESTOUT) $(ITESTOUT) $(SCHECKOUT) outputs valgrind ckstyle $(COBJS) + +.PHONY: default clean test unittest inttest + diff --git a/p3-dhcps/tests/client b/p3-dhcps/tests/client new file mode 100755 index 0000000000000000000000000000000000000000..31ba445b9b0fe09274fc502167d00a4489dd7aef GIT binary patch literal 35248 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"$OUTPUT" 2>/dev/null & + else + $EXE $SARGS >/dev/null 2>&1 & + fi + sleep .5 + if [[ $TAG == "A_threads" ]] ; then + ./threads 2>/dev/null >"$OUTPUT" + elif [[ $(echo $TAG | cut -d'_' -f1) == 'D' ]] ; then + ./client $ARGS >/dev/null 2>&1 + else + ./client $ARGS 2>/dev/null >"$OUTPUT" + fi + sleep 1.5 + + diff -u "$OUTPUT" "$EXPECT" >"$DIFF" + if [ -s "$DIFF" ]; then + + if [[ $TAG == "A_threads" ]] ; then + echo "$PTAG FAIL - Command line: $EXE $SARGS ; ./threads $ARGS" + else + echo "$PTAG FAIL - Command line: $EXE $SARGS ; ./client $ARGS" + fi + if [[ $(echo $PTAG | cut -d'_' -f1) == 'D' ]] ; then + SKIP_C="C" + SKIP_B="B" + SKIP_A="A" + elif [[ $(echo $PTAG | cut -d'_' -f1) == 'C' ]] ; then + SKIP_B="B" + SKIP_A="A" + elif [[ $(echo $PTAG | cut -d'_' -f1) == 'B' ]] ; then + SKIP_A="A" + fi + else + echo "$PTAG pass" + fi + + # run valgrind + #valgrind $EXE -s 2 >"$VALGRND" 2>&1 & + #sleep 1 + #./client $ARGS >/dev/null 2>&1 + #sleep 2 +} + +# initialize output folders +mkdir -p outputs +mkdir -p valgrind +rm -f outputs/* valgrind/* + +# run individual tests +source itests.include + +# check for memory leaks +#LEAK=`cat valgrind/*.txt | grep 'definitely lost' | grep -v ' 0 bytes in 0 blocks'` +#LEAK= +#if [ -z "$LEAK" ]; then +# echo "No memory leak found." +#else +# echo "Memory leak(s) found. See files listed below for details." +# grep 'definitely lost' valgrind/*.txt | sed -e 's/:.*$//g' | sed -e 's/^/ - /g' +#fi + diff --git a/p3-dhcps/tests/itests.include b/p3-dhcps/tests/itests.include new file mode 100644 index 0000000..b825936 --- /dev/null +++ b/p3-dhcps/tests/itests.include @@ -0,0 +1,21 @@ +# list of integration tests +# format: run_test +# used as the root for all filenames (i.e., "expected/$TAG.txt") +# command-line arguments to test + +run_test D_eth_disc "data/eth-disc" +run_test D_ieee_req "data/ieee-req" + +run_test C_eth_dhcp "data/eth-dhcp" +run_test C_arc_two "data/arc-two" +run_test C_multi "data/multi-dhcp" +#run_test C_arc_bad "data/arc-req-bad" + +run_test B_ieee_release "data/ieee-release" +run_test B_arc_two "data/arc-two-rel" +run_test B_frame_interleave "data/frame-inter" +run_test B_fibre_repeat "data/fibre-repeat" +run_test B_multi_reuse "data/multi-reuse" +run_test B_multi_release "data/multi-release" + +run_test A_threads "" diff --git a/p3-dhcps/tests/public.c b/p3-dhcps/tests/public.c new file mode 100644 index 0000000..c9d0f62 --- /dev/null +++ b/p3-dhcps/tests/public.c @@ -0,0 +1,29 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +START_TEST (C_test_template) +{ + int x = 5; + int y = 5; + ck_assert_int_eq (x, y); +} +END_TEST + +void public_tests (Suite *s) +{ + TCase *tc_public = tcase_create ("Public"); + tcase_add_test (tc_public, C_test_template); + suite_add_tcase (s, tc_public); +} + diff --git a/p3-dhcps/tests/style.sh b/p3-dhcps/tests/style.sh new file mode 100755 index 0000000..ba2210a --- /dev/null +++ b/p3-dhcps/tests/style.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +STYLE="gnu" + +IGNORE=() +FAIL=0 + +function comp_file { + + SRC=$1 + SRC_NAME=$2 + + # file paths + FORMAT=ckstyle/${SRC_NAME}.$STYLE + DIFF=ckstyle/${SRC_NAME}.diff + + # run clang-format and compare results + clang-format --style=$STYLE $source > $FORMAT + diff -u $SRC $FORMAT >$DIFF + + PTAG=$(printf '%-30s' "$SRC_NAME") + if [ -s $DIFF ]; then + echo "$PTAG FAIL (see $DIFF for details)" + FAIL=1 + else + echo "$PTAG pass" + rm $DIFF + fi + rm $FORMAT +} + +mkdir -p ckstyle +rm -f ckstyle/* + +for source in $(ls ../*.c ../*.h) ; do + SKIP=0 + src=$(basename $source) + for ignore in ${IGNORE[*]} ; do + if [ "$src" = "$ignore" ] ; then + SKIP=1 + fi + done + if [ $SKIP = 0 ] ; then + comp_file $source $src + fi +done + +if [ $FAIL != 0 ] ; then + echo "Code that does not adhere to GNU standards will not be accepted." + echo "You must fix these files before submission." +else + echo "Code correctly adheres to required style." +fi diff --git a/p3-dhcps/tests/testsuite.c b/p3-dhcps/tests/testsuite.c new file mode 100644 index 0000000..79db20b --- /dev/null +++ b/p3-dhcps/tests/testsuite.c @@ -0,0 +1,32 @@ +#include +#include +#include +#include +#include +#include + +#include + +extern void public_tests (Suite *s); + +Suite * test_suite (void) +{ + Suite *s = suite_create ("Default"); + public_tests (s); + return s; +} + +void run_testsuite () +{ + Suite *s = test_suite (); + SRunner *sr = srunner_create (s); + srunner_run_all (sr, CK_NORMAL); + srunner_free (sr); +} + +int main (void) +{ + srand((unsigned)time(NULL)); + run_testsuite (); + return EXIT_SUCCESS; +} diff --git a/p3-dhcps/tests/threads b/p3-dhcps/tests/threads new file mode 100755 index 0000000000000000000000000000000000000000..10ead327fe447baacabe37e786fa76c7d066f35f GIT binary patch literal 35040 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