diff --git a/01_git/.gitignore b/01_git/.gitignore new file mode 100644 index 0000000..92e163a --- /dev/null +++ b/01_git/.gitignore @@ -0,0 +1,3 @@ +*.out +*.json +game diff --git a/01_git/scissors/Makefile b/01_git/scissors/Makefile new file mode 100644 index 0000000..5631a8f --- /dev/null +++ b/01_git/scissors/Makefile @@ -0,0 +1,20 @@ +CXX=g++ +CXX_FLAGS=-std=c++11 -O3 -Wall -Werror -Wpedantic +INC=-I. +LDFLAGS=-lcrypto + +CXX_SOURCES=choice.cpp game.cpp +OUT_NAME=game + +RM=rm -rf + +# The project is tiny so do clean build at all times +# to suppress $TARGET is up to date" in case we're +# updating the module. +game: clean + $(CXX) $(CXX_FLAGS) $(INC) $(CXX_SOURCES) -o$@ $(LDFLAGS) + +all: game + +clean: + $(RM) $(OUT_NAME) diff --git a/01_git/scissors/choice.cpp b/01_git/scissors/choice.cpp new file mode 100644 index 0000000..1e28b80 --- /dev/null +++ b/01_git/scissors/choice.cpp @@ -0,0 +1,150 @@ +#include "choice.h" + +#include +#include +#include + +#include + +namespace +{ + +const char* const gaItemsStrRepr[] +{ + "scissors", // SCISSORS + "paper", // PAPER + "rock" // ROCK +}; + +} //namespace + +namespace game +{ +// Auxiliary stuff. + +CVal decodeChoiceStdin(char rdRawChoice) +{ + switch (std::tolower(rdRawChoice)) + { + // In-game items. + case 's': + return CVal::SCISSORS; + case 'p': + return CVal::PAPER; + case 'r': + return CVal::ROCK; + + // Control codes. + case 'q': + // FALLTHROUGH + case EOF: + // FALLTHROUGH + case 0: // ^D - a corner-case for std::cin! In case ^D is pressed + // we get into an infinite loop. + return CVal::QUIT; + case '\n': + // FALLTHROUGH + case '\r': + return CVal::IGNORE; + + default: + return CVal::CHOICE_LAST; + } +} + +CVal decodeChoiceInt(int rdRawChoice) +{ + for (auto i = Choice::ToInt(CVal::SCISSORS); + i < Choice::ToInt(CVal::CHOICE_LAST); ++i) + { + if (i == rdRawChoice) + { + return static_cast(i); + } + } + + return CVal::CHOICE_LAST; +} + +// Choice class impl. + +const char* const Choice::ToString(CVal reValue) +{ + assert(reValue < CVal::CHOICE_LAST); + return gaItemsStrRepr[ToInt(reValue)]; +} + +void Choice::PrintMd5FromChoice(CVal reValue) +{ + assert(reValue < CVal::CHOICE_LAST); + const auto pChoiceString = ToString(reValue); + const auto dChoiceLength = std::strlen(pChoiceString); + + unsigned char aDigest[MD5_DIGEST_LENGTH] {}; + const auto pDigest = MD5(reinterpret_cast(pChoiceString), + dChoiceLength, aDigest); + std::cout << "MD5(choice): 0x"; + for (size_t i = 0; i < sizeof(aDigest); ++i) + { + std::cout << std::hex << short(pDigest[i]); + } + + std::cout << std::endl; +} + +// ChoiceStdin class impl. + +CVal ChoiceStdin::Get() +{ + bool bSkipHeading = false; + + while (true) + { + if (not bSkipHeading) + { + std::cout << "Please choose: rock (r) - paper (p) - scissors (s)" << std::endl; + } + else + { + bSkipHeading = false; + } + + auto dInput = '\0'; + std::cin >> dInput; + const auto eChoice {decodeChoiceStdin(dInput)}; + + if (eChoice == CVal::IGNORE) + { + // Eat up LFs and CRs, do not repeat the prompt above. + bSkipHeading = true; + continue; + } + + if (eChoice == CVal::CHOICE_LAST) + { + std::cout << "Wrong input"; + if (std::isalnum(dInput)) + { + // Check letters & numbers only, do not overcomplicate things. + std::cout << ": " << char(dInput); + } + + std::cout << "!" << std::endl << "Press 'q' or '^C'/'^D' to exit" << std::endl; + continue; + } + + return eChoice; + } +} + +// ChoiceRandom class impl. + +CVal ChoiceRandom::Get() +{ + const auto eChoice = decodeChoiceInt(mtUniDistr(mtRandSrc)); + assert(eChoice != CVal::CHOICE_LAST); // Double-checking UniDistr range. + PrintMd5FromChoice(eChoice); + return eChoice; +} + +} // namespace game diff --git a/01_git/scissors/choice.h b/01_git/scissors/choice.h new file mode 100644 index 0000000..ae332c9 --- /dev/null +++ b/01_git/scissors/choice.h @@ -0,0 +1,69 @@ +#pragma once + +#include + +namespace game +{ + +class Choice +{ +public: + + enum class Value : int + { + // Game-related values. + SCISSORS = 0, + PAPER, + ROCK, + + // Enum size & invalid input marker. + CHOICE_LAST, + // Crutch to skip some control chars, like CR & LF + IGNORE, + // "game quit" choice. + QUIT + }; + + virtual Value Get() = 0; + + static constexpr int ToInt(Value reValue) + { + return static_cast(reValue); + } + + static const char* const ToString(Value reValue); + static void PrintMd5FromChoice(Value reValue); + +}; + +using CVal = Choice::Value; + +class ChoiceStdin : public Choice +{ +public: + + Value Get() override; +}; + +class ChoiceRandom : public Choice +{ + using UniDistr = std::uniform_int_distribution<>; + +public: + + ChoiceRandom() : + mtUniDistr(ToInt(CVal::SCISSORS), ToInt(CVal::ROCK)) + { + std::random_device tDrbg; + mtRandSrc.seed(tDrbg()); + } + + Value Get() override; + +private: + + std::mt19937 mtRandSrc; + UniDistr mtUniDistr; +}; + +} // namespace game diff --git a/01_git/scissors/game.cpp b/01_git/scissors/game.cpp new file mode 100644 index 0000000..4fbc1a1 --- /dev/null +++ b/01_git/scissors/game.cpp @@ -0,0 +1,79 @@ +#include "choice.h" + +#include + +enum class Result : int +{ + PLAYER_WINS = 0, + CPU_WINS, + DRAW +}; + +using namespace game; + +namespace +{ + +const char* const gaGameResStrRepr[] +{ + "You win", // PLAYER_WINS + "I win", // CPU_WINS + "Draw!" // DRAW +}; + +const Result gaResolveTbl[][Choice::ToInt(CVal::CHOICE_LAST)] +{ + // SCISSORS | PAPER | ROCK <- Player's choice + // + // CPU's choice + // | + // V + { Result::DRAW, Result::PLAYER_WINS, Result::CPU_WINS }, // SCISSORS + { Result::CPU_WINS, Result::DRAW, Result::PLAYER_WINS }, // PAPER + { Result::PLAYER_WINS, Result::CPU_WINS, Result::DRAW } // ROCK +}; + +void resolveGame(CVal rePlayerChoice, CVal reCpuChoice) +{ + const auto eResult = gaResolveTbl + [Choice::ToInt(rePlayerChoice)] + [Choice::ToInt(reCpuChoice)]; + + std::cout << gaGameResStrRepr[static_cast(eResult)]; + + if (eResult != Result::DRAW) + { + const auto eFirstNoun = (eResult == Result::CPU_WINS) ? reCpuChoice : rePlayerChoice; + const auto eSecondNoun = (eFirstNoun == reCpuChoice) ? rePlayerChoice : reCpuChoice; + std::cout << ": " << Choice::ToString(eFirstNoun); + std::cout << " beat" << (eFirstNoun != CVal::SCISSORS ? "s" : ""); + std::cout << " " << Choice::ToString(eSecondNoun); + } + + std::cout << std::endl; +} +} // namespace + +int main() +{ + ChoiceStdin tPlayerInput; + ChoiceRandom tCpuInput; + + while (true) + { + + // Make CPU's move first, we need to play fair. + const auto eCpuChoice = tCpuInput.Get(); + const auto ePlayerChoice = tPlayerInput.Get(); + if (ePlayerChoice == CVal::QUIT) + { + break; + } + + std::cout << "You chose " << Choice::ToString(ePlayerChoice); + std::cout << ", I chose " << Choice::ToString(eCpuChoice) << std::endl; + resolveGame(ePlayerChoice, eCpuChoice); + } + + std::cout << "Bye!" << std::endl; +} diff --git a/02_bash/hwdetect/hwdetect.sh b/02_bash/hwdetect/hwdetect.sh new file mode 100755 index 0000000..e5c84ab --- /dev/null +++ b/02_bash/hwdetect/hwdetect.sh @@ -0,0 +1,91 @@ +#!/bin/bash + +# PLEASE NOTE: the script works with bash only! +# Other shells, like zsh are not supported. + +# Add other subsystems which can be tested with hotplugging to monitor. +# Currently we listen to addition/removal of: +# 1) USB pendrives/SD/MMC cards: mmc and block +# 2) USB-HID devices (and generic HID devices, like PS/2? I tested with yubikey & mice only): hid +# 3) USB-to-UART converters: usb-serial (also tty may be added) +monitored_subsys=(block hid usb-serial mmc) + +temp_buf=`mktemp` + +for subsys in "${monitored_subsys[@]}" +do + query+="--subsystem-match=$subsys " +done + +udevadm monitor -k $query > $temp_buf & +udevadm_pid=$! + +cleanup () { + kill -SIGTERM $udevadm_pid + rm -rf $temp_buf +} + +filter() { + # tail + grep combo returns a single block of strings separated with + # spaces instead of '\n's. udevadm does not have an ability to filter + # events by action types by itself so, for example inserting/removing a + # USB device generates lots of output which contain also 'bind/unbind' actions + # which we consider just a noise for this task. These must be excluded + # from the output, so we iterate through the list of words in search + # for the given tag (like 'add' or 'remove'); in case one is found + # the next token is the device name (according to udevadm format). Sub-classes + # are filtered by --subsystem-match so we don't need to filter that lines. + # This also eats up [KERNEL... headers. + # The same could be probably achieved by awk? + + local tag=$1 + local tag_found=0 + local input=("$@") + + # Iterate from the first arg (not zero-th!) + # this is a crutch to suppress garbage in output + for line in "${input[@]:1}"; + do + if [ $tag_found -eq 0 ]; + then + if [ "$tag" == "$line" ]; + then + tag_found=1 + fi + else + echo $line + tag_found=0 + fi + done +} + +trap cleanup EXIT SIGTERM + +read_pos=0 + +while : ; +do + diff_list=`tail -c +$read_pos $temp_buf` + diff_len=${#diff_list} + ((read_pos+=$diff_len)) + + # Preprocess add/remove lists in order not to + # filter empty input or an input which does not + # contain data which we're interested in. + added_list=`echo $diff_list | grep add` + removed_list=`echo $diff_list | grep remove` + + if [ ! -z "${added_list}" ]; then + echo + echo Added devices: + filter "add" $added_list + fi + + if [ ! -z "${removed_list}" ]; then + echo + echo Removed devices: + filter "remove" $removed_list + fi + + sleep 1 +done diff --git a/03_module/Makefile b/03_module/Makefile new file mode 100644 index 0000000..7337fb9 --- /dev/null +++ b/03_module/Makefile @@ -0,0 +1,17 @@ +KDIR ?= ~/work/buildroot-2021.02.7/output/build/linux-5.10.7 +CHECKPATCH := $(KDIR)/scripts/checkpatch.pl + +SRC := argsmod.c +OBJS := $(SRC:.c=.o) + +obj-m += $(OBJS) + +all: + $(CHECKPATCH) -f $(SRC) || exit 1 + $(MAKE) -C $(KDIR) M=$(PWD) modules + +default: all + +clean: + $(MAKE) -C $(KDIR) M=$(PWD) clean + diff --git a/03_module/argsmod.c b/03_module/argsmod.c new file mode 100644 index 0000000..6f9d69b --- /dev/null +++ b/03_module/argsmod.c @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Victor Krasnoshchok "); +MODULE_DESCRIPTION("Compute sum & diff of mod's args upon init/exit."); +MODULE_VERSION("0.1"); + +static int first_arg; +static int second_arg; + +module_param(first_arg, int, 0444); /* S_IRUGO */ +MODULE_PARM_DESC(first_arg, "The first int arg."); + +module_param(second_arg, int, 0444); +MODULE_PARM_DESC(second_arg, "The second int arg."); + +static int __init args_test_init(void) +{ + pr_info("%d + %d = %d\n", first_arg, second_arg, (first_arg + second_arg)); + return 0; +} + +static void __exit args_test_exit(void) +{ + pr_info("%d - %d = %d\n", first_arg, second_arg, (first_arg - second_arg)); +} + +module_init(args_test_init); +module_exit(args_test_exit); + diff --git a/03_module/compilation_log.txt b/03_module/compilation_log.txt new file mode 100644 index 0000000..a74411b --- /dev/null +++ b/03_module/compilation_log.txt @@ -0,0 +1,13 @@ +victor@orion5491  ~/work/gl_kernel_procamp_2021/03_module   task03  make +~/work/buildroot-2021.02.7/output/build/linux-5.10.7/scripts/checkpatch.pl -f argsmod.c || exit 1 +total: 0 errors, 0 warnings, 34 lines checked + +argsmod.c has no obvious style problems and is ready for submission. +make -C ~/work/buildroot-2021.02.7/output/build/linux-5.10.7 M=/home/victor/work/gl_kernel_procamp_2021/03_module modules +make[1]: Entering directory '/home/victor/work/buildroot-2021.02.7/output/build/linux-5.10.7' + CC [M] /home/victor/work/gl_kernel_procamp_2021/03_module/argsmod.o + MODPOST /home/victor/work/gl_kernel_procamp_2021/03_module/Module.symvers +WARNING: modpost: Symbol info of vmlinux is missing. Unresolved symbol check will be entirely skipped. + CC [M] /home/victor/work/gl_kernel_procamp_2021/03_module/argsmod.mod.o + LD [M] /home/victor/work/gl_kernel_procamp_2021/03_module/argsmod.ko +make[1]: Leaving directory '/home/victor/work/buildroot-2021.02.7/output/build/linux-5.10.7' diff --git a/03_module/console_log.txt b/03_module/console_log.txt new file mode 100644 index 0000000..fea22b0 --- /dev/null +++ b/03_module/console_log.txt @@ -0,0 +1,32 @@ +Starting dropbear sshd: OK + +Welcome to Buildroot +buildroot login: root +# cd /home/user/ +# ls +argsmod.ko +# insmod argsmod.ko first_arg=3 second_arg=4 +argsmod: loading out-of-tree module taints kernel. +3 + 4 = 7 +# rmmod argsmod.ko +3 - 4 = -1 +# insmod argsmod.ko first_arg=8 second_arg=6 +8 + 6 = 14 +# rmmod argsmod.ko +8 - 6 = 2 +# insmod argsmod.ko first_arg=5 +5 + 0 = 5 +# rmmod argsmod.ko +5 - 0 = 5 +# insmod argsmod.ko second_arg=10 +0 + 10 = 10 +# rmmod argsmod.ko +0 - 10 = -10 +# insmod argsmod.ko +0 + 0 = 0 +# rmmod argsmod.ko +0 - 0 = 0 +# exit + +Welcome to Buildroot +buildroot login: qemu-system-x86_64: terminating on signal 2 diff --git a/03_module/dmesg_log.txt b/03_module/dmesg_log.txt new file mode 100644 index 0000000..5ebded5 --- /dev/null +++ b/03_module/dmesg_log.txt @@ -0,0 +1,12 @@ +# dmesg +argsmod: loading out-of-tree module taints kernel. +3 + 4 = 7 +3 - 4 = -1 +8 + 6 = 14 +8 - 6 = 2 +5 + 0 = 5 +5 - 0 = 5 +0 + 10 = 10 +0 - 10 = -10 +0 + 0 = 0 +0 - 0 = 0