Chapter 4. Using the Gogo Shell and Commands

Although not defined as an OSGi specification, all (non-embedded) OSGi frameworks have had a console to provide a means to interact with the framework. Some, such as Equinox, had a console built into the core JAR; others, such as Felix, provided console services through separate bundles.

In this chapter, we'll look at the Gogo shell, which is used by Felix and Equinox, and learn how to write commands in Gogo script as well as Java.

Consoles in Equinox

Until the end of Eclipse 3.7, Equinox supported a built-in console that was available by running the org.eclipse.osgi JAR file with a -console argument:

$ java -jar org.eclipse.osgi_3.7*.jar -console
Framework is launched.
osgi>

With the release of Eclipse 4.2 (Juno) and onwards, this console is no longer available by default:

$ java -jar org.eclipse.osgi_3.8*.jar -console

This is because the implementation provider for the console defers to the Gogo shell, which was developed by the Apache Felix project.

Tip

Eclipse 4.2 and 4.3 provided an osgi.console.enable.builtin flag to enable the older console, but this was removed in Eclipse 4.4 (Luna).

With Equinox 3.8 (Eclipse Juno 4.2 and above), it is necessary to install additional bundles at start-up to provide a console. This is covered in the Running Equinox from the command line section later in this chapter.

Host OSGi Console

The easiest way to experiment with the console is to use Eclipse's Console View. This is typically used for seeing the output of running Java programs, but in fact the console view can show many other types of consoles as well. In the top-right corner of the view, there is a dropdown that can show alternative consoles.

Host OSGi Console

Choosing the Host OSGi Console action creates a new Gogo shell. It warns that the console is connected to the running Eclipse instance; typing exit will call System.exit and terminate Eclipse and the JVM:

Host OSGi Console

The console has a built-in help system that can be used to find out what commands are available and what their individual functions are. The help command will provide a list of all the available commands, and running help command will give more information:

osgi> help getprop
getprop - displays the system properties with the given name, or all of them
   scope: equinox
   parameters:
      String[]   name of system property to display
osgi> getprop os.name
os.name=Mac OS X

Running commands

Each command has a scope and a name. Optionally, it may require a number of parameters. Many commands take no arguments, but the help text should say what is required. Some commands have limited help, but they will display additional information when run with no arguments.

Commands may be prefixed with their scope to avoid ambiguity. These two commands are therefore equivalent:

osgi> echo Hello World
Hello World
osgi> gogo:echo Hello World
Hello World

Disambiguation is necessary for some command names that are defined in more than one scope. For example, the ls command is provided by both the equinox scope (to list the Declarative Services components) and by the felix scope (to list the contents of the current directory):

osgi> felix:ls
/Applications/Eclipse.app/Contents/MacOS/eclipse
/Applications/Eclipse.app/Contents/MacOS/eclipse.ini
osgi> equinox:ls
All Components:
ID State      Component Name
1  Registered org.eclipse.e4.core.services.preferences
2  Registered org.eclipse.e4.core.services.events
...

By default, the shell prints out a value after each statement. To disable this, run .Gogo.format=false (with the correct capitalization):

osgi> 'Hello World'
Hello World
osgi> .Gogo.format=false
osgi> 'Hello World'
osgi> .Gogo.format=true
true
osgi> 'Hello World'
Hello World

With formatting disabled, the echo or format commands can be used to display results. Along with printing the output, format will also return the value. This may result in two values being displayed if autoformatting is turned on:

osgi> echo 'hello'
hello
osgi> format 'hello'
hello
hello
osgi>

Variables and pipes

The shell has a way to set and get variables, which can be useful when interacting with bundle identifiers or names. Variables can be assigned with the equals sign (=) and can be evaluated with a dollar symbol ($), similar to Unix shell scripts. Identifiers start with an extended alphabet followed by alphanumeric characters (including underscores):

osgi> name = Alex
Alex
osgi> echo Hello $name
Hello Alex
osgi> id = 0
0
osgi> headers $id
Bundle headers:
 Built-By = e4Build
 Bundle-Description = OSGi System Bundle
 Bundle-SymbolicName = org.eclipse.osgi; singleton:=true

The special variable $_ is used to store the result of the last command. Other variables are also predefined; exception is used to store the result of the last exception and e is a function that will print out the last exception's stack trace. The set command will print out all the currently defined variables:

osgi> 'hello'
hello
osgi> echo $_
hello
osgi> set
null            0               null
String          SCOPE           equinox:*
null            _               null
Closure         e               $exception printStackTrace
HeapCharBuffer  prompt          osgi>
osgi> misteak
gogo: CommandNotFoundException: Command not found: misteak
osgi> $exception
Command              misteak
Cause                null
Message              Command not found: misteak
osgi> e
  org.apache.felix.gogo.runtime.CommandNotFoundException:
   Command not found: misteak
  at org.apache.felix.gogo.runtime.Closure.executeCmd
  at org.apache.felix.gogo.runtime.Closure.executeStatement

Along with being able to assign variables from literal values on the command line, it is also possible to capture the output of a command and assign that to a variable. While the command cat copies output from a source to the console, tac works in the opposite direction:

osgi> contents = (felix:ls | tac)
/Applications/Eclipse.app/Contents/MacOS/eclipse /Applications/Eclipse.app/Contents/MacOS/eclipse.ini 
osgi> echo $contents
/Applications/Eclipse.app/Contents/MacOS/eclipse /Applications/Eclipse.app/Contents/MacOS/eclipse.ini

It is possible to pipe the content through other commands; the most useful one is grep, which can be used to search for specific patterns:

osgi> lb -s | grep osgi
   0|Active  |0|org.eclipse.osgi (3.9.1.v20140110-1610)
 185|Resolved|4|org.eclipse.osgi.services (3.3.100.v20130513-1956)
 186|Resolved|4|org.eclipse.osgi.util (3.2.300.v20130513-1956)
1103|Resolved|4|osgi.enterprise (4.2.0.v201108120515)

The grep command doesn't support a full set of POSIX arguments, but it does provide some options. The built-in help documentation does not show it, but if run without arguments, it provides more useful output:

osgi> help grep
grep
   scope: gogo
   parameters:
      CommandSession   
      String[]   
osgi> grep
grep: no pattern supplied.
Usage: grep [OPTIONS] PATTERN [FILES]
  -? --help                show help
  -i --ignore-case         ignore case distinctions
  -n --line-number         prefix each line with line number
  -q --quiet, --silent     suppress all normal output
  -v --invert-match        select non-matching lines
gogo: IllegalArgumentException: grep: no pattern supplied.

Functions and scripts

Besides providing an interactive Read Evaluate Print Loop (REPL), the console also permits the creation of functions and scripts. This allows common functions to be defined in a persistent file and then be reused between sessions.

A function is defined in curly braces, and it can use the special variables $args or $argv to refer to arguments, just like the Unix shell's $* or Windows' %*. It is possible to refer to the first nine arguments with $1 to $9, or $it as an alias for the first argument; $it is commonly used in each with an anonymous function, covered in the Processing a list with each section later in this chapter.

osgi> pwd
gogo: CommandNotFoundException: Command not found: pwd
osgi> pwd = {getprop user.dir}
getprop user.dir
osgi> pwd
user.dir=/Applications/Eclipse.app/Contents/MacOS
osgi> greeting = {echo Hello $args}
echo Hello $args
osgi> greeting World
Hello World

Functions can be saved in an external file and then loaded into a Gogo shell session. Create a file called fns in the temp directory (/tmp on Unix/OS X and c:TEMP on Windows) as follows:

# Lines beginning with # are comments

# Blank lines are also permitted
#     v  curly braces  v
pwd = {getprop user.dir}
greeting = {echo Hello $args}

From the Gogo shell, run the following:

osgi> source /tmp/fns # source c:TEMPfns on Windows
Loaded file successfully
osgi> pwd
user.dir=/Applications/Eclipse.app/Contents/MacOS
osgi> greeting Alex
Hello Alex

Literals and objects

Strings that are passed in are interpreted as string literals. Strings surrounded with double quotes allow replacement of variables with $, whereas those with single quotes do not perform replacement:

osgi> name=Alex
Alex
osgi> 'Hello $name'
Hello $name
osgi> "Hello $name"
Hello Alex

Numbers are available as both floating point and integers. By default, they are represented as Double and Long instances respectively. They can be used to pass into methods that expect smaller types (such as float and int) and are cast down automatically. Suffix flags of f and d are used to denote floating point values, but both are converted to Double:

osgi> lightspeed = 299792458
299792458
osgi> ncc = 1701d
1701.0

It is possible for lists and maps to be entered in literal form in the console. They are separated by spaces rather than commas, and the syntax for maps is almost the same, with the addition of keys:

osgi> numbers = [ 1 2 3 ]
1
2
3
osgi> words = [ one=1 two=2 three=3 ]
one                 1
two                 2
three               3
osgi> echo $one
null

Note

Note that using the syntax one=1 does not perform an assignment, as shown in the example. It defines a key for the map. If the key contains special characters or spaces, it must be specified in quotes.

There are explicit literals for boolean values true and false.

The console allows objects to be instantiated with the new command. The fully qualified name of the class is passed in along with any arguments:

osgi> new java.util.ArrayList
osgi> random = new java.util.Random
java.util.Random@768c5708

Calling and chaining methods

The values displayed on the console are actually Java objects, so true is a literal that maps to Boolean.TRUE and false maps to Boolean.FALSE. Similarly, integral values are represented under the covers as Long instances and strings are all instances of String.

The Gogo shell can invoke arbitrary methods on instance methods using the dot (.) operator. It's possible to chain more than one method call by using one dot operator after another:

osgi> "hello" . length
5
osgi> "hello" . getClass . getName
java.lang.String

Since the Gogo shell is dynamic and the methods are looked up dynamically, the methods can be specified in a case-insensitive manner:

osgi> "hello" . getclass . getname
java.lang.String

Although methods can be called in a case-insensitive manner, variable names are case sensitive. Note that the dot (.) operator can be left out for the first method call, as everything else will be interpreted as arguments:

osgi> $numbers . get 0
1
osgi> $words get one
1

Parentheses can be used to evaluate nested expressions, as in other languages:

osgi> ("hello" getClass) getName
java.lang.String
osgi> (("hello" getClass) getPackage) getName
java.lang

Control flow

The Gogo shell supports basic control flow, including if and each:

osgi> if {true} {echo Yes}
Yes
osgi> if {false} {echo Yes}
osgi> if {false} {echo Yes} {echo No}
No

Multiple commands can be put inside the braces, separated by semicolons:

osgi> if {true} {echo Yes; echo Still yes}
Yes
Still yes

There are also other functions such as not, which can be used to negate the result of a boolean expression:

osgi> if {not {true}} {echo Yes} {echo No}
No

Although there aren't built-in functions for logical operators such as and and or, it's possible to create functions to do this fairly simply:

osgi> and = { if {$1} {if {$2} {true} {false}} {false}}
osgi> or  = { if {$1} {true} {if {$2} {true} {false}}}
osgi> or true false
true
osgi> and true false
false

Finally, the each command allows iteration over an array of elements:

osgi> directions = [ "Up" "Down" ]
osgi> each $directions { echo $it }
Up
Down
osgi> each $directions { echo "->$it<-" }
->Up<-
->Down<-

The each command actually provides a map function (which takes an array of values), invokes a function on each element, and returns an array of results. This allows operations to be nested:

osgi> (each [ "" 1 true ] { ($it getClass) getName }) get 0
java.lang.String

Running Equinox from the command line

To launch Equinox as a standalone OSGi application with a Gogo shell, the minimal dependencies are as follows:

  • org.apache.felix.gogo.shell (provides the I/O processing and parser)
  • org.apache.felix.gogo.runtime (provides the language runtime)
  • org.eclipse.osgi (the Equinox kernel)

    Tip

    The org.apache.felix.gogo.command bundle provides a number of the built-in functions such as ls and start, as well as those that interact with repositories. It is useful but not necessary to run a basic shell.

To run Equinox as a launch in Eclipse, go to the Run menu and then choose Run Configurations... after which a dialog will appear. Choose OSGi Framework and set it up with the mentioned bundles (a quick way is to add the org.apache.felix.gogo.shell bundle, then deselect the Include optional dependencies option and click on Add Required Bundles). The resulting launch configuration now looks like the following screenshot:

Running Equinox from the command line

Click on Run and a console will be launched.

Tip

An exception may be thrown at start-up if the org.eclipse.equinox.console bundle is not found:

org.osgi.framework.BundleException: Could not find:
 org.eclipse.equinox.console
  at org.eclipse.osgi.framework.internal.core.ConsoleManager
   .checkForConsoleBundle(ConsoleManager.java:211)
  at org.eclipse.core.runtime.adaptor.EclipseStarter
   .startup(EclipseStarter.java:298)

To resolve the problem, add the org.eclipse.equinox.console bundle to the runtime.

To run from a command line instead of an Eclipse launch configuration, the bundles need to be specified as either relative files or URLs. Equinox supports the osgi.bundles system property, which provides a comma-separated list of the JARs that the framework should attempt to bring up at boot. Note that @start is required to bring the console up when the org.eclipse.equinox.console bundle isn't present:

$ java -Dosgi.bundles=
 org.apache.felix.gogo.runtime_0.10.0.v201209301036.jar@start,
 org.apache.felix.gogo.shell_0.10.0.v201212101605.jar@start 
 -jar org.eclipse.osgi_3.9.1.v20140110-1610.jar -console
osgi> bundles
0|Active|0|org.eclipse.osgi (3.9.1.v20140110-1610)
1|Active|4|org.apache.felix.gogo.shell (0.10.0.v201212101605)
2|Active|4|org.apache.felix.gogo.runtime (0.10.0.v201209301036)

Relative paths may be used by starting with ./ or file:./ and absolute paths may be used with / or file:///.

The -jar argument runs the org.eclipse.osgi JAR using Main-Class from the manifest (which is org.eclipse.core.runtime.adaptor.EclipseStarter in Equinox).

Finally, the -console argument is passed to the running Eclipse instance inside String[] args, which indicates that Equinox should start up the console.

Tip

The version numbers may differ; these were taken from Kepler SR2. Eclipse Luna (4.4.0) uses org.eclipse.osgi_3.10.0.v20140606-1445.jar as the entry point.

The Equinox commands (those in scope equinox:) are provided by the org.eclipse.osgi.console bundle. Adding this removes the exception highlighted previously and supplies some of the commands such as ss (short status) and b (bundle).

Understanding osgi.bundles and config.ini

The Equinox runtime can be configured in a couple of different ways. One way is to specify system properties on the command line with the -Dosgi.* parameters. (Despite being prefixed with osgi, they aren't standardized by an OSGi specification; they're all specific to Equinox.)

To prevent large command-line arguments, properties may instead be specified in a file called config.ini, which is stored in the configuration area of Eclipse. The configuration area is a directory that stores Equinox runtime information, and it is typically referred to as the configuration directory, since that is the default value. Running Equinox with -config can specify a different directory to be used.

One advantage of the config.ini file is that it can be updated by installers. P2 has a means to amend the contents of this file, which is used when updating between releases of Eclipse and in which the filenames (which have embedded version numbers) can be modified. Equinox reads the config.ini file and sets lines as system properties for the application.

When a stock Eclipse application runs, either the Equinox framework (if launched via the -jar option) or the eclipse.exe executable boots the JVM with the framework on the classpath, and then Equinox reads the osgi.bundles property (potentially set from the config.ini file) to bring it into a started state.

In the case of Eclipse, the org.eclipse.equinox.simpleconfigurator bundle is started, which reads a file named bundles.info, containing a list of bundles that need to be installed. Each line represents a single bundle, which is comma-separated and contains the following parts:

  • The bundle name
  • The bundle version number
  • The location of the bundle (either as a relative path or as a fully qualified URL)
  • The start level of the plug-in
  • Whether the bundle should be started

For example, the Gogo shell is installed with the following (as a single line):

org.apache.felix.gogo.shell,
0.10.0.v201212101605,
plugins/org.apache.felix.gogo.shell_0.10.0.v201212101605.jar,
4,
false

When bundles are installed via P2, the bundles.info file is updated to reflect the new state of the system. Upon restart, the new set of bundles are used. The file is written by the utilities in org.eclipse.equinox.simpleconfigurator.manipulator, and it is sorted alphabetically by the bundle identifier and then in reverse version order. When changes are made, the entries are updated and the sorting ensures minimal changes to the content.

Tip

Sorting the bundles in reverse version order means that the highest version is considered first.

Connecting remotely

The Gogo shell has a telnet daemon that can be used to listen for network connections. This can be started interactively from the console with telnetd, or it can be run from the command line with the -console argument and an associated port:

$ java -Dosgi.bundles=… -jar org.eclipse.osgi_*.jar -console
osgi> telnetd --port=1234 start

$ java -Dosgi.bundles=… -jar org.eclipse.osgi_*.jar -console 1234

Note that the console-with-port from the command line requires the org.eclipse.equinox.console bundle to be installed in addition to the Gogo shell. Alternatively, the system property -Dosgi.console=1234 can be specified at the command line or via the config.ini file.

Once the daemon is running, the Equinox process can be connected to via telnet:

$ telnet localhost 1234
Trying ::1...
telnet: connect to address ::1: Connection refused
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
osgi> bundles
org.eclipse.osgi_3.9.1.v20140110-1610 [0] Id=0, Status=ACTIVE

Securing the connection

While telnet is good for debugging, it is not a secure way of connecting to a networked machine. SSH provides a way of connecting securely to remote machines.

Equinox can start an SSH daemon, but it requires more bundles to be added as well as an appropriate means to verify users and passwords. Unlike the command-line console or the telnet daemon, the SSH service requires that the Equinox console implementation be available. The full set of bundles is as follows:

  • org.apache.felix.gogo.shell (provides the I/O processing and parser)
  • org.apache.felix.gogo.runtime (provides the language runtime)
  • org.eclipse.osgi (the Equinox kernel)
  • org.eclipse.equinox.console (the Equinox console service)
  • org.eclipse.equinox.console.jaas.fragment (adds JAAS support to the SSHD server)
  • org.eclipse.equinox.console.ssh (the SSHD server support)
  • org.apache.sshd.core (the SSHD server libraries)
  • org.apache.mina.core (needed by the SSH server libraries)
  • slf4j-api (the logging framework used by the libraries)

These bundles are available from the Equinox downloads page at http://download.eclipse.org/equinox/ and the Orbit downloads page at http://download.eclipse.org/tools/orbit/downloads/. The GitHub repository, https://github.com/alblue/com.packtpub.e4.advanced, associated with this book has a set of the required bundles along with a demonstration runtime in the com.packtpub.e4.advanced.console.ssh directory.

To start an SSHD server in Equinox, it is easier to use a config.ini file instead of passing in many arguments via the command line. (However, either approach will still work, so use whichever is more convenient.)

Creating a JAAS configuration

JAAS is used to provide user ID/password authentication. To do this, a JAAS configuration file needs to be created with an equinox_console entry. As with other Java programs, this login module is set with the java.security.auth.login system property.

Create a file called jaas.config in the configuration directory with the following content:

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

The java.security.auth.login property can be set in the config.ini file that lists the bundles required:

osgi.console.ssh=1234
osgi.console.ssh.useDefaultSecureStorage=true
org.eclipse.equinox.console.jaas.file=configuration/store
ssh.server.keystore=configuration/hostkey.ser
java.security.auth.login.config=configuration/jaas.config
osgi.bundles=
 ./org.apache.felix.gogo.runtime_0.10.0.v201209301036.jar@start,
 ./org.apache.felix.gogo.shell_0.10.0.v201212101605.jar@start,
 ./org.apache.mina.core_2.0.2.v201108120515.jar,
 ./org.apache.sshd.core_0.7.0.v201303101611.jar,
 ./org.eclipse.equinox.console.ssh_1.0.0...jar@start,
 ./org.eclipse.equinox.console.jaas.fragment_1.0.0...jar,
 ./org.slf4j.api_1.7.2.v20121108-1250,
 ./org.eclipse.equinox.console_1.0.100.v20130429-0953.jar

Understanding the configuration options

The osgi.console.ssh port 1234 is used to start up the SSH server. If this configuration line is missed out, the SSH server won't be started.

The osgi.console.ssh.useDefaultSecureStorage property is required if the SecureStorageLoginModule is used. It is possible to use alternative LoginModules here, but this is not covered in this book. See the tutorials on JAAS on the Java home page for more information.

The org.eclipse.equinox.console.jaas.file property specifies where the SecureStorageLoginModule writes the user/password values. If not specified, it uses configuration/store as default.

Note

The secure storage login module uses a fairly simple means to store hashed passwords. It first generates an MD5 hash of the password, concatenates the password with this hash, and then stores the resulting SHA1 hash. So, password becomes password5f4dcc3b5aa765d61d8327deb882cf99 and then ends up as 0d85584b3529eaac630d1b7ddde2418308d56317.

The ssh.server.keystore file contains a serialized Java object (java.security.KeyPair) of the host's SSH key, which is automatically generated and persisted on first run. It defaults to hostkey.ser.

Finally, java.security.auth.login.config is the standard JAAS property that refers to a configuration file that defines the JAAS modules. The final property, osgi.bundles, lists the bundles that are required and the ones that should be started.

Launching the SSH daemon

Now a console can be accessed via SSH:

$ ssh -p 1234 equinox@localhost
The authenticity of host '[localhost]:1234 ([::1]:1234)' can't be established.
DSA key fingerprint is 0c:40:ff:ba:0a:c8:bc:3d:a9:72:9f:05:5f:c6:96:35.
Are you sure you want to continue connecting (yes/no)? Yes
Warning: Permanently added '[localhost]:1234' (DSA) to the list of known hosts.
equinox@localhost's password: 
Currently the default user is the only one; since it will be deleted after first login, create a new user:
username: alex
password: 
Confirm password: 
roles: 
osgi> ss
"Framework is launched."
id State       Bundle
0  ACTIVE      org.eclipse.osgi_3.9.1.v20140110-1610
1  ACTIVE      org.apache.felix.gogo.runtime_0.10.0.v201209301036
2  ACTIVE      org.apache.felix.gogo.shell_0.10.0.v201212101605
3  RESOLVED    org.apache.mina.core_2.0.2.v201108120515
4  RESOLVED    org.apache.sshd.core_0.7.0.v201303101611
  Fragments=6
5  ACTIVE      org.eclipse.equinox.console.ssh_1.0.0...
6  RESOLVED    org.eclipse.equinox.console.jaas.fragment_1.0.0...
  Master=4
7  RESOLVED    org.slf4j.api_1.7.2.v20121108-1250
8  ACTIVE      org.eclipse.equinox.console_1.0.100.v20130429-0953

Note that the jaas.fragment bundle has been wired to the org.apache.sshd.core bundle, which allows the sshd.core bundle to connect to the SecureStorageLoginModule. In fact, an investigation of the jaas.fragment bundle shows that it is almost empty; the only thing it has is a manifest file with the following content:

DynamicImport-Package: org.eclipse.equinox.console.jaas
Fragment-Host: org.apache.sshd.core;bundle-version="0.5.0"

The preceding snippet says that the fragment's host is the sshd.core bundle, and it should add a DynamicImport-Package of the org.eclipse.equinox.console.jaas package. As a result, although the org.apache.sshd.core bundle doesn't know anything about the Equinox secure storage module, when the fragment is injected, it permits the bundle to be wired up to the Equinox bundle:

osgi> bundle 4 | grep equinox
org.eclipse.equinox.console.jaas; version="0.0.0"
 <org.eclipse.equinox.console.ssh_1.0.0.v20130515-2026 [5]>
org.eclipse.equinox.console.jaas.fragment_1.0.0.v20130327-1442 [6]

Fragments are covered in more detail in Chapter 5, Native Code and Fragment Bundles.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset