0 votes
by (420 points)

Hello.

For example, I made a limit on the number of terminal columns:

var shell = client.StartScripting(new TerminalOptions() { Columns = 15 });

I can see it in the log - 0F:

2025-05-26 16:47:30.7253|TRACE|log|SSH:92f9e636-53d6-4ec4-9dcc-fafd2198a486 -> 2025-05-26 16:47:30.729 VERBOSE Ssh(1)[1] SSH: Sending packet SSH_MSG_CHANNEL_REQUEST (46 bytes).
 0000 |62-00-00-00-00-00-00-00 07-70-74-79-2D-72-65-71| b........pty-req
 0010 |01-00-00-00-05-78-74-65 72-6D-00-00-00-0F-00-00| .....xterm......
 0020 |00-19-00-00-00-78-00-00 01-90-00-00-00-00      | .....x........

Now I connect to a host running the alpine operating system and the busybox shell. Their feature is the presence of cursor position control, data about which is transmitted from the client to the server:

025-05-26 16:47:30.7326|TRACE|log|SSH:92f9e636-53d6-4ec4-9dcc-fafd2198a486 -> 2025-05-26 16:47:30.740 VERBOSE Ssh(1)[11] SSH: Received packet SSH_MSG_CHANNEL_DATA (23 bytes).
 0000 |5E-00-00-00-00-00-00-00 0E-61-6C-70-69-6E-65-3A| ^........alpine:
 0010 |7E-24-20-1B-5B-36-6E                           | ~$ .[6n

...

2025-05-26 16:47:30.7326|TRACE|log|SSH:92f9e636-53d6-4ec4-9dcc-fafd2198a486 -> 2025-05-26 16:47:30.745 VERBOSE Ssh(1)[1] Info: Received data: 
 0000 |57-65-6C-63-6F-6D-65-20 74-6F-20-41-6C-70-69-6E| Welcome to Alpin
 0010 |65-21-0D-0A-0D-0A-54-68 65-20-41-6C-70-69-6E-65| e!....The Alpine
 0020 |20-57-69-6B-69-20-63-6F 6E-74-61-69-6E-73-20-61|  Wiki contains a
 0030 |20-6C-61-72-67-65-20-61 6D-6F-75-6E-74-20-6F-66|  large amount of
 0040 |20-68-6F-77-2D-74-6F-20 67-75-69-64-65-73-20-61|  how-to guides a
 0050 |6E-64-20-67-65-6E-65-72 61-6C-0D-0A-69-6E-66-6F| nd general..info
 0060 |72-6D-61-74-69-6F-6E-20 61-62-6F-75-74-20-61-64| rmation about ad
 0070 |6D-69-6E-69-73-74-72-61 74-69-6E-67-20-41-6C-70| ministrating Alp
 0080 |69-6E-65-20-73-79-73-74 65-6D-73-2E-0D-0A-53-65| ine systems...Se
 0090 |65-20-3C-68-74-74-70-73 3A-2F-2F-77-69-6B-69-2E| e <https://wiki.
 00A0 |61-6C-70-69-6E-65-6C-69 6E-75-78-2E-6F-72-67-2F| alpinelinux.org/
 00B0 |3E-2E-0D-0A-0D-0A-59-6F 75-20-63-61-6E-20-73-65| >.....You can se
 00C0 |74-75-70-20-74-68-65-20 73-79-73-74-65-6D-20-77| tup the system w
 00D0 |69-74-68-20-74-68-65-20 63-6F-6D-6D-61-6E-64-3A| ith the command:
 00E0 |20-73-65-74-75-70-2D-61 6C-70-69-6E-65-0D-0A-0D|  setup-alpine...
 00F0 |0A-59-6F-75-20-6D-61-79 20-63-68-61-6E-67-65-20| .You may change 
 0100 |74-68-69-73-20-6D-65-73 73-61-67-65-20-62-79-20| this message by 
 0110 |65-64-69-74-69-6E-67-20 2F-65-74-63-2F-6D-6F-74| editing /etc/mot
 0120 |64-2E-0D-0A-0D-0A-61-6C 70-69-6E-65-3A-7E-24-20| d.....alpine:~$ 
 0130 |1B-5B-36-6E                                    | .[6n

2025-05-26 16:47:30.7628|TRACE|log|SSH:92f9e636-53d6-4ec4-9dcc-fafd2198a486 -> 2025-05-26 16:47:30.762 VERBOSE Ssh(1)[1] SSH: Sending packet SSH_MSG_CHANNEL_DATA (17 bytes).
 0000 |5E-00-00-00-00-00-00-00 08-1B-5B-32-35-3B-31-31| ^.........[25;11
 0010 |52                                             | R

2025-05-26 16:47:30.7628|TRACE|log|SSH:92f9e636-53d6-4ec4-9dcc-fafd2198a486 -> 2025-05-26 16:47:30.762 DEBUG Ssh(1)[1] Info: Sent 8 bytes of data.

2025-05-26 16:47:30.7628|TRACE|log|SSH:92f9e636-53d6-4ec4-9dcc-fafd2198a486 -> 2025-05-26 16:47:30.762 VERBOSE Ssh(1)[1] Info: Sent data: 
 0000 |1B-5B-32-35-3B-31-31-52                        | .[25;11R

But when I execute a command whose length, together with the length of the prompt string, exceeds the number of columns, the server recalculates the output and splits the command into multiple lines:

2025-05-26 16:47:33.7480|TRACE|log|SSH:92f9e636-53d6-4ec4-9dcc-fafd2198a486 -> 2025-05-26 16:47:33.751 VERBOSE Ssh(1)[1] SSH: Sending packet SSH_MSG_CHANNEL_DATA (17 bytes).
 0000 |5E-00-00-00-00-00-00-00 08-65-63-68-6F-20-24-3F| ^........echo $?
 0010 |0D                                             | .

2025-05-26 16:47:33.7480|TRACE|log|SSH:92f9e636-53d6-4ec4-9dcc-fafd2198a486 -> 2025-05-26 16:47:33.751 DEBUG Ssh(1)[1] Info: Sent 8 bytes of data.

2025-05-26 16:47:33.7480|TRACE|log|SSH:92f9e636-53d6-4ec4-9dcc-fafd2198a486 -> 2025-05-26 16:47:33.751 VERBOSE Ssh(1)[1] Info: Sent data: 
 0000 |65-63-68-6F-20-24-3F-0D                        | echo $?.

2025-05-26 16:47:33.7480|TRACE|log|SSH:92f9e636-53d6-4ec4-9dcc-fafd2198a486 -> 2025-05-26 16:47:33.751 DEBUG VirtualTerminal(0)[1] Scripting: WaitFor(Line) [Timeout=60000]

2025-05-26 16:47:33.7480|TRACE|log|SSH:92f9e636-53d6-4ec4-9dcc-fafd2198a486 -> 2025-05-26 16:47:33.751 VERBOSE VirtualTerminal(0)[1] Scripting: WaitFor: polling data for max 60000 ms.

2025-05-26 16:47:33.7480|TRACE|log|SSH:92f9e636-53d6-4ec4-9dcc-fafd2198a486 -> 2025-05-26 16:47:33.753 VERBOSE Ssh(1)[11] SSH: Received packet SSH_MSG_CHANNEL_DATA (38 bytes).
 0000 |5E-00-00-00-00-00-00-00 1D-65-63-68-6F-20-0D-0D| ^........echo ..
 0010 |0A-24-3F-0D-0A-30-0D-0A 61-6C-70-69-6E-65-3A-7E| .$?..0..alpine:~
 0020 |24-20-1B-5B-36-6E                              | $ .[6n

2025-05-26 16:47:33.7480|TRACE|log|SSH:92f9e636-53d6-4ec4-9dcc-fafd2198a486 -> 2025-05-26 16:47:33.753 VERBOSE VirtualTerminal(0)[1] Scripting: WaitFor: data received, reset last data received time stamp.

2025-05-26 16:47:33.7480|TRACE|log|SSH:92f9e636-53d6-4ec4-9dcc-fafd2198a486 -> 2025-05-26 16:47:33.753 VERBOSE VirtualTerminal(0)[1] Scripting: WaitFor: processing received data.

2025-05-26 16:47:33.7480|TRACE|log|SSH:92f9e636-53d6-4ec4-9dcc-fafd2198a486 -> 2025-05-26 16:47:33.753 DEBUG Ssh(1)[1] Info: Received 29 bytes of data.

2025-05-26 16:47:33.7480|TRACE|log|SSH:92f9e636-53d6-4ec4-9dcc-fafd2198a486 -> 2025-05-26 16:47:33.753 VERBOSE Ssh(1)[1] Info: Received data: 
 0000 |65-63-68-6F-20-0D-0D-0A 24-3F-0D-0A-30-0D-0A-61| echo ...$?..0..a
 0010 |6C-70-69-6E-65-3A-7E-24 20-1B-5B-36-6E         | lpine:~$ .[6n

The client processes the response from the server and clears only the first line of the command and does not clear wrapped to a new row lines. Then the user receives not quite the expected data.

shell.Prompt = "alpine:~$ ";
shell.SendCommand("echo $?");
var response = shell.ReadUntilPrompt();

The expected data is "0\r\n".
The actual data is "$?\r\n0\r\n".

  • Does the Rebex library scripting feature have the option to handle such cases?

I can increase the number of columns in the TerminalOptions, but I am limited to 1023, which prevents me from running long commands.

For this example, I deliberately chose a very small value to make it simple and clear. But there is a need to execute very long scripts.

  • Can you please explain, what is the reason for this limitation, is it possible to increase it?

The RFC initially allows larger values ​​to be specified:

6.2.  Requesting a Pseudo-Terminal

   A pseudo-terminal can be allocated for the session by sending the
   following message.

      byte      SSH_MSG_CHANNEL_REQUEST
      uint32    recipient channel
      string    "pty-req"
      boolean   want_reply
      string    TERM environment variable value (e.g., vt100)
      uint32    terminal width, characters (e.g., 80)
      uint32    terminal height, rows (e.g., 24)
      uint32    terminal width, pixels (e.g., 640)
      uint32    terminal height, pixels (e.g., 480)
      string    encoded terminal modes

Thank you.

1 Answer

0 votes
ago by (149k points)

Thanks for bringing this to our attention. Unfortunately, terminal behavior varies a lot and some terminals are quite a challenge. I'm not sure whether we would be able to address this particular problem in the near term, but we will look into it, because BusyBox is quite common.

Does the Rebex library scripting feature have the option to handle such cases?

You might be able to work around this by using some clever combination of Send/WaitFor instead of SendCommand/ReadUntilPrompt.

Another possibility is to execute long commands without using the Scriptiong API, without a pseudo-terminal, which would get rid of the concept of rows/columns, along with BusyBox cursor position control:

var shell = client.StartCommand("echo $?");
var response = shell.ReadAll();

The drawback is that the Shell API is somewhat limited, which is why we introduced Scripting. But in some scenarios, it might be a better choice.

Can you please explain, what is the reason for [number of columns] limitation, is it possible to increase it?

The RFC does indeed allow very large values for columns and rows, but there have to be some limits in practice. Otherwise, all servers would have to support terminal sizes of up to 4294967295x4294967295, which would not even fit into memory. At the client side in Rebex.SshShell, each terminal cell consumes roughly 20 bytes. Additionally, we had some doubts that servers and applications would be able to correctly handle exceedingly large values, so imposing limits of 1024 columns/rows seemed about right. However, that was 17 years - I guess we can increase this now a bit. How long can your scripts actually get?

ago by (420 points)
Thanks for the reply.

My scripts can range from 1000 to 4000 characters at the moment, but still much smaller than the shell can handle. There is a tendency for the length to increase slightly from year to year due to the increasing functional needs that the scripts provide, but without huge jumps.

The solution appears to be additional manual processing of the server response within the Scripting API using Send/WaitFor.
...