# 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 `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

    vertx run -conf '{"telnetOptions":{"port":5000}}' maven:io.vertx:vertx-shell:3.1.0

or

Starting a shell service available via SSH

    # 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:

    vertx.deployVerticle("maven:{maven-groupId}:{maven-artifactId}:{maven-version}", {
      "config" : {
        "telnetOptions" : {
          "host" : "localhost",
          "port" : 4000
        }
      }
    });

or

    vertx.deployVerticle("maven:{maven-groupId}:{maven-artifactId}:{maven-version}", {
      "config" : {
        "sshOptions" : {
          "host" : "localhost",
          "port" : 5000,
          "keyPairOptions" : {
            "path" : "server-keystore.jks",
            "password" : "wibble"
          },
          "config" : {
            "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 `ShellService` takes care of starting an instance of Vert.x Shell.

Starting a shell service available via SSH:

    var ShellService = require("vertx-shell-js/shell_service");
    var service = ShellService.create(vertx, {
      "sSHOptions" : {
        "host" : "localhost",
        "port" : 5000,
        "keyPairOptions" : {
          "path" : "server-keystore.jks",
          "password" : "wibble"
        },
        "shiroAuthOptions" : {
          "type" : 'PROPERTIES',
          "config" : {
            "properties_path" : "file:/path/to/my/auth.properties"
          }
        }
      }
    });
    service.start();

Starting a shell service available via Telnet:

    var ShellService = require("vertx-shell-js/shell_service");
    var service = ShellService.create(vertx, {
      "telnetOptions" : {
        "host" : "localhost",
        "port" : 4000
      }
    });
    service.start();

The `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 `telnetOptions`, the `TelnetOptions` extends the `NetServerOptions` so they have the exact same configuration.

## SSH configuration

The SSH connector is configured by `SSHOptions`:

- `port`: port

- `host`: 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:

- `shiroAuthOptions`: configures user authentication

The server key configuration reuses the key pair store configuration scheme provided by Vert.x Core:

- `keyPairOptions`: set `.jks` key pair store

- `pfxKeyPairOptions`: set `.pfx` key pair store

- `pemKeyPairOptions`: set `.pem` key pair store

## Base commands

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

1. Verticle commands

   1. verticle-ls: list all deployed verticles

   2. verticle-undeploy: undeploy a verticle

   3. verticle-deploy: deployes a verticle

   4. verticle-factories: list all known verticle factories

2. File system commands

   1. ls

   2. cd

   3. pwd

3. Bus commands

   1. bus-tail: display all incoming messages on an event bus address

   2. bus-send: send a message on the event bus

4. Net commands

   1. net-ls: list all available net servers, including HTTP servers

5. Shared data commands

   1. local-map-put

   2. local-map-get

   3. local-map-rm

6. Metrics commands (requires Dropwizard metrics setup)

   1. metrics-ls: show all available metrics

   2. metrics-info: show particular metrics

7. Various commands

   1. echo

   2. sleep

   3. help

   4. exit

   5. logout

8. Job control

   1. fg

   2. bg

   3. 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 `CommandBuilder.command` method: the command process handler is called by the shell when the command is executed, this handler can be set with the `processHandler` method:

    var CommandBuilder = require("vertx-shell-js/command_builder");
    var CommandRegistry = require("vertx-shell-js/command_registry");

    var builder = CommandBuilder.command("my-command");
    builder.processHandler(function (process) {

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

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

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

After a command is created, it needs to be registed to a `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 `CommandProcess` object can be used for interacting with the shell.

### Command arguments

The `args` returns the command arguments:

    command.processHandler(function (process) {

      Array.prototype.forEach.call(process.args(), function(arg) {
        // Print each argument on the console
        process.write("Argument " + arg);
      });

      process.end();
    });

Besides it is also possible to create commands using `Vert.x CLI`: it makes easier to write command line argument parsing:

- option and argument parsing

- argument validation

- generation of the command usage

    var CLI = require("vertx-js/cli");
    var CommandBuilder = require("vertx-shell-js/command_builder");
    var cli = CLI.create("my-command").addArgument({
      "argName" : "my-arg"
    }).addOption({
      "shortName" : "m",
      "longName" : "my-option"
    });
    var command = CommandBuilder.command(cli);
    command.processHandler(function (process) {

      var commandLine = process.commandLine();

      var argValue = commandLine.getArgumentValue(0);
      var 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:

    var CLI = require("vertx-js/cli");
    var CommandBuilder = require("vertx-shell-js/command_builder");
    var cli = CLI.create("my-command").addArgument({
      "argName" : "my-arg"
    }).addOption({
      "argName" : "help",
      "shortName" : "h",
      "longName" : "help"
    });
    var command = CommandBuilder.command(cli);
    command.processHandler(function (process) {
      // ...
    });

### Terminal size

The current terminal size can be obtained using `width` and `height`.

    command.processHandler(function (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 `session`:

    command.processHandler(function (process) {

      var 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 `setStdin` handler to be notified when the shell receives data, e.g the user uses his keyboard:

    command.processHandler(function (process) {
      process.setStdin(function (data) {
        console.log("Received " + data);
      });
    });

A command can use the `stdout` to write to the standard output.

    command.processHandler(function (process) {
      process.stdout().write("Hello World");
      process.end();
    });

Or it can use the `write` method:

    command.processHandler(function (process) {
      process.write("Hello World");
      process.end();
    });

### Process termination

Calling `end` ends the current process. It can be called directly in the invocation of the command handler or any time later:

    command.processHandler(function (process) {
      var vertx = process.vertx();

      // Set a timer
      vertx.setTimer(1000, function (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 `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:

    command.processHandler(function (process) {
      var vertx = process.vertx();

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

      // When user press Ctrl+C: cancel the timer and end the process
      process.eventHandler('SIGINT', function (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 `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 `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

    command.processHandler(function (process) {

      // Command is suspended
      process.eventHandler('SIGTSTP', function (event) {
        console.log("Suspended");
      });

      // Command is resumed
      process.eventHandler('SIGCONT', function (event) {
        console.log("Resumed");
      });
    });

#### `SIGWINCH` event

The `SIGWINCH` event is fired when the size of the terminal changes, the new terminal size can be obtained with `width` and `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 `completion handler` is non blocking because the implementation may use Vert.x services, e.g the file system.

The `lineTokens` returns a list of `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 `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 `complete`.