= Vert.x Shell

Vert.x Shell is a command line interface for the Vert.x runtime available from regular
terminals using different protocols.

Vert.x Shell provides a variety of commands for interacting live with Vert.x services.

Vert.x Shell can be extended with custom commands in any language supported by Vert.x

== Using Vert.x Shell

Vert.x Shell is a Vert.x Service and can be started programmatically via the `link:../../apidocs/io/vertx/ext/shell/ShellService.html[ShellService]`
or deployed as a service.

=== Deployed service

The shell can be started as a service directly either from the command line or as a the Vert.x deployment:

.Starting a shell service available via Telnet
[source,subs="+attributes"]
----
vertx run -conf '{"telnetOptions":{"port":5000}}' maven:io.vertx:vertx-shell:3.1.0
----

or

.Starting a shell service available via SSH
[source,subs="+attributes"]
----
# create a key pair for the SSH server
keytool -genkey -keyalg RSA -keystore ssh.jks -keysize 2048 -validity 1095 -dname CN=localhost -keypass secret -storepass secret
# create the auth config
echo user.admin=password > auth.properties
# start the shell
vertx run -conf '{"sshOptions":{"port":4000,"keyPairOptions":{"path":"ssh.jks","password":"secret"},"shiroAuthOptions":{"config":{"properties_path":"file:auth.properties"}}}}' maven:io.vertx:vertx-shell:3.1.0
----

You can also deploy this service inside your own verticle:

[source,java,subs="+attributes"]
----
vertx.deployVerticle("maven:{maven-groupId}:{maven-artifactId}:{maven-version}",
    new DeploymentOptions().setConfig(
        new JsonObject().put("telnetOptions",
            new JsonObject().
                put("host", "localhost").
                put("port", 4000))
    )
);
----

or

[source,java,subs="+attributes"]
----
vertx.deployVerticle("maven:{maven-groupId}:{maven-artifactId}:{maven-version}",
    new DeploymentOptions().setConfig(
        new JsonObject().put("sshOptions",
            new JsonObject().
                put("host", "localhost").
                put("port", 5000).
                put("keyPairOptions",
                    new JsonObject().
                        put("path", "server-keystore.jks").
                        put("password", "wibble")).
                put("config", new JsonObject().
                    put("properties_path", "file:/path/to/my/auth.properties")))
    )
);
----

NOTE: when Vert.x Shell is already on your classpath you can use `service:io.vertx.ext.shell` instead
or `maven:io.vertx:vertx-shell:3.1.0`

=== Programmatic service

The `link:../../apidocs/io/vertx/ext/shell/ShellService.html[ShellService]` takes care of starting an instance of Vert.x Shell.

Starting a shell service available via SSH:

[source,java]
----
ShellService service = ShellService.create(vertx,
    new ShellServiceOptions().setSSHOptions(
        new SSHOptions().
            setHost("localhost").
            setPort(5000).
            setKeyPairOptions(new JksOptions().
                    setPath("server-keystore.jks").
                    setPassword("wibble")
            ).
            setShiroAuthOptions(new ShiroAuthOptions().
                setType(ShiroAuthRealmType.PROPERTIES).
                setConfig(new JsonObject().
                    put("properties_path", "file:/path/to/my/auth.properties"))
            )
    )
);
service.start();
----

Starting a shell service available via Telnet:

[source,java]
----
ShellService service = ShellService.create(vertx,
    new ShellServiceOptions().setTelnetOptions(
        new TelnetOptions().
            setHost("localhost").
            setPort(4000)
    )
);
service.start();
----

The `link:../../apidocs/io/vertx/ext/shell/net/TelnetOptions.html[TelnetOptions]` extends the Vert.x Core `NetServerOptions` as the Telnet server
implementation is based on a `NetServer`.

CAUTION: Telnet does not provide any authentication nor encryption at all.

== Telnet configuration

The telnet connector is configured by `link:../../apidocs/io/vertx/ext/shell/ShellServiceOptions.html#setTelnetOptions-io.vertx.ext.shell.net.TelnetOptions-[setTelnetOptions]`,
the `link:../../apidocs/io/vertx/ext/shell/net/TelnetOptions.html[TelnetOptions]` extends the `link:../../apidocs/io/vertx/core/net/NetServerOptions.html[NetServerOptions]` so they
have the exact same configuration.

== SSH configuration

The SSH connector is configured by `link:../../apidocs/io/vertx/ext/shell/ShellServiceOptions.html#setSSHOptions-io.vertx.ext.shell.net.SSHOptions-[setSSHOptions]`:

- `link:../../apidocs/io/vertx/ext/shell/net/SSHOptions.html#setPort-int-[setPort]`: port
- `link:../../apidocs/io/vertx/ext/shell/net/SSHOptions.html#setHost-java.lang.String-[setHost]`: host

Only username/password authentication is supported at the moment, it can be configured with property file
or LDAP, see Vert.x Auth for more info:

- `link:../../apidocs/io/vertx/ext/shell/net/SSHOptions.html#setShiroAuthOptions-io.vertx.ext.shell.auth.ShiroAuthOptions-[setShiroAuthOptions]`: configures user authentication

The server key configuration reuses the key pair store configuration scheme provided by _Vert.x Core_:

- `link:../../apidocs/io/vertx/ext/shell/net/SSHOptions.html#setKeyPairOptions-io.vertx.core.net.JksOptions-[setKeyPairOptions]`: set `.jks` key pair store
- `link:../../apidocs/io/vertx/ext/shell/net/SSHOptions.html#setPfxKeyPairOptions-io.vertx.core.net.PfxOptions-[setPfxKeyPairOptions]`: set `.pfx` key pair store
- `link:../../apidocs/io/vertx/ext/shell/net/SSHOptions.html#setPemKeyPairOptions-io.vertx.core.net.PemKeyCertOptions-[setPemKeyPairOptions]`: set `.pem` key pair store

== Base commands

To find out the available commands you can use the _help_ builtin command:

. Verticle commands
.. verticle-ls: list all deployed verticles
.. verticle-undeploy: undeploy a verticle
.. verticle-deploy: deployes a verticle
.. verticle-factories: list all known verticle factories
. File system commands
.. ls
.. cd
.. pwd
. Bus commands
.. bus-tail: display all incoming messages on an event bus address
.. bus-send: send a message on the event bus
. Net commands
.. net-ls: list all available net servers, including HTTP servers
. Shared data commands
.. local-map-put
.. local-map-get
.. local-map-rm
. Metrics commands (requires Dropwizard metrics setup)
.. metrics-ls: show all available metrics
.. metrics-info: show particular metrics
. Various commands
.. echo
.. sleep
.. help
.. exit
.. logout
. Job control
.. fg
.. bg
.. jobs

NOTE: this command list should evolve in next releases of Vert.x Shell

== Extending Vert.x Shell

Vert.x Shell can be extended with custom commands in any of the languages supporting code generation.

A command is created by the `link:../../apidocs/io/vertx/ext/shell/command/CommandBuilder.html#command-java.lang.String-[CommandBuilder.command]` method: the command process handler is called
by the shell when the command is executed, this handler can be set with the `link:../../apidocs/io/vertx/ext/shell/command/CommandBuilder.html#processHandler-io.vertx.core.Handler-[processHandler]`
method:

[source,java]
----
CommandBuilder builder = CommandBuilder.command("my-command");
builder.processHandler(process -> {

  // Write a message to the console
  process.write("Hello World");

  // End the process
  process.end();
});

// Register the command
CommandRegistry registry = CommandRegistry.get(vertx);
registry.registerCommand(builder.build());
----

After a command is created, it needs to be registed to a `link:../../apidocs/io/vertx/ext/shell/registry/CommandRegistry.html[CommandRegistry]`. The
command registry holds all the commands for a Vert.x instance.

NOTE: Command callbacks are invoked in the `io.vertx.core.Context` when the command is registered in the
registry. Keep this in mind if you maintain state in a command.

The `link:../../apidocs/io/vertx/ext/shell/command/CommandProcess.html[CommandProcess]` object can be used for interacting with the shell.

=== Command arguments

The `link:../../apidocs/io/vertx/ext/shell/command/CommandProcess.html#args--[args]` returns the command arguments:

[source,java]
----
command.processHandler(process -> {

  for (String arg : process.args()) {
    // Print each argument on the console
    process.write("Argument " + arg);
  }

  process.end();
});
----

Besides it is also possible to create commands using `link:../../apidocs/io/vertx/core/cli/CLI.html[Vert.x CLI]`: it makes easier to
write command line argument parsing:

- _option_ and _argument_ parsing
- argument _validation_
- generation of the command _usage_

[source,java]
----
CLI cli = CLI.create("my-command").
    addArgument(new Argument().setArgName("my-arg")).
    addOption(new Option().setShortName("m").setLongName("my-option"));
CommandBuilder command = CommandBuilder.command(cli);
command.processHandler(process -> {

  CommandLine commandLine = process.commandLine();

  String argValue = commandLine.getArgumentValue(0);
  String optValue = commandLine.getOptionValue("my-option");
  process.write("The argument is " + argValue + " and the option is " + optValue);

  process.end();
});
----

When an option named _help_ is added to the CLI object, the shell will take care of generating the command usage
when the option is activated:

[source,java]
----
CLI cli = CLI.create("my-command").
    addArgument(new Argument().setArgName("my-arg")).
    addOption(new Option().setArgName("help").setShortName("h").setLongName("help"));
CommandBuilder command = CommandBuilder.command(cli);
command.processHandler(process -> {
  // ...
});
----

=== Terminal size

The current terminal size can be obtained using `link:../../apidocs/io/vertx/ext/shell/io/Tty.html#width--[width]` and
`link:../../apidocs/io/vertx/ext/shell/io/Tty.html#height--[height]`.

[source,java]
----
command.processHandler(process -> {
  process.write("Current terminal size: (" + process.width() + ", " + process.height() + ")").end();
});
----

=== Shell session

The shell is a connected service that naturally maintains a session with the client, this session can be
used in commands to scope data. A command can get the session with `link:../../apidocs/io/vertx/ext/shell/process/ProcessContext.html#session--[session]`:

[source,java]
----
command.processHandler(process -> {

  Session session = process.session();

  if (session.get("my_key") == null) {
    session.put("my key", "my value");
  }

  process.end();
});
----

=== Process I/O

A command can set a `link:../../apidocs/io/vertx/ext/shell/io/Tty.html#setStdin-io.vertx.core.Handler-[setStdin]` handler
to be notified when the shell receives data, e.g the user uses his keyboard:

[source,java]
----
command.processHandler(process -> {
  process.setStdin(data -> {
    System.out.println("Received " + data);
  });
});
----

A command can use the `link:../../apidocs/io/vertx/ext/shell/io/Tty.html#stdout--[stdout]` to write to the standard output.

[source,java]
----
command.processHandler(process -> {
  process.stdout().write("Hello World");
  process.end();
});
----

Or it can use the `link:../../apidocs/io/vertx/ext/shell/command/CommandProcess.html#write-java.lang.String-[write]` method:

[source,java]
----
command.processHandler(process -> {
  process.write("Hello World");
  process.end();
});
----

=== Process termination

Calling `link:../../apidocs/io/vertx/ext/shell/command/CommandProcess.html#end--[end]` ends the current process. It can be called directly
in the invocation of the command handler or any time later:

[source,java]
----
command.processHandler(process -> {
  Vertx vertx = process.vertx();

  // Set a timer
  vertx.setTimer(1000, id -> {

    // End the command when the timer is fired
    process.end();
  });
});
----

=== Process events

A command can subscribe to a few process events, named after the posix signals.

==== `SIGINT` event

The `link:../../apidocs/io/vertx/ext/shell/io/EventType.html#SIGINT[SIGINT]` event is fired when the process is interrupted, this event is fired when the user press
_Ctrl+C_ during the execution of a command. This handler can be used for interrupting commands _blocking_ the CLI and
gracefully ending the command process:

[source,java]
----
command.processHandler(process -> {
  Vertx vertx = process.vertx();

  // Every second print a message on the console
  long periodicId = vertx.setPeriodic(1000, id -> {
    process.write("tick\n");
  });

  // When user press Ctrl+C: cancel the timer and end the process
  process.eventHandler(EventType.SIGINT, event -> {
    vertx.cancelTimer(periodicId);
    process.end();
  });
});
----

When no `SIGINT` handler is registered, pressing _Ctrl+C_ will have no effect on the current process and the event
will be delayed and will likely be handled by the shell, like printing a new line on the console.

==== `SIGTSTP`/`SIGCONT` events

The `link:../../apidocs/io/vertx/ext/shell/io/EventType.html#SIGTSTP[SIGTSTP]` event is fired when the process is running and the user press _Ctrl+Z_: the command
is _suspended_:

- the command can receive the `SIGTSTP` event when it has registered an handler for this event
- the command will not receive anymore data from the standard input
- the shell prompt the user for input

The `link:../../apidocs/io/vertx/ext/shell/io/EventType.html#SIGCONT[SIGCONT]` event is fired when the process is resumed, usually when the user types _fg_:

- the command can receive the `SIGCONT` event when it has registered an handler for this event
- the command will receive anymore data from the standard input when it has registered an stdin handler

[source,java]
----
command.processHandler(process -> {

  // Command is suspended
  process.eventHandler(EventType.SIGTSTP, event -> {
    System.out.println("Suspended");
  });

  // Command is resumed
  process.eventHandler(EventType.SIGCONT, event -> {
    System.out.println("Resumed");
  });
});
----

==== `SIGWINCH` event

The `link:../../apidocs/io/vertx/ext/shell/io/EventType.html#SIGWINCH[SIGWINCH]` event is fired when the size of the terminal changes, the new terminal size can be obtained
with `link:../../apidocs/io/vertx/ext/shell/io/Tty.html#width--[width]` and `link:../../apidocs/io/vertx/ext/shell/io/Tty.html#height--[height]`.

=== Command completion

A command can provide a completion handler when it wants to provide contextual command line interface completion.

Like the process handler, the `link:../../apidocs/io/vertx/ext/shell/command/CommandBuilder.html#completionHandler-io.vertx.core.Handler-[completion
handler]` is non blocking because the implementation may use Vert.x services, e.g the file system.

The `link:../../apidocs/io/vertx/ext/shell/cli/Completion.html#lineTokens--[lineTokens]` returns a list of `link:../../apidocs/io/vertx/ext/shell/cli/CliToken.html[tokens]`
from the beginning of the line to the cursor position. The list can be empty if the cursor when the cursor is at the
beginning of the line.

The `link:../../apidocs/io/vertx/ext/shell/cli/Completion.html#rawLine--[rawLine]` returns the current completed from the beginning
of the line to the cursor position, in raw format, i.e without any char escape performed.

Completion ends with a call to `link:../../apidocs/io/vertx/ext/shell/cli/Completion.html#complete-java.util.List-[complete]`.