A GUI for ps(1) Built with Mozilla
One of the more powerful features of Linux is the simple way that new commands can be constructed using aliases, shell scripts and other textual tricks. These techniques rely on a command-line interface, but what if you need a tool with a GUI interface? Few techniques exist that are both easy to use and professional looking. This article discusses a promising technique that uses the Mozilla platform. It focuses on a rather hard but standard problem: how to display the hierarchical information delivered by the ps(1) command usefully. A recent version of Mozilla (at least 1.4) is required.
Numerous GUI toolkits are available for Linux, from Xt to Tcl/Tk. Tutorials for these kits usually start with a button example. That's very routine, so let's see it and move on. In Mozilla, GUIs are described using XML syntax. A document named button.xul that specifies a button looks like this:
<?xml version="1.0"?> <window xmlns="http://www.mozilla.org/keymaster/ ↪gatekeeper/there.is.only.xul"> <button label="Press Me"/> </window>
The unmanageably long string, http://www.mozilla.org/keymaster/gatekeeper/etc..., tells Mozilla this file isn't HTML. It's instead XUL, a GUI description language that is Mozilla-specific and a dialect of XML. Make the button's window appear with this command:
mozilla -chrome button.xul
This example is simple and not worth dwelling on, although there's a lot going on even for a simple button. A ps(1) display is a far more ambitious goal, so let's leap forward.
Instead of the simple <button> widget, one of Mozilla and XUL's bigger guns is required, the <tree> widget. Some coding also is required and a lot more XML. Here, the focus is on fast development, not on seamless perfection. The coding part comes first.
To begin, ps(1) does the initial data gathering. Listing 1 shows the file psdata.ksh, with mode 777.
Listing 1. A Command-Line Wrapper for ps(1)
#!/bin/ksh export COLUMNS=300 ps h -ew -o '%p,%P,%C,%x,%z,%G,%n,%U,%a' \ > /tmp/psdata
The output holds all the interesting fields, comma-separated with no header line. Mandatory components are PID and PPID; the rest are optional but informative fields, such as COMMAND. That's all traditional Linux requires.
cd mozilla ./configure --disable-debug make make install
Debug versions are slow and have messy diagnostics; although harmless, they're avoided here. The build takes an hour-plus to finish and requires up to 1GB of space. The resulting binaries are located in mozilla/dist/bin. They can be run from that directory or from anywhere if the MOZILLA_FIVE_HOME and LD_LIBRARY_PATH environment variables are set and exported to that directory's absolute path. Now all the required binaries and shell scripts are available.
XPCOM is an implementation of Microsoft's COM, and it works portably on Linux/UNIX, Windows and Macintosh. It's restricted to a single process at the moment; there's no DCOM. XPCOM/COM is the fastest way to add new functionality to a scripting environment. It hooks up a compiled (say C or C++) object to an object reference in the scripting language. The nearest Perl equivalent is XM, but XPCOM does not require the re-linking that XM demands. Mozilla includes thousands of XPCOM objects by default. XPCOM is not some Java-like virtual machine at work, however. XPCOM objects usually are compiled code that runs efficiently on the bare metal.
It might seem strange to use Microsoft ideas on Linux, but XPCOM is fully open source and occupies a UNIX niche that has long been unaddressed: Linux/UNIX lacks a useful intermediate-sized component model. There have been CORBA and dynamic link libraries in the past, but those things are, respectively, very heavyweight and very lightweight. XPCOM is suited perfectly to middle-sized jobs, to application development of large binaries and to performance-critical work. Here it's simply extremely handy.
Use of XPCOM or COM typically includes many calls to the Windows QueryInterface() method. For the sake of Linux programmer sensibilities, this article uses createInstance() and getService() instead. QueryInterface() is available too.
Back to the code. Let's suck up the output of the ps(1) wrapper. Listing 2 shows how.
- Bruce Nikkel's Practical Forensic Imaging (No Starch Press)
- Transitioning to Python 3
- Progress on Privacy
- Stepping into Science
- Linux Journal December 2016
- Radio Free Linux
- The Tiny Internet Project, Part II
- CORSAIR's Carbide Air 740
- FutureVault Inc.'s FutureVault
- A Better Raspberry Pi Streaming Solution