diff --git a/.github/workflows/push-pull.yml b/.github/workflows/push-pull.yml index 4aa54f3..ecc76fc 100644 --- a/.github/workflows/push-pull.yml +++ b/.github/workflows/push-pull.yml @@ -17,7 +17,48 @@ jobs: - name: free space run: | - ./free_space.sh + df . -h + sudo docker rmi $(docker image ls -aq) >/dev/null 2>&1 || true + sudo rm -rf \ + /usr/share/dotnet /usr/local/lib/android /opt/ghc \ + /usr/local/share/powershell /usr/share/swift /usr/local/.ghcup \ + /usr/lib/jvm || true + echo "some directories deleted" + sudo apt install aptitude -y >/dev/null 2>&1 + sudo aptitude purge aria2 ansible azure-cli shellcheck rpm xorriso zsync \ + esl-erlang firefox gfortran-8 gfortran-9 google-chrome-stable \ + google-cloud-sdk imagemagick \ + libmagickcore-dev libmagickwand-dev libmagic-dev ant ant-optional kubectl \ + mercurial apt-transport-https mono-complete libmysqlclient \ + unixodbc-dev yarn chrpath libssl-dev libxft-dev \ + libfreetype6 libfreetype6-dev libfontconfig1 libfontconfig1-dev \ + snmp pollinate libpq-dev postgresql-client powershell ruby-full \ + sphinxsearch subversion mongodb-org azure-cli microsoft-edge-stable \ + -y -f >/dev/null 2>&1 + sudo aptitude purge google-cloud-sdk -f -y >/dev/null 2>&1 + sudo aptitude purge microsoft-edge-stable -f -y >/dev/null 2>&1 || true + sudo apt purge microsoft-edge-stable -f -y >/dev/null 2>&1 || true + sudo aptitude purge '~n ^mysql' -f -y >/dev/null 2>&1 + sudo aptitude purge '~n ^php' -f -y >/dev/null 2>&1 + sudo aptitude purge '~n ^dotnet' -f -y >/dev/null 2>&1 + sudo rm -rf /usr/share/dotnet + sudo rm -rf "$AGENT_TOOLSDIRECTORY" + echo "some packages purged" echo -e "Removing unnecessary packages" + sudo apt-get autoremove -y >/dev/null 2>&1 + sudo apt-get autoclean -y >/dev/null 2>&1 + echo -e "Cleaning package cache" + sudo apt-get clean >/dev/null 2>&1 + echo -e "Removing orphaned packages" + sudo deborphan | xargs -r sudo apt-get -y remove >/dev/null 2>&1 + echo -e "Cleaning thumbnail cache" + rm -rf ~/.cache/thumbnails/* + echo -e "Cleaning system temporary files" + sudo rm -rf /tmp/* /var/tmp/* + echo -e "Cleaning journal logs (optional, keep last 2 weeks)" + sudo journalctl --vacuum-time=2weeks + echo -e "Cleaning old log files" + sudo find /var/log -type f -name "*.log" -exec truncate -s 0 {} \; + df . -h - uses: dorny/paths-filter@v3 id: changes diff --git a/.github/workflows/test-pr.yml b/.github/workflows/test-pr.yml deleted file mode 100644 index e21266a..0000000 --- a/.github/workflows/test-pr.yml +++ /dev/null @@ -1,48 +0,0 @@ -name: test-pr - -concurrency: - group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -on: [pull_request] - -jobs: - test-pr-job: - runs-on: ubuntu-latest - steps: - - - uses: actions/checkout@v4 - with: - github-server-url: https://github.com/dogusyuksel/embedded_docker - lfs: true - - - name: free space - run: | - ./free_space.sh - - - name: Login to Docker Hub - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_PASSWORD }} - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Build Project Docker - uses: docker/build-push-action@v5 - with: - load: true - tags: | - ${{ secrets.DOCKER_REPO }}:master - context: . - file: Dockerfile - pull: true - push: false - provenance: false - - - name: check fs - run: | - df . -h - ./run_docker.sh ${{ secrets.DOCKER_REPO }}:master "cd / && ls -al && ls -al thirdparty" - diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c025128 --- /dev/null +++ b/.gitignore @@ -0,0 +1,28 @@ +cjson +libcanard +libcsp +cmsis-header-stm32 +CMSIS_5 +FreeRTOS-Kernel +STM32F103X_HAL +custom_printf +openocd +STM32L4XX_HAL +STM32F103X_HAL + +esp8266/ESP8266_RTOS_SDK/ +esp8266/esp8266/ +esp32cam/esp-idf/ +esp32cam/esp32cam/ +cc2640r2f/ti_cc2640r2f_sdk/ +cc2640r2f/cc2640r2f/ +stm32l4a6_freertos/stm32l4a6_freertos/ +stm32f103_libcanard/stm32f103_libcanard/ +bbb_yocto/poky/ +bbb_yocto/bbb_yocto/ +atmega328p/atmega328p/ +libcsp_works/libcsp_works/ +helper/libcanard_sniffer/build/ +renode/renode/ + +*.exe diff --git a/Dockerfile b/Dockerfile index 409bc90..923127e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,13 +7,13 @@ RUN apt-get update && \ apt-get -y upgrade && \ apt-get -y --no-install-recommends install \ build-essential \ - clang-format \ - valgrind \ - gdb \ - ruby \ + software-properties-common \ + language-pack-en-base \ git \ git-core \ git-lfs \ + ffmpeg \ + python3 \ python3-dbg \ python3-dev \ python3-pip \ @@ -22,41 +22,37 @@ RUN apt-get update && \ python3-jinja2 \ python3-subunit \ vim \ - language-pack-en-base \ - tree \ + wget \ + gcc \ + g++ \ + gdbserver \ + gcc-avr \ + binutils-avr \ + avr-libc \ + gdb-avr \ + libusb-dev \ + avrdude \ can-utils \ - socat \ - cmake \ + gdb-multiarch \ + graphviz \ gcc-multilib \ g++-multilib \ - software-properties-common \ - wget \ - openocd \ stlink-tools \ gdb-multiarch \ - usbutils \ - libusb-1.0-0-dev \ - default-jdk \ - gawk \ - diffstat \ + can-utils \ + openocd \ + valgrind \ + libncurses5 \ + libncurses5-dev \ + cmake \ + gdb \ + clang \ + clang-format \ unzip \ - texinfo \ - chrpath \ - cpio \ - xz-utils \ - debianutils \ - iputils-ping \ - libegl1-mesa \ - libsdl1.2-dev \ - xterm \ - file \ - mesa-common-dev \ - zstd \ - liblz4-tool \ - bluetooth build-essential \ - libglib2.0-dev \ - libdbus-1-dev \ - libncurses-dev \ + ruby \ + texlive-xetex \ + inkscape \ + libcairo2-dev \ flex \ bison \ gperf \ @@ -66,70 +62,105 @@ RUN apt-get update && \ libffi-dev \ libssl-dev \ dfu-util \ - splint && \ - apt-get -y clean + default-jre \ + default-jdk \ + diffstat \ + chrpath \ + cpio \ + gawk \ + file \ + zstd \ + liblz4-tool \ + python3.10-venv \ + curl \ + autoconf \ + automake \ + libtool \ + libzmq3-dev \ + && rm -rf /var/lib/apt/lists/* \ + && apt-get -y clean -RUN python3 -m pip install pyserial +RUN git config --global --add safe.directory /workspace + +RUN ln -s /usr/bin/python3 /usr/bin/python RUN gem install ceedling +RUN python3 -m pip install pyserial + RUN pip install gcovr +RUN pip install Pillow==9.5.0 +RUN pip install json_repair==0.30.0 +RUN pip install Requests==2.32.3 +RUN pip install black==21.12b0 +RUN pip install click==8.0.4 +RUN pip install flake8==4.0.1 +RUN pip install isort==6.0.1 +RUN pip install pandas==2.3.0 +RUN pip install xgboost==3.0.2 +RUN pip install pulp==3.2.1 +RUN pip install -U scikit-learn +RUN pip install ffmpeg_python==0.2.0 +RUN pip install openai==1.86.0 +RUN pip install opencv_python==4.10.0.84 +RUN pip install matplotlib==3.10.5 +RUN pip install graphviz +RUN pip install reportlab +RUN pip install google-auth==2.40.3 +RUN pip install google-auth-oauthlib==1.2.2 +RUN pip install google-auth-httplib2==0.2.0 +RUN pip install google-api-python-client==2.172.0 +RUN pip install scikit-learn==1.7.2 +RUN pip install pytest +RUN pip install gTTS==2.5.4 +RUN pip install googletrans==4.0.2 +RUN pip install flatlib==0.2.3 +RUN pip install pyswisseph==2.10.3.2 +RUN pip install kerykeion==4.26.2 +RUN pip install geopy==2.4.1 +RUN pip install pytz==2025.2 +RUN pip install timezonefinder==6.5.9 +RUN pip install pylatex==1.4.2 +RUN pip install numpy==2.0.2 +RUN pip install skyfield==1.53 +RUN pip install pikepdf==9.8.1 +RUN pip install pyparsing==2.4.2 +RUN pip install flask==3.1.2 +RUN pip install jsonify==0.5 -RUN git config --global --add safe.directory /workspace +# Install non-eabi-gcc +RUN wget https://developer.arm.com/-/media/Files/downloads/gnu-rm/10.3-2021.10/gcc-arm-none-eabi-10.3-2021.10-x86_64-linux.tar.bz2 && \ + tar -xf gcc-arm-none-eabi-10.3-2021.10-x86_64-linux.tar.bz2 +ENV PATH="/gcc-arm-none-eabi-10.3-2021.10/bin:${PATH}" -RUN ln -s /usr/bin/python3 /usr/bin/python -RUN pip install matplotlib -RUN pip install pytest -RUN python3 -m pip install flake8 +# Install Renode +ARG RENODE_VERSION=1.16.0 +RUN wget https://github.com/renode/renode/releases/download/v${RENODE_VERSION}/renode_${RENODE_VERSION}_amd64.deb && \ + apt-get update && \ + apt-get install -y --no-install-recommends ./renode_${RENODE_VERSION}_amd64.deb python3-dev && \ + rm ./renode_${RENODE_VERSION}_amd64.deb && \ + rm -rf /var/lib/apt/lists/* +RUN pip3 install -r /opt/renode/tests/requirements.txt --no-cache-dir -RUN cd / && \ - git clone git://git.openembedded.org/bitbake +# Yocto/Bitbake related +RUN cd / && git clone git://git.openembedded.org/bitbake ENV PATH="${PATH}:/bitbake/bin" ENV PYTHONPATH="${PYTHONPATH}:/bitbake/lib" RUN pip install -r bitbake/toaster-requirements.txt -RUN wget https://developer.arm.com/-/media/Files/downloads/gnu-rm/10.3-2021.10/gcc-arm-none-eabi-10.3-2021.10-x86_64-linux.tar.bz2 && \ - tar -xf gcc-arm-none-eabi-10.3-2021.10-x86_64-linux.tar.bz2 -ENV PATH="/gcc-arm-none-eabi-10.3-2021.10/bin:${PATH}" - -RUN pip install pyparsing==2.4.2 -RUN cd / && \ - mkdir esp && \ - cd esp && \ +# esp8266 related +RUN cd / && mkdir esp && cd esp && \ wget https://dl.espressif.com/dl/xtensa-lx106-elf-gcc8_4_0-esp-2020r3-linux-amd64.tar.gz && \ tar -xzf xtensa-lx106-elf-gcc8_4_0-esp-2020r3-linux-amd64.tar.gz ENV PATH="/esp/xtensa-lx106-elf/bin:${PATH}" -RUN cd / && \ - git clone --recursive https://github.com/espressif/ESP8266_RTOS_SDK.git ESP8266_RTOS_SDK && \ - python3 -m pip install --user -r ESP8266_RTOS_SDK/requirements.txt -RUN cd / && \ - git clone -b v5.3.2 --recursive https://github.com/espressif/esp-idf.git esp-idf && \ - cd esp-idf && \ - apt install -y python3.10-venv && \ - ./install.sh esp32,esp32s2,esp32s3 && \ - . ./export.sh && \ - git clone https://github.com/espressif/esp32-camera.git && \ - idf.py add-dependency "espressif/esp32-camera" - -ADD /patches/ /thirdparty -RUN git clone https://github.com/STMicroelectronics/stm32l4xx_hal_driver.git /thirdparty/STM32L4XX_HAL && \ - git clone https://github.com/modm-io/cmsis-header-stm32.git /thirdparty/cmsis-header-stm32 && \ - git clone https://github.com/ARM-software/CMSIS_5.git /thirdparty/CMSIS_5 && \ - git clone https://github.com/FreeRTOS/FreeRTOS-Kernel.git /thirdparty/FreeRTOS-Kernel && \ - git clone https://github.com/STMicroelectronics/stm32f1xx_hal_driver.git /thirdparty/STM32F103X_HAL && \ - git clone https://github.com/dogusyuksel/embedded_linting.git /thirdparty/linting && \ - git clone https://github.com/mpaland/printf.git /thirdparty/custom_printf && \ - git clone https://github.com/STMicroelectronics/OpenOCD.git /thirdparty/openocd && \ - cd /thirdparty && git clone -b kirkstone git://git.yoctoproject.org/poky.git && \ - git clone https://github.com/dogusyuksel/ti_cc2640r2f_sdk.git ti_cc2640r2f_sdk && \ - cat /thirdparty/openocd/tcl/board/stm32f103c8_blue_pill.cfg | sed -e "s/set FLASH_SIZE 0x20000/set FLASH_SIZE 0x10000/" > /thirdparty/openocd/tcl/board/stm32f103c8_custom.cfg && \ - git clone https://github.com/OpenCyphal/libcanard.git /thirdparty/libcanard && \ - cd /thirdparty/libcanard && git checkout 43de1c4966b8d1e5d57978949d63e697f045b358 && \ - git submodule update --init --recursive && \ - cd / && patch -d . -p1 < /thirdparty/transport.py.patch && patch -d . -p1 < /thirdparty/canard_stm32.patch && \ - cd /thirdparty/ && git clone https://github.com/DaveGamble/cJSON.git cjson && \ - cd /thirdparty/cjson && git checkout 87d8f0961a01bf09bef98ff89bae9fdec42181ee && \ - mkdir build && cd build && cmake .. && make +# rust related +# Rust + Cargo +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \ + | sh -s -- -y +# cargo and rust path +ENV PATH="/root/.cargo/bin:${PATH}" +# test cargo +RUN cargo --version && rustc --version CMD ["/bin/bash"] diff --git a/README.md b/README.md index d80d750..cf320ae 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,25 @@ # embedded_docker + A Docker File to be used in different kind of embedded projects. + +This is kind of a base project to work on different kind of technologies. + +main script is "bake" and the below is its help menu + +``` +This script is used for setting up the environment. +Options are are: + --verbose : Enable verbose output + --module : Specify a module to include (can be used multiple times) + : Available modules are: + - helper + - esp8266 + - esp32cam + - cc2640r2f + - stm32l4a6_freertos + - stm32f103_libcanard + - bbb_yocto + - atmega328p + - libcsp_works + - renode +``` diff --git a/atmega328p/setupenv b/atmega328p/setupenv new file mode 100755 index 0000000..ed34f83 --- /dev/null +++ b/atmega328p/setupenv @@ -0,0 +1,9 @@ +#!/bin/bash + +# clone and build atmega328p module +echo "==================> clone and build atmega328p module" +if [ ! -d "atmega328p" ]; then + git clone https://github.com/dogusyuksel/atmega328p.git atmega328p +fi + +cd atmega328p && make && cd - diff --git a/bake b/bake new file mode 100755 index 0000000..f81b82c --- /dev/null +++ b/bake @@ -0,0 +1,129 @@ +#!/bin/bash + +print_help() { + echo "This script is used for setting up the environment." + echo "Options are are:" + echo " --verbose : Enable verbose output" + echo " --module : Specify a module to include (can be used multiple times)" + echo " : Available modules are:" + echo " - helper" + echo " - esp8266" + echo " - esp32cam" + echo " - cc2640r2f" + echo " - stm32l4a6_freertos" + echo " - stm32f103_libcanard" + echo " - bbb_yocto" + echo " - atmega328p" + echo " - libcsp_works" + echo " - renode" +} + +# args +PARSED=$(getopt \ + --options "" \ + --longoptions verbose,help,module: \ + --name "$0" \ + -- "$@" +) + +# if getopt failed, then return +if [[ $? -ne 0 ]]; then exit 1; fi + +# set not-parsed parameters +eval set -- "$PARSED" + +VERBOSE=0 +MODULES=() # list + +while true; do + case "$1" in + --help) + print_help + exit 0 + ;; + --verbose) + VERBOSE=1 + shift + ;; + --module) + MODULES+=("$2") # add into the list + shift 2 + ;; + --) + shift + break + ;; + *) + echo "Unknown option: $1" + exit 1 + ;; + esac +done + +echo "Verbose: $VERBOSE" + +echo "Modules:" +for item in "${MODULES[@]}"; do + echo " - $item" +done + +echo "==================> Lets download and compile common dependencies..." + +echo "==================> CJSON" +# CJSON +if [ ! -d "cjson" ]; then + git clone https://github.com/DaveGamble/cJSON.git cjson + cd cjson && git checkout 87d8f0961a01bf09bef98ff89bae9fdec42181ee + mkdir build && cd build && cmake .. && make && cd ../../ +fi + +echo "==================> libcanard" +# libcanard +if [ ! -d "libcanard" ]; then + git clone https://github.com/OpenCyphal/libcanard.git libcanard + cd libcanard && git checkout 43de1c4966b8d1e5d57978949d63e697f045b358 + git submodule update --init --recursive + cp -rf ../libcanard_patches/* . + patch -d . -p1 < canard_stm32.patch && patch -d . -p1 < transport.py.patch +fi + +echo "==================> libcsp" +# libcsp +if [ ! -d "libcsp" ]; then + git clone https://github.com/libcsp/libcsp.git libcsp + cd libcsp && git checkout 48f7fb0b57f610bf65bab1aa2d1357c3b9722782 && cd - +fi + +echo "==================> custom_printf" +# custom_printf +if [ ! -d "custom_printf" ]; then + git clone https://github.com/mpaland/printf.git custom_printf +fi + +echo "==================> Lets download selected modules' dependencies" +for item in "${MODULES[@]}"; do + if [[ "$item" == *"stm32l4a6_freertos"* ]]; then + git clone https://github.com/STMicroelectronics/stm32l4xx_hal_driver.git STM32L4XX_HAL + fi + if [[ "$item" == *"stm32f103_libcanard"* ]]; then + git clone https://github.com/STMicroelectronics/stm32f1xx-hal-driver.git STM32F103X_HAL + fi + if [[ "$item" == *"stm32l4a6_freertos"* || "$item" == *"stm32f103_libcanard"* ]]; then + git clone https://github.com/modm-io/cmsis-header-stm32.git cmsis-header-stm32 + git clone https://github.com/ARM-software/CMSIS_5.git CMSIS_5 + git clone https://github.com/FreeRTOS/FreeRTOS-Kernel.git FreeRTOS-Kernel + + git clone https://github.com/STMicroelectronics/OpenOCD.git openocd + cat openocd/tcl/board/stm32f103c8_blue_pill.cfg | sed -e "s/set FLASH_SIZE 0x20000/set FLASH_SIZE 0x10000/" > openocd/tcl/board/stm32f103c8_custom.cfg + fi +done + +echo "==================> Lets download and compile modules" +for item in "${MODULES[@]}"; do + if [[ "$item" == *"bbb_yocto"* ]]; then + ## special case for bbb_yocto, it cannot be run as root + ./dyshell -b -c "cd /workspace/$item && ./setupenv && cd -" + else + ./dyshell -b -s -c "cd /workspace/$item && ./setupenv && cd -" + fi +done diff --git a/bbb_yocto/setupenv b/bbb_yocto/setupenv new file mode 100755 index 0000000..b3b1691 --- /dev/null +++ b/bbb_yocto/setupenv @@ -0,0 +1,20 @@ +#!/bin/bash + +# clone and build bbb_yocto module +echo "==================> clone and build bbb_yocto module" +if [ ! -d "bbb_yocto" ]; then + git clone https://github.com/dogusyuksel/bbb-yocto.git bbb_yocto +fi + +# clone kirkstone poky module +echo "==================> clone kirkstone poky" +if [ ! -d "poky" ]; then + git clone -b kirkstone git://git.yoctoproject.org/poky.git poky + cd bbb_yocto && source /workspace/bbb_yocto/poky/oe-init-build-env + sed -i 's|^#MACHINE ?= "beaglebone-yocto"|MACHINE ?= "beaglebone-yocto"\nRM_OLD_IMAGE = "1"\nINHERIT += "rm_work"|' /workspace/bbb_yocto/bbb_yocto/build/local.conf +fi + +cd /workspace/bbb_yocto/bbb_yocto/build && bitbake-layers add-layer /workspace/bbb_yocto/bbb_yocto/meta-custom && bitbake-layers show-recipes hello +bitbake hello && ls -l ./tmp/deploy/rpm/*/ | grep hello +echo 'RUN THIS (BUT CANNOT BECAUSE OF MEMORY) ==> cd /workspace/bbb_yocto/bbb_yocto/build && bitbake core-image-full-cmdline && find . -name "*.wic"' + diff --git a/build_docker.sh b/build_docker.sh deleted file mode 100755 index 46a5dad..0000000 --- a/build_docker.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -if [ "$#" -lt 1 ]; then - echo "you should give the image name" - exit 1 -fi - -if [ "$#" -gt 1 ]; then - echo "you should not give additional args" - exit 1 -fi - -docker build -t $@ . diff --git a/cc2640r2f/environment b/cc2640r2f/environment new file mode 100644 index 0000000..826d487 --- /dev/null +++ b/cc2640r2f/environment @@ -0,0 +1,5 @@ +export XDCTOOLS_JAVA_HOME="/usr/lib/jvm/default-java" +export CCS_PATH="/workspace/cc2640r2f/ti_cc2640r2f_sdk/ccs1281" +export TI_SDK_PATH="/workspace/cc2640r2f/ti_cc2640r2f_sdk/simplelink_cc2640r2_sdk_5_30_01_11" +export APP_PATH="/workspace/cc2640r2f/cc2640r2f/firmware/ble5_simple_peripheral_cc2640r2lp_app" +export STACK_LIBRARY_PATH="/workspace/cc2640r2f/cc2640r2f/firmware/ble5_simple_peripheral_cc2640r2lp_stack_library" diff --git a/cc2640r2f/setupenv b/cc2640r2f/setupenv new file mode 100755 index 0000000..baa42fc --- /dev/null +++ b/cc2640r2f/setupenv @@ -0,0 +1,26 @@ +#!/bin/bash + +# clone cc2640r2 sdk +echo "==================> clone cc2640r2 sdk" +if [ ! -d "ti_cc2640r2f_sdk" ]; then + git clone https://github.com/dogusyuksel/ti_cc2640r2f_sdk.git ti_cc2640r2f_sdk +fi + + +# clone and build cc2640r2f module +echo "==================> clone and build cc2640r2f module" +if [ ! -d "cc2640r2f" ]; then + git clone https://github.com/dogusyuksel/cc2640r2f_ble.git cc2640r2f +fi +source environment + +# set linker cmd file +LINKER_FILE="./cc2640r2f/firmware/ble5_simple_peripheral_cc2640r2lp_stack_library/FlashROM_Library/lib_linker.cmd" +# replace /thirdparty with actual path +sed -i "s|/thirdparty|/workspace/cc2640r2f|g" "$LINKER_FILE" +echo "==================> linker file manually edited" + +cd cc2640r2f/firmware/ble5_simple_peripheral_cc2640r2lp_app +make -C ./FlashROM_StackLibrary/ clean +make -C ./FlashROM_StackLibrary/ all +cd - \ No newline at end of file diff --git a/dyshell b/dyshell new file mode 100755 index 0000000..9d64a4b --- /dev/null +++ b/dyshell @@ -0,0 +1,100 @@ +#!/bin/bash + +BASENAME="dyshell" +preferred_name=$BASENAME":latest" + +if [ "$#" -eq 1 ]; then + if [ "$1" = "-h" ]; then + echo "USAGE:" + echo " '-b' to build new docker (optional)" + echo " '-c ' to run command (optional)" + echo " '-s' to execute docker in root (optional)" + exit 0 + fi +fi + +system_cleanup() { + echo "Disk usage before cleanup" + df -h + + echo -e "Removing unnecessary packages" + sudo apt-get autoremove -y >/dev/null 2>&1 + sudo apt-get autoclean -y >/dev/null 2>&1 + + echo -e "Cleaning package cache" + sudo apt-get clean >/dev/null 2>&1 + + echo -e "Removing orphaned packages" + sudo deborphan | xargs -r sudo apt-get -y remove >/dev/null 2>&1 + + echo -e "Cleaning thumbnail cache" + rm -rf ~/.cache/thumbnails/* + + echo -e "Cleaning system temporary files" + sudo rm -rf /tmp/* /var/tmp/* + + echo -e "Cleaning journal logs (optional, keep last 2 weeks)" + sudo journalctl --vacuum-time=2weeks + + echo -e "Cleaning old log files" + sudo find /var/log -type f -name "*.log" -exec truncate -s 0 {} \; + + echo -e "Disk usage after cleanup" + df -h + + echo -e "\nCleanup completed safely!" +} + +system_cleanup + +# Default values: +build_docker=false +is_sudo=false +command="" + +# It's the : after d that signifies that it takes an option argument. + +while getopts bsc: opt; do + case $opt in + b) build_docker=true ;; + s) is_sudo=true ;; + c) command=$OPTARG ;; + *) echo 'error in command line parsing' >&2 + exit 1 + esac +done + +shift "$(( OPTIND - 1 ))" + +"$build_docker" && echo 'Got the -b option' +"$is_sudo" && echo 'Got the -s option' +printf 'Option -c: %s\n' "$command" + +if [ "$build_docker" = "true" ]; then + docker build -t $preferred_name . +fi + +docker images | grep $BASENAME +if [ "$?" -ne 0 ]; then + echo "it means there is no built docker, cannot proceed" + exit 1 +fi + +# here, we probably have the docker, then run it + +if [ "$command" != "" ]; then + echo "command running" + if [ "$is_sudo" = "true" ]; then + docker run --rm -t --net=host -v $(pwd):/workspace --entrypoint=/bin/bash $preferred_name -c "$command" + else + docker run --rm -t --net=host -v $(pwd):/workspace -v /etc/passwd:/etc/passwd:ro -v /etc/group:/etc/group:ro -u $(id -u ${USER}):$(id -g ${USER}) --entrypoint=/bin/bash $preferred_name -c "$command" + fi + exit 0 +fi + +# if no command run, lets start the docker for personal use +if [ "$is_sudo" = "true" ]; then + docker run --rm -it --net=host -v $(pwd):/workspace --entrypoint=/bin/bash $preferred_name +else + docker run --rm -it --net=host -v $(pwd):/workspace -v /etc/passwd:/etc/passwd:ro -v /etc/group:/etc/group:ro -u $(id -u ${USER}):$(id -g ${USER}) --entrypoint=/bin/bash $preferred_name +fi \ No newline at end of file diff --git a/esp32cam/setupenv b/esp32cam/setupenv new file mode 100755 index 0000000..7ba5415 --- /dev/null +++ b/esp32cam/setupenv @@ -0,0 +1,27 @@ +#!/bin/bash + +# clone esp32 idf and camera module +echo "==================> clone esp32 idf and camera module" +if [ ! -d "esp-idf" ]; then + git clone -b v5.3.2 --recursive https://github.com/espressif/esp-idf.git esp-idf +fi +cd esp-idf +./install.sh esp32,esp32s2,esp32s3 +. ./export.sh +if [ ! -d "esp32-camera" ]; then + git clone https://github.com/espressif/esp32-camera.git +fi +cd esp32-camera && git checkout 38674c1f1570c63c4db4fcf21cf6cbf3bc595de4 && cd - +idf.py add-dependency "espressif/esp32-camera" +cd .. + +# clone and build esp32cam module +echo "==================> clone and build esp32cam module" +if [ ! -d "esp32cam" ]; then + git clone https://github.com/dogusyuksel/esp32-cam.git esp32cam +fi +cd esp-idf +. ./export.sh +cd ../esp32cam/ip_camera +idf.py build +cd ../.. \ No newline at end of file diff --git a/esp8266/setupenv b/esp8266/setupenv new file mode 100755 index 0000000..e9c2e6d --- /dev/null +++ b/esp8266/setupenv @@ -0,0 +1,23 @@ +#!/bin/bash + +# because of some dependency issues, we need to set pyparsing explicitly +pip install pyparsing==2.4.2 + +# clone esp8266 sdk +echo "==================> clone esp8266 sdk" +if [ ! -d "ESP8266_RTOS_SDK" ]; then + git clone --recursive https://github.com/espressif/ESP8266_RTOS_SDK.git ESP8266_RTOS_SDK + cd ESP8266_RTOS_SDK && git checkout 2f586ea43f18a7d818c32b746a73e3302ad14ce2 && cd - +fi +python3 -m pip install --user -r ESP8266_RTOS_SDK/requirements.txt + +# clone and build esp8266 module +echo "==================> clone and build esp8266 module" +if [ ! -d "esp8266" ]; then + git clone https://github.com/dogusyuksel/esp-8266.git esp8266 +fi +export IDF_PATH="/workspace/esp8266/ESP8266_RTOS_SDK" # important step +cd esp8266 +./build_all.sh "./projects/protocols/sockets/tcp_server" +# ./build_all.sh --> it builds all examples +cd - \ No newline at end of file diff --git a/free_space.sh b/free_space.sh deleted file mode 100755 index b6e6930..0000000 --- a/free_space.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash - -df . -h -sudo docker rmi $(docker image ls -aq) >/dev/null 2>&1 || true -sudo rm -rf \ - /usr/share/dotnet /usr/local/lib/android /opt/ghc \ - /usr/local/share/powershell /usr/share/swift /usr/local/.ghcup \ - /usr/lib/jvm || true -echo "some directories deleted" -sudo apt install aptitude -y >/dev/null 2>&1 -sudo aptitude purge aria2 ansible azure-cli shellcheck rpm xorriso zsync \ - esl-erlang firefox gfortran-8 gfortran-9 google-chrome-stable \ - google-cloud-sdk imagemagick \ - libmagickcore-dev libmagickwand-dev libmagic-dev ant ant-optional kubectl \ - mercurial apt-transport-https mono-complete libmysqlclient \ - unixodbc-dev yarn chrpath libssl-dev libxft-dev \ - libfreetype6 libfreetype6-dev libfontconfig1 libfontconfig1-dev \ - snmp pollinate libpq-dev postgresql-client powershell ruby-full \ - sphinxsearch subversion mongodb-org azure-cli microsoft-edge-stable \ - -y -f >/dev/null 2>&1 -sudo aptitude purge google-cloud-sdk -f -y >/dev/null 2>&1 -sudo aptitude purge microsoft-edge-stable -f -y >/dev/null 2>&1 || true -sudo apt purge microsoft-edge-stable -f -y >/dev/null 2>&1 || true -sudo aptitude purge '~n ^mysql' -f -y >/dev/null 2>&1 -sudo aptitude purge '~n ^php' -f -y >/dev/null 2>&1 -sudo aptitude purge '~n ^dotnet' -f -y >/dev/null 2>&1 -sudo apt-get autoremove -y >/dev/null 2>&1 -sudo apt-get autoclean -y >/dev/null 2>&1 -sudo rm -rf /usr/share/dotnet -sudo rm -rf "$AGENT_TOOLSDIRECTORY" -echo "some packages purged" -df . -h - diff --git a/helper/.gitkeep b/helper/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/helper/comment_remover/.gitkeep b/helper/comment_remover/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/helper/comment_remover/Makefile b/helper/comment_remover/Makefile new file mode 100644 index 0000000..e4e7a38 --- /dev/null +++ b/helper/comment_remover/Makefile @@ -0,0 +1,19 @@ +CC = gcc +RM = rm -rf +CFLAGS = -Wall -Wextra -g3 -O0 -lpthread -Wno-deprecated-declarations +all_c_files := $(wildcard *.c) + +.PHONY: all clean + +all: + for file in $(all_c_files); do \ + $(CC) $$file -o $$file".exe" $(CFLAGS); \ + done + +partial: + $(CC) $(FILE) -o $(FILE)".exe" $(CFLAGS); \ + +clean: + for file in $(all_c_files); do \ + $(RM) -rf $$file".exe"; \ + done diff --git a/helper/comment_remover/comment_remover.c b/helper/comment_remover/comment_remover.c new file mode 100644 index 0000000..812f1ac --- /dev/null +++ b/helper/comment_remover/comment_remover.c @@ -0,0 +1,441 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define VERSION "1.0.0" + +#define MAX_LINE_CHAR_LEN 1024 + +#define PRINT_ERROR(s) fprintf(stderr, "(%s - %d): %s\n", __func__, __LINE__, #s) +#define PRINT_ERROR_PRM1(s, prm1) fprintf(stderr, "(%s - %d): %s %s\n", __func__, __LINE__, #s, prm1) + +struct file_queue_entry { + char *filename; + TAILQ_ENTRY(file_queue_entry) entries; +}; +TAILQ_HEAD(file_queue_head, file_queue_entry); + +static struct option parameters[] = { + {"help", no_argument, 0, 'h'}, + {"directory", required_argument, 0, 'd'}, + {"file", required_argument, 0, 'f'}, + {NULL, 0, 0, 0}, +}; + +static void print_help(char *app_name) { + int i = 0; + + if (!app_name) { + return; + } + + printf("usage of %s (version %s)\n", app_name, VERSION); + while (parameters[i].name != NULL) { + printf("\t--%s or -%c, %s\n", parameters[i].name, (char)parameters[i].val, + (parameters[i].has_arg == no_argument) + ? ("no_argument") + : ((parameters[i].has_arg == required_argument) ? ("required_argument") : ("optional_argument"))); + i++; + } +} + +static bool is_dir_exist(char *directory) { + DIR *dir = NULL; + + if (!directory) { + return false; + } + + dir = opendir(directory); + if (dir) { + closedir(dir); + return true; + } + + return false; +} + +static void add_filename_to_queue(char *filename, struct file_queue_head *queue) { + struct file_queue_entry *fentry; + + if (!filename || !queue) { + return; + } + + fentry = malloc(sizeof(struct file_queue_entry)); + if (!fentry) { + PRINT_ERROR("malloc() failed"); + return; + } + + fentry->filename = strdup(filename); + if (!(fentry->filename)) { + PRINT_ERROR("strdup() failed"); + return; + } + + TAILQ_INSERT_HEAD(queue, fentry, entries); +} + +static void print_queue(struct file_queue_head *queue) { + int i = 0; + struct file_queue_entry *np = NULL; + + if (!queue) { + return; + } + + TAILQ_FOREACH(np, queue, entries) { + printf("entry %d\n", i); + if (np->filename) { + printf("\tfilename: %s\n", np->filename); + } + i++; + } +} + +static void free_queue(struct file_queue_head *queue) { + struct file_queue_entry *n1, *n2 = NULL; + + if (!queue) { + return; + } + + n1 = TAILQ_FIRST(queue); + while (n1 != NULL) { + n2 = TAILQ_NEXT(n1, entries); + if (n1->filename) { + free(n1->filename); + } + if (n1) { + free(n1); + } + n1 = n2; + } + TAILQ_INIT(queue); +} + +static int ptree(char *curpath, char *const path, struct file_queue_head *queue) { + char ep[512]; + char p[512]; + DIR *dirp; + struct dirent entry; + struct dirent *endp; + struct stat st; + + if (curpath != NULL) + snprintf(ep, sizeof(ep), "%s/%s", curpath, path); + else + snprintf(ep, sizeof(ep), "%s", path); + + if (stat(ep, &st) == -1) + return EXIT_FAILURE; + + if ((dirp = opendir(ep)) == NULL) + return EXIT_FAILURE; + + for (;;) { + endp = NULL; + if (readdir_r(dirp, &entry, &endp) == -1) { + closedir(dirp); + return EXIT_FAILURE; + } + if (endp == NULL) + break; + assert(endp == &entry); + if (strcmp(entry.d_name, ".") == 0 || strcmp(entry.d_name, "..") == 0) + continue; + if (curpath != NULL) + snprintf(ep, sizeof(ep), "%s/%s/%s", curpath, path, entry.d_name); + else + snprintf(ep, sizeof(ep), "%s/%s", path, entry.d_name); + if (stat(ep, &st) == -1) { + closedir(dirp); + return EXIT_FAILURE; + } + if (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode)) { + if (!S_ISDIR(st.st_mode)) { + if (ep[strlen(ep) - 2] == '.' && (ep[strlen(ep) - 1] == 'c' || ep[strlen(ep) - 1] == 'C')) + add_filename_to_queue(ep, queue); + if (ep[strlen(ep) - 2] == '.' && (ep[strlen(ep) - 1] == 'h' || ep[strlen(ep) - 1] == 'H')) + add_filename_to_queue(ep, queue); + if (ep[strlen(ep) - 2] == '.' && (ep[strlen(ep) - 1] == 'hpp' || ep[strlen(ep) - 1] == 'HPP')) + add_filename_to_queue(ep, queue); + if (ep[strlen(ep) - 2] == '.' && (ep[strlen(ep) - 1] == 'cpp' || ep[strlen(ep) - 1] == 'CPP')) + add_filename_to_queue(ep, queue); + } + } + + if (S_ISDIR(st.st_mode) == 0) + continue; + if (curpath != NULL) + snprintf(p, sizeof(p), "%s/%s", curpath, path); + else + snprintf(p, sizeof(p), "%s", path); + snprintf(ep, sizeof(ep), "%s", entry.d_name); + ptree(p, ep, queue); + } + closedir(dirp); + + return EXIT_SUCCESS; +} + +static int find_files_in_directory(char *directory, struct file_queue_head *queue) { + if (!directory || !queue) { + return EXIT_FAILURE; + } + + return ptree(NULL, directory, queue); +} + +static void trim_beginning(char *input) { + char *beg = NULL; + int i = 0, len = 0; + + if (!input) { + return; + } + + len = strlen(input); + for (i = 0; i < len; i++) { + if (input[i] == '\r' || input[i] == '\n' || input[i] == '\t' || input[i] == ' ') { + input[i] = ' '; + } else { + break; + } + } + + i = 0, len = 0; + while ((beg = strstr(input, " ")) != NULL) { + len = strlen(input); + memmove(beg, beg + 1, len - (beg - input)); + beg = NULL; + } +} + +static void trim_end(char *input) { + int i = 0, len = 0; + + if (!input) { + return; + } + + len = strlen(input); + for (i = len - 1; i >= 0; i--) { + if (input[i] == '\r' || input[i] == '\n' || input[i] == '\t' || input[i] == ' ') { + input[i] = 0; + } else { + break; + } + } +} + +static bool is_whole_unprintable(char *input) { + int i = 0, len = 0; + + if (!input) { + return; + } + + len = strlen(input); + + for (i = 0; i < len; i++) { + if (input[i] >= 0x20 && input[i] <= 0x7e) { + return false; + } + } + + return true; +} + +static int remove_comments_in_a_file(char *filename) { + bool case3_started = false; + bool last_line_was_unprintable = false; + FILE *fs = NULL, *fs_new = NULL; + char buffer[MAX_LINE_CHAR_LEN] = {0}; + char line_buffer[MAX_LINE_CHAR_LEN] = {0}; + char line_buffer_backup[MAX_LINE_CHAR_LEN] = {0}; + char new_file_name[MAX_LINE_CHAR_LEN] = {0}; + + if (!filename) { + return EXIT_FAILURE; + } + + fs = fopen(filename, "r"); + if (!fs) { + PRINT_ERROR("fopen() failed"); + EXIT_FAILURE; + } + + snprintf(new_file_name, sizeof(new_file_name), "%s.edited", filename); + fs_new = fopen(new_file_name, "w+"); + if (!fs_new) { + PRINT_ERROR("fopen() failed"); + EXIT_FAILURE; + } + + while (fgets(line_buffer, MAX_LINE_CHAR_LEN, fs) != NULL) { + memcpy(line_buffer_backup, line_buffer, sizeof(line_buffer_backup)); + trim_beginning(line_buffer); + trim_end(line_buffer); + + if (case3_started) { + if (strlen(line_buffer) >= 2 && line_buffer[strlen(line_buffer) - 1] == '/' && + line_buffer[strlen(line_buffer) - 2] == '*') { + case3_started = false; + } + goto clean_continue; + } + + if (strlen(line_buffer) >= 2 && line_buffer[0] == '/' && line_buffer[1] == '/') { + goto clean_continue; + } + + if (strlen(line_buffer) >= 2 && line_buffer[0] == '/' && line_buffer[1] == '*' && + line_buffer[strlen(line_buffer) - 1] == '/' && line_buffer[strlen(line_buffer) - 2] == '*') { + goto clean_continue; + } + + if (strlen(line_buffer) >= 2 && line_buffer[0] == '/' && line_buffer[1] == '*') { + case3_started = true; + goto clean_continue; + } + + if (is_whole_unprintable(line_buffer_backup) && last_line_was_unprintable == false) { + fputs("\n", fs_new); + last_line_was_unprintable = true; + goto clean_continue; + } + + if (is_whole_unprintable(line_buffer_backup) == false) { + last_line_was_unprintable = false; + fputs(line_buffer_backup, fs_new); + } + + clean_continue: + memset(line_buffer, 0, sizeof(line_buffer)); + } + + sync(); + + if (fs) { + fclose(fs); + } + + if (fs_new) { + fclose(fs_new); + } + + snprintf(buffer, sizeof(buffer), "mv -f %s %s", new_file_name, filename); + system(buffer); + + return EXIT_SUCCESS; +} + +static int remove_comments(struct file_queue_head *queue) { + struct file_queue_entry *np = NULL; + + if (!queue) { + return EXIT_FAILURE; + } + + TAILQ_FOREACH(np, queue, entries) { + if (remove_comments_in_a_file(np->filename)) { + return EXIT_FAILURE; + } + } + + return EXIT_SUCCESS; +} + +int main(int argc, char **argv) { + int ret = EXIT_SUCCESS; + int c, o; + char *directory = NULL; + char *file = NULL; + struct file_queue_head head; + + while ((c = getopt_long(argc, argv, "hd:f:", parameters, &o)) != -1) { + switch (c) { + case 'h': + print_help(argv[0]); + goto success; + case 'd': + directory = strdup(optarg); + if (!directory) { + PRINT_ERROR("strdup() failed"); + } else { + if (directory[strlen(directory) - 1] == '/') { + directory[strlen(directory) - 1] = '\0'; + } + } + break; + case 'f': + file = strdup(optarg); + if (!file) { + PRINT_ERROR("strdup() failed"); + } + break; + default: + PRINT_ERROR("option cannot be found"); + print_help(argv[0]); + goto fail; + } + } + + if (!directory && !file) { + PRINT_ERROR("you should point one directory or file out"); + print_help(argv[0]); + goto fail; + } + + TAILQ_INIT(&head); + + if (directory) { + if (find_files_in_directory(directory, &head)) { + PRINT_ERROR("find_files_in_directory() failed"); + print_help(argv[0]); + goto fail; + } + } else if (file) { + add_filename_to_queue(file, &head); + } else { + PRINT_ERROR("you should point one directory or file out"); + print_help(argv[0]); + goto fail; + } + + if (remove_comments(&head)) { + PRINT_ERROR("remove_comments() failed"); + print_help(argv[0]); + goto fail; + } + + goto success; + +fail: + ret = EXIT_FAILURE; + +success: + + free_queue(&head); + if (file) { + free(file); + } + if (directory) { + free(directory); + } + + return ret; +} \ No newline at end of file diff --git a/helper/format_check/.gitkeep b/helper/format_check/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/helper/format_check/format_check.sh b/helper/format_check/format_check.sh new file mode 100755 index 0000000..7318652 --- /dev/null +++ b/helper/format_check/format_check.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +source="./../" + +if [ "$#" -eq 1 ]; then + source=$1 +fi + +files=$(find $source -name '*.c' -o -name '*.cpp' -o -name '*.h') +while IFS= read -r line; do + if [[ "$line" != *"test_"* ]] && [[ "$line" != *"thirdparty"* ]]; then + clang-format -verbose -i -style=file --Werror $line + fi +done <<< "$files" + +./dirty_check.sh + +exit 0 diff --git a/helper/glibc/Makefile b/helper/glibc/Makefile new file mode 100644 index 0000000..afc1322 --- /dev/null +++ b/helper/glibc/Makefile @@ -0,0 +1,19 @@ +CC = gcc +RM = rm -rf +CFLAGS = -Wall -Wextra -g3 -O0 -lpthread +all_c_files := $(wildcard *.c) + +.PHONY: all clean + +all: + for file in $(all_c_files); do \ + $(CC) $$file -o $$file".exe" $(CFLAGS); \ + done + +partial: + $(CC) $(FILE) -o $(FILE)".exe" $(CFLAGS); \ + +clean: + for file in $(all_c_files); do \ + $(RM) -rf $$file".exe"; \ + done diff --git a/helper/glibc/README.md b/helper/glibc/README.md new file mode 100644 index 0000000..ead77f2 --- /dev/null +++ b/helper/glibc/README.md @@ -0,0 +1,4 @@ + +This source files' name has their meaning. + +I read the glibc manual and foudn these useful (best practices) diff --git a/helper/glibc/binarysearchtree.c b/helper/glibc/binarysearchtree.c new file mode 100644 index 0000000..090ab96 --- /dev/null +++ b/helper/glibc/binarysearchtree.c @@ -0,0 +1,152 @@ +/* +* Root: A root is a node without a parent. +* Siblings: Siblings mean that nodes which have the same parent node. +* Internal Node: Internal Node means that a node which has at least a single +child. +* External Node: External Node means that a node which has no children. It is +also known as leaf. +* Ancestors: Ancestors include the parent, grandparent and so on of a node. +* Descendants: Descendants are the opposite of ancestors, It includes the child, +grandchild and so on of a node. +* Edge: An edge means a connection between one node to another node. +* Path: Path is a combination of nodes and edges connected with each other. +* Depth: You can calculate depth by the number of edges from node to the root of +the tree. +* Height: Height is the maximum depth of a node. +* Level: Level of a node is equal to depth of the node + 1. + +* Proper Binary Tree (Full Binary Tree) +In a proper binary tree, every internal node has exactly wo children. A binary +tree that is not proper is improper + +* Balanced Binary Tree +A balanced binary tree, also referred to as a height-balanced binary tree, is +defined as a binary tree in which the height of the left and right (node to +leaf, depth is node to root) subtree of any node differ by not more than 1. To +do that, all nodes' lef and right subtrees also should be balanced + +* Full Binary Tree +every parent node / internal node has either two or no children (it means, if +all leafes have siblings)18 + +* Perfect Binary Tree +all internal nodes have two children and all leafes are at same level + +* Complete Binary Tree +every level must be completely filled. all the leaf elements must lean towards +the left. the last leaf element might not have a right sibling. + +* Degenerate or Pathological Tree +having a single children left or right + +* Skewer Binary Tree +similar to degenerate but have children only one side +*/ + +#define _GNU_SOURCE /* Expose declaration of tdestroy() */ +#include +#include +#include +#include +#include + +static void *root = NULL; + +static int compare(const void *pa, const void *pb) { return (*(int *)pa - *(int *)pb); } + +static void action(const void *nodep, VISIT which, int depth) { + /*preorder, postorder, and endorder are known as preorder, inorder, and + postorder: before visiting the children, after the first and before the + second, and after visiting the children. Thus, the choice of name postorder is + rather confusing*/ + int *datap; + + switch (which) { + case preorder: + break; + case postorder: + datap = *(int **)nodep; + printf("%6d\tdepth: %d\n", *datap, depth); + break; + case endorder: + break; + case leaf: + datap = *(int **)nodep; + printf("%6d\tdepth: %d\n", *datap, depth); + break; + } +} + +static void freenode(void *nodep) { + // since our nodes are just integer pointer + // simple free is enough + if (nodep) { + free(nodep); + } +} + +int main(void) { + int i = 0; + int **val = NULL; + int dummy[11] = {179, 102, 220, 124, 214, 40, 16, 12, 70, 161, 35}; + + for (i = 0; i < 11; i++) { + int *ptr = (int *)malloc(sizeof(int)); + if (!ptr) { + perror("malloc failed\n"); + goto out; + } + + *ptr = dummy[i]; + + // If the tree does not contain a matching entry the key value will be added + // to the tree + val = tsearch(ptr, &root, compare); + + if (*val != ptr) { + goto out; + } + } + + twalk(root, action); + + if (NULL != tfind(&dummy[10], &root, compare)) { + printf("%d is found\n", dummy[10]); + + if (NULL != tdelete(&dummy[10], &root, compare)) { + printf("%d is deleted successfully\n", dummy[10]); + + twalk(root, action); + } + } + +out: + tdestroy(root, freenode); + + exit(EXIT_SUCCESS); +} + +/*OUTPUT: + 12 depth: 2 + 16 depth: 1 + 35 depth: 3 + 40 depth: 2 + 70 depth: 3 + 102 depth: 0 + 124 depth: 2 + 161 depth: 3 + 179 depth: 1 + 214 depth: 3 + 220 depth: 2 +35 is found +35 is deleted successfully + 12 depth: 2 + 16 depth: 1 + 40 depth: 2 + 70 depth: 3 + 102 depth: 0 + 124 depth: 2 + 161 depth: 3 + 179 depth: 1 + 214 depth: 3 + 220 depth: 2*/ diff --git a/helper/glibc/custom_fprintf.c b/helper/glibc/custom_fprintf.c new file mode 100644 index 0000000..33c0104 --- /dev/null +++ b/helper/glibc/custom_fprintf.c @@ -0,0 +1,43 @@ +/* + //%d, %i --> signed int + //%u --> unsigned int + //%hi --> signed short + //%hu --> unsigned short + //%l, %ld, %li --> signed long + //%lu --> unsigned long + //%lld --> signed long long + //%llu --> unsigned long long + //%f --> float + //%lf --> double + //%Lf --> long double + //%8d --> add space + //%08d --> add zero + //%0.2f --> specify precision +*/ + +#define _GNU_SOURCE // asprintf +#include +#include +#include + +void custom_fprint(FILE *out_stream, const char *format, ...) { + va_list mlist; + char *buffer = NULL; + + va_start(mlist, format); + vasprintf(&buffer, format, mlist); + va_end(mlist); + + if (!buffer) + exit(1); + + fprintf(out_stream, "%s\n", buffer); + free(buffer); +} + +int main(void) { + custom_fprint(stderr, "printed error"); + custom_fprint(stdout, "printed log"); + + exit(EXIT_SUCCESS); +} \ No newline at end of file diff --git a/helper/glibc/embeddedc.c b/helper/glibc/embeddedc.c new file mode 100644 index 0000000..460740f --- /dev/null +++ b/helper/glibc/embeddedc.c @@ -0,0 +1,49 @@ +#include +#include +#include + +uint8_t portA[4]; + +#define TRISA &portA[0] +#define LATA &portA[1] +#define PORTA &portA[2] +#define BSRA &portA[3] + +struct bits_region { + unsigned int bit0 : 1; + unsigned int bit1 : 1; + unsigned int bit2 : 1; + unsigned int bit3 : 1; + unsigned int bit4 : 1; + unsigned int bit5 : 1; + unsigned int bit6 : 1; + unsigned int bit7 : 1; +} __attribute__((packed)); + +union reg { + unsigned char data; + struct bits_region bits; +}; + +typedef struct { + union reg TRIS; + union reg LAT; + union reg PORT; + union reg BSR; +} GPIO_t; + +GPIO_t *GPIO_A = (GPIO_t *)portA; + +int main(void) { + *TRISA = 255; + printf("%d\n", GPIO_A->TRIS.data); + + *LATA = 0b01010101; + printf("%d\n", GPIO_A->LAT.bits.bit0); + printf("%d\n", GPIO_A->LAT.bits.bit7); + printf("%d\n", GPIO_A->LAT.bits.bit6); + + while (1) { + sleep(10); + } +} diff --git a/helper/glibc/file_io.c b/helper/glibc/file_io.c new file mode 100644 index 0000000..e0a8b24 --- /dev/null +++ b/helper/glibc/file_io.c @@ -0,0 +1,102 @@ +#define _GNU_SOURCE // vasprintf +#include +#include +#include +#include +#include +#include + +void print_error_and_exit(const char *format, ...) { + va_list mlist; + char *buffer = NULL; + + va_start(mlist, format); + vasprintf(&buffer, format, mlist); + va_end(mlist); + + if (!buffer) + exit(1); + + fprintf(stderr, "%s\n", buffer); + free(buffer); + + //%d, %i --> signed int + //%u --> unsigned int + //%hi --> signed short + //%hu --> unsigned short + //%l, %ld, %li --> signed long + //%lu --> unsigned long + //%lld --> signed long long + //%llu --> unsigned long long + //%f --> float + //%lf --> double + //%Lf --> long double + //%8d --> add space + //%08d --> add zero + //%0.2f --> specify precision + + exit(2); +} + +int main(void) { + int flag = 0; + int fd = open("legacyfile", O_CREAT | O_RDWR | O_NONBLOCK, 0666); + + // O_CREATE + // O_RDWR + // O_RDONLY + // O_WRONLY + // O_NONBLOCK + + // MSB LSB + // 0 0 1 --> execute + // 0 1 0 --> write + // 1 0 0 --> read + + ftruncate(fd, 0); + + if (fd < 0) + print_error_and_exit("open failed\n"); + + if ((flag = fcntl(fd, F_GETFL)) < 0) { + close(fd); + print_error_and_exit("fcntl error\n"); + } + printf("fcntl: %x\n", flag); + + write(fd, "3 selam1\n", 9); + write(fd, "5 selam2\n", 9); + + fsync(fd); + + lseek(fd, 0, SEEK_SET); + + char sp[100] = {0}; + read(fd, sp, sizeof(sp)); + + close(fd); + + printf("%s", sp); + + int base = 10; + long value = 0; + char *endptr = NULL; + + value = strtol(sp, &endptr, base); + + if (endptr == sp) { + printf("value: %ld no valid digit\n", value); + } else if (!endptr && endptr[0] != '\0') { + printf("value: %ld but there are more\n", value); + } else { + printf("value: %ld and there is NO more\n", value); + } + + // OUTPUT: + // fcntl: 8802 + // 3 selam1 + // 5 selam2 + // value: 3 and there is NO more + + return 0; +} diff --git a/helper/glibc/file_stream.c b/helper/glibc/file_stream.c new file mode 100644 index 0000000..ac5b0c8 --- /dev/null +++ b/helper/glibc/file_stream.c @@ -0,0 +1,113 @@ +/* +for stdout; + -> putchar(int c) + -> puts(char *str) + -> int getchar(void) + -> gets(char *str) !!!!!!!!!!!!! do not use this. use fgets instead + -> printf() + -> scanf(template, args ...) +*/ + +#define _GNU_SOURCE // fcloseall +#include +#include +#include +#include +#include +#include + +void print_error_and_exit(const char *format, ...) { + va_list mlist; + char *buffer = NULL; + + va_start(mlist, format); + vasprintf(&buffer, format, mlist); + va_end(mlist); + + if (!buffer) + exit(1); + + fprintf(stderr, "%s\n", buffer); + free(buffer); + + exit(2); +} + +int main(void) { + int i = 0; + FILE *fs = fopen("streamfile", "w+"); + + if (!fs) + print_error_and_exit("open failed\n"); + + flockfile(fs); + + fputc((int)'a', fs); + fputc((int)'b', fs); + fputc((int)'c', fs); + fputc((int)'\n', fs); + + fputs("hello world\n", fs); + + fprintf(fs, "%d %s\n", 3, "ppp"); + + sync(); + + fseek(fs, 0, SEEK_SET); + int c = 0; + while ((c = (int)fgetc(fs)) != EOF) { + printf("%c", (char)c); + } + + // RETURN + // abc + // hello world + // 3 ppp + + printf("------- seperator -------\n"); + + rewind(fs); + char s[100] = {0}; + while (fgets(s, 100, fs) != NULL) { + i++; + printf("asd: %s", (char *)s); + + if (i == 3) { + long lon = 0; + char data[20] = {0}; + + errno = 0; + sscanf(s, "%ld %s\n", &lon, data); + printf("errno: %d\n", errno); + + printf("int: %ld, %s\n", lon, data); + } + memset(s, 0, sizeof(s)); + } + + // RETURN: + // asd: abc + // asd: hello world + // asd: 3 ppp + // errno: 0 + // int: 3, ppp + + printf("------- seperator -------\n"); + + rewind(fs); + char sp[100] = {0}; + fread(sp, sizeof(sp), 1, fs); + printf("%s", sp); + + // RETURN: + // abc + // hello world + // 3 ppp + + funlockfile(fs); + + fclose(fs); + fcloseall(); + + return 0; +} diff --git a/helper/glibc/fork-pipe-exec.c b/helper/glibc/fork-pipe-exec.c new file mode 100644 index 0000000..6907695 --- /dev/null +++ b/helper/glibc/fork-pipe-exec.c @@ -0,0 +1,237 @@ +#define _GNU_SOURCE // vasprintf +#include +#include +#include +#include +#include +#include +#include +#include + +static pid_t childpid; +static int writepipe[2] = {-1, -1}, readpipe[2] = {-1, -1}; +char **args_dup = NULL; +int argc_dup = 0; +FILE *stream; + +#define WORDS_SIZE 32 + +/* read and write pipes from the point of view of parent */ +#define PARENT_READ readpipe[0] +#define CHILD_WRITE readpipe[1] +#define CHILD_READ writepipe[0] +#define PARENT_WRITE writepipe[1] + +static void dealloc_all(void) { + if (stream) { + fclose(stream); + } + + if (!args_dup) { + return; + } + + for (int i = 0; i < argc_dup; i++) { + if (args_dup[i]) { + free(args_dup[i]); + } + } + + if (args_dup) { + free(args_dup); + } +} + +static void print_error_and_exit(const char *format, ...) { + va_list mlist; + char *buffer = NULL; + + va_start(mlist, format); + vasprintf(&buffer, format, mlist); + va_end(mlist); + + if (!buffer) + exit(1); + + fprintf(stderr, "%s\n", buffer); + free(buffer); + + dealloc_all(); + + exit(2); +} + +static void read_from_pipe(int fd) { + int c; + + stream = fdopen(fd, "r"); // file descriptor to file stream conversion + if (!stream) { + return; + } + + while (1) { + c = fgetc(stream); + putchar(c); + } + fclose(stream); +} + +static void collect_zombie(int sig, siginfo_t *siginfo, void *context) { + int ret; + int status; + + if (context) { + fprintf(stdout, "APP_TRACKER ==> context: %p\n", context); + } + + fprintf(stderr, "APP_TRACKER ==> caught signal: %d\n", sig); + + if (sig != SIGCHLD) { + return; + } + + switch (siginfo->si_code) { + case CLD_EXITED: + fputs("APP_TRACKER ==> Caught SIGCHLD: (Child has exited)\n", stderr); + break; + case CLD_KILLED: + fputs("APP_TRACKER ==> Caught SIGCHLD: (Child was killed)\n", stderr); + break; + case CLD_DUMPED: + fputs("APP_TRACKER ==> Caught SIGCHLD: (Child terminated abnormally)\n", stderr); + break; + case CLD_TRAPPED: + fputs("APP_TRACKER ==> Caught SIGCHLD: (Traced child has trapped)\n", stderr); + break; + case CLD_STOPPED: + fputs("APP_TRACKER ==> Caught SIGCHLD: (Child has stopped)\n", stderr); + break; + case CLD_CONTINUED: + fputs("APP_TRACKER ==> Caught SIGCHLD: (Stopped child has continued)\n", stderr); + break; + default: + fputs("APP_TRACKER ==> Caught SIGCHLD: default\n", stderr); + break; + } + + if (childpid != (ret = waitpid(childpid, &status, WNOHANG))) { + print_error_and_exit("APP_TRACKER ==> waitpid() failed with %d", ret); + return; + } + + if (WIFEXITED(status)) { + fprintf(stderr, "APP_TRACKER ==> child is exited, status = %d\n", WEXITSTATUS(status)); + } else if (WIFSIGNALED(status)) { + fprintf(stderr, "APP_TRACKER ==> child is killed by signal %d\n", WTERMSIG(status)); + } else if (WIFSTOPPED(status)) { + fprintf(stderr, "APP_TRACKER ==> child is stopped by signal %d\n", WSTOPSIG(status)); + } else if (WIFCONTINUED(status)) { + fprintf(stderr, "APP_TRACKER ==> child is continued\n"); + } + + dealloc_all(); + + exit(0); +} + +int main(int argc, char **args) { + struct sigaction sa; + argc_dup = argc; + + if (argc < 2) { + print_error_and_exit("APP_TRACKER ==> at least one arg necessary to start " + "the given application"); + } + + sigemptyset(&sa.sa_mask); + + sa.sa_flags = 0; + sa.sa_sigaction = collect_zombie; + sa.sa_flags = SA_SIGINFO; + + sigaction(SIGCHLD, &sa, NULL); + + if (pipe(writepipe) != 0) { + print_error_and_exit("APP_TRACKER ==> pipe() failed"); + } + if (pipe(readpipe) != 0) { + print_error_and_exit("APP_TRACKER ==> pipe() failed"); + } + + args_dup = calloc(argc + 2, sizeof(char *)); // 2 because "python3" kw + if (!args_dup) { + print_error_and_exit("APP_TRACKER ==> calloc() failed"); + } + + int i = 0, j = 0; + for (i = 0; i < argc; i++) { + if (i == 0) { + args_dup[j++] = strdup("python3"); + args_dup[j++] = strdup("./../python/custom_echo/echo.py"); + } else { + args_dup[j++] = strdup(args[i]); + } + if (!args_dup[j - 1] || !strlen(args_dup[j - 1])) { + print_error_and_exit("APP_TRACKER ==> calloc() failed"); + } + } + + char *bin_file = args_dup[0]; + + if ((childpid = fork()) < 0) { + close(PARENT_READ); + close(CHILD_WRITE); + close(CHILD_READ); + close(PARENT_WRITE); + print_error_and_exit("APP_TRACKER ==> fork() failed"); + } else if (childpid == 0) { + /* in order for the parent to send data, + it should remain open on only parent side + */ + close(PARENT_WRITE); + /* in order to send data to parent, + this port remains open on only parent side + */ + close(PARENT_READ); + /* child makes STDIN the read side of the pipe. + So the data send by PARENT_WRITE will be gathered by child over STDIN + */ + dup2(CHILD_READ, STDIN_FILENO); + /* should be closed because we already duplicated + */ + close(CHILD_READ); + /* child makes STDOUT the write side of the pipe. + It means, the data send over CHILD_WRITE will be seen on PARENT_READ on + parent side + */ + dup2(CHILD_WRITE, STDOUT_FILENO); + /* should be closed because we already duplicated + */ + close(CHILD_WRITE); + + sleep(1); + errno = 0; + if (execvp(bin_file, args_dup) == -1) { + print_error_and_exit("APP_TRACKER ==> execvp() failed errno: %s", strerror(errno)); + } + + while (1) + ; // never reach here + } else { + /* in order to send data to the child, it must remain open only on child + * side + */ + close(CHILD_READ); + /* in order to read from child, it must remain open on only child side + */ + close(CHILD_WRITE); + + /* do parent stuff */ + read_from_pipe(PARENT_READ); + } + + close(PARENT_WRITE); + close(PARENT_READ); + + return 0; +} diff --git a/helper/glibc/functionpointer.c b/helper/glibc/functionpointer.c new file mode 100644 index 0000000..b7368de --- /dev/null +++ b/helper/glibc/functionpointer.c @@ -0,0 +1,20 @@ +#define _GNU_SOURCE // vasprintf +#include +#include +#include + +int execute_callback(int (*callback_funct)(int), int data) { + if (!callback_funct) + return 0; + return callback_funct(data); +} + +int my_callback(int data) { + printf("data: %d\n", data); + return 0; +} + +int main(void) { + execute_callback(&my_callback, 10); + return 0; +} diff --git a/helper/glibc/getopt.c b/helper/glibc/getopt.c new file mode 100644 index 0000000..30c7c08 --- /dev/null +++ b/helper/glibc/getopt.c @@ -0,0 +1,41 @@ +#include +#include +#include +#include + +#define OK 0 +#define NOK 1 + +#define OPTIONAL_ARGUMENT_IS_PRESENT \ + ((optarg == NULL && optind < argc && argv[optind][0] != '-') ? (bool)(optarg = argv[optind++]) : (optarg != NULL)) + +static struct option parameters[] = { + {"aaa", required_argument, 0, 'a'}, + {"bbb", no_argument, 0, 'b'}, + {"ccc", optional_argument, 0, 'c'}, + {NULL, 0, 0, 0}, +}; + +int main(int argc, char **argv) { + int c, o; + while ((c = getopt_long(argc, argv, "ba:c::", parameters, &o)) != -1) { + switch (c) { + case 'a': + printf("'a' argument's data is %s\n", optarg); + break; + case 'b': + printf("'b' is the argument\n"); + break; + case 'c': + if (OPTIONAL_ARGUMENT_IS_PRESENT) { + printf("'c' argument's data is %s\n", optarg); + } else { + printf("'c' is the argument\n"); + } + break; + default: + printf("unknown argument\n"); + return NOK; + } + } +} diff --git a/helper/glibc/hash.c b/helper/glibc/hash.c new file mode 100644 index 0000000..9a5ea6c --- /dev/null +++ b/helper/glibc/hash.c @@ -0,0 +1,53 @@ +/* + Wen you now delete the first item and then try to find the second, it + will not be found, because the "other" buckets are only considered when the + "optimum" bucket where the hash code is the bucket index is full. + + Besides the non-updating re-additions and the missing deletion option, + there are other restrictions to hsearch_r. For example, the maximum nuber of + entries must be estimated beforehand and cannot be changed later. I think + hsearch_r is intended as a fast hash table for a limited range of + applications. + + source: + https://stackoverflow.com/questions/25971505/how-to-delete-element-from-hsearch + + example usage: https://linux.die.net/man/3/hsearch +*/ +#include +#include +#include + +static char *data[] = {"alpha", "bravo", "charlie", "delta", "echo", "foxtrot", "golf", "hotel", "india", + "juliet", "kilo", "lima", "mike", "november", "oscar", "papa", "quebec", "romeo", + "sierra", "tango", "uniform", "victor", "whisky", "x-ray", "yankee", "zulu"}; + +int main(void) { + ENTRY e; + ENTRY *ep; + + hcreate(30); + + for (int i = 0; i < 24; i++) { + e.key = data[i]; + /* data is just an integer, instead of a + pointer to something */ + e.data = (void *)&i; + ep = hsearch(e, ENTER); + /* there should be no failures */ + if (ep == NULL) { + fprintf(stderr, "entry failed\n"); + exit(EXIT_FAILURE); + } + } + + for (int i = 22; i < 26; i++) { + /* print two entries from the table, and + show that two are not in the table */ + e.key = data[i]; + ep = hsearch(e, FIND); + printf("%9.9s -> %9.9s:%d\n", e.key, ep ? ep->key : "NULL", ep ? *((int *)(ep->data)) : 0); + } + hdestroy(); + exit(EXIT_SUCCESS); +} \ No newline at end of file diff --git a/helper/glibc/ipc_fifo.c b/helper/glibc/ipc_fifo.c new file mode 100644 index 0000000..bf2f992 --- /dev/null +++ b/helper/glibc/ipc_fifo.c @@ -0,0 +1,108 @@ +#define _GNU_SOURCE // vasprintf +#define _POSIX_C_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct option parameters[] = { + {"reader", no_argument, 0, 'r'}, {"writer", no_argument, 0, 'w'}, {"help", no_argument, 0, 'h'}, {NULL, 0, 0, 0}}; + +void print_help(void) { + printf("type \"reader\" or 'r' to start fifo reader\n"); + printf("and use \"write\" or 'w' for writer\n"); + printf("Note that: no option is used for reader\n"); + exit(1); +} + +void print_error_and_exit(const char *format, ...) { + va_list mlist; + char *buffer = NULL; + + va_start(mlist, format); + vasprintf(&buffer, format, mlist); + va_end(mlist); + + if (!buffer) + exit(1); + + fprintf(stderr, "%s\n", buffer); + free(buffer); + + exit(2); +} + +int main(int argc, char **argv) { + int fd_read = 0, fd_write = 0; + int option = 0, longid = 0; + bool is_writer = false; + char buffer[100] = {0}; + + while ((option = getopt_long(argc, argv, "hrw", parameters, &longid)) != -1) { + switch (option) { + case 'h': + // "optarg" variable is used to get option itself + print_help(); + break; + case 'r': + is_writer = false; + break; + case 'w': + is_writer = true; + break; + default: + perror("unknown argument\n"); + return 2; + } + } + + mkfifo("myfifo", 0666); + + if (!is_writer) { + // it is reader + // since both ends should open the fifo simultaneously + // O_NONBLOCK options is meaningless here + fd_read = open("myfifo", O_RDONLY); + if (fd_read < 0) { + perror("open failed\n"); + return 2; + } + + read(fd_read, buffer, sizeof(buffer)); + printf("reader reads \"%s\"\n", buffer); + + memset(buffer, 0, sizeof(buffer)); + + close(fd_read); + unlink("myfifo"); + + return 0; + } + + // it is writer + // since both ends should open the fifo simultaneously + // O_NONBLOCK options is meaningless here + fd_write = open("myfifo", O_WRONLY); + if (fd_write < 0) { + perror("open failed\n"); + return 2; + } + + strncpy(buffer, "hello from writer", sizeof(buffer)); + + write(fd_write, buffer, sizeof(buffer)); + printf("writer writes \"%s\"\n", buffer); + + memset(buffer, 0, sizeof(buffer)); + + close(fd_write); + unlink("myfifo"); + + return 0; +} diff --git a/helper/glibc/ipc_msgqueue.c b/helper/glibc/ipc_msgqueue.c new file mode 100644 index 0000000..abebc73 --- /dev/null +++ b/helper/glibc/ipc_msgqueue.c @@ -0,0 +1,96 @@ +#define _GNU_SOURCE // vasprintf +#define _POSIX_C_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct option parameters[] = { + {"reader", no_argument, 0, 'r'}, {"writer", no_argument, 0, 'w'}, {"help", no_argument, 0, 'h'}, {NULL, 0, 0, 0}}; + +void print_help(void) { + printf("type \"reader\" or 'r' to start queue reader\n"); + printf("and use \"write\" or 'w' for writer\n"); + printf("Note that: no option is used for reader\n"); + exit(1); +} + +int main(int argc, char **argv) { + int option = 0, longid = 0; + bool is_writer = false; + char buffer[100] = {0}; + key_t key; + char proj_id = 'D'; + int msqid; + + while ((option = getopt_long(argc, argv, "hrw", parameters, &longid)) != -1) { + switch (option) { + case 'h': + // "optarg" variable is used to get option itself + print_help(); + break; + case 'r': + is_writer = false; + break; + case 'w': + is_writer = true; + break; + default: + perror("unknown argument\n"); + return 2; + } + } + + // create the file first + FILE *fs = fopen("msgqueue", "r"); + if (!fs) { + fs = fopen("msgqueue", "a+"); + if (!fs) + perror("fopen"); + + fclose(fs); + } + + if ((key = ftok("msgqueue", proj_id)) == -1) { + perror("ftok"); + exit(1); + } + + // connect to the queue + if ((msqid = msgget(key, 0666 | IPC_CREAT)) == -1) { + perror("msgget"); + exit(1); + } + + if (!is_writer) { + // it is reader + if (msgrcv(msqid, buffer, sizeof(buffer), 0, 0) == -1) { + perror("msgrcv"); + exit(1); + } + printf("recvd: \"%s\"\n", buffer); + + return 0; + } + + // it is writer + if (msgsnd(msqid, "hello from writer", 18, 0) == -1) + perror("msgsnd"); + + if (msgctl(msqid, IPC_RMID, NULL) == -1) { + perror("msgctl"); + exit(1); + } + + unlink("msgqueue"); + + return 0; +} diff --git a/helper/glibc/memmove-memmem.c b/helper/glibc/memmove-memmem.c new file mode 100644 index 0000000..7cfa316 --- /dev/null +++ b/helper/glibc/memmove-memmem.c @@ -0,0 +1,27 @@ +#define _GNU_SOURCE +#include +#include +#include +#include + +int main(void) { + /*****************************************************************************************/ + + char s_memmove[23] = "this is memmove string"; + char s_memmove_original[23] = "this is memmove string"; + memmove(s_memmove, &s_memmove[2], strlen(s_memmove) - 2); + printf("after s_memmove \"%s\" became \"%s\"\n", s_memmove_original, s_memmove); + + // RETURN: after s_memmove "this is memmove string" became "is is memmove + // stringng" + + /*****************************************************************************************/ + + char mm[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}; + char needle[2] = {5, 6}; + printf("mm: %p, memmem return: %p\n", (int *)mm, (int *)memmem(mm, sizeof(mm), needle, sizeof(needle))); + + // RETURN: mm: 0x7ffec209b916, memmem return: 0x7ffec209b91a + + exit(EXIT_SUCCESS); +} diff --git a/helper/glibc/mmap.c b/helper/glibc/mmap.c new file mode 100644 index 0000000..acdc69d --- /dev/null +++ b/helper/glibc/mmap.c @@ -0,0 +1,55 @@ +#define _GNU_SOURCE // vasprintf +#include +#include +#include +#include +#include +#include +#include + +void print_error_and_exit(const char *format, ...) { + va_list mlist; + char *buffer = NULL; + + va_start(mlist, format); + vasprintf(&buffer, format, mlist); + va_end(mlist); + + if (!buffer) + exit(1); + + fprintf(stderr, "%s\n", buffer); + free(buffer); + + exit(2); +} + +int main(void) { + int estimatedbuffersize = 100; + char *buffer = NULL, *buff_start = NULL; + int fd = open("mmapfile", O_CREAT | O_RDWR | O_NONBLOCK, 0666); + + ftruncate(fd, estimatedbuffersize); + + buffer = buff_start = mmap(NULL, estimatedbuffersize, PROT_WRITE, MAP_SHARED, fd, 0); + if (!buffer) { + close(fd); + print_error_and_exit("mmap failed\n"); + } + + buffer[0] = 'a'; + buffer[1] = 's'; + buffer[2] = 'd'; + buffer[3] = '\n'; + + msync(buff_start, estimatedbuffersize, MS_SYNC); + + munmap(buff_start, estimatedbuffersize); + + close(fd); + + // OUTPUT: cat mmapfile + // asd + + return 0; +} diff --git a/helper/glibc/poll_socket.c b/helper/glibc/poll_socket.c new file mode 100644 index 0000000..cb952ab --- /dev/null +++ b/helper/glibc/poll_socket.c @@ -0,0 +1,191 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SERVER_PORT 9191 + +#define TRUE 1 +#define FALSE 0 + +int main(void) { + int rc, on = 1; + int listen_sd = -1, new_sd = 0; + int close_conn; + int timeout; + int nfds = 1, current_size = 0, i, j; + char buffer[64]; + struct sockaddr_in6 addr; + struct pollfd fds[32]; + + listen_sd = socket(AF_INET6, SOCK_STREAM, 0); + if (listen_sd < 0) { + perror("socket() failed"); + exit(EXIT_FAILURE); + } + + rc = setsockopt(listen_sd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)); + if (rc < 0) { + perror("setsockopt() failed"); + close(listen_sd); + exit(-1); + } + + /*************************************************************/ + /* Set socket to be nonblocking. All of the sockets for */ + /* the incoming connections will also be nonblocking since */ + /* they will inherit that state from the listening socket. */ + /*************************************************************/ + // this is important to revisit to poll function to read data + // otherwise reading cannot be done because it is blocking + rc = ioctl(listen_sd, FIONBIO, (char *)&on); + if (rc < 0) { + perror("ioctl() failed"); + close(listen_sd); + exit(-1); + } + + memset(&addr, 0, sizeof(addr)); + addr.sin6_family = AF_INET6; + memcpy(&addr.sin6_addr, &in6addr_any, sizeof(in6addr_any)); + addr.sin6_port = htons(SERVER_PORT); + + rc = bind(listen_sd, (struct sockaddr *)&addr, sizeof(addr)); + if (rc < 0) { + perror("bind() failed"); + close(listen_sd); + exit(-1); + } + + rc = listen(listen_sd, 32); + if (rc < 0) { + perror("listen() failed"); + close(listen_sd); + exit(-1); + } + + memset(fds, 0, sizeof(fds)); + + fds[0].fd = listen_sd; + fds[0].events = POLLIN; // poll input events + + timeout = 1000; // time is milliseconds --> it is 1 second now + + while (TRUE) { + printf("Waiting on poll()...\n"); + rc = poll(fds, nfds, timeout); + if (rc < 0) { + perror(" poll() failed"); + break; + } else if (rc == 0) { + printf(" poll() timed out.\n"); + } + + current_size = nfds; + for (i = 0; i < current_size; i++) { + // go throught the current list and skip if there is no POLLIN + if (fds[i].revents != POLLIN) { + continue; + } + + if (fds[i].fd == listen_sd) { + printf(" Listening socket is readable\n"); + + /*******************************************************/ + /* Accept all incoming connections that are */ + /* queued up on the listening socket before we */ + /* loop back and call poll again. */ + /*******************************************************/ + while (new_sd != -1) { + errno = 0; + new_sd = accept(listen_sd, NULL, NULL); + if (new_sd < 0) { + // EWOULDBLOCK means blocked operations + if (errno != EWOULDBLOCK) { + perror(" accept() failed"); + goto out; + } + break; + } + + printf(" New incoming connection - %d added end of the poll list\n", new_sd); + fds[nfds].fd = new_sd; + fds[nfds].events = POLLIN; + nfds++; + } + } else { + /*********************************************************/ + /* This is not the listening socket, therefore an */ + /* existing connection must be readable */ + /*********************************************************/ + + printf(" Descriptor %d is readable\n", fds[i].fd); + close_conn = FALSE; + + while (TRUE) { + memset(buffer, 0, sizeof(buffer)); + + errno = 0; + rc = recv(fds[i].fd, buffer, sizeof(buffer), 0); + if (rc < 0) { + if (errno != EWOULDBLOCK) { + perror(" recv() failed"); + close_conn = TRUE; + } + break; + } + + if (rc == 0) { + printf(" Connection closed by the client\n"); + close_conn = TRUE; + break; + } + + printf(" received data: %s\n", buffer); + + /*****************************************************/ + /* Echo the data back to the client */ + /*****************************************************/ + rc = send(fds[i].fd, buffer, strlen(buffer) + 1, 0); + if (rc < 0) { + perror(" send() failed"); + close_conn = TRUE; + break; + } + } + + if (!close_conn) { + continue; + } + + // if connection is closed, cleanup the polled list + close(fds[i].fd); + fds[i].fd = -1; + } + } + + // optimize the array + for (i = 0; i < nfds; i++) { + if (fds[i].fd == -1) { + for (j = i; j < nfds; j++) { + fds[j].fd = fds[j + 1].fd; + } + i--; + nfds--; + } + } + } + +out: + // close all before exit + for (i = 0; i < nfds; i++) { + if (fds[i].fd >= 0) + close(fds[i].fd); + } +} diff --git a/helper/glibc/poll_timer.c b/helper/glibc/poll_timer.c new file mode 100644 index 0000000..289aae2 --- /dev/null +++ b/helper/glibc/poll_timer.c @@ -0,0 +1,58 @@ +#include +#include +#include +#include +#include +#include + +#define SERVER_PORT 9191 + +#define TRUE 1 +#define FALSE 0 + +int main(int argc, char **args) { + int rc; + int nfds = 1; + struct pollfd fds[1]; + unsigned long timeout = 0; + char *rest = NULL; + struct timespec ts_start, ts_now; + + if (argc != 2) { + perror("give arg for timeout\n"); + exit(EXIT_FAILURE); + } + + fds[0].fd = 0; + fds[0].events = POLLIN; // poll input events + + timeout = strtoul(args[1], &rest, 10); + if (rest == args[1]) { + perror("strtoul failed\n"); + exit(EXIT_FAILURE); + } + + if (clock_gettime(CLOCK_BOOTTIME, &ts_start) == -1) { + perror("clock_gettime"); + exit(EXIT_FAILURE); + } + + while (TRUE) { + printf("Waiting on poll()...\n"); + rc = poll(fds, nfds, timeout); + if (rc < 0) { + perror(" poll() failed"); + break; + } else if (rc == 0) { + printf(" poll() timed out.\n"); + + if (clock_gettime(CLOCK_BOOTTIME, &ts_now) == -1) { + perror("clock_gettime"); + exit(EXIT_FAILURE); + } + + printf("elapsed time %lu\n", (unsigned long)(ts_now.tv_sec - ts_start.tv_sec)); + ts_start = ts_now; + } + } +} diff --git a/helper/glibc/popen.c b/helper/glibc/popen.c new file mode 100644 index 0000000..f719ff4 --- /dev/null +++ b/helper/glibc/popen.c @@ -0,0 +1,21 @@ +#include +#include + +int main(void) { + char buffer[256] = {0}; + FILE *fp = popen("ifconfig lo", "r"); + + if (!fp) { + perror("popen error"); + return 1; + } + + while (fgets(buffer, sizeof(buffer), fp)) { + printf("%s", buffer); + memset(buffer, 0, sizeof(buffer)); + } + + pclose(fp); + + return 0; +} diff --git a/helper/glibc/random.c b/helper/glibc/random.c new file mode 100644 index 0000000..dc6ecfa --- /dev/null +++ b/helper/glibc/random.c @@ -0,0 +1,44 @@ +#include +#include +#include +#include +#include + +int main(void) { + struct timespec ts; + + if (clock_gettime(CLOCK_BOOTTIME, &ts) == -1) { + perror("clock_gettime"); + exit(1); + } + + srand((unsigned int)ts.tv_nsec); + unsigned int randnum = rand(); + + printf("random between 100 and 10000 is %d\n", (unsigned int)(100 + (randnum % 10000))); + + char buffer[100] = {0}; + + /*Most applications should use getentropy. The getrandom function is + intended for lowlevel applications which need additional control over blocking + behavior.*/ + memset(buffer, 0, sizeof(buffer)); + getentropy(buffer, sizeof(buffer)); + + unsigned int i = 0; + for (i = 0; i < (unsigned int)sizeof(buffer); i++) { + printf("%d ", buffer[i]); + } + printf("\n"); + + // OUTPUT: + // random between 100 and 10000 is 7225 + // -53 -42 -74 67 65 -124 18 -27 -23 -18 -6 -100 -114 -8 125 -57 -32 -38 49 + // 121 -108 54 -42 -12 -69 120 -95 59 37 80 27 -77 -103 -119 74 113 -108 -128 + // -4 58 -75 47 55 25 -50 95 -6 102 17 76 -15 -113 96 111 -99 -123 110 -110 + // -118 56 -52 113 -84 27 -2 108 84 -17 -109 -123 74 -5 -25 0 55 34 113 -16 + // 52 -16 44 122 -72 123 -47 -81 40 -125 4 21 47 108 18 -46 25 126 98 113 -71 + // -87 + + return 0; +} diff --git a/helper/glibc/reallocarray-alloca-strndup-strndupa.c b/helper/glibc/reallocarray-alloca-strndup-strndupa.c new file mode 100644 index 0000000..0ef395b --- /dev/null +++ b/helper/glibc/reallocarray-alloca-strndup-strndupa.c @@ -0,0 +1,71 @@ +#define _GNU_SOURCE +#include +#include +#include +#include + +int main(void) { + // it should be used instead of realloc because newsize prm of it lead us to + // set it wrong + int *ptr_reallocarray = (int *)reallocarray(NULL, 20, sizeof(int)); + if (!ptr_reallocarray) { + perror("reallocarray failed\n"); + exit(EXIT_FAILURE); + } + + printf("reallocarray allocates 20 integer\n"); + free(ptr_reallocarray); + + // RETURN: reallocarray allocates 20 integer + + /*****************************************************************************************/ + + // allocates memory from STACK. No need explicit FREE operation + int *ptr_alloca = (int *)alloca(20 * sizeof(int)); + if (!ptr_alloca) { + perror("alloca failed\n"); + } + + printf("alloca allocates 20 integer\n"); + // no need to free it + + // RETURN: alloca allocates 20 integer + + /*****************************************************************************************/ + + char *s_strndup_source = "this is strndup string"; + char *s_strndup = strndup(s_strndup_source, 10); + if (!s_strndup) { + perror("strndup failed\n"); + } + + printf("s_strndup lenght is %lu while strlen is %lu, \"%s\"-\"%s\"\n", strlen(s_strndup), strlen(s_strndup_source), + s_strndup, s_strndup_source); + + free(s_strndup); + + // RETURN: s_strndup lenght is 10 while strlen is 22, "this is st"-"this is + // strndup string" + + /*****************************************************************************************/ + + // uses alloca instead of alloc-malloc + char *s_strndupa_source = "this is strndupa string"; + char *s_strndupa = strndupa(s_strndupa_source, 10); + if (!s_strndupa) { + perror("strndup failed\n"); + } + + printf("s_strndupa lenght is %lu while strlen is %lu, \"%s\"-\"%s\"\n", strlen(s_strndupa), + strlen(s_strndupa_source), s_strndupa, s_strndupa_source); + + // no need to free it + + // RETURN: s_strndupa lenght is 10 while strlen is 23, "this is st"-"this is + // strndupa string" + + /*****************************************************************************************/ + + exit(EXIT_SUCCESS); + ; +} diff --git a/helper/glibc/sigaction.c b/helper/glibc/sigaction.c new file mode 100644 index 0000000..f505b3a --- /dev/null +++ b/helper/glibc/sigaction.c @@ -0,0 +1,379 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_STACK_FRAMES 64 + +static char const *icky_global_program_name; +static void *stack_traces[MAX_STACK_FRAMES]; +static char const *icky_global_program_name; + +int divide_by_zero(); +void cause_segfault(); +void stack_overflow(); +void infinite_loop(); +void illegal_instruction(); +void cause_calamity(); + +/* Resolve symbol name and source location given the path to the executable +and an address */ +int addr2line(char const *const program_name, char const *addr) { + char addr2line_cmd[512] = {0}; + + sprintf(addr2line_cmd, "addr2line -e %.256s -f %s", program_name, addr); + + printf("\t\n"); + + return system(addr2line_cmd); +} + +int getaddr(const char *line, char *address, int size) { + char *addr_beg = NULL; + char *addr_end = NULL; + + if (!line || !sizeof(line)) { + return 0; + } + + addr_beg = strrchr(line, (int)'+'); + addr_beg++; // skip + + addr_end = strrchr(line, (int)')'); + + if ((addr_end - addr_beg) > size) { + return 0; + } + + memcpy(address, addr_beg, (addr_end - addr_beg)); + address[(addr_end - addr_beg)] = '\0'; + + return 1; +} + +void posix_print_stack_trace() { + int i = 0, f = 0, trace_size = 0; + char **messages = (char **)NULL; + char addr[32]; + + trace_size = backtrace(stack_traces, MAX_STACK_FRAMES); + messages = backtrace_symbols(stack_traces, trace_size); + + /* skip the first couple stack frames (as they are this function and + our handler) and also skip the last frame as it's (always?) junk. */ + for (i = 0; i < trace_size; ++i) { + printf("%s\n", messages[i]); + if (strstr(messages[i], icky_global_program_name)) { + if (!(getaddr(messages[i], addr, 32) && !addr2line(icky_global_program_name, addr))) { + printf("show partially: % 2d: %s\n", ++f, messages[i]); + } + } + } + + if (messages) { + free(messages); + } +} + +void posix_signal_handler(int sig, siginfo_t *siginfo, void *context) { + (void)context; + switch (sig) { + case SIGCHLD: + switch (siginfo->si_code) { + case CLD_EXITED: + fputs("Caught SIGCHLD: (Child has exited)\n", stderr); + break; + case CLD_KILLED: + fputs("Caught SIGCHLD: (Child was killed)\n", stderr); + break; + case CLD_DUMPED: + fputs("Caught SIGCHLD: (Child terminated abnormally)\n", stderr); + break; + case CLD_TRAPPED: + fputs("Caught SIGCHLD: (Traced child has trapped)\n", stderr); + break; + case CLD_STOPPED: + fputs("Caught SIGCHLD: (Child has stopped)\n", stderr); + break; + case CLD_CONTINUED: + fputs("Caught SIGCHLD: (Stopped child has continued)\n", stderr); + break; + default: + fputs("Caught SIGCHLD: default\n", stderr); + break; + } + break; + case SIGTRAP: + fputs("Caught SIGTRAP\n", stderr); + break; + case SIGBUS: + switch (siginfo->si_code) { + case BUS_ADRALN: + fputs("Caught SIGBUS: (Invalid address alignment)\n", stderr); + break; + case BUS_ADRERR: + fputs("Caught SIGBUS: (Nonexistent physical address)\n", stderr); + break; + case BUS_OBJERR: + fputs("Caught SIGBUS: (Object-specific hardware error)\n", stderr); + break; + case BUS_MCEERR_AR: + fputs("Caught SIGBUS: (Hardware memory error consumed on a machine " + "check; action required)\n", + stderr); + break; + case BUS_MCEERR_AO: + fputs("Caught SIGBUS: (Hardware memory error detected in process but not " + "consumed; action optional)\n", + stderr); + break; + default: + fputs("Caught SIGBUS: default\n", stderr); + break; + } + break; + case SIGSEGV: + switch (siginfo->si_code) { + case SEGV_MAPERR: + fputs("Caught SIGSEGV: (Address not mapped to object)\n", stderr); + break; + case SEGV_ACCERR: + fputs("Caught SIGSEGV: (Invalid permissions for mapped object)\n", stderr); + break; + case SEGV_BNDERR: + fputs("Caught SIGSEGV: (Failed address bound checks)\n", stderr); + break; + case SEGV_PKUERR: + fputs("Caught SIGSEGV: (Access was denied by memory protection keys)\n", stderr); + break; + default: + fputs("Caught SIGSEGV: Segmentation Fault\n", stderr); + break; + } + break; + case SIGINT: + fputs("Caught SIGINT: Interactive attention signal, (usually ctrl+c)\n", stderr); + break; + case SIGFPE: + switch (siginfo->si_code) { + case FPE_INTDIV: + fputs("Caught SIGFPE: (integer divide by zero)\n", stderr); + break; + case FPE_INTOVF: + fputs("Caught SIGFPE: (integer overflow)\n", stderr); + break; + case FPE_FLTDIV: + fputs("Caught SIGFPE: (floating-point divide by zero)\n", stderr); + break; + case FPE_FLTOVF: + fputs("Caught SIGFPE: (floating-point overflow)\n", stderr); + break; + case FPE_FLTUND: + fputs("Caught SIGFPE: (floating-point underflow)\n", stderr); + break; + case FPE_FLTRES: + fputs("Caught SIGFPE: (floating-point inexact result)\n", stderr); + break; + case FPE_FLTINV: + fputs("Caught SIGFPE: (floating-point invalid operation)\n", stderr); + break; + case FPE_FLTSUB: + fputs("Caught SIGFPE: (subscript out of range)\n", stderr); + break; + default: + fputs("Caught SIGFPE: Arithmetic Exception\n", stderr); + break; + } + break; + case SIGILL: + switch (siginfo->si_code) { + case ILL_ILLOPC: + fputs("Caught SIGILL: (illegal opcode)\n", stderr); + break; + case ILL_ILLOPN: + fputs("Caught SIGILL: (illegal operand)\n", stderr); + break; + case ILL_ILLADR: + fputs("Caught SIGILL: (illegal addressing mode)\n", stderr); + break; + case ILL_ILLTRP: + fputs("Caught SIGILL: (illegal trap)\n", stderr); + break; + case ILL_PRVOPC: + fputs("Caught SIGILL: (privileged opcode)\n", stderr); + break; + case ILL_PRVREG: + fputs("Caught SIGILL: (privileged register)\n", stderr); + break; + case ILL_COPROC: + fputs("Caught SIGILL: (coprocessor error)\n", stderr); + break; + case ILL_BADSTK: + fputs("Caught SIGILL: (internal stack error)\n", stderr); + break; + default: + fputs("Caught SIGILL: Illegal Instruction\n", stderr); + break; + } + break; + case SIGTERM: + fputs("Caught SIGTERM: a termination request was sent to the program\n", stderr); + break; + case SIGABRT: + fputs("Caught SIGABRT: usually caused by an abort() or assert()\n", stderr); + break; + case SIGUSR1: + fputs("Caught SIGUSR1\n", stderr); + break; + default: + break; + } + posix_print_stack_trace(); + exit(1); +} + +void set_signal_handler() { + /* setup alternate stack */ + { + stack_t ss = {}; + + ss.ss_sp = malloc(SIGSTKSZ); + if (ss.ss_sp == NULL) { + perror("malloc"); + exit(EXIT_FAILURE); + } + ss.ss_size = SIGSTKSZ; + ss.ss_flags = 0; + + /*sigaltstack() allows a thread to define a new alternate signal + stack and/or retrieve the state of an existing alternate signal + stack. An alternate signal stack is used during the execution of + a signal handler if the establishment of that handler (see + sigaction(2)) requested it.*/ + if (sigaltstack(&ss, NULL) != 0) { + perror("sigaltstack"); + exit(EXIT_FAILURE); + } + } + + /* register our signal handlers */ + { + struct sigaction sig_action = {}; + sigset_t sigset; + + sig_action.sa_sigaction = posix_signal_handler; + sigemptyset(&sig_action.sa_mask); + sig_action.sa_flags = SA_SIGINFO | SA_ONSTACK; + + if (sigaction(SIGSEGV, &sig_action, NULL) != 0) { + perror("sigaction"); + exit(EXIT_FAILURE); + } + if (sigaction(SIGFPE, &sig_action, NULL) != 0) { + perror("sigaction"); + exit(EXIT_FAILURE); + } + if (sigaction(SIGINT, &sig_action, NULL) != 0) { + perror("sigaction"); + exit(EXIT_FAILURE); + } + if (sigaction(SIGILL, &sig_action, NULL) != 0) { + perror("sigaction"); + exit(EXIT_FAILURE); + } + if (sigaction(SIGTERM, &sig_action, NULL) != 0) { + perror("sigaction"); + exit(EXIT_FAILURE); + } + if (sigaction(SIGABRT, &sig_action, NULL) != 0) { + perror("sigaction"); + exit(EXIT_FAILURE); + } + if (sigaction(SIGUSR1, &sig_action, NULL) != 0) { + perror("sigaction"); + exit(EXIT_FAILURE); + } + + sigemptyset(&sigset); + sigaddset(&sigset, SIGUSR1); + if (sigprocmask(SIG_BLOCK, &sigset, NULL) == 0) { + printf("Blocking SISGUSR1...\n"); + } + + kill(getpid(), SIGUSR1); + + sleep(2); + + sigemptyset(&sigset); + sigpending(&sigset); + if (sigismember(&sigset, SIGUSR1) == 1) { + printf("The signal is blocked %d\n", __LINE__); + } + + // after unblocking, handler got the signal which was pending + // sigemptyset(&sigset); + // sigaddset(&sigset, SIGUSR1); + // if(sigprocmask(SIG_UNBLOCK, &sigset, NULL) == 0) { + // printf("Unblocking SISGUSR1...\n"); + // } + } +} + +int main(int argc, char *argv[]) { + (void)argc; + + /* store off program path so we can use it later */ + icky_global_program_name = argv[0]; + + set_signal_handler(); + + cause_calamity(); + + puts("OMG! Nothing bad happend!"); +} + +void cause_calamity() { + /* uncomment one of the following error conditions to cause a calamity of + your choosing! */ + + //(void)divide_by_zero(); + // cause_segfault(); + assert(false); + // infinite_loop(); + // illegal_instruction(); + // stack_overflow(); +} + +int divide_by_zero() { + int a = 1; + int b = 0; + return a / b; +} + +void cause_segfault() { + int *p = (int *)0x12345678; + *p = 0; +} + +void stack_overflow() { + int foo[1000]; + (void)foo; + stack_overflow(); +} + +/* break out with ctrl+c to test SIGINT handling */ +void infinite_loop() { + while (1) { + }; +} + +void illegal_instruction() { + /* I couldn't find an easy way to cause this one, so I'm cheating */ + raise(SIGILL); +} diff --git a/helper/glibc/socket_tcp.c b/helper/glibc/socket_tcp.c new file mode 100644 index 0000000..2c250f0 --- /dev/null +++ b/helper/glibc/socket_tcp.c @@ -0,0 +1,301 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// only differences are listen and accept on server side +// and connect on client side + +#define BUF_SIZE 500 + +void work_as_client(char *portnum, char *data) { + int sock; + int s; + struct addrinfo hints; + struct addrinfo *result, *rp; + struct sockaddr_in6 clientsideaddr; + char buffer[BUF_SIZE], ip[64]; + int enable = 1; + + if (!portnum || !data) { + perror("data is NULL\n"); + return; + } + + sock = socket(PF_INET6, SOCK_STREAM, 0); + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) { + perror("setsockopt reuseport fail\n"); + return; + } + + memset(&clientsideaddr, 0, sizeof(struct sockaddr_in6)); + clientsideaddr.sin6_port = htons(9191); // 0 makes OS selected suitable port number + clientsideaddr.sin6_family = AF_INET6; + clientsideaddr.sin6_addr = in6addr_loopback; + + s = bind(sock, (struct sockaddr *)&clientsideaddr, sizeof(struct sockaddr_in6)); + + if (-1 == s) { + perror("bind"); + exit(EXIT_FAILURE); + } + + memset(&hints, 0, sizeof(hints)); + + hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + hints.ai_socktype = SOCK_STREAM; /* Datagram socket */ + hints.ai_flags = AI_NUMERICSERV; // this is for numeric port //AI_PASSIVE; + // /* For wildcard IP address */ + hints.ai_protocol = IPPROTO_TCP; + + s = getaddrinfo(NULL, portnum, &hints, + &result); // since null, return addres will be loopback, "::1" + // could be written aswell + if (s != 0) { + fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s)); + exit(EXIT_FAILURE); + } + + /* getaddrinfo() returns a list of address structures. + Try each address until we successfully bind(2). + If socket(2) (or bind(2)) fails, we (close the socket + and) try the next address. */ + + for (rp = result; rp != NULL; rp = rp->ai_next) { + switch (rp->ai_family) { + case AF_INET: + if (!inet_ntop(AF_INET, &(((struct sockaddr_in *)rp->ai_addr)->sin_addr), ip, sizeof(ip))) { + freeaddrinfo(result); /* No longer needed */ + exit(1); + } + break; + case AF_INET6: + if (!inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)rp->ai_addr)->sin6_addr), ip, sizeof(ip))) { + freeaddrinfo(result); /* No longer needed */ + exit(1); + } + break; + default: + freeaddrinfo(result); /* No longer needed */ + exit(1); + break; + } + + printf("found ip: %s\n", ip); + + if (rp->ai_family == AF_INET) { + continue; // to pick IPV6 loopback + } + + break; + } + + if (rp == NULL) + exit(2); + + connect(sock, (struct sockaddr *)rp->ai_addr, rp->ai_addrlen); + + if (send(sock, data, strlen(data), MSG_NOSIGNAL) == -1) { + perror("send failed\n"); + } + + if (recv(sock, buffer, sizeof(buffer), 0) < 0) { + perror("recv failed\n"); + } + + printf("Received : %s\n", buffer); + + freeaddrinfo(result); /* No longer needed */ + close(sock); +} + +void work_as_server(char *portnum, char *file_path) { + struct addrinfo hints; + struct addrinfo *result, *rp; + int sfd, s; + char buf[BUF_SIZE]; + char ip[64] = {0}; + int enable = 1; + + if (!portnum || !file_path) { + perror("data is NULL\n"); + } + + memset(&hints, 0, sizeof(hints)); + + hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + hints.ai_socktype = SOCK_STREAM; /* Datagram socket */ + hints.ai_flags = AI_NUMERICSERV; // this is for numeric port //AI_PASSIVE; + // /* For wildcard IP address */ + hints.ai_protocol = IPPROTO_TCP; + + s = getaddrinfo("::", portnum, &hints, + &result); // since null, return addres will be loopback, "::1" + // could be written aswell + if (s != 0) { + fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s)); + exit(EXIT_FAILURE); + } + + /* getaddrinfo() returns a list of address structures. + Try each address until we successfully bind(2). + If socket(2) (or bind(2)) fails, we (close the socket + and) try the next address. */ + + for (rp = result; rp != NULL; rp = rp->ai_next) { + switch (rp->ai_family) { + case AF_INET: + if (!inet_ntop(AF_INET, &(((struct sockaddr_in *)rp->ai_addr)->sin_addr), ip, sizeof(ip))) { + freeaddrinfo(result); /* No longer needed */ + exit(1); + } + break; + case AF_INET6: + if (!inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)rp->ai_addr)->sin6_addr), ip, sizeof(ip))) { + freeaddrinfo(result); /* No longer needed */ + exit(1); + } + break; + default: + freeaddrinfo(result); /* No longer needed */ + exit(1); + break; + } + + printf("found ip: %s\n", ip); + + if (rp->ai_family == AF_INET) { + continue; // to pick IPV6 loopback + } + + sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (sfd == -1) + continue; + + if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) { + perror("setsockopt reuseport fail\n"); + break; + } + + if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == 0) + break; /* Success */ + + close(sfd); + } + + freeaddrinfo(result); /* No longer needed */ + + if (rp == NULL) { /* No address succeeded */ + fprintf(stderr, "Could not bind\n"); + exit(EXIT_FAILURE); + } + + if (listen(sfd, 20) == -1) + perror("listen fail\n"); + + /* Read datagrams and echo them back to sender. */ + + for (;;) { + struct sockaddr_in6 client_addr; + socklen_t client_addr_len = sizeof(client_addr); + int client_sock_fd = accept(sfd, (struct sockaddr *)&client_addr, &client_addr_len); + if (client_sock_fd == -1) { + perror("accept()"); + close(sfd); + } + + char str_addr[64]; + + inet_ntop(AF_INET6, &(client_addr.sin6_addr), str_addr, sizeof(str_addr)); + printf("New connection from: %s:%d ...\n", str_addr, ntohs(client_addr.sin6_port)); + + /* Wait for data from client */ + if (recv(client_sock_fd, buf, sizeof(buf), 0) == -1) { + perror("read()"); + close(client_sock_fd); + continue; + } + printf("received-->\n %s\n", buf); + + memset(buf, 0, sizeof(buf)); + + int fd = open(file_path, O_RDONLY | O_NONBLOCK, 0444); + if (fd < 0) { + perror("open failed\n"); + exit(EXIT_FAILURE); + } + read(fd, buf, sizeof(buf)); + + if (send(client_sock_fd, buf, strlen(buf) + 1, MSG_NOSIGNAL) == -1) { + perror("read()"); + close(client_sock_fd); + } + close(fd); + } +} + +int main(int argc, char *argv[]) { + int option = 0; + bool is_server = true; + char port[32] = {0}; + char data[BUF_SIZE] = {0}; + char file[BUF_SIZE] = {0}; + + memset(port, 0, sizeof(port)); + memset(data, 0, sizeof(data)); + memset(file, 0, sizeof(file)); + + while ((option = getopt(argc, argv, "hcp:d:f:")) != -1) { + switch (option) { + case 'h': + // "optarg" variable is used to get option itself + perror("read the source code\n"); + exit(EXIT_SUCCESS); + case 'c': + is_server = false; + break; + case 'p': + strncpy(port, optarg, sizeof(port) - 1); + break; + case 'd': + strncpy(data, optarg, sizeof(data) - 1); + break; + case 'f': + strncpy(file, optarg, sizeof(file) - 1); + break; + default: + perror("unknown argument\n"); + return 2; + } + } + + if (strlen(port) == 0) { + perror("specify port number\n"); + exit(EXIT_FAILURE); + } + + if (is_server) { + if (strlen(file) == 0) { + perror("specify file\n"); + exit(EXIT_FAILURE); + } + printf("work as server\n"); + work_as_server(port, file); + } else { + if (strlen(data) == 0) { + perror("specify data\n"); + exit(EXIT_FAILURE); + } + printf("work as client\n"); + work_as_client(port, data); + } +} diff --git a/helper/glibc/socket_udp.c b/helper/glibc/socket_udp.c new file mode 100644 index 0000000..4311dbc --- /dev/null +++ b/helper/glibc/socket_udp.c @@ -0,0 +1,245 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BUF_SIZE 500 + +static struct option parameters[] = {{"client", no_argument, 0, 'c'}, {"help", no_argument, 0, 'h'}, {NULL, 0, 0, 0}}; + +void print_help(void) { + printf("type for port, for client\n"); + printf("otherwise, it is server\n"); + exit(1); +} + +void work_as_client(char *portnum) { + int sock; + int s; + struct addrinfo hints; + struct addrinfo *result, *rp; + struct sockaddr_in6 clientsideaddr, peer_addr; + char buffer[BUF_SIZE], ip[64]; + int peer_addr_len = sizeof(peer_addr); + + sock = socket(PF_INET6, SOCK_DGRAM, 0); + + memset(&clientsideaddr, 0, sizeof(struct sockaddr_in6)); + clientsideaddr.sin6_port = htons(9192); // 0 makes OS selected suitable port number + clientsideaddr.sin6_family = AF_INET6; + clientsideaddr.sin6_addr = in6addr_loopback; + + s = bind(sock, (struct sockaddr *)&clientsideaddr, sizeof(struct sockaddr_in6)); + + if (-1 == s) + perror("bind"), exit(1); + + memset(&hints, 0, sizeof(hints)); + + hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */ + hints.ai_flags = AI_NUMERICSERV; // this is for numeric port //AI_PASSIVE; + // /* For wildcard IP address */ + hints.ai_protocol = IPPROTO_UDP; + + s = getaddrinfo(NULL, portnum, &hints, + &result); // since null, return addres will be loopback, "::1" + // could be written aswell + if (s != 0) { + fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s)); + exit(EXIT_FAILURE); + } + + /* getaddrinfo() returns a list of address structures. + Try each address until we successfully bind(2). + If socket(2) (or bind(2)) fails, we (close the socket + and) try the next address. */ + + for (rp = result; rp != NULL; rp = rp->ai_next) { + switch (rp->ai_family) { + case AF_INET: + if (!inet_ntop(AF_INET, &(((struct sockaddr_in *)rp->ai_addr)->sin_addr), ip, sizeof(ip))) { + freeaddrinfo(result); /* No longer needed */ + exit(1); + } + break; + case AF_INET6: + if (!inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)rp->ai_addr)->sin6_addr), ip, sizeof(ip))) { + freeaddrinfo(result); /* No longer needed */ + exit(1); + } + break; + default: + freeaddrinfo(result); /* No longer needed */ + exit(1); + break; + } + + printf("found ip: %s\n", ip); + + if (rp->ai_family == AF_INET) { + continue; // to pick IPV6 loopback + } + + break; + } + + if (rp == NULL) + exit(2); + + if (sendto(sock, "selam", 6, 0, (struct sockaddr *)rp->ai_addr, sizeof(struct sockaddr_in6)) != 6) + fprintf(stderr, "Error sending response\n"); + + if (recvfrom(sock, buffer, sizeof(buffer), 0, (struct sockaddr *)&peer_addr, (socklen_t *)&peer_addr_len) == -1) + perror("recvfrom"); + + char host[NI_MAXHOST], service[NI_MAXSERV]; + + s = getnameinfo((struct sockaddr *)&peer_addr, peer_addr_len, host, NI_MAXHOST, service, NI_MAXSERV, + NI_NUMERICSERV); + + printf("Received : %s\n", buffer); + + freeaddrinfo(result); /* No longer needed */ + close(sock); +} + +void work_as_server(char *portnum) { + struct addrinfo hints; + struct addrinfo *result, *rp; + int sfd, s; + struct sockaddr_storage peer_addr; + socklen_t peer_addr_len; + ssize_t nread; + char buf[BUF_SIZE]; + char ip[64] = {0}; + int enable = 1; + + if (!portnum) { + perror("data is NULL\n"); + } + + memset(&hints, 0, sizeof(hints)); + + hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */ + hints.ai_flags = AI_NUMERICSERV; // this is for numeric port //AI_PASSIVE; + // /* For wildcard IP address */ + hints.ai_protocol = IPPROTO_UDP; + + s = getaddrinfo(NULL, portnum, &hints, + &result); // since null, return addres will be loopback, "::1" + // could be written aswell + if (s != 0) { + fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s)); + exit(EXIT_FAILURE); + } + + /* getaddrinfo() returns a list of address structures. + Try each address until we successfully bind(2). + If socket(2) (or bind(2)) fails, we (close the socket + and) try the next address. */ + + for (rp = result; rp != NULL; rp = rp->ai_next) { + switch (rp->ai_family) { + case AF_INET: + if (!inet_ntop(AF_INET, &(((struct sockaddr_in *)rp->ai_addr)->sin_addr), ip, sizeof(ip))) { + freeaddrinfo(result); /* No longer needed */ + exit(1); + } + break; + case AF_INET6: + if (!inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)rp->ai_addr)->sin6_addr), ip, sizeof(ip))) { + freeaddrinfo(result); /* No longer needed */ + exit(1); + } + break; + default: + freeaddrinfo(result); /* No longer needed */ + exit(1); + break; + } + + printf("found ip: %s\n", ip); + + if (rp->ai_family == AF_INET) { + continue; // to pick IPV6 loopback + } + + sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (sfd == -1) + continue; + + if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) { + perror("setsockopt reuseport fail\n"); + break; + } + + if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == 0) + break; /* Success */ + + close(sfd); + } + + freeaddrinfo(result); /* No longer needed */ + + if (rp == NULL) { /* No address succeeded */ + fprintf(stderr, "Could not bind\n"); + exit(EXIT_FAILURE); + } + + /* Read datagrams and echo them back to sender. */ + + for (;;) { + peer_addr_len = sizeof(peer_addr); + nread = recvfrom(sfd, buf, BUF_SIZE, 0, (struct sockaddr *)&peer_addr, &peer_addr_len); + if (nread == -1) + continue; /* Ignore failed request */ + + char host[NI_MAXHOST], service[NI_MAXSERV]; + + s = getnameinfo((struct sockaddr *)&peer_addr, peer_addr_len, host, NI_MAXHOST, service, NI_MAXSERV, + NI_NUMERICSERV); + if (s == 0) + printf("Received %zd bytes from %s:%s: %s\n", nread, host, service, buf); + else + fprintf(stderr, "getnameinfo: %s\n", gai_strerror(s)); + + if (sendto(sfd, "selam2", 7, 0, (struct sockaddr *)&peer_addr, peer_addr_len) != 7) + fprintf(stderr, "Error sending response\n"); + } +} + +int main(int argc, char *argv[]) { + int option = 0, longid = 0; + bool is_server = true; + char port[5] = "9191"; + + while ((option = getopt_long(argc, argv, "hc", parameters, &longid)) != -1) { + switch (option) { + case 'h': + // "optarg" variable is used to get option itself + print_help(); + break; + case 'c': + is_server = false; + break; + default: + perror("unknown argument\n"); + return 2; + } + } + + if (is_server) { + work_as_server(port); + } else { + work_as_client(port); + } +} diff --git a/helper/glibc/sortandsearch.c b/helper/glibc/sortandsearch.c new file mode 100644 index 0000000..bcc073b --- /dev/null +++ b/helper/glibc/sortandsearch.c @@ -0,0 +1,83 @@ +#include +#include +#include +#include + +int comparison(const void *a, const void *b) { return (int)(*(int *)a - *(int *)b); } + +int main(int argc, char **args) { + int *buffer = NULL; + char *rest = NULL; + unsigned long len = 0; + unsigned int i = 0; + int key = 0; + + if (argc != 2) { + perror("need to type len to generate random-data-buffer\n"); + exit(EXIT_FAILURE); + } + + len = strtoul(args[1], &rest, 10); + if (rest == args[1]) { + perror("strtoul failed\n"); + exit(EXIT_FAILURE); + } + + if (len > 256) { + perror("max len is 256\n"); + exit(EXIT_FAILURE); + } + + buffer = (int *)reallocarray(NULL, len, sizeof(int)); + if (!buffer) { + perror("reallocarray failed\n"); + exit(EXIT_FAILURE); + } + + memset(buffer, 0, len * sizeof(int)); + + ////////////////////////////////////////////// generate random numbers + /////////////////////////////////// + + if (getentropy(buffer, len * sizeof(int))) { + perror("getentropy failed\n"); + exit(EXIT_FAILURE); + } + + for (i = 0; i < (unsigned int)len; i++) { + if (i == 0) { + // take a sample + key = buffer[i]; + } + printf("%d ", buffer[i]); + } + printf("\n"); + + ////////////////////////////////////////////// quick sort + ////////////////////////////////////////////////// + + /*Quick Sort*/ + qsort(buffer, len, sizeof(int), comparison); + + for (i = 0; i < len; i++) { + printf("%d ", buffer[i]); + } + printf("\n"); + + ////////////////////////////////////////////// binary search + /////////////////////////////////////////////// + + /*If there are multiple elements that match the key, the element returned is + * unspecified*/ + /*Note that, compared elements could be anything like a struct. All needed is + * to pass suitable key to the comprarator function*/ + if (NULL == (int *)bsearch(&key, buffer, len, sizeof(int), comparison)) { + printf("%d NOT found\n", key); + } else { + printf("%d found\n", key); + } + + free(buffer); + + return EXIT_SUCCESS; +} diff --git a/helper/glibc/str_ncpy-casecmp-chr-rchr-fry-tok_r.c b/helper/glibc/str_ncpy-casecmp-chr-rchr-fry-tok_r.c new file mode 100644 index 0000000..c2da958 --- /dev/null +++ b/helper/glibc/str_ncpy-casecmp-chr-rchr-fry-tok_r.c @@ -0,0 +1,79 @@ +#define _GNU_SOURCE +#include +#include +#include +#include + +int main(void) { + /*****************************************************************************************/ + + char *s_strncpy_source = "this is strncpy string"; + char s_strncpy[10]; + + strncpy(s_strncpy, s_strncpy_source, sizeof(s_strncpy)); + printf("s_strncpy lenght is %lu while strlen is %lu, \"%s\"-\"%s\"\n", strlen(s_strncpy), strlen(s_strncpy_source), + s_strncpy, s_strncpy_source); + + // RETURN: s_strncpy lenght is 10 while strlen is 22, "this is st"-"this is + // strncpy string" + + /*****************************************************************************************/ + + // case differences are ignored + char *s_strcasecmp_source1 = "this is strcasecmp string"; + char *s_strcasecmp_source2 = "This Is strcasecmp STRING"; + + printf("strcasecmp return is: %d, case diff are ignored\n", strcasecmp(s_strcasecmp_source1, s_strcasecmp_source2)); + + // RETURN: strcasecmp return is: 0, case diff are ignored + + /*****************************************************************************************/ + + char *s_strchr = "this is strchr string"; + + printf("strchr is: \"%s\", strrchr is: \"%s\"\n", strchr(s_strchr, (int)'s'), strrchr(s_strchr, (int)'s')); + + // RETURN: strchr is: "s is strchr string", strrchr is: "string" + + /*****************************************************************************************/ + + char s_strfry[22] = "this is strfry string"; + + printf("strfry is: \"%s\"\n", strfry(s_strfry)); + + // RETURN: strfry is: "issgt isi rsn tthyrfr" + + /*****************************************************************************************/ + + char s_strtok_r[31] = "this is strtok string1,string2"; + char *token1 = NULL, *restbuff1 = NULL, *str1 = NULL; + char *token2 = NULL, *restbuff2 = NULL, *str2 = NULL; + + for (str1 = s_strtok_r;; str1 = NULL) { + token1 = strtok_r(str1, " ", &restbuff1); + if (token1 == NULL) + break; + printf(" --> %s\n", token1); + + if (strchr(token1, (int)',')) { + for (str2 = token1;; str2 = NULL) { + token2 = strtok_r(str2, ",", &restbuff2); + if (token2 == NULL) + break; + printf(" --> %s\n", token2); + } + } + } + + // RETURN: + // --> this + // --> is + // --> strtok + // --> string1,string2 + // --> string1 + // --> string2 + + /*****************************************************************************************/ + + exit(EXIT_SUCCESS); +} diff --git a/helper/glibc/tailq.c b/helper/glibc/tailq.c new file mode 100644 index 0000000..6defa66 --- /dev/null +++ b/helper/glibc/tailq.c @@ -0,0 +1,64 @@ + +/* +https://man7.org/linux/man-pages/man3/tailq.3.html + +FIFO + +OUTPUT: +2 +1 +0 + +*/ + +#include +#include +#include +#include + +struct entry { + int data; + TAILQ_ENTRY(entry) entries; /* Tail queue */ +}; + +TAILQ_HEAD(tailhead, entry); + +int main(void) { + struct entry *n1, *n2, *n3, *np; + struct tailhead head; /* Tail queue head */ + int i; + + TAILQ_INIT(&head); /* Initialize the queue */ + + n1 = malloc(sizeof(struct entry)); /* Insert at the head */ + TAILQ_INSERT_HEAD(&head, n1, entries); + + n1 = malloc(sizeof(struct entry)); /* Insert at the tail */ + TAILQ_INSERT_TAIL(&head, n1, entries); + + n2 = malloc(sizeof(struct entry)); /* Insert after */ + TAILQ_INSERT_AFTER(&head, n1, n2, entries); + + n3 = malloc(sizeof(struct entry)); /* Insert before */ + TAILQ_INSERT_BEFORE(n2, n3, entries); + + TAILQ_REMOVE(&head, n2, entries); /* Deletion */ + free(n2); + /* Forward traversal */ + i = 0; + TAILQ_FOREACH(np, &head, entries) + np->data = i++; + /* Reverse traversal */ + TAILQ_FOREACH_REVERSE(np, &head, tailhead, entries) + printf("%i\n", np->data); + /* TailQ deletion */ + n1 = TAILQ_FIRST(&head); + while (n1 != NULL) { + n2 = TAILQ_NEXT(n1, entries); + free(n1); + n1 = n2; + } + TAILQ_INIT(&head); + + exit(EXIT_SUCCESS); +} diff --git a/helper/glibc/thread_conditionvariable.c b/helper/glibc/thread_conditionvariable.c new file mode 100644 index 0000000..d11696b --- /dev/null +++ b/helper/glibc/thread_conditionvariable.c @@ -0,0 +1,101 @@ +/* +The condition variable is used to signal changes in the variable’s state +The principal condition variable operations are signal and wait. The signal +operation is a notification to one or more waiting threads that a shared +variable’s state has changed. The wait operation is the means of blocking until +such a notification is received. They are used with mutexes + +While mutex implement synchronization by controlling thread access to data, +condition variables allow threads to synchronize based upon the actual value of +data. + +Without condition variables, the programmer would need to have threads +continually polling (possibly in a critical section), to check if the condition +is met. + +OUTPUT: +Waiting on condition variable +Signaling condition variable +Returning thread +Returning thread +*/ + +#include +#include +#include +#include + +#define TRUE 1 +#define FALSE 0 + +#define RESPONSE_TIMEOUT_MS 5000 + +pthread_cond_t cond1 = PTHREAD_COND_INITIALIZER; +pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; + +int is_packet_received = FALSE; + +void *receiver(void *args) { + if (args) { + printf("args: %p\n", args); + } + + pthread_mutex_lock(&lock); + // read the packet and handle it + // then inform the sender again to send other chunks + is_packet_received = TRUE; + pthread_cond_signal(&cond1); + pthread_mutex_unlock(&lock); + + printf("Returning receiver thread\n"); + + return NULL; +} + +void *sender(void *args) { + struct timespec ts; + + if (args) { + printf("args: %p\n", args); + } + + clock_gettime(CLOCK_REALTIME, &ts); + ts.tv_sec += (RESPONSE_TIMEOUT_MS / 1000); + + int is_packet_received_copy = FALSE; + + pthread_mutex_lock(&lock); + if (is_packet_received == FALSE) { + pthread_cond_timedwait(&cond1, &lock, &ts); + } + is_packet_received_copy = is_packet_received; + is_packet_received = FALSE; + pthread_mutex_unlock(&lock); + + if (is_packet_received_copy == FALSE) { + printf("timeout, retry\n"); + } else { + printf("packet received successfully\n"); + } + + printf("Returning sender thread\n"); + + return NULL; +} + +int main() { + pthread_t tid1, tid2; + + pthread_create(&tid1, NULL, receiver, NULL); + + // sleep for 1 sec so that thread 1 + // would get a chance to run first + sleep(1); + + pthread_create(&tid2, NULL, sender, NULL); + + pthread_join(tid1, NULL); + pthread_join(tid2, NULL); + + return 0; +} diff --git a/helper/glibc/thread_mutex.c b/helper/glibc/thread_mutex.c new file mode 100644 index 0000000..10df599 --- /dev/null +++ b/helper/glibc/thread_mutex.c @@ -0,0 +1,103 @@ + +/* +Protecting Accesses to Shared Variables +A mutex has two states: locked and unlocked. At any moment, at most one thread +may hold the lock on a mutex. Attempting to lock a mutex that is already locked +either blocks or fails with an error, depending on the method used to place the +lock. Sometimes, a thread needs to simultaneously access two or more different +shared resources, each of which is governed by a separate mutex. When more than +one thread is locking the same set of mutexes, deadlock situations can arise. +Figure 30-3 shows an example of a deadlock in which each thread successfully +locks one mutex, and then tries to lock the mutex that the other thread has +already locked. Both threads will remain blocked indefinitely. The simplest way +to avoid such deadlocks is to define a mutex hierarchy. When threads can lock +the same set of mutexes, they should always lock them in the same order. An +alternative strategy that is less frequently used is “try, and then back off.” + +OUTPUT: +passed data is 20 +counter: 1 +passed data is 10 +counter: 2 +returned retval 212531 from 993f6640 +returned retval 212532 from 98bf5640 + +if not mutex then the output +passed data is 10 +passed data is 20 +counter: 2 +returned retval 212652 from 587a7640 +counter: 2 +returned retval 212653 from 57fa6640 +*/ + +#define _GNU_SOURCE // gettid +#include +#include +#include +#include +#include + +int shared_counter; +pthread_mutex_t lock; + +void *trythis(void *arg) { + char *buffer = NULL; + + pthread_mutex_lock(&lock); + if ((int *)arg) { + printf("passed data is %d\n", *(int *)arg); + } + shared_counter += 1; + sleep(5); + printf("counter: %d \n", shared_counter); + + if ((buffer = (char *)malloc(64)) == NULL) { + perror("malloc() error"); + exit(EXIT_FAILURE); + } + + snprintf(buffer, 64, "retval %d", gettid()); + pthread_mutex_unlock(&lock); + + return (buffer) ? buffer : NULL; + ; +} + +int main(void) { + pthread_t thread_id1, thread_id2; + int passed_data1 = 10, passed_data2 = 20; + char *retval_ptr = NULL; + + if (pthread_mutex_init(&lock, NULL) != 0) { + perror("mutex init has failed\n"); + exit(EXIT_FAILURE); + } + + if (pthread_create(&(thread_id1), NULL, &trythis, (void *)&passed_data1) != 0) { + perror("error while creating the thread\n"); + exit(EXIT_FAILURE); + } + if (pthread_create(&(thread_id2), NULL, &trythis, (void *)&passed_data2) != 0) { + perror("error while creating the thread\n"); + exit(EXIT_FAILURE); + } + + pthread_join(thread_id1, (void **)&retval_ptr); + printf("returned %s from %x\n", retval_ptr, (int)thread_id1); + if (retval_ptr) { + free(retval_ptr); + } + + pthread_join(thread_id2, (void **)&retval_ptr); + printf("returned %s from %x\n", retval_ptr, (int)thread_id2); + if (retval_ptr) { + free(retval_ptr); + } + + pthread_mutex_destroy(&lock); + // note: if "PTHREAD_MUTEX_INITIALIZER" is used while initializing, then + // destroy is needed + + exit(EXIT_SUCCESS); +} diff --git a/helper/glibc/thread_sem_detached.c b/helper/glibc/thread_sem_detached.c new file mode 100644 index 0000000..e8afea0 --- /dev/null +++ b/helper/glibc/thread_sem_detached.c @@ -0,0 +1,101 @@ +/* +There are two basic sorts of semaphores: binary semaphores, which never take on +values other than zero or one, and counting semaphores, which can take on +arbitrary nonnegative values. +A binary semaphore is logically just like a mutex. +Counting semaphore is used for multi process systems for example. + +OUTPUT: (while sem initial value is 1) +//it works like mutexes + +Entered.. + +Just Exiting... + +Entered.. + +Just Exiting... + +Entered.. + +Just Exiting... + +OUTPUT: (while sem initial value is 2) +//it works like 2-core + +Entered.. + +Entered.. + +Just Exiting... + +Entered.. + +Just Exiting... + +Just Exiting... +*/ + +#include +#include +#include +#include +#include + +sem_t sem; + +void *thread(void *arg) { + if (arg) { + printf("arg: %p\n", arg); + } + + // wait + sem_wait(&sem); // decrement semaphore value and waits until the value is + // equal to zero + printf("\nEntered..\n"); + + // critical section + sleep(4); + + // signal + printf("\nJust Exiting...\n"); + sem_post(&sem); + + return NULL; +} + +int main(void) { + pthread_t t1, t2, t3; + pthread_attr_t thread_attr; + + // 0 means not shared, only in same process + // 1 means semaphores initial value (it is binary semaphore) + sem_init(&sem, 0, 1); + + if (pthread_attr_init(&thread_attr) != 0) { + perror("pthread_attr_init failure.\n"); + exit(EXIT_FAILURE); + } + + if (pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED) != 0) { + perror("pthread_attr_setdetachstate failure.\n"); + exit(EXIT_FAILURE); + } + + pthread_create(&t1, &thread_attr, thread, NULL); + + sleep(2); + + pthread_create(&t2, &thread_attr, thread, NULL); + + sleep(2); + + pthread_create(&t3, &thread_attr, thread, NULL); + + sem_destroy(&sem); + + // wait to see all threads are finished + sleep(30); + + exit(EXIT_SUCCESS); +} diff --git a/helper/glibc/time.c b/helper/glibc/time.c new file mode 100644 index 0000000..331eb1d --- /dev/null +++ b/helper/glibc/time.c @@ -0,0 +1,100 @@ +// OUTPUT: +// ********************************************************** +// CLOCK_REALTIME : 1685604063.267 (19509 days + 7h 21m 3s) +// 1 5 123 - 10:21:3.267 +// CLOCK_TAI : 1685604063.267 (19509 days + 7h 21m 3s) +// CLOCK_MONOTONIC: 162014.364 (1 days + 21h 0m 14s) +// CLOCK_BOOTTIME : 257861.041 (2 days + 23h 37m 41s) +// CLOCK_PROCESS_CPUTIME_ID: 0.005 ( 0h 0m 0s) +// CLOCK_THREAD_CPUTIME_ID: 0.005 ( 0h 0m 0s) +// ********************************************************** +// CLOCK_REALTIME : 1685604064.267 (19509 days + 7h 21m 4s) +// 1 5 123 - 10:21:4.267 +// CLOCK_TAI : 1685604064.268 (19509 days + 7h 21m 4s) +// CLOCK_MONOTONIC: 162015.364 (1 days + 21h 0m 15s) +// CLOCK_BOOTTIME : 257862.041 (2 days + 23h 37m 42s) +// CLOCK_PROCESS_CPUTIME_ID: 0.005 ( 0h 0m 0s) +// CLOCK_THREAD_CPUTIME_ID: 0.005 ( 0h 0m 0s) +// ********************************************************** +// CLOCK_REALTIME : 1685604065.268 (19509 days + 7h 21m 5s) +// 1 5 123 - 10:21:5.268 +// CLOCK_TAI : 1685604065.268 (19509 days + 7h 21m 5s) +// CLOCK_MONOTONIC: 162016.364 (1 days + 21h 0m 16s) +// CLOCK_BOOTTIME : 257863.041 (2 days + 23h 37m 43s) +// CLOCK_PROCESS_CPUTIME_ID: 0.005 ( 0h 0m 0s) +// CLOCK_THREAD_CPUTIME_ID: 0.005 ( 0h 0m 0s) +// ********************************************************** + +#define _XOPEN_SOURCE 600 +#include +#include +#include +#include +#include +#include + +#define SECS_IN_DAY (24 * 60 * 60) + +static void display_clock(clockid_t clock, const char *name) { + struct timespec ts; + + if (clock_gettime(clock, &ts) == -1) { + perror("clock_gettime"); + exit(EXIT_FAILURE); + } + + printf("%-15s: %10jd.%03ld (", name, (intmax_t)ts.tv_sec, ts.tv_nsec / 1000000); + + long days = ts.tv_sec / SECS_IN_DAY; + if (days > 0) { + printf("%ld days + ", days); + } + + printf("%2dh %2dm %2ds", (int)(ts.tv_sec % SECS_IN_DAY) / 3600, (int)(ts.tv_sec % 3600) / 60, (int)ts.tv_sec % 60); + printf(")\n"); + + if (clock == CLOCK_REALTIME) { + // show brokentime + struct tm *brokentime = localtime(&(ts.tv_sec)); + printf(" %d %d %d - %d:%d:%d.%ld\n", brokentime->tm_mday, brokentime->tm_mon, brokentime->tm_year, + brokentime->tm_hour, brokentime->tm_min, brokentime->tm_sec, (ts.tv_nsec / 1000000)); + } +} + +int main(void) { + + while (1) { + display_clock(CLOCK_REALTIME, "CLOCK_REALTIME"); + // this is realtime clock +#ifdef CLOCK_TAI + display_clock(CLOCK_TAI, "CLOCK_TAI"); + // A nonsettable system-wide clock derived from wall-clock + // time but ignoring leap seconds. This clock does not + // experience discontinuities and backwards jumps caused by + // NTP inserting leap seconds as CLOCK_REALTIME does. +#endif + display_clock(CLOCK_MONOTONIC, "CLOCK_MONOTONIC"); + // Counts from system boot but it does not count time that the system is + // suspended +#ifdef CLOCK_BOOTTIME + display_clock(CLOCK_BOOTTIME, "CLOCK_BOOTTIME"); + // A nonsettable system-wide clock that is identical to + // CLOCK_MONOTONIC, except that it also includes any time + // that the system is suspende +#endif +#ifdef CLOCK_PROCESS_CPUTIME_ID + display_clock(CLOCK_PROCESS_CPUTIME_ID, "CLOCK_PROCESS_CPUTIME_ID"); + // This is a clock that measures CPU time consumed by this + // process (i.e., CPU time consumed by all thread_mutex in the + // process) +#endif +#ifdef CLOCK_THREAD_CPUTIME_ID + display_clock(CLOCK_THREAD_CPUTIME_ID, "CLOCK_THREAD_CPUTIME_ID"); + // This is a clock that measures CPU time consumed by this thread +#endif + printf("**********************************************************\n"); + sleep(1); + } + + exit(EXIT_SUCCESS); +} diff --git a/helper/glibc/variadic_sum.c b/helper/glibc/variadic_sum.c new file mode 100644 index 0000000..5f5203b --- /dev/null +++ b/helper/glibc/variadic_sum.c @@ -0,0 +1,24 @@ +#include +#include +#include +#include + +int sum(int num_args, ...) { + int val = 0; + va_list ap; + int i; + + va_start(ap, num_args); + for (i = 0; i < num_args; i++) { + val += va_arg(ap, int); + } + va_end(ap); + + return val; +} + +int main(void) { + printf("Sum of 15 and 56 = %d\n", sum(2, 15, 56)); + + exit(EXIT_SUCCESS); +} \ No newline at end of file diff --git a/helper/hm10_driver/.gitkeep b/helper/hm10_driver/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/helper/hm10_driver/hm10_hm11_ctl.py b/helper/hm10_driver/hm10_hm11_ctl.py new file mode 100755 index 0000000..a6ccfbd --- /dev/null +++ b/helper/hm10_driver/hm10_hm11_ctl.py @@ -0,0 +1,603 @@ + +import argparse +import codecs +import select +import sys +import time + +import serial + +import utils + +VERSION = "0.0.1" + +role = 'peripheral' + +parser = argparse.ArgumentParser( + prog='hm10_hm11_ctl', + description='a control tool to derive hm10_hm11 ble devices', + epilog='hm10_hm11_ctl Help') +parser.add_argument('-v', '--version', + action='store_true', + help='show version number and return') +parser.add_argument('-d', '--verbose', + action='store_true', + help='verbosity setting') +parser.add_argument('-i', '--interface', + action='store', + help='serial interface name') +parser.add_argument('-r', '--role', + action='store', + help='\"peripheral\" or \"central\" whould be typed') +parser.add_argument('-p', '--power', + action='store', + help='\"sleep\" or \"wakeup\" whould be typed') +parser.add_argument('-e', '--remotecontrol', + action='store_true', + help='enable remote AT command control') +parser.add_argument('-c', '--customcommand', + action='store', + help='to pass the argument directly to the module') +parser.add_argument('-m', '--setmac', + action='store', + help='set MAC address') +parser.add_argument('-a', '--setadv', + action='store', + help='set advertising value') +parser.add_argument('-w', '--setwhitemac', + action='store', + help='add the mac address into white list') +parser.add_argument('-l', '--listnumber', + action='store', + help='specify the list number (1-2-3) if setwhitemac is used') +parser.add_argument('-b', '--battmonitor', + action='store_true', + help='enable batter monitor') +parser.add_argument('-n', '--name', + action='store', + help='set module name, max len 12') +parser.add_argument('-x', '--connectmac', + action='store', + help='type mac to be connected, max len 12') +args = parser.parse_args() + + +def serial_open(port, wait_time): + ser = serial.Serial(port, 9600, timeout=wait_time) # open serial port + return ser + + +def serial_close(ser): + ser.close() # close port + return ser + + +def execute_string_command(ser, command, lenght): + command_b = bytes(command, 'utf-8') + ser.write(command_b) # write a string + response = ser.read(lenght) # read up to lenght bytes (timeout) + response = codecs.decode(response, encoding='utf-8') + if args.verbose is True: + print('RESPONSE: ', response) + return response + + +if args.version is True: + print('version', VERSION) + sys.exit(0) + +u = utils.Utils(args) +if u.arg_check() is False: + sys.exit(1) + +serial_handle = serial_open(args.interface, 10) + +print('EXECUTED: enabled uart wakeup') +response = execute_string_command(serial_handle, 'AT+UART0', 8) + +if args.power is None or args.power.__contains__('wakeup'): + # you can send a long string (Length > 1024 or more), + # that string can caused module wake up, and you will be received “OK+WAKE” string from module UART + print('EXECUTED: wake up from sleep') + execute_string_command(serial_handle, 'I am iron man, I am iron man, I am iron man I am iron man, \ + I am iron man, I am iron man, I am iron man I am iron man, I am iron man, I am iron man, I am iron \ + man I am iron man, I am iron man, I am iron man, I am iron man I am iron manI am iron man, I am iron \ + man, I am iron man I am iron man, I am iron man, I am iron man, I am iron man I am iron manI am iron man, \ + I am iron man, I am iron man I am iron man, I am iron man, I am iron man, I am iron man I am iron mann \ + I am iron man, I am iron man, I am iron man I am iron man, I am iron man, I am iron man, I am iron man \ + I am iron manI am iron man, I am iron man, I am iron man I am iron man, I am iron man, I am iron man, \ + I am iron man I am iron manI am iron man, I am iron man, I am iron man I am iron man, I am iron man, \ + I am iron man, I am iron man I am iron manI am iron man, I am iron man, I am iron man I am iron man, \ + I am iron man, I am iron man, I am iron man I am iron manI am iron man, I am iron man, I am iron man \ + I am iron man, I am iron man, I am iron man, I am iron man I am iron manI am iron man, I am iron man, \ + I am iron man I am iron man, I am iron man, I am iron man, I am iron man I am iron manI am iron man, \ + I am iron man, I am iron man I am iron man, I am iron man, I am iron man, I am iron man I am iron man', 7) +else: + print('EXECUTED: going into sleep') + execute_string_command(serial_handle, 'AT+SLEEP', 8) + serial_close(serial_handle) + exit(0) + +print('EXECUTED: query software version') +response = execute_string_command(serial_handle, 'AT+VERR?', 20) +print('INFO: version ', response) + +# use AT+BAUD[P1] to set it where P1 is +# 0: 9600; 1: 19200;2: 38400; +# 3: 57600; 4: 115200; 5: 4800; +# 6: 2400; 7: 1200; 8: 230400; +# Default: 0(9600) +print('EXECUTED: query baud rate') +response = execute_string_command(serial_handle, 'AT+BAUD?', 13) +interval = response.split(":") +if interval[1].__contains__('0'): + print('INFO: baud 9600') +elif interval[1].__contains__('1'): + print('INFO: baud 19200') +elif interval[1].__contains__('2'): + print('INFO: baud 38400') +elif interval[1].__contains__('3'): + print('INFO: baud 57600') +elif interval[1].__contains__('4'): + print('INFO: baud 115200') +elif interval[1].__contains__('5'): + print('INFO: baud 4800') +elif interval[1].__contains__('6'): + print('INFO: baud 2400') +elif interval[1].__contains__('7'): + print('INFO: baud 1200') +elif interval[1].__contains__('8'): + print('INFO: baud 230400') + +print('EXECUTED: get stop bit') +response = execute_string_command(serial_handle, 'AT+STOP?', 8) +interval = response.split(":") +if interval[1].__contains__('0'): + print('INFO: stop bit 1') +elif interval[1].__contains__('1'): + print('INFO: stop bit 2') + +print('EXECUTED: get parity') +response = execute_string_command(serial_handle, 'AT+PARI?', 8) +interval = response.split(":") +if interval[1].__contains__('0'): + print('INFO: parity NONE') +elif interval[1].__contains__('1'): + print('INFO: parity ODD') +elif interval[1].__contains__('2'): + print('INFO: parity EVEN') + +# use this command AT+UUID[P1] to set it where P1: 0x0001~0xFFFE +print('EXECUTED: get UUID') +response = execute_string_command(serial_handle, 'AT+UUID?', 13) +interval = response.split(":") +print('INFO: UUID ', interval[1]) + +print('EXECUTED: get temperature') +response = execute_string_command(serial_handle, 'AT+TEMP?', 14) +interval = response.split(":") +print('INFO: temperature ', interval[1]) + +print('EXECUTED: get last connected device mac') +response = execute_string_command(serial_handle, 'AT+RADD?', 20) +interval = response.split(":") +print('INFO: last device mac ', interval[1]) + +# use this command AT+TYPE? to get it where P1: 0~2 +# 0: Not need PIN Code +# 1: Auth not need PIN +# 2: Auth with PIN +# 3: Auth and bonded +# Default: 0 +print('EXECUTED: set bond mode for no PIN') +response = execute_string_command(serial_handle, 'AT+TYPE0', 8) + +print('EXECUTED: set for reliable advertise') +response = execute_string_command(serial_handle, 'AT+RELI1', 8) + +print('EXECUTED: do not auto sleep') +response = execute_string_command(serial_handle, 'AT+PWRM1', 8) + +print('EXECUTED: maximize power (6 dbm)') +response = execute_string_command(serial_handle, 'AT+POWE3', 8) +response = execute_string_command(serial_handle, 'AT+PCTL1', 8) + +print('EXECUTED: open rx gain') +response = execute_string_command(serial_handle, 'AT+GAIN1', 8) + +print('EXECUTED: enable notify information with address') +response = execute_string_command(serial_handle, 'AT+NOTI1', 8) +response = execute_string_command(serial_handle, 'AT+NOTP1', 8) + +if args.name is not None: + if len(args.name) > 12: + print('Please be sure the name lenght must be less than 12') + serial_close(serial_handle) + exit(0) + concat_command = "AT+NAME" + args.name + print('EXECUTED: setting name command: ', concat_command) + execute_string_command(serial_handle, concat_command, 18) + +print('EXECUTED: query name') +response = execute_string_command(serial_handle, 'AT+NAME?', 18) +print('INFO: name: ', response[8:]) + +# 1 will turn it on +print('EXECUTED: turn off ibeacon') +response = execute_string_command(serial_handle, 'AT+IBEA0', 8) + +if args.role is None or args.role.__contains__('peripheral'): + # AT+ROLE0” is peripheral role + print('EXECUTED: peripheral role selected') + execute_string_command(serial_handle, 'AT+ROLE0', 8) +else: + # AT+ROLE1” is central role + print('EXECUTED: central role selected') + execute_string_command(serial_handle, 'AT+ROLE1', 8) + +# For central: In AT+IMME0 mode, when power on, module will check if have a device address +# have already success connect before. If have, module will try to connect that device +# for ever, if we setup AT+TCON, when AT+TCON value timeout, module will forget that +# device and try scan devices, then try connect the device what we scanned; If not have +# a device address, then module will try scan devices and try connect them. AT+SAVE +# command will let module know if save devices address or not. + +# For peripheral: If AT+IMME0 is setup module will auto into discoverable mode. + +# With IMME1: the device will be in AT configuration mode +# If the mode is central, In AT+IMME1 modem, when power on, module do nothing, module will wait your +# command, now you can use AT+DISC? Or AT+DISI? Or AT+DISA? Command to +# scan devices. Also, you can use AT+CO command connect to a device +print('EXECUTED: we are in AT configuration mode') +execute_string_command(serial_handle, 'AT+IMME1', 8) + +if args.customcommand is not None: + # can be used for and AT commands just to see their outputs + # eg: AT+ADC[P1]? where P1: 3,4,5,6,7,8,9, A, B + # eg: AT+ANCS? or AT+ANCSP1 where P1 is 0 for OFF and 1 for ON, apple ANCS switch defaultly OFF + # eg: AT+ALLO? or AT+ALLOP1 where P1 is 0 for OFF and 1 for ON, white list switch defaultly OFF + # eg: AT+RENEW for FACTORY RESET + # eg: AT+MEAS? to get ibeacon measured power + # eg: AT+MINO? or AT+MARJ? to get ibeacon minor marjor + # eg AT+IBE0, AT+IBE1, AT+IBE2, AT+IBE3 are used to set ibeacon UUID (one part at a time) + # eg: AT+ERASE to remove bond information + # eg: AT+CHAR? to read characteristic value (changeable). default: 0xFFE1 + print('EXECUTED: ', args.customcommand) + execute_string_command(serial_handle, args.customcommand, 100) + serial_close(serial_handle) + exit(0) + +# Mode 0(Transmission mode, command string: “AT+MODE0”): +# Before connect, you can configure module parameters with AT Commands +# After connect, you can send and receive Bluetooth data through UART + +# Mode 2 (Remote control mode command string: “AT+MODE2”): +# Before connect, you can configure module parameters with AT Commands +# After connect: +# 1) Send and receive Bluetooth data through UART. +# 2) Remote device could configure module parameters with AT Commands +# 3) Remote device could control PIO2~11 output low or high +# HM-11 only has PIO2 and PIO3. +if args.remotecontrol is True: + print('EXECUTED: remote control mode selected') + execute_string_command(serial_handle, 'AT+MODE2', 8) +else: + print('EXECUTED: transmission mode selected') + execute_string_command(serial_handle, 'AT+MODE0', 8) + +# P1: 0 ~ 3 +# 0: Advertising +# ScanResponse, Connectable +# 1: Only allow last device connect +# in 1.28 seconds +# 2: Only allow Advertising and +# ScanResponse. +# 3: Only allow Advertising +print('EXECUTED: setting advertising type') +execute_string_command(serial_handle, 'AT+ADTY0', 8) + +# If module is in sleep mode, module will output “OK+WAKE” and into standby mode. +# If Module is not connected to remote device, you will receive: “OK” +# If Module is connected, module will be disconnected from remote device, if “AT + +# NOTI” is setup to 1, module will output “OK+LOST” through UART. +print('EXECUTED: test') +response = execute_string_command(serial_handle, 'AT', 7) +if response.__contains__('OK+LOST'): + print('INFO: disconnected and ready') +elif response.__contains__('OK+WAKE'): + print('INFO: wakeup and ready') +elif response.__contains__('OK'): + print('INFO: not connected and ready') + +if args.setmac is not None: + if len(args.setmac) != 12: + print('Please be sure the MAC address lenght must be 12') + serial_close(serial_handle) + exit(0) + concat_command = "AT+ADDR" + args.setmac + print('EXECUTED: setting mac command: ', concat_command) + execute_string_command(serial_handle, concat_command, 24) + +# receive format "OK+ADDR:MAC Address" +print('EXECUTED: address query') +response = execute_string_command(serial_handle, 'AT+ADDR?', 20) +mac = response.split(":") +print('INFO: MAC: ', mac[1]) + +# receive format "OK+Get:[P1]" where P1 is +# P1: 0 ~ F +# 0: 100ms; 1: 252.5ms; +# 2: 211.25ms; 3:318.75ms +# 4: 417.50ms; 5:546.25ms +# 6: 760.00ms; 7:852,50ms; +# 8: 1022.5ms; 9:1285ms +# A: 2000ms; B: 3000ms +# C: 4000ms; D: 5000ms +# E: 6000ms; F: 7000ms +# HMSoft Default: 0 +# HMSensor Default: 9 +if args.verbose is True: + print('INFO: 0 means 100ms') + print('INFO: 1 means 252.5ms') + print('INFO: 2 means 211.25ms') + print('INFO: 3 means 318.75ms') + print('INFO: 4 means 417.50ms') + print('INFO: 5 means 546.25ms') + print('INFO: 6 means 760.00ms') + print('INFO: 7 means 852,50ms') + print('INFO: 8 means 1022.5ms') + print('INFO: 9 means 1285ms') + print('INFO: A means 2000ms') + print('INFO: B means 3000ms') + print('INFO: C means 4000ms') + print('INFO: D means 5000ms') + print('INFO: E means 6000ms') + print('INFO: F means 7000ms') + print('INFO: The maximum 1285ms recommendations from the IOS system') + +if args.setadv is not None: + if len(args.setadv) != 1: + print('Please be sure the data lenght must be 1') + serial_close(serial_handle) + exit(0) + concat_command = "AT+ADVI" + args.setadv + print('EXECUTED: setting advertise interval: ', concat_command) + execute_string_command(serial_handle, concat_command, 8) + +print('EXECUTED: advertising interval query') +response = execute_string_command(serial_handle, 'AT+ADVI?', 8) +interval = response.split(":") +print('INFO: interval: ', interval[1]) + +if args.setwhitemac is not None: + if len(args.setwhitemac) != 12: + print('Please be sure the mac address len') + serial_close(serial_handle) + exit(0) + if args.listnumber is None: + print('Please be sure the mac address len') + serial_close(serial_handle) + exit(0) + if len(args.listnumber) != 1: + print('Please be sure list number must be 1, 2 or 3') + serial_close(serial_handle) + exit(0) + concat_command = "AT+AD" + args.listnumber + args.setwhitemac + print('EXECUTED: white list adding mac: ', concat_command) + execute_string_command(serial_handle, concat_command, 19) + +print('EXECUTED: query white lists') +response = execute_string_command(serial_handle, 'AT+AD1??', 20) +print('INFO: list 1: ', response) +response = execute_string_command(serial_handle, 'AT+AD2??', 20) +print('INFO: list 2: ', response) +response = execute_string_command(serial_handle, 'AT+AD3??', 20) +print('INFO: list 3: ', response) + +if args.battmonitor is True: + # it means it is enabled + print('EXECUTED: added battery info into scan response') + execute_string_command(serial_handle, 'AT+BATC1', 8) +else: + print('EXECUTED: NOT added battery info into scan response') + execute_string_command(serial_handle, 'AT+BATC0', 8) + +print('EXECUTED: query battery monitor') +response = execute_string_command(serial_handle, 'AT+BATC?', 8) +interval = response.split(":") +print('INFO: enabled: ', interval[1]) + +print('EXECUTED: query battery level') +response = execute_string_command(serial_handle, 'AT+BATT?', 10) +interval = response.split(":") +print('INFO: battery %', interval[1]) + +# to set it use AT+COMI[P1] command +# where P1 is P1: 0 ~ 9 +# 0: 7.5ms; 1: 10ms; 2: 15ms; +# 3: 20ms; 4: 25ms; 5: 30ms; +# 6: 35ms; 7: 40ms; 8: 45ms; +# 9: 4000ms; +# Default: 3(20ms) +print('EXECUTED: query min Link Layer Connection Interval') +response = execute_string_command(serial_handle, 'AT+COMI?', 8) +interval = response.split(":") +if interval[1].__contains__('0'): + print('INFO: interval 7.5ms') +elif interval[1].__contains__('1'): + print('INFO: interval 10ms') +elif interval[1].__contains__('2'): + print('INFO: interval 15ms') +elif interval[1].__contains__('3'): + print('INFO: interval 20ms') +elif interval[1].__contains__('4'): + print('INFO: interval 25ms') +elif interval[1].__contains__('5'): + print('INFO: interval 30ms') +elif interval[1].__contains__('6'): + print('INFO: interval 35ms') +elif interval[1].__contains__('7'): + print('INFO: interval 40ms') +elif interval[1].__contains__('8'): + print('INFO: interval 45ms') +elif interval[1].__contains__('9'): + print('INFO: interval 4000ms') + +# to set it use AT+COMA[P1] command +# where P1 is P1: 0 ~ 9 +# 0: 7.5ms; 1: 10ms; 2: 15ms; +# 3: 20ms; 4: 25ms; 5: 30ms; +# 6: 35ms; 7: 40ms; 8: 45ms; +# 9: 4000ms; +# Default: 7(40ms) +print('EXECUTED: query max Link Layer Connection Interval') +response = execute_string_command(serial_handle, 'AT+COMA?', 8) +interval = response.split(":") +if interval[1].__contains__('0'): + print('INFO: interval 7.5ms') +elif interval[1].__contains__('1'): + print('INFO: interval 10ms') +elif interval[1].__contains__('2'): + print('INFO: interval 15ms') +elif interval[1].__contains__('3'): + print('INFO: interval 20ms') +elif interval[1].__contains__('4'): + print('INFO: interval 25ms') +elif interval[1].__contains__('5'): + print('INFO: interval 30ms') +elif interval[1].__contains__('6'): + print('INFO: interval 35ms') +elif interval[1].__contains__('7'): + print('INFO: interval 40ms') +elif interval[1].__contains__('8'): + print('INFO: interval 45ms') +elif interval[1].__contains__('9'): + print('INFO: interval 4000ms') + +print('EXECUTED: query supervision timeout') +response = execute_string_command(serial_handle, 'AT+COSU?', 8) +interval = response.split(":") +if interval[1].__contains__('0'): + print('INFO: 100ms') +elif interval[1].__contains__('1'): + print('INFO: 1000ms') +elif interval[1].__contains__('2'): + print('INFO: 2000ms') +elif interval[1].__contains__('3'): + print('INFO: 3000ms') +elif interval[1].__contains__('4'): + print('INFO: 4000ms') +elif interval[1].__contains__('5'): + print('INFO: 5000ms') +elif interval[1].__contains__('6'): + print('INFO: 6000ms') + +print('EXECUTED: query Link Layer connection slave latency') +response = execute_string_command(serial_handle, 'AT+COLA?', 8) +interval = response.split(":") +print('INFO: latency ', interval[1]) + +if args.role is not None and args.role.__contains__('central'): + # now you can use AT+DISC? Or AT+DISI? Or AT+DISA? Command to + # scan devices. Also, you can use AT+CO command connect to a device. + + # where P1 value: 000000~999999 + print('EXECUTED: connect forever') + response = execute_string_command(serial_handle, 'AT+TCON000000', 13) + + # 0: none, 1: only name, 2: only RSSI + print('EXECUTED: show RSSI and Name at DISC result') + response = execute_string_command(serial_handle, 'AT+SHOW3', 8) + + # P1: 1 ~ 9 in seconds + print('EXECUTED: set module discovery time') + response = execute_string_command(serial_handle, 'AT+SCAN9', 8) + + # 0 means save + print('EXECUTED: do not save connected device mac address') + response = execute_string_command(serial_handle, 'AT+SAVE1', 8) + + # 0 means save + print('EXECUTED: Clear Last Connected device address') + response = execute_string_command(serial_handle, 'AT+CLEAR', 8) + + # start central here + execute_string_command(serial_handle, 'AT+START', 8) + print('EXECUTED: START executed') + + print('EXECUTED: Start a device discovery scan') + response = execute_string_command(serial_handle, 'AT+DISC?', 5000) + print('INFO: response', response) + + if args.connectmac is not None: + if len(args.connectmac) != 12: + print('Please be sure the MAC address lenght must be 12') + serial_close(serial_handle) + exit(0) + concat_command = "AT+CON" + args.connectmac + print('EXECUTED: setting mac command: ', concat_command) + response = execute_string_command(serial_handle, concat_command, 32) + if response.__contains__('OK+CONNA'): + print('INFO: Accept request, connecting') + time.sleep(5) + print('EXECUTED: get RSSI') + response = execute_string_command(serial_handle, 'AT+RSSI?', 20) + print('INFO: RSSI', response) + if response.__contains__('OK+CONNE'): + print('INFO: Connect error') + if response.__contains__('OK+CONNF'): + print('INFO: Connect failed') + +else: + execute_string_command(serial_handle, 'AT+START', 8) + print('EXECUTED: START executed') + +serial_close(serial_handle) + +serial_handle = serial_open(args.interface, 1) + +print('INFO: start listening...') + + +def inp_ready(line): + user_inp_b = bytes(line, 'utf-8') + serial_handle.write(user_inp_b) + + +def check_serial(): + response = serial_handle.read(1) + response = codecs.decode(response, encoding='utf-8') + return response + + +t = time.time() +last_packet_get_time = int(t * 1000) +cumulative_packet_content = "" + +condition = True +while condition is True: + # If there's input ready, do inp_ready, else do check_serial. Note timeout is zero so select won't block at all. + while sys.stdin in select.select([sys.stdin], [], [], 0)[0]: + line = sys.stdin.readline() + if line: + if line.__contains__('exit()'): + condition = False + else: + inp_ready(line) + + resp = check_serial() + if resp: + t = time.time() + last_packet_get_time = int(t * 1000) + cumulative_packet_content = cumulative_packet_content + resp + else: + t = time.time() + current_ms = int(t * 1000) + time_diff = current_ms - last_packet_get_time + if time_diff > 1000: + if cumulative_packet_content: + print(cumulative_packet_content) + cumulative_packet_content = "" + last_packet_get_time = current_ms + +serial_close(serial_handle) diff --git a/helper/hm10_driver/test_util.py b/helper/hm10_driver/test_util.py new file mode 100644 index 0000000..eddc6aa --- /dev/null +++ b/helper/hm10_driver/test_util.py @@ -0,0 +1,20 @@ +import argparse + +import utils + + +def test_arg_check_filename(): + parser = argparse.ArgumentParser( + prog='test', + description='test desc', + epilog='test epilog') + parser.add_argument('-d', '--verbose', + action='store_true', + help='verbosity setting') + parser.add_argument('-i', '--interface', + action='store', + help='serial interface name') + args = parser.parse_args(['--verbose']) + u = utils.Utils(args) + + assert u.arg_check() is False diff --git a/helper/hm10_driver/utils.py b/helper/hm10_driver/utils.py new file mode 100644 index 0000000..4c76d5b --- /dev/null +++ b/helper/hm10_driver/utils.py @@ -0,0 +1,10 @@ +class Utils: + def __init__(self, args): + self.args = args + + def arg_check(self): + if self.args.interface is None: + print('interface argument is must') + return False + + return True diff --git a/helper/libcanard_sniffer/CMakeLists.txt b/helper/libcanard_sniffer/CMakeLists.txt new file mode 100644 index 0000000..f9d0b47 --- /dev/null +++ b/helper/libcanard_sniffer/CMakeLists.txt @@ -0,0 +1,25 @@ +cmake_minimum_required(VERSION 3.1) +project(libcanard) + +set(WORKSPACE_PATH "$ENV{WORKSPACE}") +set(THIRDPARTY_PATH "$ENV{THIRDPARTY}") + +# Libcanard +include_directories(${THIRDPARTY_PATH}/libcanard) +include_directories(${THIRDPARTY_PATH}/libcanard/drivers/socketcan) + +# Compiler configuration - supporting only Clang and GCC +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -Wall -Wextra -Werror -m32 -pedantic -g -O0") + +# C warnings +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wdouble-promotion -Wswitch-enum -Wfloat-equal -Wundef") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wconversion -Wtype-limits") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wsign-conversion -Wcast-align -Wmissing-declarations -Wno-conversion -Wno-float-equal") + +# Expose internal API for unit testing +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DCANARD_INTERNAL=''") + +add_executable(can_dumper.exe + can_dumper.c + ${THIRDPARTY_PATH}/libcanard/canard.c + ${THIRDPARTY_PATH}/libcanard/drivers/socketcan/socketcan.c) diff --git a/helper/libcanard_sniffer/build.sh b/helper/libcanard_sniffer/build.sh new file mode 100755 index 0000000..005810b --- /dev/null +++ b/helper/libcanard_sniffer/build.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +export THIRDPARTY="/workspace" + +sudo rm -rf build +mkdir build +cd build +cmake .. && make + diff --git a/helper/libcanard_sniffer/can_dumper.c b/helper/libcanard_sniffer/can_dumper.c new file mode 100644 index 0000000..6372cb5 --- /dev/null +++ b/helper/libcanard_sniffer/can_dumper.c @@ -0,0 +1,276 @@ +// This is needed to enable necessary declarations in sys/ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static uint8_t LOCAL_NODE_ID = 5; +static char INTERFACE_NAME[128] = "can0"; +static char BCASTDATA[5] = ""; + +/* + * Application constants + */ +#define VERSION "00.00" + +/* + * Some useful constants defined by the UAVCAN specification. + * Data type signature values can be easily obtained with the script show_data_type_info.py + */ +#define UAVCAN_NODE_ID_ALLOCATION_DATA_TYPE_ID 1 +#define UAVCAN_NODE_ID_ALLOCATION_DATA_TYPE_SIGNATURE 0x0b2a812620a11d40 +#define UAVCAN_NODE_STATUS_MESSAGE_SIZE 7 + +/* + * Library instance. + * In simple applications it makes sense to make it static, but it is not necessary. + */ +static CanardInstance canard; ///< The library instance +static uint8_t canard_memory_pool[1024]; ///< Arena for memory allocation, used by the library + +static uint64_t getMonotonicTimestampUSec(void) { + struct timespec ts; + memset(&ts, 0, sizeof(ts)); + if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { + abort(); + } + return (uint64_t)(ts.tv_sec * 1000000LL + ts.tv_nsec / 1000LL); +} + +static void dump_buffer(const unsigned char *buffer, unsigned int len) { + unsigned int i = 0, j = 0; + unsigned int j_max = 0; + + if (!buffer) { + return; + } + + for (i = 0; i < len; i = i + 16) { + j_max = 16; + if (i + j_max > len) { + j_max = len - i; + } + for (j = 0; j < j_max; j++) { + fprintf(stderr, "0x%.02X ", buffer[i + j]); + } + for (j = 0; j < 16 - j_max; j++) { + fprintf(stderr, " "); + } + fprintf(stderr, "\t\t"); + for (j = 0; j < j_max; j++) { + if (buffer[i + j] >= 0x20 && buffer[i + j] <= 0x7e) + fprintf(stderr, "%c", buffer[i + j]); + } + fprintf(stderr, "\n"); + } +} + +/** + * This callback is invoked by the library when a new message or request or response is received. + */ +static void onTransferReceived(CanardInstance *ins, CanardRxTransfer *transfer) { + (void)ins; + + if (!transfer || !transfer->payload_head) { + return; + } + + printf("payload_len: %d\n", transfer->payload_len); + printf("payload_head:\n"); + dump_buffer(transfer->payload_head, CANARD_MULTIFRAME_RX_PAYLOAD_HEAD_SIZE); + printf("-----------------------------------------------------------------------------------------------------------" + "-------------------\n"); +} + +static char *transfer_type_to_str(CanardTransferType type) { + switch (type) { + case CanardTransferTypeResponse: + return "Response"; + case CanardTransferTypeRequest: + return "Request"; + case CanardTransferTypeBroadcast: + return "Broadcast"; + ; + default: + fprintf(stderr, "unhandled type: %d\n", (int)type); + break; + } + + return "Unknown_Type"; +} + +/** + * This callback is invoked by the library when it detects beginning of a new transfer on the bus that can be received + * by the local node. + * If the callback returns true, the library will receive the transfer. + * If the callback returns false, the library will ignore the transfer. + * All transfers that are addressed to other nodes are always ignored. + */ +static bool shouldAcceptTransfer(const CanardInstance *ins, uint64_t *out_data_type_signature, uint16_t data_type_id, + CanardTransferType transfer_type, uint8_t source_node_id) { + (void)out_data_type_signature; + + printf("-----------------------------------------------------------------------------------------------------------" + "-------------------\n"); + printf("from(%d) -> to(%d)(%s_ %d)\n", source_node_id, ins->node_id, transfer_type_to_str(transfer_type), + data_type_id); + + return true; +} + +/** + * This function is called at 1 Hz rate from the main loop. + */ +static void process1HzTasks(uint64_t timestamp_usec) { + /* + * Purging transfers that are no longer transmitted. This will occasionally free up some memory. + */ + canardCleanupStaleTransfers(&canard, timestamp_usec); + + if (strlen(BCASTDATA)) { + static uint8_t transfer_id; // Note that the transfer ID variable MUST BE STATIC (or heap-allocated)! + uint8_t buffer[UAVCAN_NODE_STATUS_MESSAGE_SIZE]; + + // fill status data here + memset(buffer, 0, UAVCAN_NODE_STATUS_MESSAGE_SIZE); + canardEncodeScalar(buffer, 0, 40, BCASTDATA); + + const int16_t bc_res = canardBroadcast(&canard, UAVCAN_NODE_ID_ALLOCATION_DATA_TYPE_SIGNATURE, + UAVCAN_NODE_ID_ALLOCATION_DATA_TYPE_ID, &transfer_id, + CANARD_TRANSFER_PRIORITY_LOW, buffer, UAVCAN_NODE_STATUS_MESSAGE_SIZE); + if (bc_res <= 0) { + fprintf(stderr, "Could not broadcast node status; error %d\n", bc_res); + } + } +} + +/** + * Transmits all frames from the TX queue, receives up to one frame. + */ +static void processTxRxOnce(SocketCANInstance *socketcan, int32_t timeout_msec) { + // Transmitting + for (const CanardCANFrame *txf = NULL; (txf = canardPeekTxQueue(&canard)) != NULL;) { + const int16_t tx_res = socketcanTransmit(socketcan, txf, 0); + if (tx_res < 0) // Failure - drop the frame and report + { + canardPopTxQueue(&canard); + (void)fprintf(stderr, "Transmit error %d, frame dropped, errno '%s'\n", tx_res, strerror(errno)); + } else if (tx_res > 0) // Success - just drop the frame + { + canardPopTxQueue(&canard); + } else // Timeout - just exit and try again later + { + break; + } + } + + // Receiving + CanardCANFrame rx_frame; + const uint64_t timestamp = getMonotonicTimestampUSec(); + const int16_t rx_res = socketcanReceive(socketcan, &rx_frame, timeout_msec); + if (rx_res < 0) // Failure - report + { + (void)fprintf(stderr, "Receive error %d, errno '%s'\n", rx_res, strerror(errno)); + } else if (rx_res > 0) // Success - process the frame + { + canardHandleRxFrame(&canard, &rx_frame, timestamp); + } else { + ; // Timeout - nothing to do + } +} + +static struct option parameters[] = { + {"iface", required_argument, 0, 'i'}, + {"nodeid", required_argument, 0, 'n'}, + {"bcast", required_argument, 0, 't'}, + {"help", no_argument, 0, 'h'}, + {NULL, 0, 0, 0}, +}; + +static void show_usage_and_exit(char *appname) { + if (appname) { + printf("usage of %s ", appname); + } + printf("(version: %s)\n", VERSION); + printf("\t\"--iface\"/\"-i\"\t:\tused for specifying can interface (eg: can0 - which is default)\n"); + printf("\t\"--nodeid\"/\"-n\"\t:\tused for specifying can id (eg: 5 - which is default)\n"); + printf("\t\"--bcast\"/\"-t\"\t:\tused for specifying broadcasted data (eg: test - no bcast if not specified)\n"); + printf("\t\tmax bcast data len is: %d\n", UAVCAN_NODE_STATUS_MESSAGE_SIZE); + + exit(EXIT_SUCCESS); +} + +int main(int argc, char **argv) { + int c, o; + char *endptr = NULL; + uint64_t next_1hz_service_at = 0; + int16_t res = 0; + SocketCANInstance socketcan; + + while ((c = getopt_long(argc, argv, "i:n:t:h:", parameters, &o)) != -1) { + switch (c) { + case 'i': + strncpy(INTERFACE_NAME, optarg, sizeof(INTERFACE_NAME)); + break; + case 'n': + LOCAL_NODE_ID = (uint8_t)strtoul(optarg, &endptr, 10); + break; + case 't': + strncpy(BCASTDATA, optarg, sizeof(BCASTDATA)); + break; + case 'h': + default: + show_usage_and_exit(argv[0]); + } + } + + printf("Configuration: \n"); + printf("\tinterface : %s\n", INTERFACE_NAME); + printf("\tnode id : %d\n", LOCAL_NODE_ID); + printf("\tbcast data : %s\n\n", BCASTDATA); + + /* + * Initializing the CAN backend driver; in this example we're using SocketCAN + */ + res = socketcanInit(&socketcan, INTERFACE_NAME); + if (res < 0) { + (void)fprintf(stderr, "Failed to open CAN iface '%s'\n", INTERFACE_NAME); + return 1; + } + + /* + * Initializing the Libcanard instance. + */ + canardInit(&canard, canard_memory_pool, sizeof(canard_memory_pool), onTransferReceived, shouldAcceptTransfer, NULL); + + if (canardGetLocalNodeID(&canard) == 0) { + canardSetLocalNodeID(&canard, LOCAL_NODE_ID); + } + + /* + * Running the main loop. + */ + next_1hz_service_at = getMonotonicTimestampUSec(); + + for (;;) { + processTxRxOnce(&socketcan, 10); + + const uint64_t ts = getMonotonicTimestampUSec(); + + if (ts >= next_1hz_service_at) { + next_1hz_service_at += 1000000; + process1HzTasks(ts); + } + } + + return 0; +} diff --git a/helper/libcanard_sniffer/peak_usb_install.md b/helper/libcanard_sniffer/peak_usb_install.md new file mode 100644 index 0000000..2ca04a1 --- /dev/null +++ b/helper/libcanard_sniffer/peak_usb_install.md @@ -0,0 +1,151 @@ + +# PCAN-USB FD pcan-linux-installation + +This guide is about the istallation and running of the `PCAN-USB FD` and running test using socketcan, with your custom CAN profile. +This is the product page : https://www.peak-system.com/PCAN-USB-FD.365.0.html?&L=1 + +The official documentation can be found here : https://www.peak-system.com/fileadmin/media/linux/files/PCAN-Driver-Linux_UserMan_eng.pdf + +This guide is for using the new `PCAN-USB FD` with the support of the CAN-FD frame using updated `socketCAN` + +This is a quick guide on how to install linux driver for the pcan usb adapter and run some demo test setup. + +This setup is tested on the moteus-motor-drivers which require custom CAN parameter to be configured : +## Installion specs. of system : +* Ubuntu 22.04 (*Kernel version : * Linux ASUS 6.0.0-1016-oem ) +* PCAN-USB FD +* Mjbots-Moteus-r4.11 +* Peak Linux Driver version : peak-linux-driver-8.16.0 +* CAN Configuration parameter from : [reference.md](https://github.com/mjbots/moteus/blob/main/docs/reference.md#80-mhz-clock-systems) + +# Installation step +1. Go to the offical website of the peak : https://www.peak-system.com/fileadmin/media/linux/index.htm or + + 1. Navigate to this section : `Driver Package for Proprietary Purposes` on the above given website. + 2. Click on `Download PCAN Driver Package` to download the latest driver.(This setup is tested on the driver version : `8.16.0` ) + 3. You can also install pcan view from website section `PCAN-View for Linux` or follow these command : + ```bash + # add peak-system.list in the update list + + $ wget -q http://www.peak-system.com/debian/dists/`lsb_release -cs`/peak-system.list -O- | sudo tee /etc/apt/sources.list.d/peak-system.list + + $ wget -q http://www.peak-system.com/debian/peak-system-public-key.asc -O- | sudo apt-key add - + + $ sudo apt-get update + $ sudo apt-get install pcanview-ncurses + ``` + +2. Now install these following pacakage in your system : + ```bash + # install required packages + sudo apt-get install libncurses5 + sudo apt-get install can-utils + sudo apt-get install gcc-multilib + sudo apt-get install libelf-dev + sudo apt-get install libpopt-dev + sudo apt-get install tree + # build and install pcan driver + + tar -xzf peak-linux-driver-X.Y.Z.tar.gz # navigate to download path + cd peak-linux-driver-X.Y.Z + make clean + make NET=NETDEV_SUPPORT # for network interface based pcan + sudo make install + + # install the modules + sudo modprobe pcan + sudo modprobe can + sudo modprobe vcan + sudo modprobe slcan + sudo modprobe pcan_usb + sudo modprobe peak_usb + + # setup and configure "can0" net device + + sudo ip link set can0 type can + + sudo ip link set can0 up type can tq 12 prop-seg 25 phase-seg1 25 phase-seg2 29 sjw 10 dtq 12 dprop-seg 6 dphase-seg1 2 dphase-seg2 7 dsjw 12 fd on loopback on + ``` + **Note** : If you want to update a new configuration try to turn off the `can0` network and + ```bash + sudo ip link set can0 down + + # then again run the new parameter of CAN command again + sudo ip link set can0 up type can tq 12 prop-seg 25 phase-seg1 25 phase-seg2 29 sjw 10 dtq 12 dprop-seg 6 dphase-seg1 2 dphase-seg2 7 dsjw 12 fd on loopback on + ``` +3. Once you made the installation let's check : + ```bash + # check installation + ./driver/lspcan --all # should print "pcanusb32" and pcan version + tree /dev/pcan-usb # should show a pcan-usb device + ip -a link # should print some "can0: ..." messages + ip -details link show can0 # should print some details about "can0" net device + ``` +4. Example output after successfull installation of a pcan usb adapter: + ```bash + mighty@ASUS:~/Downloads/peak-linux-driver-8.16.0$ ./driver/lspcan --all + pcanusbfd32 CAN1 - 80MHz 1M+5M WARNING 0.00 4 2 114461 + + mighty@ASUS:~/Downloads/peak-linux-driver-8.16.0$ tree /dev/pcan* + /dev/pcan32 [error opening dir] + /dev/pcan-usb_fd + └── 0 + └── can0 -> ../../pcanusbfd32 + /dev/pcanusbfd32 [error opening dir] + + 1 directory, 1 file + + mighty@ASUS:~/Downloads/peak-linux-driver-8.16.0$ ip -details link show can0 + 21: can0: mtu 72 qdisc pfifo_fast state UP mode DEFAULT group default qlen 10 + link/can promiscuity 0 minmtu 0 maxmtu 0 + can state ERROR-WARNING (berr-counter tx 127 rx 0) restart-ms 0 + bitrate 1000000 sample-point 0.637 + tq 12 prop-seg 25 phase-seg1 25 phase-seg2 29 sjw 10 + pcan: tseg1 1..256 tseg2 1..128 sjw 1..128 brp 1..1024 brp-inc 1 + dbitrate 5000000 dsample-point 0.562 + dtq 12 dprop-seg 6 dphase-seg1 2 dphase-seg2 7 dsjw 12 + pcan: dtseg1 1..32 dtseg2 1..16 dsjw 1..16 dbrp 1..1024 dbrp-inc 1 + clock 80000000 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535 + ``` +5. Now, Once we have done with the installation, we will try to send the the can data and receive the can data as well : + + 1. Run this command in one terminal : + ```bash + cansend can0 00008001##0420120 + # where fdcan id is 00008001 on can0 + # the '##' represent the fdcan frame + # and the first bit is '0' represent 'without switching bitrate' + # the data is '420120' + ``` + + 2. Run this command in another terminal : + ```bash + candump -x can0 + ``` +6. The output of the above command if you are using `moteus-r4.11` : + ```bash + # in the first terminal + mighty@ASUS:~$ cansend can0 00008001##0420120 + + #in the second terminal + mighty@ASUS:~$ candump -x can0 + can0 TX - - 00008001 [03] 42 01 20 + can0 RX - - 00008001 [03] 42 01 20 + can0 RX - - 100 [03] 41 01 00 + ``` + + +## Reference : + +* PCAN-USB FD : https://www.peak-system.com/PCAN-USB-FD.365.0.html?&L=1 +* Peak Linux system : https://www.peak-system.com/fileadmin/media/linux/index.htm +* mjbot moteus r4.11 CAN id setting: https://github.com/mjbots/moteus/blob/main/docs/reference.md#80-mhz-clock-systems +* mjbot moteus r4.11 : https://mjbots.com/products/moteus-r4-11 +* can-utils guide: https://dissec.to/kb/chapters/can/canfd.html +* peak usb guide :https://github.com/SICKAG/sick_line_guidance/blob/master/doc/pcan-linux-installation.md +* CAN Configuration parameter : https://github.com/mjbots/moteus/blob/main/docs/reference.md#80-mhz-clock-systems + +## aliases + +* candown +* canup \ No newline at end of file diff --git a/helper/serial_terminal/Makefile b/helper/serial_terminal/Makefile new file mode 100644 index 0000000..c127c40 --- /dev/null +++ b/helper/serial_terminal/Makefile @@ -0,0 +1,17 @@ +CC = gcc +RM = rm -rf +CFLAGS = -Wall -Wextra -g3 -O0 -lpthread -Wno-deprecated-declarations +all_c_files := $(wildcard *.c) + +.PHONY: all clean + +all: + $(CC) $(all_c_files) -o "serial.exe" $(CFLAGS); \ + +partial: + $(CC) $(FILE) -o $(FILE)".exe" $(CFLAGS); \ + +clean: + for file in $(all_c_files); do \ + $(RM) -rf $$file".exe"; \ + done diff --git a/helper/serial_terminal/serial.c b/helper/serial_terminal/serial.c new file mode 100644 index 0000000..9bb8b71 --- /dev/null +++ b/helper/serial_terminal/serial.c @@ -0,0 +1,155 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define VERSION "2.0.0." +#define TIMEOUT_MS 5000 + +static int set_interface_attribs(int fd, int speed) { + struct termios tty; + (void)speed; + memset(&tty, 0, sizeof(tty)); + + if (tcgetattr(fd, &tty) != 0) { + fprintf(stderr, "Serial tcgetattr error\n"); + return 1; + } + + tty.c_cflag &= ~PARENB; + tty.c_cflag &= ~CSTOPB; + tty.c_cflag |= CS8; + tty.c_cflag &= ~CSIZE; + + cfsetospeed(&tty, speed); + cfsetispeed(&tty, speed); + cfmakeraw(&tty); + + if (tcsetattr(fd, TCSANOW, &tty)) { + fprintf(stderr, "Serial tcsetattr error\n"); + return 1; + } + + return 0; +} + +static void *console_listener_task(void *vargp) { + int rc; + char buffer[5000]; + struct pollfd fds[1]; + unsigned char byte = 0; + int fd = -1; + + if (!vargp) { + goto out; + } + + fd = *((int *)vargp); + + fds[0].fd = STDIN_FILENO; + fds[0].events = POLLIN; + + while (1) { + rc = poll(fds, 1, TIMEOUT_MS); + if (rc < 0) { + fprintf(stderr, "poll() failed"); + goto out; + } + + if (fds[0].revents != POLLIN) { + continue; + } + + memset(buffer, 0, sizeof(buffer)); + + while (read(fds[0].fd, &byte, sizeof(unsigned char)) == 1) { + if (write(fd, &byte, 1) == -1) { + fprintf(stderr, "write() failed"); + } + } + } + +out: + pthread_exit(NULL); +} + +static void *uart_listener_task(void *vargp) { + int rc; + struct pollfd fds[1]; + unsigned char byte = 0; + int fd = -1; + + if (!vargp) { + goto out; + } + + fd = *((int *)vargp); + + fds[0].fd = fd; + fds[0].events = POLLIN; // poll input events + + while (1) { + rc = poll(fds, 1, TIMEOUT_MS); + if (rc < 0) { + fprintf(stderr, "poll() failed"); + goto out; + } + + if (fds[0].revents != POLLIN) { + continue; + } + + while (read(fds[0].fd, &byte, sizeof(unsigned char)) == 1) { + if (write(STDOUT_FILENO, &byte, 1) == -1) { + fprintf(stderr, "write() failed"); + } + } + } + +out: + pthread_exit(NULL); +} + +int main(int argn, char **args) { + char *portname = NULL; + pthread_t tid1; + pthread_t tid2; + int fd = -1; + + if (argn < 2) { + fprintf(stderr, "give port name as argument\n"); + return 1; + } + fprintf(stderr, "%s version: %s\n", args[0], VERSION); + + portname = args[1]; + + fd = open(portname, O_RDWR); + if (fd < 0) { + fprintf(stderr, "port cannot opened\n"); + return 1; + } + + set_interface_attribs(fd, B115200); + + pthread_create(&tid2, NULL, console_listener_task, &fd); + sleep(2); // be sure console_listener_task() is executed first + pthread_create(&tid1, NULL, uart_listener_task, &fd); + + pthread_join(tid1, NULL); + pthread_join(tid2, NULL); + + if (fd > 0) { + close(fd); + } + + return 0; +} diff --git a/helper/setupenv b/helper/setupenv new file mode 100755 index 0000000..d12221b --- /dev/null +++ b/helper/setupenv @@ -0,0 +1,9 @@ +#!/bin/bash + +for mk in $(find . -name Makefile); do + dir=$(dirname "$mk") + echo "Running make in: $dir" + make -C "$dir" +done + +cd libcanard_sniffer && ./build.sh && cd - diff --git a/helper/tcp_client_cjson/Makefile b/helper/tcp_client_cjson/Makefile new file mode 100644 index 0000000..48f02da --- /dev/null +++ b/helper/tcp_client_cjson/Makefile @@ -0,0 +1,20 @@ +CFILES = tcp_client.c + +OFILES = $(CFILES:.c=.o) + +CFLAGS = -Wall -Wextra -Werror + +LIB_PATH = /workspace/cjson/build +INC_PATH = /workspace/cjson +LIB_NAME = cjson + +NAME = tcp_client.exe + +all: + gcc $(CFILES) -L$(LIB_PATH) -l$(LIB_NAME) -I$(INC_PATH) -o $(NAME) -Wl,-rpath=$(LIB_PATH) + +clean: + rm -f $(OFILES) $(NAME) + + +.PHONY: all clean diff --git a/helper/tcp_client_cjson/conf.json b/helper/tcp_client_cjson/conf.json new file mode 100644 index 0000000..34ba2cb --- /dev/null +++ b/helper/tcp_client_cjson/conf.json @@ -0,0 +1,4 @@ +{ + "IP":"172.20.10.13", + "PORT":3333 +} diff --git a/helper/tcp_client_cjson/tcp_client.c b/helper/tcp_client_cjson/tcp_client.c new file mode 100644 index 0000000..ca69ed7 --- /dev/null +++ b/helper/tcp_client_cjson/tcp_client.c @@ -0,0 +1,227 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cJSON.h" + +#define BUF_SIZE 256 + +void chat(int sockfd) +{ + char buff[BUF_SIZE]; + int n; + for (;;) { + bzero(buff, sizeof(buff)); + printf("Enter the string : "); + n = 0; + while ((buff[n++] = getchar()) != '\n') + ; + write(sockfd, buff, sizeof(buff)); + bzero(buff, sizeof(buff)); + read(sockfd, buff, sizeof(buff)); + printf("From Server : %s", buff); + if ((strncmp(buff, "exit", 4)) == 0) { + printf("Client Exit...\n"); + break; + } + } +} + +char * reconstruct_config(char *IP, uint16_t port) +{ + char *string = NULL; + cJSON *reconstructed_conf_cjson = cJSON_CreateObject(); + + if (cJSON_AddStringToObject(reconstructed_conf_cjson, "IP", IP) == NULL) { + goto end; + } + if (cJSON_AddNumberToObject(reconstructed_conf_cjson, "PORT", port) == NULL) { + goto end; + } + + string = cJSON_Print(reconstructed_conf_cjson); + if (string == NULL) { + fprintf(stderr, "Failed to print reconstructed_conf_cjson.\n"); + } + +end: + cJSON_Delete(reconstructed_conf_cjson); + return string; +} + +void reconstruct_config_check(char *IP, uint16_t port) +{ + char *str = reconstruct_config(IP, port); + + if (str) { + printf("--> %s\n", str); + free(str); + } else { + fprintf(stderr, "failed\n"); + } +} + +char* read_file(const char *filename) +{ + FILE *file = NULL; + long length = 0; + char *content = NULL; + size_t read_chars = 0; + + /* open in read binary mode */ + file = fopen(filename, "rb"); + if (file == NULL) + { + goto cleanup; + } + + /* get the length */ + if (fseek(file, 0, SEEK_END) != 0) + { + goto cleanup; + } + length = ftell(file); + if (length < 0) + { + goto cleanup; + } + if (fseek(file, 0, SEEK_SET) != 0) + { + goto cleanup; + } + + /* allocate content buffer */ + content = (char*)malloc((size_t)length + sizeof("")); + if (content == NULL) + { + goto cleanup; + } + + /* read the file into memory */ + read_chars = fread(content, sizeof(char), (size_t)length, file); + if ((long)read_chars != length) + { + free(content); + content = NULL; + goto cleanup; + } + content[read_chars] = '\0'; + + +cleanup: + if (file != NULL) + { + fclose(file); + } + + return content; +} + +int main(int argc, char *argv[]) { + int option = 0; + char port[32] = {0}; + char file[BUF_SIZE] = {0}; + + memset(port, 0, sizeof(port)); + memset(file, 0, sizeof(file)); + + while ((option = getopt(argc, argv, "hf:")) != -1) { + switch (option) { + case 'h': + // "optarg" variable is used to get option itself + perror("read the source code\n"); + exit(EXIT_SUCCESS); + case 'f': + strncpy(file, optarg, sizeof(file) - 1); + break; + default: + perror("unknown argument\n"); + return 2; + } + } + +/////// CJSON RELATED + uint16_t PORT = 0; + char *IP = NULL; + cJSON *file_content = NULL; + cJSON *PORT_json = NULL; + cJSON *IP_json = NULL; + char *content = read_file(file); + file_content = cJSON_Parse(content); + if (!file_content) { + fprintf(stderr, "cJSON_Parse() failed\n"); + goto cleanup; + } + + PORT_json = cJSON_GetObjectItemCaseSensitive(file_content, "PORT"); + if (PORT_json && cJSON_IsNumber(PORT_json)) { + PORT = (uint32_t)PORT_json->valuedouble; + } else { + fprintf(stderr, "PORT did not specified in the config json\n"); + goto cleanup; + } + + IP_json = cJSON_GetObjectItemCaseSensitive(file_content, "IP"); + if (IP_json && cJSON_IsString(IP_json)) { + IP = (uint8_t *)strdup(IP_json->valuestring); + if (!IP) { + fprintf(stderr, "IP did not specified in the config json\n"); + goto cleanup; + } + } else { + fprintf(stderr, "IP did not specified in the config json\n"); + goto cleanup; + } + printf("PORT: %u, IP: %s\n", PORT, IP); + reconstruct_config_check(IP, PORT); + + +/////// SOCKET RELATED + int sockfd, connfd; + struct sockaddr_in servaddr, cli; + + // socket create and verification + sockfd = socket(AF_INET, SOCK_STREAM, 0); + if (sockfd == -1) { + printf("socket creation failed...\n"); + exit(0); + } + else + printf("Socket successfully created..\n"); + bzero(&servaddr, sizeof(servaddr)); + + // assign IP, PORT + servaddr.sin_family = AF_INET; + servaddr.sin_addr.s_addr = inet_addr(IP); + servaddr.sin_port = htons(PORT); + + // connect the client socket to server socket + if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) + != 0) { + printf("connection with the server failed...\n"); + exit(0); + } + else + printf("connected to the server..\n"); + + chat(sockfd); + + // close the socket + close(sockfd); +cleanup: + if (file_content) { + cJSON_Delete(file_content); + } + if (IP) { + free(IP); + } +} diff --git a/patches/canard_stm32.patch b/libcanard_patches/canard_stm32.patch similarity index 60% rename from patches/canard_stm32.patch rename to libcanard_patches/canard_stm32.patch index 9cb2be1..d03b2b5 100644 --- a/patches/canard_stm32.patch +++ b/libcanard_patches/canard_stm32.patch @@ -1,5 +1,5 @@ ---- /thirdparty/libcanard/drivers/stm32/canard_stm32.c 2024-07-28 22:03:11.918575022 +0100 -+++ /thirdparty/libcanard/drivers/stm32/canard_stm32.c 2024-07-28 22:02:34.480882822 +0100 +--- ./drivers/stm32/canard_stm32.c ++++ ./drivers/stm32/canard_stm32.c @@ -136,7 +136,7 @@ } diff --git a/patches/transport.py.patch b/libcanard_patches/transport.py.patch similarity index 63% rename from patches/transport.py.patch rename to libcanard_patches/transport.py.patch index ea9e4b1..21547bc 100644 --- a/patches/transport.py.patch +++ b/libcanard_patches/transport.py.patch @@ -1,5 +1,5 @@ ---- /thirdparty/libcanard/dsdl_compiler/pyuavcan/uavcan/transport.py 2024-07-26 16:06:48.899239901 +0100 -+++ /thirdparty/libcanard/dsdl_compiler/pyuavcan/uavcan/transport.py 2024-07-26 16:06:42.164516871 +0100 +--- ./dsdl_compiler/pyuavcan/uavcan/transport.py ++++ ./dsdl_compiler/pyuavcan/uavcan/transport.py @@ -315,7 +315,7 @@ raise ValueError('Bad float, no donut') self._bits = format(int_value, "0" + str(self._type.bitlen) + "b") diff --git a/libcsp_works/setupenv b/libcsp_works/setupenv new file mode 100755 index 0000000..fbe14f4 --- /dev/null +++ b/libcsp_works/setupenv @@ -0,0 +1,14 @@ +#!/bin/bash + +# clone and build libcsp_works module +echo "==================> clone and build libcsp_works module" +if [ ! -d "libcsp_works" ]; then + git clone https://github.com/dogusyuksel/libcsp-works.git libcsp_works + cd libcsp_works && git submodule update --init --recursive && cd - +fi + +export WORKSPACE_PATH="/workspace/libcsp_works/libcsp_works" +cd libcsp_works/rust-server +cd bindings-creator && cargo clean && cargo build && cd - +cd csp-server && cargo clean && cargo build && cd - +cd ../../ diff --git a/renode/setupenv b/renode/setupenv new file mode 100755 index 0000000..a28aea2 --- /dev/null +++ b/renode/setupenv @@ -0,0 +1,5 @@ +#!/bin/bash + +# clone and build renode module +echo "==================> clone only renode module" +git clone https://github.com/dogusyuksel/renode-web.git renode diff --git a/run_docker.sh b/run_docker.sh deleted file mode 100755 index ccb3a8a..0000000 --- a/run_docker.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash - -if [ "$#" -lt 1 ]; then - echo "you should give the image name" - exit 1 -fi - -if [ "$#" -gt 1 ]; then - argc=$# - argv=("$@") - full_string="" - priv_string="" - - for (( j=1; j$full_string<-" - docker run --rm -t --net=host -v $(pwd):/workspace -v /etc/passwd:/etc/passwd:ro -v /etc/group:/etc/group:ro -u $(id -u ${USER}):$(id -g ${USER}) --entrypoint=/bin/bash ${argv[0]} -c "$full_string" -else - docker run --rm -it --net=host -v $(pwd):/workspace -v /etc/passwd:/etc/passwd:ro -v /etc/group:/etc/group:ro -u $(id -u ${USER}):$(id -g ${USER}) --entrypoint=/bin/bash $@ -fi - diff --git a/stm32f103_libcanard/setupenv b/stm32f103_libcanard/setupenv new file mode 100755 index 0000000..02b7b60 --- /dev/null +++ b/stm32f103_libcanard/setupenv @@ -0,0 +1,14 @@ +#!/bin/bash + +# since we are building more than one stm32 module in this project, we treat their depedencies common and handled in parent folder +# so we dont need to do anything here + +# clone and build stm32f103_libcanard module +echo "==================> clone and build stm32f103_libcanard module" +if [ ! -d "stm32f103_libcanard" ]; then + git clone https://github.com/dogusyuksel/stm32f1-master-example.git stm32f103_libcanard +fi + +export WORKSPACE="/workspace/stm32f103_libcanard/stm32f103_libcanard" +export THIRDPARTY="/workspace" +cd stm32f103_libcanard/application_firmware && ./build.sh && cd - diff --git a/stm32l4a6_freertos/setupenv b/stm32l4a6_freertos/setupenv new file mode 100755 index 0000000..d7c172b --- /dev/null +++ b/stm32l4a6_freertos/setupenv @@ -0,0 +1,22 @@ +#!/bin/bash + +# since we are building more than one stm32 module in this project, we treat their depedencies common and handled in parent folder +# so we dont need to do anything here + +# clone and build stm32l4a6_freertos module +echo "==================> clone and build stm32l4a6_freertos module" +if [ ! -d "stm32l4a6_freertos" ]; then + git clone https://github.com/dogusyuksel/rtos_hal_stm32.git stm32l4a6_freertos + # we need to edit some paths to make it work in our multi-module workspace + sed -i 's|/workspace|/workspace/stm32l4a6_freertos/stm32l4a6_freertos|g' stm32l4a6_freertos/meta-rtos/classes/base.bbclass + sed -i 's|/workspace|/workspace/stm32l4a6_freertos/stm32l4a6_freertos|g' stm32l4a6_freertos/meta-rtos/recipes-core/queueisr/queueisr_0.1.bb + sed -i 's|/workspace|/workspace/stm32l4a6_freertos/stm32l4a6_freertos|g' stm32l4a6_freertos/meta-rtos/recipes-core/taskcreate/taskcreate_0.1.bb + sed -i 's|/workspace|/workspace/stm32l4a6_freertos/stm32l4a6_freertos|g' stm32l4a6_freertos/meta-rtos/recipes-core/mutex/mutex_0.1.bb + sed -i 's|/workspace|/workspace/stm32l4a6_freertos/stm32l4a6_freertos|g' stm32l4a6_freertos/meta-rtos/recipes-core/tasknotify/tasknotify_0.1.bb + sed -i 's|/workspace|/workspace/stm32l4a6_freertos/stm32l4a6_freertos|g' stm32l4a6_freertos/meta-rtos/recipes-core/qsetandsemaphore/qsetandsemaphore_0.1.bb + sed -i 's|/workspace|/workspace/stm32l4a6_freertos/stm32l4a6_freertos|g' stm32l4a6_freertos/meta-rtos/recipes-core/timer/timer_0.1.bb + sed -i 's|/workspace|/workspace/stm32l4a6_freertos/stm32l4a6_freertos|g' stm32l4a6_freertos/meta-rtos/recipes-core/streambuffer/streambuffer_0.1.bb + sed -i 's|^THIRDPARTY ?=.*|THIRDPARTY ?= "/workspace"|' stm32l4a6_freertos/meta-rtos/classes/base.bbclass +fi + +cd stm32l4a6_freertos/build && ./build_all.sh && cd - \ No newline at end of file diff --git a/sudo_run_docker.sh b/sudo_run_docker.sh deleted file mode 100755 index fba0e8e..0000000 --- a/sudo_run_docker.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash - -if [ "$#" -lt 1 ]; then - echo "you should give the image name" - exit 1 -fi - -if [ "$#" -gt 1 ]; then - argc=$# - argv=("$@") - full_string="" - priv_string="" - - for (( j=1; j$full_string<-" - docker run --rm -t --net=host -v $(pwd):/workspace --entrypoint=/bin/bash ${argv[0]} -c "$full_string" -else - docker run --rm -it --net=host -v $(pwd):/workspace --entrypoint=/bin/bash $@ -fi -