thin-edge / thin-edge.io

The open edge framework for lightweight IoT devices
https://thin-edge.io
Apache License 2.0
219 stars 54 forks source link

custom operation handler does not receive full text input as first argument #1687

Closed reubenmiller closed 1 year ago

reubenmiller commented 1 year ago

Describe the bug

When implementing a custom operation, the mqtt message is not passed to the custom operation handler in its entirity. Most notably if the custom operation contains a newline (\n) characters. In this case only the first line is passed to the handler.

This is best observed when implementing a custom Cumulocity IoT c8y_Command handler. The handler receives a multi-lined command from the Cumulocity IoT Shell interface, and the script only receives the first line.

To Reproduce

  1. Add the following files with the listed contents to the device running thin-edge.io

    file: /etc/tedge/operations/c8y/c8y_Command

    [exec]
    topic = "c8y/s/ds"
    on_message = "511"
    command = "/etc/tedge/operations/command_handler.sh"

    file: /etc/tedge/operations/command_handler.sh

    #!/bin/bash
    
    info() {
        echo "$(date --iso-8601=seconds 2>/dev/null || date -Iseconds) : INFO : $*" >&2
    }
    
    # Parse the smart rest message, ignore the first two field, and everything afterwards is the command
    COMMAND="${1#*,*,}"
    
    # Check if command is wrapped with quotes, if so then remove them
    if [[ "$COMMAND" == \"*\" ]]; then
        COMMAND="${COMMAND:1:-1}"
    fi
    
    info "Raw command: $*"
    info "Executing command: $COMMAND"
    bash -c "$COMMAND"
    EXIT_CODE=$?
    if [ $EXIT_CODE -ne 0 ]; then
        info "Command returned a non-zero exit code. code=$EXIT_CODE"
    fi
    exit $EXIT_CODE
  2. Make sure the command_handler.sh script is executable

    chmod a+x /etc/tedge/operations/command_handler.sh
  3. Restart the tedge-agent and mapper

    sudo systemctl restart tedge-agent
    sudo tedge reconnect c8y
    
    # Or if you don't have a recent version of tedge that supports `reconnect`
    sudo tedge disconnect c8y && sudo tedge connect c8y
  4. Create an c8y_Command operation with the following values`

    {
        "description": "Execute shell command",
        "c8y_Command": {
            "text": "echo hello;\necho world"
        }
    }
  5. Wait for the operation to be set to SUCCESSFUL

  6. Inspect the operation, and the c8y_Command.result should be exactly hello\nworld\n (see below for the full operation snippet)

    {
        "description": "Execute shell command",
        "c8y_Command": {
            "result": "hello\nworld\n",
            "text": "echo hello;\necho world"
        }
    }

Expected behavior

The handler should be given the full command passed as the first argument to the custom operation handler.

Screenshots

image

Environment (please complete the following information):

Additional context

The same produre was executed but with the debug line uncommented, and the following output showing how the handler was called

Debug: Custom operation: script=/etc/tedge/operations/command_handler.sh, args=511,TST-trim_eager_poltergeist,"echo hello
hello

And below shows the full raw json of the operation after it was set to SUCCESSFUL

{
  "c8y_Command": {
    "result": "Debug: Custom operation: script=/etc/tedge/operations/command_handler.sh, args=511,TST-trim_eager_poltergeist,\"echo hello\nhello\n",
    "text": "echo hello\necho world"
  },
  "creationTime": "2023-01-18T11:53:49.954Z",
  "description": "Execute shell command",
  "deviceId": "55477984",
  "deviceName": "TST-trim_eager_poltergeist",
  "id": "478470",
  "self": "https://t12345.dummy.c8y.io/devicecontrol/operations/478470",
  "status": "SUCCESSFUL"
}
rina23q commented 1 year ago

The bug is fixed by #1692. I have tested manually with the shell script that @reubenmiller provided above and the following inputs and outputs.

# How to read the following the test cases.
<user input on c8y>
=>
<expected output recorded on c8y as RESPONSE>
1.
echo hello
echo world
=>
hello
world

2.
echo "helloworld"
=>
helloworld

3.
echo "hello"; echo "world"
=>
hello
world

4.
echo hello; echo world
=>
hello
world

5.
echo hello,world
=>
hello,world

6.
echo \"hello\"
=>
"hello"

7.
echo "testme" 
echo "test again"
=>
testme
test again

8.
echo "line 1";
echo "line 2";
echo "line, 3";
=>
line 1
line 2
line, 3