Problem or bug in API

We have variable “status” in environment.

Run the command by API:

>>> /system/script/environment/set
>>> =numbers=status
>>> =value=1465304159
Check read_len, got '5'
recv 5
<<< !done
Check read_len, got '0'

>>> /ip/firewall/address-list/add
>>> =address=10.20.20.253
>>> =list=accept
Check read_len, got '5'
recv 5
<<< !done
Check read_len, got '8'
recv 8
<<< =ret=*3C
Check read_len, got '0'

We dont have variable “status” in environment.
Run the command:

>>> /system/script/environment/set
>>> =numbers=status
>>> =value=1465304300
Check read_len, got '5'
recv 5
<<< !trap
Check read_len, got '21'
recv 21
<<< =message=no such item
Check read_len, got '0'

>>> /ip/firewall/address-list/add
>>> =address=10.20.20.253
>>> =list=accept
Check read_len, got '5'
recv 5
<<< !done
Check read_len, got '0'

Command “/ip/firewall/address-list/add” is not apply…

P.S.
version: 6.35.1 (stable)
cpu: MIPS 24Kc V7.4

What about

/set
=name=status
=value=1465304159

(assuming the variable already exists)

or

/global
=name=status
=value=1465304159

(if it doesn’t)

Not variable problem. It is taken as an example.

The problem is that the subsequent instruction is not satisfied …
More accurately - it will be done after the following command:

>>> /system/script/environment/set
>>> =numbers=status
>>> =value=1465363657
Check read_len, got '5'
recv 5
<<< !trap
Check read_len, got '21'
recv 21
<<< =message=no such item
Check read_len, got '0'

>>> /ip/firewall/address-list/add
>>> =address=10.20.20.253
>>> =list=accept
Check read_len, got '5'
recv 5
<<< !done
Check read_len, got '0'


>>> /system/script/environment/print
Check read_len, got '5'
recv 5
<<< !done
Check read_len, got '8'
recv 8
<<< =ret=*3E
Check read_len, got '0'

Right. The “/system/environment/print” is the one that fails… But what I’m saying is that it fails because the command

/system/script/environment/set
=numbers=status
=value=1465363657

Fails.

The failure is detectable - that’s what the !trap is, i.e. the part

!trap
=message=no such item

is telling you that the “set” failed, supposedly because the variable doesn’t exist prior to the call.

And if you want to create the variable, you need to use “/global” instead of “/system/script/environment/set”.

The problem is not the creation of a variable. The problem is the following command after creating the variable.

If the command has caused the error, execution of subsequent commands via the API becomes problematic.

You cannot get the value if you have not set it in the first place. No variable - no value to get.

Aaah. I see it now. You’re intentionally causing a !trap to demonstrate its handling…

But I’m not sure the bug is in RouterOS. It looks more like a bug of either your API client of choice (which one is it anyway?) or the code you’re using around it.

When there are errors, RouterOS returns a !trap reply AND a !done reply, not just a !trap reply. At the same time, a command is only REALLY executed when you receive its !done reply. What your code does is that it sends two commands, but only keeps reading until it gets one !done reply, and it should instead keep reading until it gets two !done replies.

How exactly should you solve this depends on whether the bug is in the API client or your code. Show us the code you’re using.

Using PERL API based on https://github.com/akschu/MikroTikPerl, with my rework.


sub read_byte{
    my $line;

    if ($error_msg) {return 0}

    eval {
        local $SIG{__DIE__} = 'IGNORE';
        local $SIG{ALRM} = sub {die};
        alarm 5;
        eval {
            $sock->read($line,1);
        };
        alarm 0;
    };
    alarm 0;

    if ($!){
    $error_msg=$!;
    if ($debug > 4)
    {
        print "\nERROR: $!\n";
    }
    return 0;
    }

    return ord($line);
}

sub read_len {
    my $len;

    if ($debug > 4) {print "Check read_len"}
    $len = read_byte();
    if ($debug > 4) {print ", got '$len'\n"}

    if ($len==0) {
    $error_len++;
    if ($error_len>3){$error_msg='connection lost'}
    } else {$error_len=0}

    if (($len & 0x80) == 0x00)
    {
    return $len
    }
    elsif (($len & 0xC0) == 0x80)
    {
        $len &= ~0x80;
        $len <<= 8;
        $len += read_byte();
    }
    elsif (($len & 0xE0) == 0xC0)
    {
    $len &= ~0xC0;
        $len <<= 8;
        $len += read_byte();
        $len <<= 8;
        $len += read_byte();
    }
    elsif (($len & 0xF0) == 0xE0)
    {
        $len &= ~0xE0;
        $len <<= 8;
        $len += read_byte();
        $len <<= 8;
        $len += read_byte();
        $len <<= 8;
        $len += read_byte();
    }
    elsif (($len & 0xF8) == 0xF0)
    {
        $len = read_byte();
        $len <<= 8;
        $len += read_byte();
        $len <<= 8;
        $len += read_byte();
        $len <<= 8;
        $len += read_byte();
    } 

    return $len;
}

focus on problem:
after recive from router: “=message=no such item”
check the router is still something send to me, but not send: “Check read_len, got ‘0’”

when you send a command to the router, router will respond with the response and then indicate that it ended sending stuff. In your case it sends !trap as your command caused some error and then sends !done, indicating that the command is done.
see here for examples:

http://wiki.mikrotik.com/wiki/Manual:API#Command_examples

->> /command
<<- !re item
<<- !re item
<<- !done


when you have traps, see /cancel command example where you can cancel individual tagged commands or what happens when you cancel everything.

OK, that’s your API client… And the Perl code from which you call it (feel free to replace your router credentials)?

Trying a similar test with my router (and API client), I get, as expected:

MODE |   LENGTH    |    LENGTH    |  CONTENTS
     |  (decoded)  |  (encoded)   |
-----|-------------|--------------|--------------------------------------------
SEND |    <prompt> |     <prompt> | /system/script/environment/set

SEND |    <prompt> |     <prompt> | =numbers=status

SEND |    <prompt> |     <prompt> | =value=1465363657

SEND |    <prompt> |     <prompt> |

SENT |          30 |         0x1E | /system/script/environment/set
SENT |          15 |         0x0F | =numbers=status
SENT |          17 |         0x11 | =value=1465363657
SENT |           0 |         0x00 |
RECV |           5 |         0x05 | !trap
RECV |          21 |         0x15 | =message=no such item
RECV |           0 |         0x00 |
SEND |    <prompt> |     <prompt> | /put

SEND |    <prompt> |     <prompt> | =message=test

SEND |    <prompt> |     <prompt> |

SENT |           4 |         0x04 | /put
SENT |          13 |         0x0D | =message=test
SENT |           0 |         0x00 |
RECV |           5 |         0x05 | !done
RECV |           0 |         0x00 |
SEND |    <prompt> |     <prompt> |

SENT |           0 |         0x00 |
RECV |           5 |         0x05 | !done
RECV |           9 |         0x09 | =ret=test
RECV |           0 |         0x00 |
SEND |    <prompt> |     <prompt> |

SENT |           0 |         0x00 |
NOTE |    Receiving timed out     |
SEND |    <prompt> |     <prompt> | /quit

SEND |    <prompt> |     <prompt> |

SENT |           5 |         0x05 | /quit
SENT |           0 |         0x00 |
RECV |           6 |         0x06 | !fatal
RECV |          29 |         0x1D | session terminated on request
RECV |           0 |         0x00 |
NOTE |   Connection terminated    |

Notice how I do the second command before I get the first one’s !done… Maybe if you were to keep reading until the !done sentence, and only send the second command there, it would work as you’d expect, e.g.

MODE |   LENGTH    |    LENGTH    |  CONTENTS
     |  (decoded)  |  (encoded)   |
-----|-------------|--------------|--------------------------------------------
SEND |    <prompt> |     <prompt> | /system/script/environment/set

SEND |    <prompt> |     <prompt> | =numbers=status

SEND |    <prompt> |     <prompt> | =value=1465363657

SEND |    <prompt> |     <prompt> |

SENT |          30 |         0x1E | /system/script/environment/set
SENT |          15 |         0x0F | =numbers=status
SENT |          17 |         0x11 | =value=1465363657
SENT |           0 |         0x00 |
RECV |           5 |         0x05 | !trap
RECV |          21 |         0x15 | =message=no such item
RECV |           0 |         0x00 |
RECV |           5 |         0x05 | !done
RECV |           0 |         0x00 |
NOTE |    Receiving timed out     |
SEND |    <prompt> |     <prompt> | /put

SEND |    <prompt> |     <prompt> | =message=test

SEND |    <prompt> |     <prompt> |

SENT |           4 |         0x04 | /put
SENT |          13 |         0x0D | =message=test
SENT |           0 |         0x00 |
RECV |           5 |         0x05 | !done
RECV |           9 |         0x09 | =ret=test
RECV |           0 |         0x00 |
NOTE |    Receiving timed out     |
SEND |    <prompt> |     <prompt> | /quit

SEND |    <prompt> |     <prompt> |

SENT |           5 |         0x05 | /quit
SENT |           0 |         0x00 |
RECV |           6 |         0x06 | !fatal
RECV |          29 |         0x1D | session terminated on request
RECV |           0 |         0x00 |
NOTE |   Connection terminated    |