Shell Protocol ============== The shell protocol is implemented by both the :doc:`shell service <../../services/shell>` and any clients interfacing with the service. All messages in the shell protocol are encoded as `CBOR` arrays and are sent in UDP packets. The first value in the encoded list is the ``channel_id``. The second value will be the ``command`` and any additional values will be parameters for the ``command``. ``{ channel_id, command, parameters.. }`` APIs ---- - |shell-protocol| - |cbor-protocol| - |channel-protocol| .. |shell-protocol| raw:: html Shell Protocol .. |cbor-protocol| raw:: html CBOR Protocol .. |channel-protocol| raw:: html Channel Protocol Messages -------- The primary purpose of the shell protocol is to allow the spawning and controlling of remote processes. These messages are used by shell clients to direct the shell service in this work of manipulating processes. Spawn Process ~~~~~~~~~~~~~ This message is sent to the shell service to request a child process to be spawned. It contains a channel ID, the string 'spawn', a command, and spawn options. The command can be an absolute path to a binary or something in the system ``$PATH``. ``{ channel_id, 'spawn', command, options.. }`` There is currently only one available option for the ``options`` argument: - ``args`` - An array of arguments to pass to the child process Example of starting a shell: ``{ 1, 'spawn', 'sh', { args = { '-l' } } }`` Write to Stdin ~~~~~~~~~~~~~~ This message is sent to the shell service to write data to the stdin of a child process. It contains a channel ID, the string 'stdin', and a data string. The data string will be written directly to the stdin of the child process. ``{ channel_id, 'stdin', data }`` Close Stdin ~~~~~~~~~~~ This message is sent to the shell service to close the stdin of a child process. It contains a channel ID and the string 'stdin'. After this message is received the shell service will close the stdin pipe for the specified child process. Any future messages attempting to write to stdin for this process will result in an error. ``{ channel_id, 'stdin' }`` Send Signal ~~~~~~~~~~~ This message is sent to the shell service to signal a child process. It contains a channel ID, the string 'kill', and optionally a signal number. If the signal number is omitted then `SIGTERM` will be sent. ``{ channel_id, 'kill', signal }`` A list of available signals can be found `here `_. Example usages: Send `SIGTERM` to a child process: ``{ channel_id, 'kill' }`` Send `SIGKILL` to a child process: ``{ channel_id, 'kill', 9 }`` Process Created ~~~~~~~~~~~~~~~ This message is sent from the shell service when a process has been created. It contains the channel ID, the string 'pid' and a decimal number which is the pid. ``{ channel_id, 'pid', pid }`` Example message - A process has been created with a pid of 10: ``{ 1, 'pid', 10 }`` Stdout Data ~~~~~~~~~~~ This message is sent from the shell service when a process has produced data via `stdout`. It contains the channel ID, the string 'stdout', and a string of the stdout data. ``{ channel_id, 'stdout', data }`` Example message - ``ls`` producing directory output of `kubos-shell-client`: ``{ 12, 'stdout', 'Cargo.toml\nsrc\n' }`` Stdout Closed ~~~~~~~~~~~~~ This message is sent from the shell service when a process's stdout pipe has been closed. It contains the channel ID and the string 'stdout'. ``{ channel_id, 'stdout' }`` Stderr Data ~~~~~~~~~~~ This message is sent from the shell service when a process has produced data via `stderr`. It contains the channel ID, the string `stderr`, and a string of the stderr data. ``{ channel_id, 'stderr', data }`` Example message - The result of running ``ls`` with an invalid argument: ``{ 13, 'stderr', "Try 'ls --help' for more information.\n" }`` Stderr Closed ~~~~~~~~~~~~~ This message is sent from the shell service when a process's stderr pipe has been closed. It contains the channel ID and the string 'stderr'. ``{ channel_id, 'stderr' }`` Process Exited ~~~~~~~~~~~~~~ This message is sent from the shell service when a process has exited. It contains the channel ID, the string 'exit', the exit signal and the exit code. ``{ channel_id, 'exit', code, signal }`` Example messages The result of a process exiting normally: ``{ 14, 'exit', 0, 0 }`` The result of sending a SIGKILL to a process: ``{ 14, 'exit', 0, 9 }`` Request List of Processes ~~~~~~~~~~~~~~~~~~~~~~~~~ This message is sent to the shell service to request a list of the current processes running in the shell service. It contains the channel ID and the string 'list'. ``{ channel_id, 'list' }`` List of Processes ~~~~~~~~~~~~~~~~~ This message is sent from the shell service when a list of processes is requested. It contains the channel ID, the string 'list', and a list of objects containing process information (channel_id, path and pid). The channel ID can be used to communicate with the corresponding process in the list. ``{ channel_id, 'list', { [channel_id] = { path, pid } } }`` Example list of processes: ``{ 16, 'list', { [12] = { path = 'sh', pid = 45 }, [14] = { path = 'sh', pid = 50 } } }`` Example Usages -------------- Running a Short-Lived Process ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The goal here is to run ``uname -a`` on a remote machine via the shell service and see the output. The shell client randomly chooses ``35`` as its ``channel_id`` and sends a ``spawn`` command with the arguments. :: Client: { 35, 'spawn', 'uname', { args = {'-a'} } } The service sends back multiple messages in quick succession because this is a short-lived process. :: Server: { 35, 'pid', 26191 } Server: { 35, 'stdout', 'Linux vagrant 4.4.0-128-generic #154-Ubuntu SMP Fri May 25 14:15:18 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux' } Server: { 35, 'stdout' } Server: { 35, 'stderr' } Server: { 35, 'exit', 0, 0 } Running a Long-Lived Process ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The goal here is to open a ``bash`` shell on a remote machine via the shell service and use that shell to execute commands. Starting the Process ^^^^^^^^^^^^^^^^^^^^ The shell client randomly chooses ``55`` as its ``channel_id`` and sends a ``spawn`` command with the arguments. :: Client: { 55, 'spawn', 'sh', { detached = true, pty = true, args = { '-l' } } } The service responds back with the ``pid`` of the newly created process. :: Server: { 55, 'pid', 26825 } Server: { 55, 'stdout', '\027kvagrant@vagrant:/home/vagrant\027\\' } Server: { 55, 'stdout', '[vagrant@vagrant vagrant]$ ' } Finding the Process ^^^^^^^^^^^^^^^^^^^ The shell client can send the ``list`` command over a new ``channel_id`` to find this process and its information. :: Client: { 65, 'list' } The service responds with the list of current processes. :: Server: { 65, 'list', { [55] = { path = '/bin/sh', pid = 26825 } } } Sending Data to the Process ^^^^^^^^^^^^^^^^^^^^^^^^^^^ The shell client can use the ``channel_id`` to send data to the ``stdin`` of the process. :: Client: { 55, 'stdin', 'echo hello\n' } The server will write this data to the ``stdin`` of the process and send back any data received over ``stdout``. :: Server: { 55, 'stdout', 'echo hello\r\n' } Server: { 55, 'stdout', 'hello\r\n\027kvagrant@vagrant:/home/vagrant\027\\' } Server: { 55, 'stdout', '[vagrant@vagrant vagrant]$ ' } Killing the Process ^^^^^^^^^^^^^^^^^^^ Once the shell client is finished it can use the ``kill`` command to terminate the process. :: Client: { 55, 'kill' } The service will terminate the process, respond with any data which was sent via ``stdout`` or ``stderr`` and send the ``exit`` message. :: Server: { 55, 'stdout', 'logout\r\n' } Server: { 55, 'exit', 0, 0 } Future Messages --------------- These messages may be implemented in the shell protocol in the future, but are not implemented as of KubOS release v1.8.0. Spawn Process ~~~~~~~~~~~~~ The spawn process is currently implemented, however the following optional arguments are not currently implemented: - ``pty`` - A boolean specifying whether a new pty is needed - ``env`` - An array of environment variable entries in the form ``"KEY=val"`` - ``cwd`` - The current working directory of the child process - ``uid`` - The uid of the process - ``gid`` - The gid of the process - ``detached`` - Determines if the child process should be detached from the service Resize Terminal ~~~~~~~~~~~~~~~ This message is sent to the shell service to resize the pseudo terminal of a child process, if one exists. It contains a channel ID, the string 'resize', the desired number of columns and the desired number of rows. ``{ channel_id, 'resize', columns, rows }`` Example message - Resizing a pseudo terminal to 10x10: ``{ 1, 'resize', 10, 10 }``