diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..a2bc6abd --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,21 @@ +name: CSH CI + +on: + push: + pull_request: + +jobs: + ci: + runs-on: ubuntu-24.04 + + steps: + - uses: actions/checkout@v4 + - uses: awalsh128/cache-apt-pkgs-action@latest + with: + packages: libcurl4-openssl-dev libzmq3-dev libsocketcan-dev libpython3-dev + - name: Install gcovr + run: pip3 install gcovr==8.3 meson==1.8.1 + - name: Build + run: ./configure -Db_coverage=true && ninja -C builddir -v + - name: Run gcovr + run: ninja -C builddir test coverage-text && tail -n3 builddir/meson-logs/coverage.txt diff --git a/.gitmodules b/.gitmodules index 42c9a0c3..0d77b58b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,27 +1,21 @@ [submodule "lib/libcsp"] path = lib/csp url = https://github.com/spaceinventor/libcsp + branch = master +[submodule "lib/libparam"] + path = lib/param + url = https://github.com/spaceinventor/libparam branch = master [submodule "lib/slash"] path = lib/slash url = https://github.com/spaceinventor/slash branch = master -[submodule "lib/yaml"] - path = lib/yaml - url = https://github.com/yaml/libyaml.git -[submodule "lib/csp_proc"] - path = lib/csp_proc - url = https://github.com/discosat/csp_proc.git -[submodule "lib/param"] - path = lib/param - url = https://github.com/spaceinventor/libparam - branch = master -[submodule "lib/csp_ippc"] - path = lib/csp_ippc - url = https://github.com/discosat/csp_ippc.git [submodule "lib/apm"] path = lib/apm_csh url = https://github.com/spaceinventor/libapm_csh [submodule "lib/pycsh_core"] path = lib/pycsh_core url = https://github.com/spaceinventor/pycsh_core.git +[submodule "lib/ossi"] + path = lib/ossi + url = https://github.com/spaceinventor/libossi.git diff --git a/.vscode/settings.json b/.vscode/settings.json index 1061dfcf..c8185256 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,7 @@ { "mesonbuild.buildFolder": "builddir", "mesonbuild.configureOnOpen": false, + "mesonbuild.modifySettings": false, "files.associations": { "*.h": "c", "*.c": "c", diff --git a/README.md b/README.md index a802ef0b..33bce1e3 100644 --- a/README.md +++ b/README.md @@ -17,22 +17,18 @@ Preferrably, you should use the provided Ubuntu Packages from Github's [Release ## Build -Requirements: libcurl4-openssl-dev build-essential, libsocketcan-dev, can-utils, libzmq3-dev, libyaml-dev, meson, ninja, pkg-config, fonts-powerline, python3-pip, libelf-dev, libbsd-dev libprotobuf-c-dev +![build.yml status](https://github.com/spaceinventor/csh/actions/workflows/build.yml/badge.svg) -``` -sudo apt install libcurl4-openssl-dev git build-essential libsocketcan-dev can-utils libzmq3-dev libyaml-dev pkg-config fonts-powerline python3-pip libelf-dev libbsd-dev libprotobuf-c-dev -sudo pip3 install meson ninja -``` +Requirements: libcurl4-openssl-dev build-essential, libsocketcan-dev, can-utils, libzmq3-dev, libyaml-dev, meson, ninja, pkg-config, fonts-powerline, python3-pip, libelf-dev, libbsd-dev -Sometimes needed: ``` -link /usr/sbin/ninja /usr/local/lib/python3.5/dist-packages/ninja -export PATH=~/.local/bin:$PATH +sudo apt install libcurl4-openssl-dev git build-essential libsocketcan-dev can-utils libzmq3-dev libyaml-dev pkg-config fonts-powerline pipx libelf-dev libbsd-dev python3-dev +pipx install meson ``` Build: ``` -git clone --recurse-submodules https://github.com/discosat/csh.git +git clone --recurse-submodules https://github.com/spaceinventor/csh.git cd csh ./configure ./install @@ -79,6 +75,28 @@ wsl --install After a reboot, you can then start the application WSL to get a virtual Ubuntu environment and follow the guidelines for installing CSH in Linux as above. Windows by default does not forward USB devices to WSL entities. To enable USB forwarding, follow the guide in https://learn.microsoft.com/en-us/windows/wsl/connect-usb. +## Testing + +Host tests live in `tests/` and run through meson: + +``` +meson test -C builddir # whole suite +meson test -C builddir hk_sniffer_tests # one test +``` + +`hk_sniffer_tests` is a regression for a `size_t` underflow in `hk_param_sniffer()`: +a short packet on source port 13 (e.g. a DIPP `ring_size`/`observation_meta` RDP +reply, which shares sport 13 with HK params) underflowed the param payload length +and walked the mpack reader off the buffer, segfaulting the param sniffer under +`prometheus start`. The test feeds the real function such packets and asserts they +are skipped. + +Note: meson build directories are not relocatable. If the tree was first configured +under a different absolute path (a moved or renamed checkout), `meson setup +--reconfigure` fails with `FileNotFoundError` on a stale `meson-private` path. +Recreate the build dir instead of reconfiguring: `rm -rf builddir && ./configure`. + + ## Extension support src/slash_apm.c defines a command, apm load, for loading a shared library as an APM, and a command, ap, info, for listing loaded APMs. diff --git a/doc/MAN/APMs/csh_example.rst b/doc/MAN/APMs/csh_example.rst new file mode 100644 index 00000000..2b855d6c --- /dev/null +++ b/doc/MAN/APMs/csh_example.rst @@ -0,0 +1 @@ +A new APM shall based on a fork of the repository https://github.com/spaceinventor/csh_example. This repository contains a simple APM that registers a custom slash command and a few example parameters, allowing the APM functionality to be remotely operated from other CSP nodes. diff --git a/doc/MAN/APMs/csh_si.rst b/doc/MAN/APMs/csh_si.rst new file mode 100644 index 00000000..689b7121 --- /dev/null +++ b/doc/MAN/APMs/csh_si.rst @@ -0,0 +1,72 @@ +Space Inventor is relasing CSH together with an APM called CSH SI that provides commands to interface with Space Inventor hardware. The following documentation describes the commands provided by the CSH SI APM. + +**node add** + +When loading the APM, this command is extended to include a Space Inventor PDU-P4 node and channel number as arguments. When registering this additional information, CSH is able to power on and off the module by remote controlling the PDU. + +The following example shows how to add a Space Inventor OBC module having node 6, connected to channel 2 of a PDU-P4 with the hostname ``pdu1-a``. + +.. class:: table + +.. list-table:: + :widths: 100 + :header-rows: 0 + + * - + .. csh-prompt:: host>6> node add -p pdu1-a -c 2 obc + .. csh-prompt:: host>obc@6> + +**power on/off** + +Having a node registered with PDU information allows the user to power cycle the module remotely using the ``power on`` and ``power off`` commands as shown in the following example. The explicit obc reference can be excluded, but preselcting the node by using ``node obc`` before starting the operations. + +.. class:: table + +.. list-table:: + :widths: 100 + :header-rows: 0 + + * - + .. csh-prompt:: host>> power on obc + .. csh-prompt:: host>> sleep 1000 + .. csh-prompt:: host>> ping obc + | Ping node 6 size 1 timeout 1000: Reply in 1 [ms] + .. csh-prompt:: host>> power off obc + +**conf get** + +This command receives and stores all persistent configuration parameters from module compliant with Space Inventor Parameter System. Optionally, the command can reboot the module into each available firmware slot to record the firmware version, and perform a list download to create a complete representation of the module configuration. + +The command can retrieve the configuration from multiple modules by a single command, by providng a list of nodes separated by commas. + +.. class:: table + +.. list-table:: + :widths: 100 + :header-rows: 0 + + * - + .. csh-prompt:: host>bat@8> conf get -r -l + | + | IDENT 8 + | bat + | samc21 01] + | v1.0-32-g61cfbc9 + | Apr 12 2024 16:04:12 + | + | IDENT 8 + | bat + | samc21 [1] + | v1.0-32-g61cfbc9 + | Apr 12 2024 16:04:46 + | + | Got param: temp:8[1] + | Got param: boot_cnt:8[1] + | Got param: boot_cur:8[1] + | ... + | Got param: heater_auto:8[1] + | Got param: cell_voltage:8[8] + | Got param: bus_current:8[1] + | + | Configuration is stored into ./configuration and ./paramdefs + .. csh-prompt:: host>bat@8> diff --git a/doc/MAN/APMs/introduction.rst b/doc/MAN/APMs/introduction.rst new file mode 100644 index 00000000..9912c25a --- /dev/null +++ b/doc/MAN/APMs/introduction.rst @@ -0,0 +1,18 @@ +CSH can be extended via Extension Modules (APMs) using C code or python. These modules provide additional commands and features that are not part of the core CSH shell but can be loaded as needed. + +Loading an APM is done by using the ``apm load`` slash command. The command enables the user to load all, or specific APMs available in the system. + +.. class:: table + +.. list-table:: + :widths: 100 + :header-rows: 0 + + * - + .. csh-prompt:: host>> apm load + | Loaded: /home/tjessen/.local/lib/csh/libcsh_obc.so + | Slash command 'node save' is overriding an existing command + | Slash command 'node list' is overriding an existing command + | Slash command 'node add' is overriding an existing command + | Loaded: /home/tjessen/.local/lib/csh/libcsh_si.so + .. csh-prompt:: host>> diff --git a/doc/MAN/Installation/build.rst b/doc/MAN/Installation/build.rst new file mode 100644 index 00000000..23592a9e --- /dev/null +++ b/doc/MAN/Installation/build.rst @@ -0,0 +1,22 @@ +As an alternative to installing a released version, in case of required local modifications, or due to a need for features not yet released, the software is available as open source on github. + +.. class:: table + +.. list-table:: + :widths: 100 + :header-rows: 0 + + * - + + .. csh-prompt:: >>$ git clone --recurse-submodules https://github.com/spaceinventor/csh.git + + .. csh-prompt:: >>$ cd csh + + .. csh-prompt:: >>$ ./configure + + .. csh-prompt:: >>$ ./install + +.. class:: centered + +*Clone, configure, build and install software* + diff --git a/doc/MAN/Installation/init_config.rst b/doc/MAN/Installation/init_config.rst index 2aa206ce..3b51f6ee 100644 --- a/doc/MAN/Installation/init_config.rst +++ b/doc/MAN/Installation/init_config.rst @@ -1,3 +1,3 @@ -In order to avoid configuring csp and interfaces on every launch an init.csh file can be created. CSH will look for one in your home directory or you can select one at launch with the -i argument. In the init folder, examples can be found for different interface configurations. - +In order to avoid configuring csp and interfaces on every launch, an init.csh file can be created. CSH will look for one in your home directory or you can select one at launch with the -i argument. In the init folder, examples can be found for different interface configurations. +Loading the default init.csh file in the home directory can be omitted by providing the -i argument without any filename. \ No newline at end of file diff --git a/doc/MAN/Installation/install.rst b/doc/MAN/Installation/install.rst index 41564a70..2226591e 100644 --- a/doc/MAN/Installation/install.rst +++ b/doc/MAN/Installation/install.rst @@ -1,5 +1,4 @@ - -The software is available as open source on github. +The preferred way to aquire the application is by installing a release. Go to https://github.com/spaceinventor/csh/releases/, and download the .deb package matching your Ubuntu LTS OS version. Open a terminal in the folder containing the package, and execute the command .. class:: table @@ -8,22 +7,9 @@ The software is available as open source on github. :header-rows: 0 * - - - .. csh-prompt:: >>$ git clone https://github.com/spaceinventor/csh.git - - .. csh-prompt:: >>$ cd csh - - .. csh-prompt:: >>$ git submodule update --init --recursive - - .. csh-prompt:: >>$ ./configure - - .. csh-prompt:: >>$ ./build + + .. csh-prompt:: >>$ sudo apt install ./si-csh__amd64_.deb .. class:: centered -*Clone, configure, build and install software* - - - - - +*Install CSH from release version* diff --git a/doc/MAN/Installation/launch_sw.rst b/doc/MAN/Installation/launch_sw.rst index a6152775..768fa824 100644 --- a/doc/MAN/Installation/launch_sw.rst +++ b/doc/MAN/Installation/launch_sw.rst @@ -11,14 +11,11 @@ In order to launch in shell / interactive mode, start csh without a command. Run * - .. csh-prompt:: >>$ csh -h - | usage: csh -i init.csh[command] + | usage: csh -i init.csh + | In CSH, type 'manual' to access CSH manuals + | + | Copyright (c) 2016-2025 Space Inventor A/S .. csh-prompt:: >>$ csh - .. csh-prompt:: host>> csp init - | Version 2 - | Hostname: lenovo - | Model: #36~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Fri Feb 17 15:17:25 UTC 2 - | Revision: 5.19.0-35-generic - | Deduplication: 3 .. csh-prompt:: host>>csp add zmq -d 8 localhost | ZMQ init ZMQO: addr: 8, pub(tx): [tcp://localhost:6000], sub(rx): [tcp://localhost:7000] diff --git a/doc/MAN/Introduction/description.rst b/doc/MAN/Introduction/description.rst index 36292b22..2bdb12fa 100644 --- a/doc/MAN/Introduction/description.rst +++ b/doc/MAN/Introduction/description.rst @@ -1,4 +1,4 @@ -CSH is a Linux program designed to interface to Space Inventor modules and satellites using a CSP interface. With a CAN dongle the PC will perform as a first-class citizen on the satellite bus and have full access to all systems. +CSH is a Linux application designed to interface to Space Inventor modules and satellites using a CSP interface. With a CAN dongle the PC will perform as a first-class citizen on the satellite bus and have full access to all systems. After launch, CSH is also used to operate the satellite through the radio interface, utilising CSPs routing functionalities. diff --git a/doc/MAN/Operating_Instructions/builtin_commands.csv b/doc/MAN/Operating_Instructions/builtin_commands.csv index e1eb0a78..22cb9e11 100644 --- a/doc/MAN/Operating_Instructions/builtin_commands.csv +++ b/doc/MAN/Operating_Instructions/builtin_commands.csv @@ -9,12 +9,9 @@ cmd add,[offset] [value],Add a new parameter to a command cmd done,-,Exit cmd edit mode cmd new, ,Create a new command cmd run,-,- -cmd server download,,- -cmd server list,-,- -cmd server rm,,- -cmd server upload,-,- confirm,-,Block until user confirmation crc32,
,Calculate CRC32 on a VMEM area +csp add alias,-,Add a new Alias address csp add can,-,Add a new CAN interface csp add eth,-,Add a new Ethernet interface csp add kiss,-,Add a new KISS/UART interface @@ -25,6 +22,7 @@ csp add zmq,-,Add a new ZMQ interface csp init,-,Initialize CSP csp scan,-,- download,
,Download from VMEM to FILE +echo,[string],Display a line of text eth debug,-,Toggle ethernet debugging eth info,-,List devices exit,-,Exit application @@ -44,7 +42,7 @@ list save,-,Save parameters loki start,-,Start Loki log push thread ls,[path],list files manual,[manual pdf],"Show CSH documentation, use with no parameter to get the list of available manuals." -node,[node],Set global default node +node,node,Set global default node node add,-,- node list,-,Save or print known nodes node save,-,Save or print known nodes @@ -53,13 +51,19 @@ ping,[node],Ping a system poke,
,Poke program,,Upload new firmware prometheus start,-,Start prometheus webserver -pull,[OPTIONS],Pull all metrics from given CSP node(s) +pull,-,Pull all metrics from given CSP node(s) pwd,-,Print current working directory python,[(-c |) [args...]],"Starts an interactive Python interpreter in the current CSH process or execute the script in given file. This allows you to run pretty much any Python code, particularly code using PyCSH which allows for interacting with CSP nodes. +Use ""Control-D"" to exit the interpreter and return to CSH." +python3,[(-c |) [args...]],"Starts an interactive Python interpreter in the current CSH process +or execute the script in given file. +This allows you to run pretty much any Python code, particularly code using PyCSH which allows for interacting +with CSP nodes. + Use ""Control-D"" to exit the interpreter and return to CSH." rdp opt,-,Set RDP options to use in stream and file transfers reboot,[node],Reboot a node @@ -80,12 +84,6 @@ Sets the following environment variables during execution: - __FILE__ to the path and name of the executed file - __FILE_DIR__ to the directory containing the executed file, useful for running other files located relative to __FILE__" -schedule cmd,-,- -schedule list,-,- -schedule push,-,- -schedule reset, [timeout],- -schedule rm, [timeout],- -schedule show,-,- set, ,Set shutdown,[node],Shutdown a node sleep,,Sleep the specified amount of milliseconds @@ -107,5 +105,7 @@ version,-,Print CSH version (and date if verbose) vm start,-,Start Victoria Metrics push thread vm stop,-,Stop Victoria Metrics push thread vmem,-,List virtual memory +vmem compress, ,Perform compression from src into dst +vmem decompress, ,Perform decompression from src into dst vts init,-,Push data to VTS watch,,Repeat a command diff --git a/doc/MAN/Operating_Instructions/command_examples.rst b/doc/MAN/Operating_Instructions/command_examples.rst index de7324db..c37abf46 100644 --- a/doc/MAN/Operating_Instructions/command_examples.rst +++ b/doc/MAN/Operating_Instructions/command_examples.rst @@ -15,7 +15,7 @@ Print out list of commands **csp init** -Initialize CSP in CSH. Optionally select CSP version. +Initialize CSP in CSH using CSP v1 or custom username. CSP is automatically initialized using default settings when starting CSH. .. class:: table @@ -75,9 +75,11 @@ Add new routes to the CSP routing table in CSH. .. csh-prompt:: host>> csp add route 64/8 CAN0 | Added route 64/8 CAN0 -**csp scan** +**ident 16383** -Scan all nodes for devices. +**ident** + +Request nodes to reply with some system info. Hostname, Vendor, Revisions and Timestamp of build. Using ident on a broadcast node or global broadcast (16383) can be used to find all devices within the local network. .. class:: table @@ -86,15 +88,12 @@ Scan all nodes for devices. :header-rows: 0 * - - .. csh-prompt:: host>> csp scan - | CSP SCAN [0:16382] - | Found something on addr 0... + .. csh-prompt:: host>> ident 16383 | lenovo | #36~22.04.1-Ubuntu SMP PREEMP | 5.19.0-35-generic | Mar 22 2023 14:17:59 | - | Found something on addr 212... | obc-hk | FLASH-1 | v1.2-1-gd958d8e+ @@ -140,7 +139,7 @@ Use the hk timeoffset command to set the node of hk server for the housekeeping **info** -Provides CSP info for the local node. First the routing table, then the connection table and finally interface statistics. +Provides CSP info for the local node, including interface configuration and statistics. .. class:: table @@ -424,41 +423,8 @@ Remotely request interface statistics. For a combined overview of all interfaces | CAN0 tx: 75840 rx: 81818 txe: 00000 rxe: 00000 | drop: 00000 autherr: 00000 frame: 06176 | txb: 3265270 rxb: 3321911 - - - -**ident** - -Responds with some system info. Hostname, Vendor, Revisions and Timestamp of build. Using ident on a broadcast node or global broadcast (16383) can be used as a csp scan to find all devices within the local network. -.. class:: table - -.. list-table:: - :widths: 100 - :header-rows: 0 - - * - - .. csh-prompt:: host>6> ident - | IDENT 6 - | obc-hk - | FLASH-1 - | v1.2-1-gd958d8e+ - | Mar 17 2023 12:18:08 - - .. csh-prompt:: host>obc-hk@6> ident 127 - | IDENT 107 - | lenovo - | #36-22.04.1-Ubuntu SMP PREEMP - | 5.19.0-35-generic - | Mar 22 2023 14:17:59 - - | IDENT 89 - | lab - | #66-Ubuntu SMP Fri Jan 20 14: - | 5.15.0-60-generic - | Oct 26 2022 16:23:29 - **uptime** Responds with the system uptime. @@ -621,7 +587,9 @@ Program a slot, with automatic search for valid binaries in the current working | ................................ - 81 K | Downloaded 82664 bytes in 4.551 s at 18163 Bps -The normal operation of the program command is to upload the entire firmware image to the module and then download it back to the CSH terminal, for bitwise comparison. This can in some circumstances prove to be very time consuming. For this reason, the system can be instructed to use a different approach using a simple CRC-32 checksum calculation on “both sides” of the communication channel. Specifying the -c option on the command line will instruct the CSH client to do a CRC-32 calculation on the firmware file prior to uploading it to the module. When the upload process has completed, the module is instructed to do the same CRC-32 calculation on all the data received and send back the result (only 32-bits) to the CSH client for verification. For this option to succeed, the module has to support the CRC-32 calculation feature, otherwise the program operation will end with a communication error. +The normal operation of the program command is to upload the entire firmware image to the module and then download it back to the CSH terminal, for bitwise comparison, which can in some circumstances prove to be very time consuming. + +For this reason, the system can be instructed to use a different approach using a simple CRC-32 checksum calculation on “both sides” of the communication channel. Specifying the -C option on the command line will instruct the CSH client to do a CRC-32 calculation on the firmware file prior to uploading it to the module. When the upload process has completed, the module is instructed to do the same CRC-32 calculation on all the data received and send back the result to the CSH client for verification. For this option to succeed, the module has to support the CRC-32 calculation feature, otherwise the program operation will end with a communication error. **sps** @@ -684,7 +652,7 @@ Here we are running sps while in slot 1, then rebooting into slot 0, programming -**stdbuf2** +**stdbuf** Retrieve the stdout buffer of node and clear it. @@ -695,7 +663,7 @@ Retrieve the stdout buffer of node and clear it. :header-rows: 0 * - - .. csh-prompt:: host>6> stdbuf2 + .. csh-prompt:: host>6> stdbuf | bootmsg: obc-hk Feb 15 2023 08:29:19 slot: 0, cause: SOFT | |Feb 15 2023 08:29:18 diff --git a/doc/MAN/Operating_Instructions/environment_variables.rst b/doc/MAN/Operating_Instructions/environment_variables.rst index 3ee06649..0cb5713a 100644 --- a/doc/MAN/Operating_Instructions/environment_variables.rst +++ b/doc/MAN/Operating_Instructions/environment_variables.rst @@ -1,7 +1,7 @@ Environment variables in CSH ----------------------------- -From version 2.5 onwards, CSH provides handling of environment variables, making it easier to perform repetitive tasks where only parameter values differ. +CSH provides handling of environment variables, making it easier to perform repetitive tasks where only parameter values differ. The environment variable related commands all start with the ``var`` prefix and are: * ``var set``: define or update an environment variable @@ -11,8 +11,6 @@ The environment variable related commands all start with the ``var`` prefix and * ``var show``: show all the variables currently defined in the environment * ``var expand``: perform variable expansion in a string -See the `Built-in commands`_ section for a description of these commands. - Environment variables examples ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -53,6 +51,6 @@ Environment variables examples :header-rows: 0 * - - .. csh-prompt:: host>6> watch -n 5 "ping $(MY_VARIABLE)" + .. csh-prompt:: host>6> watch -n 5 ping $(MY_VARIABLE) diff --git a/doc/MAN/Operating_Instructions/example.rst b/doc/MAN/Operating_Instructions/example.rst index 65d3f8d3..2ed3b7bd 100644 --- a/doc/MAN/Operating_Instructions/example.rst +++ b/doc/MAN/Operating_Instructions/example.rst @@ -1,9 +1,8 @@ 1. Connect your module to a CAN dongle and to a PC running Linux with the socketcan driver. -2. Install csh from https://github.com/spaceinventor/csh. -3. Run the script `caninit` to initialize the CAN interface -4. Start csh and register the CAN interface -5. The command `ident 16383` (broadcast address) will display all nodes on the local network. -6. Once the node where your module is located is identified, simply navigate to it using the command `node `. -7. Use `list download` to download a list of parameters of your module. Use `pull` to retrieve the value of all parameters. -8. Use `set` to set a specific parameter to a value. For more examples and information about CSH, refer to the following sections of this manual. +2. Install csh from https://github.com/spaceinventor/csh/releases. +3. Start csh and register the CAN interface as a default interface by ``csp add can -d 3``. +4. The command ``ident 16383`` (2¹⁴-1 = CSP broadcast address) will display all nodes on the local network. +5. Once the node where your module is located is identified, simply navigate to it using the command ``node ``. +6. Use ``list download`` to download a list of parameters of your module. Use ``pull`` to retrieve the value of all parameters. +7. Use ``set`` to set a specific parameter to a value. For more examples and information about CSH, refer to the following sections of this manual. diff --git a/doc/MAN/Operating_Instructions/shell_interface.rst b/doc/MAN/Operating_Instructions/shell_interface.rst index 61fed4fa..1e8ba12c 100644 --- a/doc/MAN/Operating_Instructions/shell_interface.rst +++ b/doc/MAN/Operating_Instructions/shell_interface.rst @@ -11,13 +11,14 @@ CSH runs a simple command line interface. After starting CSH you will see a prom .. csh-prompt:: >>$ csh .. csh-prompt:: host>>node 212 .. csh-prompt:: host>212>ping - |Ping node 212 size 0 timeout 1000: Reply in 14... - .. csh-prompt:: host>obc@212> ident + |Ping node 212 size 0 timeout 1000: Reply in 14 [ms] + .. csh-prompt:: host>212> ident |IDENT 212 | obc-hk | FLASH-1 | v1.2-1-gd958d8e+ | Mar 17 2023 12:18:08 + .. csh-prompt:: host>obc@212> .. class:: centered diff --git a/doc/index_man.rst b/doc/index_man.rst index 41501ec0..a3c45fc5 100644 --- a/doc/index_man.rst +++ b/doc/index_man.rst @@ -39,10 +39,14 @@ Requirements ------------------- .. include:: MAN/Installation/requirements.rst -Build and install +Installation ------------------------ .. include:: MAN/Installation/install.rst +Manual build from sources +------------------------- +.. include:: MAN/Installation/build.rst + Launch software -------------------- .. include:: MAN/Installation/launch_sw.rst @@ -71,42 +75,47 @@ Example module testing procedure using CSH PageBreak oneColumn -List of commands +Command examples ----------------------------- +.. include:: MAN/Operating_Instructions/command_examples.rst -Built-in commands -~~~~~~~~~~~~~~~~~ - -.. partool -q --csv -s builddir/csh > doc/MAN/Operating_Instructions/builtin_commands.csv +Named Commands +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. include:: MAN/Operating_Instructions/scheduler_examples.rst -.. csv-table:: Built-in commands - :file: MAN/Operating_Instructions/builtin_commands.csv - :widths: 20 20 70 - :header-rows: 1 +.. include:: MAN/Operating_Instructions/environment_variables.rst .. raw:: pdf PageBreak -Command examples ------------------------------ -.. include:: MAN/Operating_Instructions/command_examples.rst +APMs +=========================================================================== +.. include:: MAN/APMs/introduction.rst -Named Commands -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. include:: MAN/Operating_Instructions/scheduler_examples.rst -.. include:: MAN/Operating_Instructions/environment_variables.rst +Commands provided by CSH SI +----------------------------- +.. include:: MAN/APMs/csh_si.rst -.. Appendices -.. =========================================================================== -.. .. include:: MAN/Appendices/abbreviation_list.rst +Creating a new APM +----------------------------- +.. include:: MAN/APMs/csh_example.rst +.. raw:: pdf + PageBreak +Appendices +=========================================================================== +List of Built-in commands +----------------------------- -.. ..................................................................... - +.. partool -q --csv -s builddir/csh > doc/MAN/Operating_Instructions/builtin_commands.csv +.. csv-table:: Built-in commands + :file: MAN/Operating_Instructions/builtin_commands.csv + :widths: 20 20 70 + :header-rows: 1 diff --git a/lib/apm_csh b/lib/apm_csh index 5e97fbf0..eb4d5606 160000 --- a/lib/apm_csh +++ b/lib/apm_csh @@ -1 +1 @@ -Subproject commit 5e97fbf06b1e85cd84f809c0d3c2d20cfa4f6634 +Subproject commit eb4d560618fd71b5d0d8f851ad6405c1e312cdc1 diff --git a/lib/csp b/lib/csp index 8cc13c66..5a95e5c4 160000 --- a/lib/csp +++ b/lib/csp @@ -1 +1 @@ -Subproject commit 8cc13c663c6db1d333bd1af6546d1f7fc2599770 +Subproject commit 5a95e5c48826c3602bf4a34aa5d60e2af9e4a004 diff --git a/lib/csp_ippc b/lib/csp_ippc deleted file mode 160000 index a093344d..00000000 --- a/lib/csp_ippc +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a093344d691e1e1df04fdcbc51c3f696e893ab26 diff --git a/lib/csp_proc b/lib/csp_proc deleted file mode 160000 index 4a78cc9c..00000000 --- a/lib/csp_proc +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 4a78cc9c69322b4d50f5441a07f80b8912930f3a diff --git a/lib/ossi b/lib/ossi new file mode 160000 index 00000000..cb7d4e10 --- /dev/null +++ b/lib/ossi @@ -0,0 +1 @@ +Subproject commit cb7d4e103ea7cd8e5c5f57f4af04d632c48a1821 diff --git a/lib/param b/lib/param index b3288a80..a0d33831 160000 --- a/lib/param +++ b/lib/param @@ -1 +1 @@ -Subproject commit b3288a801050f69f1f263795021da5e2759d08c9 +Subproject commit a0d3383199708ee5256787f2efa36e1fb15ca924 diff --git a/lib/pycsh_core b/lib/pycsh_core index 7128656f..e94d13be 160000 --- a/lib/pycsh_core +++ b/lib/pycsh_core @@ -1 +1 @@ -Subproject commit 7128656f89dc126bff66dea35c045e3294635ea2 +Subproject commit e94d13bea16fec9080b69056ec650c450263bec4 diff --git a/lib/slash b/lib/slash index 7b0c33b3..21420aa6 160000 --- a/lib/slash +++ b/lib/slash @@ -1 +1 @@ -Subproject commit 7b0c33b39d8b73c861efd1ddbcd10c4fe69f2308 +Subproject commit 21420aa61715aba84cfa33613b335d7a24ea908a diff --git a/meson.build b/meson.build index e430ec75..4ef53fb0 100644 --- a/meson.build +++ b/meson.build @@ -1,26 +1,31 @@ -project('csh', ['c', 'cpp'], subproject_dir: 'lib', default_options: [ - 'csp_ippc:slash=true', - 'csp_proc:slash=true', - 'csp_proc:RESERVED_PROC_SLOTS=10', - 'buildtype=debug', - 'optimization=2', - 'warning_level=0', - 'c_std=gnu11', - 'b_lto=false', - 'default_library=static', - 'slash:builtins=true', - 'csp:packet_padding_bytes=42', - 'csp:buffer_count=1000', - 'csp:buffer_size=2048', - 'csp:conn_max=20', - 'csp:conn_rxqueue_len=1000', - 'csp:qfifo_len=1000', - 'csp:rdp_max_window=1000', - 'csp:port_max_bind=16', - 'csp:use_rtable=true', - 'param:have_fopen=true', - 'param:collector=false', - 'param:list_dynamic=true']) +project('csh', + ['c', 'cpp'], + subproject_dir: 'lib', + default_options: [ + 'buildtype=debug', + 'optimization=2', + 'warning_level=0', + 'c_std=gnu11', + 'b_lto=false', + 'default_library=static', + 'slash:builtins=true', + 'csp:packet_padding_bytes=42', + 'csp:buffer_count=1000', + 'csp:buffer_size=2048', + 'csp:conn_max=20', + 'csp:conn_rxqueue_len=1000', + 'csp:qfifo_len=1000', + 'csp:rdp_max_window=1000', + 'csp:port_max_bind=16', + 'csp:use_rtable=true', + 'param:have_fopen=true', + 'param:have_timestamp=true', + 'param:collector=false', + 'param:list_dynamic=true' + ], + meson_version: '>= 1.6', + version: run_command('git', 'describe', '--long', '--always', '--dirty', check: true).stdout().strip() +) # Adding -Wall and -Wextra generate harmless (or senseless if you ask me) Meson warnings: # 'Consider using the built-in warning_level option instead of using "-Wall"' @@ -31,11 +36,11 @@ project('csh', ['c', 'cpp'], subproject_dir: 'lib', default_options: [ # they want. # As Meson doesn't propagate the arguments in add_project_arguments() to the dependencies, we can # accept the Meson warnings given the flexibilty we gain -add_project_arguments(['-Wall', '-Wextra', '-Wno-unused-parameter'], language: 'c') +add_project_arguments(['-Wall', '-Wextra', '-Wstrict-prototypes', '-Wmissing-prototypes', '-Wno-unused-parameter'], language: 'c') # add_project_arguments(['-fanalyzer'], language: 'c') curl_dep = dependency( - 'libcurl', + 'libcurl', not_found_message: 'libcurl not found! Please install libcurl4-openssl-dev or the appropriate package for your system.' ) @@ -47,9 +52,7 @@ zmq_dep = dependency( csp_dep = dependency('csp', fallback: ['csp', 'csp_dep'], required: true).as_link_whole() slash_dep = dependency('slash', fallback: ['slash', 'slash_dep'], required: true).as_link_whole() param_dep = dependency('param', fallback: ['param', 'param_dep'], required: true).as_link_whole() -lua_dep = dependency('lua5.4', required: false) -csp_ippc_dep = dependency('csp_ippc', fallback: ['csp_ippc', 'csp_ippc_dep'], required: true).as_link_whole() -csp_proc_dep = dependency('csp_proc', fallback: ['csp_proc', 'csp_proc_dep'], required: true).as_link_whole() +ossi_dep = dependency('ossi', version: ['>=0.0.2'],fallback: ['ossi', 'ossi_dep'], required: true).as_link_whole() apm_api_dep = dependency( 'apm_csh', fallback: @@ -74,7 +77,6 @@ csh_sources = [ 'src/prometheus.c', 'src/resbuf_dump.c', 'src/stdbuf_client.c', - 'src/randombytes.c', 'src/csp_scan.c', 'src/sleep_slash.c', 'src/spaceboot_slash.c', @@ -91,10 +93,10 @@ csh_sources = [ 'src/slash_nodes_cmds.c', 'src/slash_run_environment.c', 'src/require_version_cmd.c', - 'src/param_list_slash.c', - 'src/param_slash.c', - 'src/vmem_client_slash.c', - 'src/vmem_client_slash_ftp.c', + 'src/param_list_slash.c', + 'src/param_slash.c', + 'src/vmem_client_slash.c', + 'src/vmem_client_slash_ftp.c', ] python_dep = dependency('python3', version : '>=3.10', required: false) @@ -134,18 +136,15 @@ if meson.is_subproject() == false subdir('tests') endif -# Include csp_ippc_dep for DIPP configuration commands (requires: libprotobuf-c libjxl libbrotlienc) csh = executable('csh', csh_sources, dependencies : [ python_dep, apm_api_dep, slash_dep, - csp_dep, param_dep, - lua_dep, + ossi_dep, + csp_dep, curl_dep, - csp_proc_dep, - csp_ippc_dep, utils_lib_dep.as_link_whole(), pycsh_dep ], diff --git a/src/csh_defaults.c b/src/csh_defaults.c index 4bc63b7d..5df4191b 100644 --- a/src/csh_defaults.c +++ b/src/csh_defaults.c @@ -1,5 +1,7 @@ #include #include +#include "csh_internals.h" + unsigned int slash_dfl_node = 0; unsigned int slash_dfl_timeout = 1000; @@ -17,7 +19,7 @@ unsigned int rdp_tmp_delayed_acks; unsigned int rdp_tmp_ack_timeout; unsigned int rdp_tmp_ack_count; -void rdp_opt_set() { +void rdp_opt_set(void) { csp_rdp_set_opt(rdp_tmp_window, rdp_tmp_conn_timeout, rdp_tmp_packet_timeout, rdp_tmp_delayed_acks, rdp_tmp_ack_timeout, rdp_tmp_ack_count); @@ -25,7 +27,7 @@ void rdp_opt_set() { rdp_tmp_window, rdp_tmp_conn_timeout, rdp_tmp_packet_timeout, rdp_tmp_ack_timeout, rdp_tmp_ack_count); } -void rdp_opt_reset() { +void rdp_opt_reset(void) { csp_rdp_set_opt(rdp_dfl_window, rdp_dfl_conn_timeout, rdp_dfl_packet_timeout, rdp_dfl_delayed_acks, rdp_dfl_ack_timeout, rdp_dfl_ack_count); } diff --git a/src/csh_internals.h b/src/csh_internals.h index 17b55ade..446d9af0 100644 --- a/src/csh_internals.h +++ b/src/csh_internals.h @@ -7,8 +7,14 @@ extern "C" { #endif void rdp_opt_add(optparse_t * parser); -void rdp_opt_set(); -void rdp_opt_reset(); +void rdp_opt_set(void); +void rdp_opt_reset(void); +void serial_init(void); +uint32_t serial_get(void); +void* si_lock_init(void); +int si_lock_take(void* lock, int block_time_ms); +int si_lock_give(void* lock); +int param_slash_parse_slice(char * token, int *start_index, int *end_index, int *slice_detected); #ifdef __cplusplus } diff --git a/src/csp_init_cmd.c b/src/csp_init_cmd.c index a5655940..d100747a 100644 --- a/src/csp_init_cmd.c +++ b/src/csp_init_cmd.c @@ -497,6 +497,7 @@ static int csp_ifadd_udp_cmd(struct slash *slash) { optparse_del(parser); return SLASH_EINVAL; } + udp_conf->sockfd = 0; udp_conf->host = strdup(server); udp_conf->lport = listen_port; udp_conf->rport = remote_port; @@ -505,6 +506,7 @@ static int csp_ifadd_udp_cmd(struct slash *slash) { iface->is_default = dfl; iface->addr = addr; iface->netmask = mask; + iface->name = strdup(name); optparse_del(parser); ifidx++; @@ -595,6 +597,49 @@ static int csp_ifadd_tun_cmd(struct slash *slash) { slash_command_subsub(csp, add, tun, csp_ifadd_tun_cmd, NULL, "Add a new TUN interface"); +static int csp_ifadd_alias_cmd(struct slash *slash) { + + optparse_t * parser = optparse_new("csp add alias", " "); + + int argi = optparse_parse(parser, slash->argc - 1, (const char **) slash->argv + 1); + + if (argi < 0) { + optparse_del(parser); + return SLASH_EINVAL; + } + + if (++argi >= slash->argc) { + printf("missing parameter addr\n"); + optparse_del(parser); + return SLASH_EINVAL; + } + char * endptr = NULL; + unsigned int addr = strtoul(slash->argv[argi], &endptr, 10); + + if (++argi >= slash->argc) { + printf("missing parameter ifname\n"); + optparse_del(parser); + return SLASH_EINVAL; + } + + csp_alias_t * addr_struct = malloc(sizeof(csp_alias_t)); + if (!addr_struct) { + printf("Failed to allocate memory for alias address.\n"); + optparse_del(parser); + return SLASH_EINVAL; + } + + addr_struct->addr = addr; + addr_struct->iface = csp_iflist_get_by_name(slash->argv[argi]); + addr_struct->iface->add_alias(addr_struct->iface->driver_data, addr); + csp_alias_add(addr_struct); + + optparse_del(parser); + return SLASH_SUCCESS; +} + +slash_command_subsub(csp, add, alias, csp_ifadd_alias_cmd, NULL, "Add a new Alias address"); + #if CSP_USE_RTABLE static int csp_routeadd_cmd(struct slash *slash) { diff --git a/src/environment.c b/src/environment.c index 418b73ab..7faec321 100644 --- a/src/environment.c +++ b/src/environment.c @@ -83,7 +83,7 @@ int csh_delvar(const char *name) { return res; } -void csh_clearenv() { +void csh_clearenv(void) { struct csh_env_entry *var; while (!SLIST_EMPTY(&csh_env)) { var = SLIST_FIRST(&csh_env); diff --git a/src/hk_param_sniffer.c b/src/hk_param_sniffer.c index 809e68ef..be318e13 100644 --- a/src/hk_param_sniffer.c +++ b/src/hk_param_sniffer.c @@ -20,6 +20,7 @@ #include #include "param_sniffer.h" +#include "hk_param_sniffer.h" pthread_t hk_param_sniffer_thread; #define MAX_HKS 16 @@ -38,27 +39,27 @@ typedef struct timesync_nodes_s { } timesync_nodes_t; static timesync_nodes_t timesync_nodes = {0}; -void hk_set_utcparam(unsigned int node, unsigned int paramid) { +static void hk_set_utcparam(unsigned int node, unsigned int paramid) { // update existing for (int i = 0; i < timesync_nodes.count; i++) { if (timesync_nodes.node[i] == node) { timesync_nodes.node[i] = node; timesync_nodes.paramid[i] = paramid; - printf("Updating HK UTC parameter from node %u\n", node); + printf("HK: Updating HK UTC parameter from node %u\n", node); return; } } if (timesync_nodes.count >= MAX_HKS) { - printf("Error: Maximum number of HK nodes reached (%d). Cannot set new utcparam for node %u\n", MAX_HKS, node); + printf("HK: Error: Maximum number of HK nodes reached (%d). Cannot set new utcparam for node %u\n", MAX_HKS, node); return; } timesync_nodes.node[timesync_nodes.count] = node; timesync_nodes.paramid[timesync_nodes.count++] = paramid; - printf("Adding HK UTC parameter from node %u\n", node); + printf("HK: Adding HK UTC parameter from node %u\n", node); } static int hk_utcparam(struct slash * slash) { @@ -106,14 +107,12 @@ void hk_set_epoch(time_t epoch, uint16_t node, bool auto_sync) { time_t current_epoch; time(¤t_epoch); - char current_time_str[32]; - strftime(current_time_str, sizeof(current_time_str), "%Y-%m-%d %H:%M:%S", gmtime(¤t_epoch)); - /* 1577833200: Jan 1st 2020 */ - if (epoch > current_epoch || epoch < 1577833200) { + /* 1577836800: Jan 1st 2020 */ + if (epoch > current_epoch || epoch < 1577836800) { char current_epoch_str[32]; strftime(current_epoch_str, sizeof(current_epoch_str), "%Y-%m-%d %H:%M:%S", gmtime(&epoch)); - printf("At %s: Illegal EPOCH %lu (%s) received\n", current_time_str, current_epoch, current_epoch_str); + printf("HK: Illegal EPOCH %lu (%s) received\n", current_epoch, current_epoch_str); return; } @@ -126,7 +125,7 @@ void hk_set_epoch(time_t epoch, uint16_t node, bool auto_sync) { strftime(time, sizeof(time), "%Y-%m-%d %H:%M:%S", gmtime(&epoch)); char time_current[32]; strftime(time_current, sizeof(time_current), "%Y-%m-%d %H:%M:%S", gmtime(&hks.local_epoch[i])); - printf("At %s: Skipping possible invalid EPOCH %s, current EPOCH for HK node %u is %s\n", current_time_str, time, node, time_current); + printf("HK: Skipping possible invalid EPOCH %s, current EPOCH for HK node %u is %s (%ld)\n", time, node, time_current, hks.local_epoch[i]); return; } @@ -134,7 +133,7 @@ void hk_set_epoch(time_t epoch, uint16_t node, bool auto_sync) { /* get unix time to string time */ char time[32]; strftime(time, sizeof(time), "%Y-%m-%d %H:%M:%S", gmtime(&epoch)); - printf("At %s: Updating HK node %u EPOCH by %ld sec to %s\n", current_time_str, node, hks.local_epoch[i] - epoch, time); + printf("HK: Updating HK node %u EPOCH by %ld sec to %s (%ld)\n", node, hks.local_epoch[i] - epoch, time, epoch); } hks.local_epoch[i] = epoch; @@ -143,14 +142,16 @@ void hk_set_epoch(time_t epoch, uint16_t node, bool auto_sync) { } if (hks.count >= MAX_HKS) { - printf("At %s: Error: Maximum number of HK nodes reached (%d). Cannot set new epoch for node %u\n", current_time_str, MAX_HKS, node); + printf("HK: Error: Maximum number of HK nodes reached (%d). Cannot set new epoch for node %u\n", MAX_HKS, node); return; } hks.node[hks.count] = node; hks.local_epoch[hks.count++] = epoch; - printf("At %s: Setting new hk node %u EPOCH to %ld\n", current_time_str, node, epoch); + char new_epoch_str[32]; + strftime(new_epoch_str, sizeof(new_epoch_str), "%Y-%m-%d %H:%M:%S", gmtime(&epoch)); + printf("HK: Setting new hk node %u EPOCH to %s (%ld)\n", node, new_epoch_str, epoch); } static int hk_timeoffset(struct slash * slash) { @@ -176,7 +177,7 @@ static int hk_timeoffset(struct slash * slash) { } else { for (int i = 0; i < hks.count; i++) { if (hks.node[i] == node) { - printf("Current satellite EPOCH is %s\nSeconds: %lu\n", ctime(&hks.local_epoch[i]), hks.local_epoch[i]); + printf("HK: Current satellite EPOCH is %s\nSeconds: %lu\n", ctime(&hks.local_epoch[i]), hks.local_epoch[i]); } } } @@ -211,7 +212,15 @@ bool hk_param_sniffer(csp_packet_t * packet) { /* Protocol has a header size of 5, and RDP adds 5 bytes to the end of the packet if activated */ size_t header_size = 5; - size_t data_len = packet->length - header_size - ((packet->id.flags & CSP_FRDP) ? 5 : 0); + size_t overhead = header_size + ((packet->id.flags & CSP_FRDP) ? 5 : 0); + /* Guard the unsigned subtraction. Other traffic also uses sport 13 (e.g. the DIPP + * ring_size / observation_meta RDP replies, which are short). Without this, a packet + * shorter than the overhead underflows data_len to ~SIZE_MAX and the mpack reader + * below walks off the buffer -> segfault. Too short to carry a param payload => skip. */ + if (packet->length < overhead) { + return false; + } + size_t data_len = packet->length - overhead; param_queue_t queue; param_queue_init(&queue, &packet->data[header_size], data_len, data_len, PARAM_QUEUE_TYPE_SET, 2); queue.last_node = packet->id.src; @@ -234,25 +243,29 @@ bool hk_param_sniffer(csp_packet_t * packet) { break; } - time_t local_epoch = -1; - for (int i = 0; i < timesync_nodes.count; i++) { - if (timesync_nodes.node[i] == node && timesync_nodes.paramid[i] == param->id) { - mpack_tag_t tag = mpack_peek_tag(&reader); - local_epoch = tag.v.i - timestamp.tv_sec; - hk_set_epoch(local_epoch, packet->id.src, true); - break; + /* Only use local epoch if not receiving a UTC timestamp. 1577836800: Jan 1st 2020 */ + if (param->timestamp->tv_sec < 1577836800) { + time_t local_epoch = -1; + for (int i = 0; i < timesync_nodes.count; i++) { + if (timesync_nodes.node[i] == node && timesync_nodes.paramid[i] == param->id) { + mpack_tag_t tag = mpack_peek_tag(&reader); + local_epoch = tag.v.i - timestamp.tv_sec; + hk_set_epoch(local_epoch, packet->id.src, true); + break; + } } - } - if (local_epoch == -1 && !hk_get_epoch(&local_epoch, packet->id.src)) { - if(!epoch_notfound_warning) { - printf("HK: No local epoch found for node %u, skipping\n", packet->id.src); - epoch_notfound_warning = true; + + if (local_epoch == -1 && !hk_get_epoch(&local_epoch, packet->id.src)) { + if(!epoch_notfound_warning) { + printf("HK: No local epoch found for node %u, skipping %u %u %u\n", packet->id.src, *param->node, param->id, param->timestamp->tv_sec); + epoch_notfound_warning = true; + } + mpack_discard(&reader); + continue; } - mpack_discard(&reader); - continue; - } - param->timestamp->tv_sec += local_epoch; + param->timestamp->tv_sec += local_epoch; + } param_sniffer_log(NULL, &queue, param, offset, &reader, param->timestamp); } else { printf("HK: Found unknown param node %d id %d\n", node, id); diff --git a/src/hk_param_sniffer.h b/src/hk_param_sniffer.h index e0403108..f8667e72 100644 --- a/src/hk_param_sniffer.h +++ b/src/hk_param_sniffer.h @@ -11,8 +11,6 @@ #include #include -bool hk_get_epoch(time_t* epoch, uint16_t node); -void hk_set_epoch(time_t epoch, uint16_t node); /* returns true if the packet was found to be for housekeeping */ bool hk_param_sniffer(csp_packet_t * packet); diff --git a/src/known_hosts.c b/src/known_hosts.c index 9ea7869f..bd39ef13 100644 --- a/src/known_hosts.c +++ b/src/known_hosts.c @@ -24,7 +24,7 @@ struct host_s { }; static uint32_t known_host_storage_size = sizeof(host_t); -SLIST_HEAD(known_host_s, host_s) known_hosts = {}; +SLIST_HEAD(known_host_s, host_s) known_hosts = {0}; /** Private (CSH-only API) */ void node_save(const char * filename) { @@ -56,13 +56,13 @@ void known_host_set_storage_size(uint32_t new_size){ known_host_storage_size = new_size; } -uint32_t known_host_get_storage_size() { +uint32_t known_host_get_storage_size(void) { return known_host_storage_size; } void host_name_completer(struct slash *slash, char * token) { - SLIST_HEAD(known_host_s, host_s) matching_hosts = {}; + SLIST_HEAD(known_host_s, host_s) matching_hosts = {0}; char *part_to_complete = token + strnlen(token, slash->length); /* Rewind to a potential whitespace */ while(part_to_complete > token) { @@ -138,7 +138,7 @@ void host_name_completer(struct slash *slash, char * token) { } -void known_hosts_del(int host) { +static void known_hosts_del(int host) { // SLIST_FOREACH(host_t host, &known_hosts, next) { for (host_t* element = SLIST_FIRST(&known_hosts); element != NULL; element = SLIST_NEXT(element, next)) { diff --git a/src/lock.c b/src/lock.c index 673b58fc..5f3ec311 100644 --- a/src/lock.c +++ b/src/lock.c @@ -3,6 +3,7 @@ #include #include #include +#include "csh_internals.h" int si_lock_take(void* lock, int block_time_ms) { @@ -56,7 +57,7 @@ int si_lock_give(void* lock) { static uint8_t lock_taken[NUM_LOCKS] = {0}; static sem_t locks[NUM_LOCKS] = {0}; -void* si_lock_init() { +void* si_lock_init(void) { for (int i = 0; i < NUM_LOCKS; i++) { if(lock_taken[i] == 0) { diff --git a/src/loki.c b/src/loki.c index 82246480..d3251a8f 100644 --- a/src/loki.c +++ b/src/loki.c @@ -15,8 +15,9 @@ #include #include -#include "arch/posix/pthread_queue.h" +#include #include "url_utils.h" +#include "loki.h" static int loki_running = 0; @@ -26,7 +27,8 @@ static int loki_running = 0; static char readbuffer[BUFFER_SIZE] = {0}; static char formatted_log[BUFFER_SIZE - 100] = {0}; static pthread_mutex_t buffer_mutex = PTHREAD_MUTEX_INITIALIZER; -static pthread_queue_t * loki_q; + +static message_queue_t loki_q; typedef struct { char * data; @@ -63,7 +65,7 @@ static size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdat /* strip ansi escape codes, special characters and newlines to nullterminator * returns if number of nulltermintors inserted */ -int strip_str(char *str) { +static int strip_str(char *str) { char *read_p = str; char *write_p = str; int str_counter = 0; @@ -96,9 +98,11 @@ int strip_str(char *str) { while (*read_p && (*read_p < 32) && *read_p != 27) { read_p++; } - *write_p = '\0'; - write_p++; - str_counter++; + if(write_p > str && *(write_p - 1) != '\0') { + *write_p = '\0'; + write_p++; + str_counter++; + } } else if (*read_p == '\t'){ *write_p = ' '; read_p++; @@ -207,15 +211,15 @@ void loki_add(char * log, int iscmd) { pthread_mutex_unlock(&buffer_mutex); json_str_t json_str = {.data = json_str_buf, .len = written}; - pthread_queue_enqueue(loki_q, &json_str, CSP_MAX_TIMEOUT); + message_queue_send(&loki_q, &json_str); } static void *post_thread(void *arg) { while(loki_running){ json_str_t json_str; - int p_res = pthread_queue_dequeue(loki_q, (void *)&json_str, CSP_MAX_TIMEOUT); - if(p_res != PTHREAD_QUEUE_OK){ + int p_res = message_queue_receive(&loki_q, &json_str); + if(p_res != 0){ continue; } curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, json_str.len); @@ -438,13 +442,11 @@ static int loki_start_cmd(struct slash * slash) { perror("dup2"); return SLASH_EINVAL; } - if(!loki_q){ - loki_q = pthread_queue_create(2000, sizeof(json_str_t)); - if(!loki_q){ - printf("\033[1;31mLOKI CURL: Out of memory!\033[0m\n"); - return SLASH_EINVAL; - } - + static bool queue_created = false; + if(!queue_created){ + queue_created = true; + static uint8_t queue_storage[2000 * sizeof(json_str_t)]; + message_queue_create(&loki_q, sizeof(json_str_t), 2000, queue_storage); } loki_running = 1; pthread_t read_thread_id; diff --git a/src/main.c b/src/main.c index d15e6126..efb285b0 100644 --- a/src/main.c +++ b/src/main.c @@ -34,8 +34,13 @@ #include #include +#include #include "slash_env_var_completion.h" +#ifdef HAVE_PYTHON +#include +#endif + extern const char *version_string; extern param_queue_t param_queue; @@ -43,15 +48,6 @@ extern param_queue_t param_queue; #define LINE_SIZE 512 #define HISTORY_SIZE 2048 -VMEM_DEFINE_FILE(col, "col", "colcnf.vmem", 120); -#ifdef PARAM_HAVE_COMMANDS -VMEM_DEFINE_FILE(commands, "cmd", "commands.vmem", 2048); -#endif -#ifdef PARAM_HAVE_SCHEDULER -VMEM_DEFINE_FILE(schedule, "sch", "schedule.vmem", 2048); -#endif - - void csp_router_set_running(bool is_running); static int (*current_apm_prompt)(struct slash * slash) = NULL; @@ -149,15 +145,9 @@ int slash_prompt(struct slash * slash) { } } -uint64_t clock_get_nsec(void) { - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - return ts.tv_sec * 1E9 + ts.tv_nsec; -} - -void usage(void) { +static void usage(void) { printf("usage: csh -i init.csh [command]\n"); - printf("Type 'manual' to open CSH manual\n"); + printf("In CSH, type 'manual' to open CSH manual\n"); printf("\n"); printf("Copyright (c) 2016-2025 Space Inventor A/S \n"); printf("\n"); @@ -221,13 +211,14 @@ static char *csh_environ_slash_process_cmd_line_hook(const char *line) { return expansion; } -void * router_task(void * param) { +static void * router_task(void * param) { while(1) { csp_route_work(); } + return NULL; } -void * vmem_server_task(void * param) { +static void * vmem_server_task(void * param) { vmem_server_loop(param); return NULL; } @@ -327,8 +318,9 @@ int main(int argc, char **argv) { pthread_create(&router_handle, NULL, &router_task, NULL); static pthread_t vmem_server_handle; pthread_create(&vmem_server_handle, NULL, &vmem_server_task, NULL); +#ifdef HAVE_PYTHON csp_router_set_running(true); - +#endif csp_rdp_set_opt(3, 10000, 5000, 1, 2000, 2); #ifdef PARAM_HAVE_COMMANDS diff --git a/src/nav.c b/src/nav.c index 50d76997..9b6a504d 100644 --- a/src/nav.c +++ b/src/nav.c @@ -33,10 +33,12 @@ static int slash_ls(struct slash *slash) { } slash_command_completer(ls, slash_ls, slash_path_completer, "[path]", "list files"); -static void print_cwd() { +static void print_cwd(void) { char *cwd = get_current_dir_name(); - printf("%s\n", cwd); - free(cwd); + if(NULL != cwd) { + printf("%s\n", cwd); + free(cwd); + } } static int slash_pwd(struct slash *slash) { @@ -65,7 +67,7 @@ static int slash_cd(struct slash *slash) { strcat(expanded_path, &slash->argv[1][1]); } if (chdir(expanded_path) < 0) { - printf("Failed to cd into %s, current dir is: ", expanded_path); + printf("Failed to cd into %s\n", expanded_path); free(expanded_path); print_cwd(); return SLASH_EINVAL; @@ -74,14 +76,14 @@ static int slash_cd(struct slash *slash) { } } else { if (chdir(slash->argv[1]) < 0) { - printf("Failed to cd into %s, current dir is: ", slash->argv[1]); + printf("Failed to cd into %s\n", slash->argv[1]); print_cwd(); return SLASH_EINVAL; } } } else { if (chdir(home) < 0) { - printf("Failed to cd into %s, current dir is: ", home); + printf("Failed to cd into %s\n", home); print_cwd(); return SLASH_EINVAL; } diff --git a/src/param_list_slash.c b/src/param_list_slash.c index ddf67b5e..0959ecaf 100644 --- a/src/param_list_slash.c +++ b/src/param_list_slash.c @@ -19,7 +19,9 @@ #include #include #include +#ifdef HAVE_PYTHON #include +#endif #include @@ -60,7 +62,7 @@ Shows cached/known values. Use -v to include parameter type and help text."); optparse_del(parser); return SLASH_SUCCESS; } -slash_command_completer(list, list, param_completer, "[OPTIONS...] [name wildcard=*]", "List parameters"); +slash_command(list, list, "[OPTIONS...] [name wildcard=*]", "List parameters"); static int list_download(struct slash *slash) diff --git a/src/param_slash.c b/src/param_slash.c index ce723de4..6fe2e318 100644 --- a/src/param_slash.c +++ b/src/param_slash.c @@ -27,6 +27,7 @@ #include #include "time.h" +#include "csh_internals.h" static char queue_buf[PARAM_SERVER_MTU]; param_queue_t param_queue = { .buffer = queue_buf, .buffer_size = PARAM_SERVER_MTU, .type = PARAM_QUEUE_TYPE_EMPTY, .version = 2 }; @@ -536,7 +537,7 @@ static int cmd_get(struct slash *slash) { continue; } - if ((param->mask & mask) == 0) { + if (mask != 0xFFFFFFFF && (param->mask & mask) == 0) { continue; } @@ -810,7 +811,7 @@ static int cmd_set(struct slash *slash) { /* Local parameters are set directly */ if (*param->node == 0) { - param_queue_apply(&queue, 0); + param_queue_apply(&queue, 0, 3); // if (offset < 0 && param->type != PARAM_TYPE_STRING && param->type != PARAM_TYPE_DATA) { // for (int i = 0; i < param->array_size; i++) @@ -945,7 +946,7 @@ static int cmd_add(struct slash *slash) { continue; } - if ((param->mask & include_mask) == 0) { + if (include_mask != 0xFFFFFFFF && (param->mask & include_mask) == 0) { continue; } @@ -1143,13 +1144,10 @@ static int cmd_new(struct slash *slash) { name = slash->argv[argi]; strncpy(param_queue.name, name, sizeof(param_queue.name)-1); // -1 to fit NULL byte - csp_timestamp_t time_now; - csp_clock_get_time(&time_now); param_queue.used = 0; param_queue.version = paramver; param_queue.last_timestamp.tv_sec = 0; param_queue.last_timestamp.tv_nsec = 0; - param_queue.client_timestamp = time_now; printf("Initialized new command: %s\n", name); diff --git a/src/param_sniffer.c b/src/param_sniffer.c index 370430fa..d5a05faf 100644 --- a/src/param_sniffer.c +++ b/src/param_sniffer.c @@ -16,6 +16,7 @@ #include #include +#include "param_sniffer.h" #include "hk_param_sniffer.h" #include "prometheus.h" #include "victoria_metrics.h" @@ -151,7 +152,7 @@ static void * param_sniffer(void * param) { continue; } - if (packet->id.sport != PARAM_PORT_SERVER) { + if (packet->id.sport != PARAM_PORT_SERVER && packet->id.dport != PARAM_PORT_SERVER) { csp_buffer_free(packet); continue; } @@ -181,7 +182,6 @@ static void * param_sniffer(void * param) { csp_timestamp_t time_now; csp_clock_get_time(&time_now); queue.last_timestamp = time_now; - queue.client_timestamp = time_now; mpack_reader_t reader; mpack_reader_init_data(&reader, queue.buffer, queue.used); diff --git a/src/param_sniffer.h b/src/param_sniffer.h index 46c39b31..f1238574 100644 --- a/src/param_sniffer.h +++ b/src/param_sniffer.h @@ -9,6 +9,8 @@ #define SRC_PARAM_SNIFFER_H_ #include +#include +#include int param_sniffer_crc(csp_packet_t * packet); int param_sniffer_log(void * ctx, param_queue_t *queue, param_t *param, int offset, void *reader, csp_timestamp_t *timestamp); diff --git a/src/prometheus.c b/src/prometheus.c index e47825b0..7562844a 100644 --- a/src/prometheus.c +++ b/src/prometheus.c @@ -33,7 +33,7 @@ static char header[1024] = "Content-Type: text/plain;\r\n\r\n"; -void * prometheus_exporter(void * param) { +static void * prometheus_exporter(void * param) { listen_fd = socket(AF_INET, SOCK_STREAM, 0); diff --git a/src/python/python_loader.c b/src/python/python_loader.c index 01bfb2b4..2e04e327 100644 --- a/src/python/python_loader.c +++ b/src/python/python_loader.c @@ -106,7 +106,9 @@ static PyObject * pycsh_integrate_pymod(const char * const _filepath) { snprintf(init_func_name, init_func_name_len, "PyInit_%s", filename); typedef PyObject* (*PyInitFunc)(void); - PyInitFunc init_func = (PyInitFunc)dlsym(handle, init_func_name); + PyInitFunc init_func; + /* Fix for: ISO C forbids conversion of object pointer to function pointer type [-Werror=pedantic] */ + *(void **)(&init_func) = dlsym(handle, init_func_name); if (!init_func) { fprintf(stderr, "Error finding initialization function: %s\n", dlerror()); @@ -143,7 +145,7 @@ static PyObject * pycsh_integrate_pymod(const char * const _filepath) { return module; } -PyObject * pycsh_load_pymod(const char * const _filepath, const char * const init_function_name, int verbose) { +static PyObject * pycsh_load_pymod(const char * const _filepath, const char * const init_function_name, int verbose) { if (_filepath == NULL) { return NULL; @@ -395,7 +397,7 @@ void py_init_interpreter(void) { return; } -static void walk_path_list(char *pathlist, char *search_str, void (*cb)(char *path, char *search_str)) { +static void walk_path_list(char *pathlist, char *search_str, void (*cb)(char *path, char *search_str, unsigned int *loaded_count), unsigned int *loaded_count) { size_t pathlist_len = strlen(pathlist); char *runner = pathlist; char *start = pathlist; @@ -403,16 +405,16 @@ static void walk_path_list(char *pathlist, char *search_str, void (*cb)(char *pa while (runner++ < end) { if(*runner == ';') { *runner = '\0'; - cb(start, search_str); + cb(start, search_str, loaded_count); start = ++runner; } else if(*runner == '\0') { - cb(start, search_str); + cb(start, search_str, loaded_count); start = ++runner; } } } -static void load_py(char *path, char *search_str) { +static void load_py(char *path, char *search_str, unsigned int *loaded_count) { struct dirent *entry; DIR *dir CLEANUP_DIR = opendir(path); if (dir == NULL) { @@ -434,27 +436,34 @@ static void load_py(char *path, char *search_str) { PyErr_Print(); continue; } - apm_entry_t * e = calloc(1, sizeof(apm_entry_t)); - if (!e) { - printf("Memory allocation error.\n"); + apm_entry_t *e = apm_get_entry(entry->d_name); + if(e) { + fprintf(stderr, "\033[33mSkipping %s already loaded\033[0m\n", entry->d_name); + continue; } else { - e->apm_init_version = APM_INIT_VERSION; - strncpy(e->path, fullpath, WALKDIR_MAX_PATH_SIZE - 1); - size_t i = strlen(e->path); - while ((i > 0) && (e->path[i-1] != '/')) { - i--; + e = calloc(1, sizeof(apm_entry_t)); + if (!e) { + printf("Memory allocation error.\n"); + continue; } - e->file = &(e->path[i]); - // TODO Kevin: Verbose argument? - printf("\033[32mLoaded: %s\033[0m\n", fullpath); - apm_queue_add(e); } + e->apm_init_version = APM_INIT_VERSION; + strncpy(e->path, fullpath, WALKDIR_MAX_PATH_SIZE - 1); + size_t i = strlen(e->path); + while ((i > 0) && (e->path[i-1] != '/')) { + i--; + } + e->file = &(e->path[i]); + // TODO Kevin: Verbose argument? + printf("\033[32mLoaded: %s\033[0m\n", fullpath); + *loaded_count = *loaded_count + 1; + apm_queue_add(e); } } } } -int py_apm_load_cmd(struct slash *slash) { +int py_apm_load_cmd(struct slash *slash, unsigned int *loaded_count) { char * path = NULL; char * search_str = NULL; @@ -496,7 +505,7 @@ int py_apm_load_cmd(struct slash *slash) { fprintf(stderr, "main_thread_state is NULL\n"); return SLASH_EINVAL; } - walk_path_list(path, search_str, load_py); + walk_path_list(path, search_str, load_py, loaded_count); } if(free_path) { free(path); @@ -506,7 +515,7 @@ int py_apm_load_cmd(struct slash *slash) { } static wchar_t **handle_py_argv(char **args, int argc) { - wchar_t **res = calloc(sizeof(wchar_t *), argc); + wchar_t **res = calloc(argc, sizeof(wchar_t *)); if (res) { for (int i = 0; i < argc; i++) { res[i] = Py_DecodeLocale(args[i], NULL); @@ -515,13 +524,30 @@ static wchar_t **handle_py_argv(char **args, int argc) { return res; } + +/* NOTE: It appears that `PySys_SetArgv(argc, w_argv);` copies `wchar_t **w_argv`, + as freeing it doesn't give any Valgrind warnings. */ +static void py_argv_free(wchar_t **w_argv, int argc) { + + if (!w_argv || !*w_argv) { + return; + } + + for (int i = 0; i < argc; i++) { + PyMem_RawFree(w_argv[i]); + } + free(w_argv); +} + int csh_python_exec_string(const char *string, int argc, char **argv) { wchar_t **w_argv = handle_py_argv(argv, argc); #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" PySys_SetArgv(argc, w_argv); #pragma GCC diagnostic pop /* -Wdeprecated-declarations */ - return PyRun_SimpleString(string); + const int res = PyRun_SimpleString(string); + py_argv_free(w_argv, argc); + return res; } int csh_python_exec_file(const char *filename, int argc, char **argv) { FILE *fp = fopen(filename, "rb"); @@ -541,7 +567,9 @@ int csh_python_exec_file(const char *filename, int argc, char **argv) { Py_DECREF(file_str); } - return PyRun_AnyFileEx(fp, filename, 1); + const int res = PyRun_AnyFileEx(fp, filename, 1); + py_argv_free(w_argv, argc); + return res; } else { return -1; } @@ -595,6 +623,8 @@ static int python_slash(struct slash *slash) { PyRun_SimpleString("import rlcompleter"); PyRun_SimpleString("import readline"); PyRun_SimpleString("readline.parse_and_bind(\"tab: complete\")"); + PyRun_SimpleString("import sys"); + PyRun_SimpleString("if sys.base_prefix != sys.prefix or hasattr(sys, \"real_prefix\"): sys.ps1 = '(venv) >>> '"); res = PyRun_InteractiveLoop(stdin, ""); } @@ -617,5 +647,5 @@ static int python_slash(struct slash *slash) { "or execute the script in given file.\n"\ "This allows you to run pretty much any Python code, particularly code using PyCSH which allows for interacting\n"\ "with CSP nodes.\n\nUse \"Control-D\" to exit the interpreter and return to CSH." -slash_command_completer(python, python_slash, slash_path_completer, _PYTHON_ARGS, _PYTHON_HELP); -slash_command_completer(python3, python_slash, slash_path_completer, _PYTHON_ARGS, _PYTHON_HELP); // Alias +slash_command_completer(python, python_slash, slash_path_completer, _PYTHON_ARGS, _PYTHON_HELP) +slash_command_completer(python3, python_slash, slash_path_completer, _PYTHON_ARGS, _PYTHON_HELP) // Alias diff --git a/src/python/python_loader.h b/src/python/python_loader.h index 636e479e..8f4e5d75 100644 --- a/src/python/python_loader.h +++ b/src/python/python_loader.h @@ -10,7 +10,7 @@ void on_python_slash_execute_pre_hook(const char *line); void on_python_slash_execute_post_hook(const char *line, struct slash_command *command); extern void py_init_interpreter(void); -extern int py_apm_load_cmd(struct slash *slash); +extern int py_apm_load_cmd(struct slash *slash, unsigned int *loaded_count); extern bool exception_allowed; diff --git a/src/randombytes.c b/src/randombytes.c deleted file mode 100644 index e2ebd637..00000000 --- a/src/randombytes.c +++ /dev/null @@ -1,23 +0,0 @@ -/* - * randombytes.c - * - * Created on: 9. sep. 2020 - * Author: johan - */ - -#include -#include - -/* Required tweetnacl.c */ -void randombytes(unsigned char * a, unsigned long long c) { - // Note: Pseudo random since we are not initializing random! - time_t t; - srand((unsigned) time(&t) + rand()); - while(c > 0) { - *a = rand() & 0xFF; - a++; - c--; - } -} - - diff --git a/src/serial.c b/src/serial.c index 5ba0acb8..d3b5c4a0 100644 --- a/src/serial.c +++ b/src/serial.c @@ -2,6 +2,7 @@ #include #include +#include "csh_internals.h" uint32_t _serial0; diff --git a/src/slash_apm.c b/src/slash_apm.c index a78b28ab..266a0b91 100644 --- a/src/slash_apm.c +++ b/src/slash_apm.c @@ -39,6 +39,17 @@ static const int apm_init_version = APM_INIT_VERSION; static apm_entry_t * apm_queue = 0; typedef void (*info_t) (void); +apm_entry_t *apm_get_entry(const char *needle) { + apm_entry_t *res = NULL; + for (apm_entry_t * e = apm_queue; e; e = e->next) { + if (strcmp(e->file, needle) == 0) { + res = e; + break; + } + } + return res; +} + void apm_queue_add(apm_entry_t * e) { if (!e) { @@ -63,7 +74,7 @@ void apm_queue_add(apm_entry_t * e) { } -apm_entry_t * load_apm(const char * path) { +static apm_entry_t * load_apm(const char * path) { void * handle = dlopen(path, RTLD_NOW); if (!handle) @@ -100,7 +111,7 @@ apm_entry_t * load_apm(const char * path) { } -int initialize_apm(apm_entry_t * e) { +static int initialize_apm(apm_entry_t * e) { const int * apm_init_version_in_apm_ptr = dlsym(e->handle, "apm_init_version"); if (apm_init_version_in_apm_ptr == NULL) { @@ -158,7 +169,7 @@ typedef struct lib_search_s { lib_info_t libs[WALKDIR_MAX_ENTRIES]; } lib_search_t; -void init_info(lib_info_t * info, const char * path) { +static void init_info(lib_info_t * info, const char * path) { if (!info) { return; @@ -214,7 +225,7 @@ static void file_callback(const char * path_and_file, const char * last_entry, v /* Verify not already loaded */ for (apm_entry_t * e = apm_queue; e; e = e->next) { if (strcmp(e->file, last_entry) == 0) { - fprintf(stderr, "\033[33mWarn skipping %s already loaded\033[0m\n", last_entry); + fprintf(stderr, "\033[33mSkipping %s already loaded\033[0m\n", last_entry); return; } } @@ -241,7 +252,7 @@ static void file_callback(const char * path_and_file, const char * last_entry, v } -void build_apm_list(lib_search_t* lib_search) { +static void build_apm_list(lib_search_t* lib_search) { /* Clear search list */ lib_search->lib_count = 0; @@ -266,9 +277,12 @@ void build_apm_list(lib_search_t* lib_search) { } -int apm_load_search(lib_search_t *lib_search) { +static int apm_load_search(lib_search_t *lib_search) { - char path[WALKDIR_MAX_PATH_SIZE] = {0}; + char *path = calloc(1, WALKDIR_MAX_PATH_SIZE); + if(!path) { + return SLASH_ENOMEM; + } int search_bin_path = 0; if (lib_search->path == NULL) { @@ -293,7 +307,6 @@ int apm_load_search(lib_search_t *lib_search) { if (count == -1) { perror("readlink"); - lib_search->path = NULL; return SLASH_EUSAGE; } @@ -308,10 +321,6 @@ int apm_load_search(lib_search_t *lib_search) { build_apm_list(lib_search); - if (lib_search->lib_count == 0) { - printf("\033[31mNo APMs found in %s\033[0m\n", lib_search->path); - } - for (unsigned i = 0; i < lib_search->lib_count; i++) { apm_entry_t * e = load_apm(lib_search->libs[i].path); @@ -331,7 +340,6 @@ int apm_load_search(lib_search_t *lib_search) { apm_queue_add(e); printf("\033[32mLoaded: %s\033[0m\n", e->path); } - lib_search->path = NULL; return SLASH_SUCCESS; } @@ -358,9 +366,16 @@ static int apm_load_cmd(struct slash *slash) { int res = apm_load_search(&lib_search); optparse_del(parser); + bool free_path = lib_search.path == NULL; #ifdef HAVE_PYTHON - res = py_apm_load_cmd(slash); + res = py_apm_load_cmd(slash, &lib_search.lib_count); #endif + if (lib_search.lib_count == 0) { + printf("\033[31mNo new APMs loaded from %s\033[0m\n", lib_search.path); + } + if (free_path) { + free(lib_search.path); + } return res; } @@ -404,7 +419,7 @@ slash_command_sub(apm, info, apm_info_cmd, "", "Information on APMs"); static char doc_folder[256] = "/usr/share/si-csh"; -void doc_found_cb(const char *a, const char *b, void *ctx) { +static void doc_found_cb(const char *a, const char *b, void *ctx) { size_t len = strlen(b); if(len > 4) { if (b[len - 1] == 'f' && @@ -454,7 +469,7 @@ struct manual_entry { SLIST_ENTRY(manual_entry) list; }; -void manual_cb(const char *a, const char *b, void *ctx) { +static void manual_cb(const char *a, const char *b, void *ctx) { struct manual_list *manuals = (struct manual_list *)ctx; size_t len = strlen(b); if(len > 4) { diff --git a/src/slash_apm.h b/src/slash_apm.h index 89fff8db..32918338 100644 --- a/src/slash_apm.h +++ b/src/slash_apm.h @@ -9,6 +9,7 @@ typedef int (*libmain_t)(void); typedef void (*libinfo_t)(void); typedef struct apm_entry_s apm_entry_t; extern void apm_queue_add(apm_entry_t * e); +apm_entry_t *apm_get_entry(const char *needle); struct apm_entry_s { void * handle; diff --git a/src/slash_eth.c b/src/slash_eth.c index 8360d2b7..343f8a22 100644 --- a/src/slash_eth.c +++ b/src/slash_eth.c @@ -57,7 +57,7 @@ static int eth_debug_toggle(struct slash *slash) slash_command_sub(eth, debug, eth_debug_toggle, "", "Toggle ethernet debugging"); -int eth_init_check(char * device) { +static int eth_init_check(char * device) { static int sockfd; struct ifreq if_idx; struct ifreq if_mac; @@ -140,7 +140,7 @@ int eth_init_check(char * device) { } -static void eth_list_interfaces() +static void eth_list_interfaces(void) { // Create link of interface adresses struct ifaddrs *addresses; diff --git a/src/slash_hooks.c b/src/slash_hooks.c index 0a25daaf..e9e8063e 100644 --- a/src/slash_hooks.c +++ b/src/slash_hooks.c @@ -4,6 +4,8 @@ #include "python/python_loader.h" #endif +#include + void slash_on_execute_hook(const char *line) { on_loki_slash_execute_hook(line); #ifdef HAVE_PYTHON diff --git a/src/slash_run_environment.c b/src/slash_run_environment.c index 08a8cc51..b1ca54e5 100644 --- a/src/slash_run_environment.c +++ b/src/slash_run_environment.c @@ -1,6 +1,7 @@ #include #include #include +#include #include diff --git a/src/slash_utils.c b/src/slash_utils.c index cd71a59d..61e531d5 100644 --- a/src/slash_utils.c +++ b/src/slash_utils.c @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -12,3 +13,48 @@ optparse_opt_t *csh_add_node_option(optparse_t * parser, unsigned int *node) { get_host_by_addr_or_name, node); } +int parse_vmem_address(struct slash *slash, int *argi, uint64_t *address) { + + if (++(*argi) >= slash->argc) { + printf("missing address\n"); + return SLASH_EUSAGE; + } + + char * endptr; + *address = strtoul(slash->argv[*argi], &endptr, 16); + if (*endptr != '\0') { + printf("Failed to parse address\n"); + return SLASH_EINVAL; + } + + return SLASH_SUCCESS; +} + +int parse_length(struct slash *slash, int *argi, uint32_t *length) { + + if (++(*argi) >= slash->argc) { + printf("missing length\n"); + return SLASH_EINVAL; + } + + char * endptr; + uint64_t length64; + length64 = strtoul(slash->argv[*argi], &endptr, 10); + if (*endptr != '\0') { + length64 = strtoul(slash->argv[*argi], &endptr, 16); + if (*endptr != '\0' || strncmp(slash->argv[*argi], "0x", 2) != 0) { + printf("Failed to parse length in base 10 or base 16\n"); + return SLASH_EUSAGE; + } + + } + + if (length64 > UINT32_MAX) { + printf("Length is too large\n"); + return SLASH_EINVAL; + } + + *length = (uint32_t)length64; + + return SLASH_SUCCESS; +} diff --git a/src/spaceboot_slash.c b/src/spaceboot_slash.c index 73c49a6b..5401f5c9 100644 --- a/src/spaceboot_slash.c +++ b/src/spaceboot_slash.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "csh_internals.h" @@ -58,6 +59,11 @@ static void reset_to_flash(int node, int flash, int times, int ms) { } } + if (flash < 0 || flash >= NUM_SLOTS) { + printf(" Invalid slot number %d\n", flash); + return; + } + printf(" Switching to flash %d\n", flash); printf(" Will run this image %d times\n", times); @@ -139,12 +145,19 @@ static int image_get(char * filename, char ** data, int * len) { struct stat file_stat; fstat(fd->_fileno, &file_stat); + /* Protect against return codes thrown by fread, and sizes that cannot possibly be valid FW */ + if (file_stat.st_size > __INT32_MAX__ || file_stat.st_size < 4) { + return -2; + } + /* Copy to memory: * Note we ignore the memory leak because the application will terminate immediately after using the data */ *data = malloc(file_stat.st_size); - *len = fread(*data, 1, file_stat.st_size, fd); + size_t length = fread(*data, 1, file_stat.st_size, fd); fclose(fd); + *len = length; + return 0; } @@ -181,7 +194,7 @@ static char wpath[WALKDIR_MAX_PATH_SIZE]; // C21: 4, E70: 2C4 static const uint32_t entry_offsets[] = { 4, 0x2c4 }; -bool is_valid_binary(const char * path, struct bin_info_t * binf, bin_file_ident_t * binf_ident) +static bool is_valid_binary(const char * path, struct bin_info_t * binf, bin_file_ident_t * binf_ident) { binf_ident->valid = false; @@ -275,13 +288,50 @@ static void file_callback(const char * path, const char * last_entry, void * cus } } +static int do_upload(int node, unsigned int timeout, int address, char * data, int len) { + int res = SLASH_SUCCESS; + uint32_t time_begin = csp_get_ms(); + int count = vmem_upload(node, timeout, address, data, len, 1); + uint32_t time_total = csp_get_ms() - time_begin; + + printf(" - %.0f K\n", (count / 1024.0)); + switch (count) { + case CSP_ERR_TIMEDOUT: + printf("Connection timeout\n"); + res = SLASH_EIO; + break; + case CSP_ERR_NOBUFS: + printf("No more CSP buffers\n"); + res = SLASH_ENOMEM; + break; + default: + { + if(count != len){ + unsigned int window_size = 0; + csp_rdp_get_opt(&window_size, NULL, NULL, NULL, NULL, NULL); + uint32_t suggested_offset = 0; + if(count > (((signed int)window_size + 1) * VMEM_SERVER_MTU)) { + suggested_offset = (count) - ((window_size + 1) * VMEM_SERVER_MTU); + } + printf("Upload didn't complete, suggested offset to resume: %"PRIu32"\n", suggested_offset); + res = SLASH_EIO; + } else { + printf("Uploaded %"PRIu32" bytes in %.03f s at %"PRIu32" Bps\n", count, time_total / 1000.0, (uint32_t)(count / ((float)time_total / 1000.0)) ); + res = SLASH_SUCCESS; + } + } + break; + } + return res; +} + static int upload_and_verify(int node, int address, char * data, int len) { unsigned int timeout = 10000; printf(" Upload %u bytes to node %u addr 0x%x\n", len, node, address); - int res = vmem_upload(node, timeout, address, data, len, 1); + int res = do_upload(node, timeout, address, data, len); if(res < 0){ - return SLASH_EINVAL; + return res; } char * datain = malloc(len); @@ -438,7 +488,10 @@ static int slash_csp_program(struct slash * slash) { crc = csp_crc32_memory((const uint8_t *)data, len); printf(" File CRC32: 0x%08"PRIX32"\n", crc); printf(" Upload %u bytes to node %u addr 0x%"PRIX32"\n", len, node, vmem.vaddr); - vmem_upload(node, 10000, vmem.vaddr, data, len, 1); + result = do_upload(node, 10000, vmem.vaddr, data, len); + if(result < 0){ + return result; + } uint32_t crc_node; int res = vmem_client_calc_crc32(node, 10000, vmem.vaddr, len, &crc_node, 1); if (res >= 0) { @@ -614,7 +667,11 @@ static int slash_sps(struct slash * slash) { crc = csp_crc32_memory((const uint8_t *)data, len); printf(" File CRC32: 0x%08"PRIX32"\n", crc); printf(" Upload %u bytes to node %u addr 0x%"PRIX32"\n", len, node, vmem.vaddr); - vmem_upload(node, 10000, vmem.vaddr, data, len, 1); + result = do_upload(node, 10000, vmem.vaddr, data, len); + if(result < 0){ + return result; + } + uint32_t crc_node; int res = vmem_client_calc_crc32(node, 10000, vmem.vaddr, len, &crc_node, 1); if (res >= 0) { @@ -649,4 +706,4 @@ static int slash_sps(struct slash * slash) { return result; } -slash_command(sps, slash_sps, " [filename]", "switch program switch"); \ No newline at end of file +slash_command(sps, slash_sps, " [filename]", "switch program switch"); diff --git a/src/spacebridge.c b/src/spacebridge.c index fcd5575a..30d7c40f 100644 --- a/src/spacebridge.c +++ b/src/spacebridge.c @@ -11,7 +11,7 @@ #include -void usage(void) +static void usage(void) { printf("usage: spacebridge\n"); printf("\n"); diff --git a/src/stdbuf_client.c b/src/stdbuf_client.c index 666bf03e..22cd5822 100644 --- a/src/stdbuf_client.c +++ b/src/stdbuf_client.c @@ -194,18 +194,25 @@ static int stdbuf_v2(unsigned int node, unsigned int timeout, char * logfile) { packet->length = 1; csp_send(conn, packet); + #define LF '\n' /* ASCII 10d */ + #define CR '\r' /* ASCII 13d */ while ((packet = csp_read(conn, timeout))) { - //csp_hex_dump("stdbuf", &packet->data[1], packet->length - 1); - int ignore __attribute__((unused)) = write(fileno(stdout), &packet->data[1], packet->length - 1); + int again = packet->data[0]; + int ignore __attribute__((unused)); + ignore = write(fileno(stdout), &packet->data[1], packet->length - 1); + if (again == 0 && packet->data[packet->length - 1] != LF) { + /* If this was the last packet and there was no LF we will add one here to flush the stdout file */ + ignore = write(fileno(stdout), "\n", 1); + } if (log_f) { int i = 1; while (i < packet->length) { if (isprint(packet->data[i])) { fprintf(log_f, "%c", packet->data[i]); i++; - } else if ((packet->data[i] == '\r') || (packet->data[i] == '\n')) { - fprintf(log_f, "\n"); - while ((i < packet->length) && ((packet->data[i] == '\r') || (packet->data[i] == '\n'))) { + } else if ((packet->data[i] == CR) || (packet->data[i] == LF)) { + fprintf(log_f, "%c", LF); + while ((i < packet->length) && ((packet->data[i] == CR) || (packet->data[i] == LF))) { i++; } } else { @@ -216,7 +223,6 @@ static int stdbuf_v2(unsigned int node, unsigned int timeout, char * logfile) { } } - int again = packet->data[0]; csp_buffer_free(packet); if (again == 0) { diff --git a/src/victoria_metrics.c b/src/victoria_metrics.c index 8eee705c..ab738a31 100644 --- a/src/victoria_metrics.c +++ b/src/victoria_metrics.c @@ -21,6 +21,7 @@ #include #include "param_sniffer.h" #include "url_utils.h" +#include "victoria_metrics.h" static pthread_t vm_push_thread; int vm_running = 0; @@ -48,7 +49,7 @@ static size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdat return size * nmemb; } -void * vm_push(void * arg) { +static void * vm_push(void * arg) { vm_args * args = arg; diff --git a/src/vmem_client_slash.c b/src/vmem_client_slash.c index 092e8838..1a8d114d 100644 --- a/src/vmem_client_slash.c +++ b/src/vmem_client_slash.c @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -50,167 +51,107 @@ static int vmem_client_slash_list(struct slash *slash) } slash_command(vmem, vmem_client_slash_list, "", "List virtual memory"); -#if 0 -static int vmem_client_slash_fram(struct slash *slash, int backup) { +static int vmem_client_slash_decompress(struct slash *slash) { - int node = 0; - int vmem_id; - int timeout = 2000; - char * endptr; + int res = SLASH_SUCCESS; - if (slash->argc < 2) - return SLASH_EUSAGE; - - vmem_id = strtoul(slash->argv[1], &endptr, 10); - if (*endptr != '\0') - return SLASH_EUSAGE; - - if (slash->argc >= 3) { - node = strtoul(slash->argv[2], &endptr, 10); - if (*endptr != '\0') - return SLASH_EUSAGE; - } - - if (slash->argc >= 4) { - timeout = strtoul(slash->argv[3], &endptr, 10); - if (*endptr != '\0') - return SLASH_EUSAGE; - } - - if (backup) { - printf("Taking backup of vmem %u on node %u\n", vmem_id, node); - } else { - printf("Restoring vmem %u on node %u\n", vmem_id, node); - } - - int result = vmem_client_backup(node, vmem_id, timeout, backup); - if (result == -2) { - printf("No response\n"); - } else { - printf("Result: %d\n", result); - } - - return SLASH_SUCCESS; -} - -static int vmem_client_slash_restore(struct slash *slash) -{ - return vmem_client_slash_fram(slash, 0); -} -slash_command_sub(vmem, restore, vmem_client_slash_restore, " [node] [timeout]", NULL); - -static int vmem_client_slash_backup(struct slash *slash) -{ - return vmem_client_slash_fram(slash, 1); -} -slash_command_sub(vmem, backup, vmem_client_slash_backup, " [node] [timeout]", NULL); - -static int vmem_client_slash_unlock(struct slash *slash) -{ - int node = 0; - int timeout = 2000; - char * endptr; - - if (slash->argc >= 2) { - node = strtoul(slash->argv[1], &endptr, 10); - if (*endptr != '\0') - return SLASH_EUSAGE; - } - - if (slash->argc >= 3) { - timeout = strtoul(slash->argv[2], &endptr, 10); - if (*endptr != '\0') - return SLASH_EUSAGE; + unsigned int node = slash_dfl_node; + unsigned int timeout = slash_dfl_timeout; + unsigned int version = 1; + + uint64_t src_address; + uint64_t dst_address; + uint32_t length; + + optparse_t * parser = optparse_new("vmem decompress", " [length base10 or base16]"); + optparse_add_help(parser); + csh_add_node_option(parser, &node); + optparse_add_unsigned(parser, 't', "timeout", "NUM", 0, &timeout, "timeout (default = )"); + optparse_add_unsigned(parser, 'v', "version", "NUM", 0, &version, "version (default = 1)"); + + int argi = optparse_parse(parser, slash->argc - 1, (const char **) slash->argv + 1); + if (argi < 0) { + res = SLASH_EINVAL; + goto failure; } - /* Step 0: Prepare request */ - csp_conn_t * conn = csp_connect(CSP_PRIO_HIGH, node, VMEM_PORT_SERVER, timeout, CSP_O_NONE); - if (conn == NULL) - return SLASH_EINVAL; + res = parse_vmem_address(slash, &argi, &src_address); + if (res < 0) { goto failure; } - csp_packet_t * packet = csp_buffer_get(sizeof(vmem_request_t)); - if (packet == NULL) - return SLASH_EINVAL; + res = parse_vmem_address(slash, &argi, &dst_address); + if (res < 0) { goto failure; } - vmem_request_t * request = (void *) packet->data; - request->version = 1; - request->type = VMEM_SERVER_UNLOCK; - packet->length = sizeof(vmem_request_t); + res = parse_length(slash, &argi, &length); + if (res < 0) { goto failure; } - /* Step 1: Check initial unlock code */ - request->unlock.code = htobe32(0x28140360); + res = vmem_client_decompress(node, timeout, src_address, dst_address, length, version); - csp_send(conn, packet); - - /* Step 2: Wait for verification sequence */ - if ((packet = csp_read(conn, timeout)) == NULL) { - csp_close(conn); - return SLASH_EINVAL; + if (res < 0) { + if (res == -1) { + res = SLASH_ENOMEM; + } else if (res == -2) { + printf("\033[31m\n"); + printf("Timed out on codec response\n"); + printf("\033[0m\n"); + res = SLASH_EIO; + } } - request = (void *) packet->data; - uint32_t sat_verification = be32toh(request->unlock.code); - - printf("Verification code received: %x\n\n", (unsigned int) sat_verification); - - printf("************************************\n"); - printf("* WARNING WARNING WARNING WARNING! *\n"); - printf("* You are about to unlock the FRAM *\n"); - printf("* Please understand the risks *\n"); - printf("* Abort now by typing CTRL + C *\n"); - printf("************************************\n"); +failure: + optparse_del(parser); + return res; +} +slash_command_sub(vmem, decompress, vmem_client_slash_decompress, " ", "Perform decompression from src into dst"); - /* Step 2a: Ask user to input sequence */ - uint32_t user_verification; - printf("Type verification sequence (you have <30 seconds): \n"); +static int vmem_client_slash_compress(struct slash *slash) { - char readbuf[9] = {}; - int cnt = 0; - while(cnt < 8) { - cnt += read(0, readbuf + cnt, 8-cnt); - } - if (sscanf(readbuf, "%x", (unsigned int *) &user_verification) != 1) { - printf("Could not parse input\n"); - return SLASH_EINVAL; - } + int res = SLASH_SUCCESS; - printf("User input: %x\n", (unsigned int) user_verification); - if (user_verification != sat_verification) { - csp_buffer_free(packet); - csp_close(conn); - return SLASH_EINVAL; + unsigned int node = slash_dfl_node; + unsigned int timeout = slash_dfl_timeout; + unsigned int version = 1; + + uint64_t src_address; + uint64_t dst_address; + uint32_t length; + + optparse_t * parser = optparse_new("vmem compress", " [length base10 or base16]"); + optparse_add_help(parser); + csh_add_node_option(parser, &node); + optparse_add_unsigned(parser, 't', "timeout", "NUM", 0, &timeout, "timeout (default = )"); + optparse_add_unsigned(parser, 'v', "version", "NUM", 0, &version, "version (default = 1)"); + + int argi = optparse_parse(parser, slash->argc - 1, (const char **) slash->argv + 1); + if (argi < 0) { + res = SLASH_EINVAL; + goto failure; } - /* Step 2b: Ask for final confirmation */ - printf("Validation sequence accepted\n"); + res = parse_vmem_address(slash, &argi, &src_address); + if (res < 0) { goto failure; } - printf("Are you sure [Y/N]?\n"); - - cnt = read(0, readbuf, 1); - if (readbuf[0] != 'Y') { - csp_buffer_free(packet); - csp_close(conn); - return SLASH_EINVAL; - } + res = parse_vmem_address(slash, &argi, &dst_address); + if (res < 0) { goto failure; } - /* Step 3: Send verification sequence */ - request->unlock.code = htobe32(user_verification); + res = parse_length(slash, &argi, &length); + if (res < 0) { goto failure; } - csp_send(conn, packet); + res = vmem_client_compress(node, timeout, src_address, dst_address, length, version); - /* Step 4: Check for result */ - if ((packet = csp_read(conn, timeout)) == NULL) { - csp_close(conn); - return SLASH_EINVAL; + if (res < 0) { + if (res == -1) { + res = SLASH_ENOMEM; + } else if (res == -2) { + printf("\033[31m\n"); + printf("Timed out on codec response\n"); + printf("\033[0m\n"); + res = SLASH_EIO; + } } - request = (void *) packet->data; - uint32_t result = be32toh(request->unlock.code); - printf("Result: %x\n", (unsigned int) result); - - csp_close(conn); - return SLASH_SUCCESS; - +failure: + optparse_del(parser); + return res; } -slash_command_sub(vmem, unlock, vmem_client_slash_unlock, "[node] [timeout]", NULL); -#endif \ No newline at end of file +slash_command_sub(vmem, compress, vmem_client_slash_compress, " ", "Perform compression from src into dst"); + diff --git a/src/vmem_client_slash_ftp.c b/src/vmem_client_slash_ftp.c index 40261e2f..d90bb95b 100644 --- a/src/vmem_client_slash_ftp.c +++ b/src/vmem_client_slash_ftp.c @@ -65,21 +65,11 @@ static int vmem_client_slash_download(struct slash *slash) return SLASH_EUSAGE; } - /* Expect length */ - if (++argi >= slash->argc) { - printf("missing length\n"); - optparse_del(parser); - return SLASH_EINVAL; - } - - uint32_t length = strtoul(slash->argv[argi], &endptr, 10); - if (*endptr != '\0') { - length = strtoul(slash->argv[argi], &endptr, 16); - if (*endptr != '\0') { - printf("Failed to parse length in base 10 or base 16\n"); - optparse_del(parser); - return SLASH_EUSAGE; - } + uint32_t length; + int result = parse_length(slash, &argi, &length); + if (result < 0) { + optparse_del(parser); + return result; } /* Expect filename */ diff --git a/src/vts.c b/src/vts.c index 9bb97f66..b9352b9e 100644 --- a/src/vts.c +++ b/src/vts.c @@ -11,6 +11,7 @@ #include #include #include "param_sniffer.h" +#include "vts.h" static int adcs_node = 0; static char *default_ip = "127.0.0.1"; diff --git a/src/zmqproxy.c b/src/zmqproxy.c index e6c819fc..8123c8b5 100644 --- a/src/zmqproxy.c +++ b/src/zmqproxy.c @@ -114,7 +114,7 @@ static int get_monitor_event(void * monitor, int * value, char ** address) { return event; } -void handle_event(int event, int value, char *address){ +static void handle_event(int event, int value, char *address){ switch (event) { case ZMQ_EVENT_ACCEPTED: { diff --git a/tests/hk_sniffer_host.c b/tests/hk_sniffer_host.c new file mode 100644 index 00000000..8677a214 --- /dev/null +++ b/tests/hk_sniffer_host.c @@ -0,0 +1,95 @@ +/* + * hk_sniffer_host.c - reproduce (and verify the fix for) the hk_param_sniffer() crash. + * + * Bug: hk_param_sniffer() accepts any packet whose CSP *source* port is 13. The DIPP + * ring_size / observation_meta RPC replies also use sport 13, and they are short RDP + * frames. The length math + * size_t data_len = packet->length - 5 - ((flags & CSP_FRDP) ? 5 : 0); + * is UNSIGNED, so a short packet underflows data_len to ~SIZE_MAX. The mpack reader is + * then pointed at buffer..buffer+SIZE_MAX and walks off the packet -> SIGSEGV. + * + * This calls the REAL hk_param_sniffer() (compiled from ../src/hk_param_sniffer.c) on + * crafted short sport-13 packets, inside a forked child so a crash is *detected* rather + * than killing the test. Child dies by signal (SIGSEGV / SIGALRM hang) => bug present; + * child returns cleanly => the length guard is in place and working. + */ +#include +#include +#include +#include +#include +#include + +#include +#include "param_sniffer.h" /* real prototypes for the two stubs below */ +#include "hk_param_sniffer.h" + +/* Stubs for the two param_sniffer.c symbols hk_param_sniffer references. Neither is on + * the crash path. The real param_sniffer_crc is a no-op unless CSP_FCRC32 is set (our + * packets don't set it), so returning 0 is faithful to production behaviour. */ +int param_sniffer_crc(csp_packet_t *packet) { (void)packet; return 0; } +int param_sniffer_log(void *ctx, param_queue_t *queue, param_t *param, int offset, + void *reader, csp_timestamp_t *timestamp) { + (void)ctx; (void)queue; (void)param; (void)offset; (void)reader; (void)timestamp; + return 0; +} + +/* Run hk_param_sniffer(packet) in a child. A short non-param packet must be SKIPPED + * (return false). The buggy code instead enters the underflowed read loop and either + * crashes/hangs or, if it survives the walk, returns true having "processed" garbage. + * So: child crash/hang OR return==true => bug; return==false => correctly skipped. + * Returns 1 if buggy, 0 if correctly skipped. */ +static int run_child(const char *label, csp_packet_t *packet) { + printf("%-58s", label); + fflush(stdout); + pid_t pid = fork(); + if (pid == 0) { + freopen("/dev/null", "w", stdout); /* silence the per-iteration HK warnings */ + alarm(5); /* backstop an infinite read loop */ + _exit(hk_param_sniffer(packet) ? 2 : 0); /* 2 = processed (bug), 0 = skipped */ + } + int status = 0; + waitpid(pid, &status, 0); + if (WIFSIGNALED(status)) { + int s = WTERMSIG(status); + printf("BUG: crash (signal %d, %s)\n", s, + s == SIGSEGV ? "SIGSEGV" : s == SIGALRM ? "hang/SIGALRM" : "other"); + return 1; + } + if (WEXITSTATUS(status) != 0) { + printf("BUG: processed a short non-param packet (returned true)\n"); + return 1; + } + printf("skipped (returned false) -- correct\n"); + return 0; +} + +static csp_packet_t *make_pkt(uint16_t sport, uint8_t flags, uint16_t length) { + csp_packet_t *p = calloc(1, sizeof(*p)); /* data[CSP_BUFFER_SIZE] is inline + zeroed */ + p->id.sport = sport; + p->id.dport = 14; /* sniffer keys on sport, not dport */ + p->id.src = 5423; /* a DIPP node, as in the live capture */ + p->id.flags = flags; + p->length = length; + return p; +} + +int main(void) { + int bug = 0; + printf("hk_sniffer_host: feeding short sport-13 packets to the real hk_param_sniffer()\n\n"); + + /* length 8, RDP: 8 - 5 - 5 underflows. This is the DIPP ring_size reply shape. */ + bug |= run_child("short RDP packet, sport 13 (len=8, the DIPP reply):", make_pkt(13, CSP_FRDP, 8)); + /* length 3, non-RDP: 3 - 5 underflows. */ + bug |= run_child("short non-RDP packet, sport 13 (len=3):", make_pkt(13, 0, 3)); + /* control: not sport 13 -> must be skipped immediately, never crashes. */ + bug |= run_child("non-13 sport, RDP (len=8, must be skipped):", make_pkt(40, CSP_FRDP, 8)); + + if (bug) { + printf("\nhk_sniffer_host: FAIL -- hk_param_sniffer crashed/hung on a short sport-13 packet.\n"); + printf("This is the data_len size_t underflow at hk_param_sniffer.c:215.\n"); + return 1; + } + printf("\nhk_sniffer_host: PASS -- short sport-13 packets are skipped, no underflow walk-off.\n"); + return 0; +} diff --git a/tests/meson.build b/tests/meson.build index 07247b15..63091644 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -36,3 +36,13 @@ set_array_tests = executable( dependencies: [csp_dep, slash_dep, param_dep, utils_lib_dep], ) test('set_array_tests', set_array_tests) + +# Regression for the hk_param_sniffer() data_len underflow segfault (short sport-13 +# DIPP RPC replies). Compiles the real hk_param_sniffer.c; crash is caught via fork(). +hk_sniffer_tests = executable( + 'hk_sniffer_tests', + sources: ['hk_sniffer_host.c', '../src/hk_param_sniffer.c'], + include_directories: ['../src'], + dependencies: [csp_dep, slash_dep, param_dep, utils_lib_dep], +) +test('hk_sniffer_tests', hk_sniffer_tests) diff --git a/yaml/ipp/module_config.yaml b/yaml/ipp/module_config.yaml deleted file mode 100644 index 05c8e6d2..00000000 --- a/yaml/ipp/module_config.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# Use following keys for type # -# bool = 2 # -# int = 3 # -# float = 4 # -# string = 5 # - -- key: effort - type: 3 - value: 7 - -- key: resampling - type: 3 - value: 1 - -- key: distance - type: 4 - value: 1.0 \ No newline at end of file diff --git a/yaml/ipp/pipeline_config.yaml b/yaml/ipp/pipeline_config.yaml deleted file mode 100644 index 95e07b72..00000000 --- a/yaml/ipp/pipeline_config.yaml +++ /dev/null @@ -1,7 +0,0 @@ -- order: 1 - param_id: 1 - name: demosaic - -- order: 2 - param_id: 3 - name: encoder \ No newline at end of file diff --git a/zmq_dipp.csh b/zmq_dipp.csh deleted file mode 100644 index b3f6bb6f..00000000 --- a/zmq_dipp.csh +++ /dev/null @@ -1,3 +0,0 @@ -csp init -csp add zmq -d 10 localhost -apm load