# Console Shell

## General features

The Equinox OSGi console is based on Apache Felix Gogo, which provides a Unix-like shell for OSGi frameworks. The OSGi console is useful for runtime configuration and management of the framework and bundles deployed within it. It can also be useful for debugging and troubleshooting of OSGi-based applications.

The Equinox console provides:

- support for console sessions over Telnet and SSH protocols
- JAAS based user authentication
- public key based authentication
- tab completion and command history

This guide shows how to configure and use the console in a minimal OSGi runtime.

## Starting a Command Line Session

The minimal bundle set needed to start Equinox with the console on the command line is as follows:

- org.apache.felix.gogo.command_<version>.jar
- org.apache.felix.gogo.runtime_<version>.jar
- org.apache.felix.gogo.shell_<version>.jar
- org.eclipse.equinox.console_<version>.jar
- org.eclipse.osgi_<version>.jar

All of these bundles are available in the Equinox SDK zip file available from the Equinox Downloads site.

Extract these bundles into a directory, then inside that directory create a configuration/config.ini file with the following content:

        osgi.bundles=\
            org.apache.felix.gogo.runtime,\
            org.apache.felix.gogo.command,\
            org.apache.felix.gogo.shell,\
            org.eclipse.equinox.console

Now this minimal Equinox configuration can be started with a console available on the command line, by passing the `-console` option, for example:

        $ java -jar org.eclipse.osgi_*.jar -console
        osgi> ss
        "Framework is launched."

        id      State       Bundle
        0       ACTIVE      org.eclipse.osgi_3.15.200.v20200214-1600
        1       ACTIVE      org.apache.felix.gogo.runtime_1.1.0.v20180713-1646
        2       ACTIVE      org.apache.felix.gogo.command_1.0.2.v20170914-1324
        3       ACTIVE      org.apache.felix.gogo.shell_1.1.0.v20180713-1646
        4       ACTIVE      org.eclipse.equinox.console_1.4.0.v20190819-1430
        osgi> exit
        Really want to stop Equinox? (y/n; default=y)

## Starting a Telnet Session

Starting the console in a Telnet session is enabled by passing a port number with the `-console` option, for example:

        $ java -jar org.eclipse.osgi_*.jar -console 1234

Once Equinox is started, connect to the Telnet session from another terminal using the port number that was specified above:

        $ telnet localhost 1234
        Trying ::1...
        Connected to localhost.
        Escape character is '^]'.
        osgi> ss
        "Framework is launched."

        id      State       Bundle
        0       ACTIVE      org.eclipse.osgi_3.15.200.v20200214-1600
        1       ACTIVE      org.apache.felix.gogo.runtime_1.1.0.v20180713-1646
        2       ACTIVE      org.apache.felix.gogo.command_1.0.2.v20170914-1324
        3       ACTIVE      org.apache.felix.gogo.shell_1.1.0.v20180713-1646
        4       ACTIVE      org.eclipse.equinox.console_1.4.0.v20190819-1430
        osgi> disconnect
        Disconnect from console? (y/n; default=y)
        Connection closed by foreign host.

It is possible to set the port in the configuration/config.ini file with the addition of this line:

        osgi.console=1234
        # ... or ...
        osgi.console=localhost:1234

The `osgi.console` property specifies the port (and optionally the hostname) on which the console should listen for Telnet connections without needing to pass the `-console` option at all, but if `-console` is passed it will override any setting present in the configuration/config.ini file.

## Starting an SSH Session with JAAS Authentication

To start a console session in an SSH session, a few more bundles are needed:

- org.apache.felix.gogo.command_<version>.jar
- org.apache.felix.gogo.runtime_<version>.jar
- org.apache.felix.gogo.shell_<version>.jar
- org.apache.sshd.osgi_<version>.jar
- org.eclipse.equinox.console_<version>.jar
- org.eclipse.equinox.console.jaas.fragment_<version>.jar
- org.eclipse.equinox.console.ssh_<version>.jar
- org.eclipse.osgi_<version>.jar
- org.slf4j.api_<version>.jar

All of these bundles are available in the Equinox SDK zip file available from the Equinox Downloads site.

Extract these bundles into a directory, then inside that directory create a configuration/config.ini file with the following content:

        osgi.bundles=\
            org.apache.felix.gogo.runtime,\
            org.apache.felix.gogo.command,\
            org.apache.felix.gogo.shell,\
            org.apache.sshd.osgi,\
            org.eclipse.equinox.console,\
            org.eclipse.equinox.console.jaas.fragment,\
            org.eclipse.equinox.console.ssh@start,\
            org.slf4j.api
        osgi.console.ssh=127.0.0.1:1234
        osgi.console.ssh.useDefaultSecureStorage=true

Equinox uses JAAS to authenticate SSH sessions, the default login provider of which must be configured by creating a JAAS configuration file, for example by creating a file configuration/console.auth.config with the following content:

        equinox_console {
            org.eclipse.equinox.console.jaas.SecureStorageLoginModule REQUIRED;
        };

Then when starting Equinox, it must be told where the JAAS configuration is by setting the `java.security.auth.login.config` system property. The default JAAS login provider stores its credentials in a one-way encrypted store file that must be specified with the `org.eclipse.equinox.console.jaas.file` system property:

        $ java -Dssh.server.keystore=configuration/hostkey.ser \
               -Dorg.eclipse.equinox.console.jaas.file=configuration/store \
               -Djava.security.auth.login.config=configuration/console.auth.config \
               -jar org.eclipse.osgi_*.jar

The default JAAS login provider dynamically creates one default user equinox with password equinox. After logging in with these credentials for the first time, the user will be prompted to create a new account and the default account will be removed.

Once Equinox is started, connect to the SSH session from another terminal using the hostname and port specified in the configuration/config.ini file:

        $ ssh -p 1234 equinox@127.0.0.1
        The authenticity of host '[127.0.0.1]:1234 ([127.0.0.1]:1234)' can't be established.
        RSA key fingerprint is SHA256:7x3eOsDRM5lyL5vRsVREy8hIawIfqRiZ7CBnk6GkfRA.
        Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
        Warning: Permanently added '[127.0.0.1]:1234' (RSA) to the list of known hosts.
        Password authentication
        Password:
        Currently the default user is the only one; since it will be deleted after first login, create a new user:
        username: mbooth
        password:
        Confirm password:
        roles: admin
        osgi> disconnect
        Disconnect from console? (y/n; default=y)
        Connection to 127.0.0.1 closed.

The password must be at least 8 symbols long and the username may contain alphanumerical characters, underscores and dots. On subsequent connections the user will be required to supply the newly created credentials:

        $ ssh -p 1234 mbooth@127.0.0.1
        Password authentication
        Password:
        osgi> disconnect
        Disconnect from console? (y/n; default=y)
        Connection to 127.0.0.1 closed.

Once logged in there are various commands for administering users. Type help at the OSGi console prompt to explore commands to perform the following functions:

- adding, deleting, listing users
- reseting and changing passwords
- adding and removing user roles

### Custom JAAS Authentication Login Providers

Only authentication is implemented in the default JAAS login provider so by default all authenticated users have similar rights. Roles exist to allow authorization to be added in custom JAAS login providers.

A custom JAAS login provider can be used by creating a bundle fragment that extends the org.apache.sshd.osgi bundle. This fragment is used to provide or import the package of the custom login provider. This is necessary for the SSH system to be able to load the provider class.

If a custom JAAS login provider is used, then the `osgi.console.ssh.useDefaultSecureStorage` property must not be set in the configuration/config.ini at all and the custom login provider must be specified in the configuration/console.auth.config file instead of the default entry there.

## Starting an SSH Session with Public Key Authentication

It's common to want to use public key authentication with SSH and it is possible to configure Equinox to do that instead of using JAAS authentication. First an SSH key-pair should be created as normal and then a file created that contains the list of authorized keys that Equinox should consult when users attempt to connect:

        $ ssh-keygen -f ~/.ssh/equinox
        Generating public/private rsa key pair.
        Enter passphrase (empty for no passphrase):
        Enter same passphrase again:
        Your identification has been saved in /home/mbooth/.ssh/equinox
        Your public key has been saved in /home/mbooth/.ssh/equinox.pub
        The key fingerprint is:
        SHA256:0k7MSbLLzhhzI7Gw6oSYEx8Fv5UpNHMPTdOUDj8rplQ mbooth@thinkpad-p50
        The key's randomart image is:
        +---[RSA 3072]----+
        |  . + oooo..     |
        |   + + =o.o      |
        |    + = o+       |
        |   . + BE.+      |
        |. o o o.S  o     |
        |o+ + +.=o .      |
        |=.o =.=o..       |
        |.o   O..         |
        |o.  . o          |
        +----[SHA256]-----+

        $ cat ~/.ssh/equinox.pub > configuration/equinox_authorized_keys

Next the following line must be removed from the configuration/config.ini file:

        # Remove this line to use public key authentication
        # Must only be present when using the default JAAS login provider
        osgi.console.ssh.useDefaultSecureStorage=true

Now when starting Equinox, instead of telling it about the JAAS configuration, it must be told about the authorized keys file by setting the `ssh.server.authorized_keys` system property:

        $ java -Dssh.server.keystore=configuration/hostkey.ser \
               -Dssh.server.authorized_keys=configuration/equinox_authorized_keys \
               -jar org.eclipse.osgi_*.jar

And that allows users to use their SSH keys instead of a username/password pair when connecting to the SSH console session:

        $ ssh -i ~/.ssh/equinox -p 1234 localhost
        The authenticity of host '[localhost]:1234 ([127.0.0.1]:1234)' can't be established.
        RSA key fingerprint is SHA256:m2JKy2fRZA1aqvxHBBe+Awsgk98ryI29fH03Rg7jeHw.
        Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
        Warning: Permanently added '[localhost]:1234' (RSA) to the list of known hosts.
        osgi> disconnect
        Disconnect from console? (y/n; default=y)
        Connection to localhost closed.

The authorized keys file given to Equinox will be read every time a connection is made, which allows adding and removing of keys dynamically at runtime.

### Custom Public Key Authentication

Equinox also supports customizing the public key authentication mechanism. If no specific authorized keys file is configured via system property then the OSGi service registry will be searched for available authenticators. To enable this feature set the `ssh.custom.publickeys.auth` system property to `true`.

## Starting Telnet and SSH Sessions Together

It is possible to start Equinox console sessions over both Telnet and SSH simultaneously. For this, both the `osgi.console` and `osgi.console.ssh` properties must be specified in the configuration/config.ini file:

        # Telnet session
        osgi.console=<hostname>:<port>
        # SSH session
        osgi.console.ssh=<hostname>:<port>

These properties specify the port on which to listen and the hostname (or IP address) to which it binds for incoming connections.

For both properties, the hostname (or IP address) is optional and may be omitted. If the hostname is omitted, then localhost is assumed. If the hostname is the same for both properties, or both hostnames are omitted then the SSH session must have a different port number to the Telnet session.

## Using the Configuration Admin Service

It is possible to configure Telnet and SSH console sessions through the Configuration Admin Service instead of using the `osgi.console` and `osgi.console.ssh` properties. This is helpful in more complex scenarios, for example when you want to run different instances of the console in different subsystems of the framework. In this case if the port is configured through a system property, the same value is used for all console instances and only one will be able to bind to the socket.

To enable this feature, the Configuration Admin bundle and it's dependencies must be added to the Equinox instance:

- org.eclipse.equinox.cm_<version>.jar
- org.eclipse.osgi.services_<version>.jar
- org.eclipse.osgi.util_<version>.jar

All of these bundles are available in the Equinox SDK zip file available from the Equinox Downloads site.

And then in the configuration/config.ini file, the `osgi.console.useConfigAdmin` property must be used in place of the usual configuration properties:

        # Remove these properties:
        #osgi.console=<hostname>:<port>
        #osgi.console.ssh=<hostname>:<port>

        # Use the Config Admin Service instead:
        osgi.console.useConfigAdmin=true

Now a custom bundle must be written that uses the Configuration Admin Service to configure Telnet and SSH console sessions. The Persistent Identity (PID) for the Telnet configuration is `osgi.console.telnet` and for the SSH configuration is `osgi.console.ssh`. Both configurations have the following properties, all expecting values of type String:

- `host`
- `port`
- `enabled`

The `enabled` property determines if the Telnet or SSH session is to be started at all. If the value is `true`, it is started. If the value is `false`, or the property `enabled` is absent, the Telnet or SSH session is not started.

## Console Command Usage

The Equinox console works similarly to Unix-style shells. Multiple commands can be sent when separated by a semi-colon and the output of commands may be piped to the input of other commands. For example:

        osgi> ss | grep slf4j
        8       ACTIVE      org.slf4j.api_1.7.2.v20121108-1250
        true
        osgi> stop 8 ; start 8

IO redirection can be simulated with the cat and tac commands. For example:

        osgi> ss | tac out.txt
        osgi> cat out.txt | grep slf4j
        8       ACTIVE      org.slf4j.api_1.7.2.v20121108-1250
        true

The console also has standard command line editing features such as:

- backspace - deletes the character to the left of the cursor
- delete - deletes the character on the cursor position
- home/end - moves the cursor to the beginning/end of the command line
- left/right arrow - moves the cursor one character left/right
- up/down arrow - moves backward/forward one entry in the command history
- pageup/pagedown - moves to the first/last entry of the command history
- tab - command completion (described in more detail below)

However some of these editing features are only supported when using the console via a Telnet or SSH session.

### Command Scopes

Equinox console commands have the notion of command scope. The scope is a kind of namespace that can be used, for example, to differentiate between commands with the same name, but provided by different providers.

The scope is a prefix of the command name, separated from it by a colon. When writing the command in the console, specifying the scope is optional: A command may be written as `command_name` or `scope:command_name`. If the scope is not specified, then the command with this name from the default scope is used. If there is no such command in the default scope, all scopes are searched.

If there is more than one command with the specified name in different, non-default scopes, it is not guaranteed which one will be actually executed. Therefore, if there are commands with the same name but in different scopes, the scope prefix must be specified explicitly with the command name to ensure that exactly the desired command is executed.

The Equinox console is now based on Apache Felix Gogo, which has a different way of implementing console commands than the traditional Equinox way. Equinox adapts these legacy commands for the Gogo-based shell and makes them available in the equinox scope. For compatibility, the default scope is the equinox scope.

### Getting Help for Commands

Typing help (or man) at the OSGi console prompt with no arguments, the help for all available commands is displayed. To limit the output to commands from a specific scope, the `-scope <scope_name>` parameter can be passed to the help command:

        osgi> help -scope equinox

        close - shutdown and exit
           scope: equinox

        diag - Displays unsatisfied constraints for the specified bundle(s)
           scope: equinox
           parameters:
              Bundle[]   IDs of bundle(s), for which to display unsatisfied constraints

        ...etc...

The help text for a specific command can be shown by passing a `command_name` or `scope:command_name` to the help command. If the scope is not specified, then the help text is shown for the command from the default scope if it exists:

        osgi> help headers

        headers - print bundle headers
           scope: equinox
           parameters:
              Bundle[]   bundles to print headers for

        osgi> help felix:headers

        headers - display bundle headers
           scope: felix
           parameters:
              Bundle[]   target bundles

The default help command provided by the Apache Felix Gogo shell does not provide help for the legacy Equinox commands, which are adapted by the Equinox console for the Gogo shell. For this reason the Equinox console provides its own help command in the equinox scope that delegates to the default help command whilst also providing help for legacy commands.

### Closing Console Sessions

When using the Equinox console standalone on the command line, the exit command can be used to terminate Equinox and return to the system command prompt.

For both Telnet and SSH sessions, the session can be closed without terminating Equinox with the disconnect command.

## Implementing Custom Console Commands

Traditionally in Equinox commands are provided by a class implementing the `org.eclipse.osgi.framework.console.CommandProvider` interface. The Equinox console provides an adapter from this legacy type of command to the new type of command used in Gogo but it is preferred that new commands are implemented as Gogo commands directly.

Commands for Apache Felix Gogo are plain old Java object (POJO) classes with all the commands implemented as public methods. The methods may have arbitrary arguments. These classes are registered as services, with two special properties:

- `osgi.command.scope` - specifies the scope of the command
- `osgi.command.function` - specifies the commands provided by this service; this is a string array containing the names of public methods in the implementing class that can be executed as commands

Gogo commands also have the notion of converters and formatters.

A converter is a class which converts the arguments passed at the OSGi console prompt, to the actual arguments that the command accepts. For example, the command may have one argument of type `Bundle`. A converter might accept a long integer and finds the bundle with this ID. Then the command may be called with the ID of the bundle as an argument, the converter will convert it to the corresponding `Bundle` object and the command method will be called with this object as an argument.

A formatter is a class which displays a result returned by a command method.

For more information on Gogo commands, see the Gogo documentation.

## Implementing Custom Tab Completion

The Equinox console provides tab completion and allows implementors to provide their own custom completion providers. This feature is available only when connecting through a Telnet or SSH session. Completion is available for:

- command names
- variable names passed as command arguments - these must be previously defined in the session
- file names passed as command arguments

When the `tab` key is typed, all possible candidates for completion for the current word are searched. If there is only one possible completion, the current word is automatically completed. If there is more than one option, all are displayed. The user can then cycle through the possible completions by hitting `tab` multiple times, until the desired completion candidate is selected.

If longest common prefix of all available completion candidates is longer than the current word, then the current word is completed automatically to this prefix before choosing the final completion. For example, if the following completions are available for the word bun:

- bundles
- bundle
- bundlelevel

Then the current word is completed automatically to bundle and the user can continue typing normally or, by hitting `tab` again, they can cycle through the possible completions.

A custom completer should implement the `org.eclipse.equinox.console.common.Completer` interface provided by the Equinox console bundle. It has the single method `getCandidates`, which take as parameters the whole command line and the current cursor position within it, and returns a map of completion candidates to positions in the command line at which the completion begins.