I'll look at creating an executable object for the Unix command line program `oggenc', which encodes WAV files as Ogg Vorbis files. (Ogg Vorbis is like the MP3 format, but a bit smaller and free of patent problems.) To invoke `oggenc' with Plash you might do:
oggenc input_file.wav => -o output_file.ogg (1)
In this case, the resulting process's file namespace will contain:
/usr/bin/oggenc (read-only)
/usr, /lib, /bin, /etc (read-only)
/dev/null (read-write)
under the pathname of the current working directory: input_file.wav (read-only), output_file.ogg (read-write slot)
/dev/tty (read-write)
However, it happens to be that `oggenc' doesn't need to access "/etc" or all of "/usr". We could define an executable object for running `oggenc' that gives the program an execution environment containing less:
def my_oggenc = capcmd exec-object '/usr/bin/oggenc' /x=(mkfs /usr/bin/oggenc /usr/lib /lib)
[This needs to be entered on one line when using the shell interactively. Alternatively, you can put it in a file and load it with "source <file>" -- each command or declaration must be terminated with ';'.]
This will create an executable object and bind it to the variable "my_oggenc". To invoke the object, we use the same syntax as before:
my_oggenc input_file.wav => -o output_file.ogg (2)
In this case, the the resulting process's file namespace will contain:
/usr/bin/oggenc (read-only)
/usr/lib, /lib (read-only)
under the pathname of the current working directory: input_file.wav (read-only), output_file.ogg (read-write slot)
/dev/tty (read-write) [actually, not included in current version]
While in (1), "oggenc" is treated as a filename and searched for in PATH, in (2), "my_oggenc" is recognised by the shell as a bound variable. The shell doesn't start a new process in this case, it just invokes the executable object that "my_oggenc" is bound to. The shell creates a namespace from the arguments, which it passes to "my_oggenc", but it doesn't include "/usr", "/lib", "/bin" and "/etc" as before -- the "my_oggenc" is expected to provide the files it needs itself.
Suppose we don't want to install "oggenc" and the libraries it uses in our system's "/usr" directory. Maybe we don't have access to that directory, because we don't have root access. Maybe we have older versions of those libraries in "/usr" which some other program uses, and we don't want to risk messing that program up by upgrading its libraries. Maybe we just want to organise our files differently from usual. Perhaps we are running RedHat, but a Debian distribution is installed under "/debian", and we want to use Debian's version of `oggenc'.
def my_oggenc = capcmd exec-object '/usr/bin/oggenc' /x=(mkfs /usr/bin/oggenc=(F /debian/usr/bin/oggenc) /usr/lib=(F /debian/usr/lib) /lib=(F /debian/lib))
[NB. This requires that Plash is installed in the Debian distribution as well, so that libc.so will still be taken from /usr/lib/plash/lib rather than /lib.]
These declarations still give `oggenc' a lot of files it doesn't need. We could give a tighter definition that lists exactly those files that `oggenc' needs in its execution environment. `oggenc' is fairly simple: it doesn't use a huge number of dynamically-linked libraries, and it doesn't need any configuration files.
Under Linux, we can find out the dynamic libraries that an executable file uses with the "ldd" command:
bash$ ldd /usr/bin/oggenc libvorbisenc.so.0 => /usr/lib/libvorbisenc.so.0 (0x40028000) libvorbis.so.0 => /usr/lib/libvorbis.so.0 (0x4009c000) libm.so.6 => /lib/i686/libm.so.6 (0x400bb000) libogg.so.0 => /usr/lib/libogg.so.0 (0x400dd000) libc.so.6 => /lib/i686/libc.so.6 (0x42000000) /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
[Run this under Plash using "!!ldd /usr/bin/oggenc".]
Given this information, we can make a new definition:
def my_oggenc = capcmd exec-object '/usr/bin/oggenc' /x=(mkfs /usr/bin/oggenc /usr/lib/libvorbisenc.so.0 /usr/lib/libvorbis.so.0 /lib/i686/libm.so.6 /usr/lib/libogg.so.0 /usr/lib/plash/lib/libc.so.6)
[Future work will be to provide tools to help with constructing a definition like this.]
("/lib/ld-linux.so.2" is the dynamic linker and doesn't need to be included.)
Suppose we want another program to be able to invoke `my_oggenc'. We can attach the object into a filesystem with a syntax like this:
bash + /my-bin/oggenc=my_oggenc
[NB. I don't use `/bin/oggenc=my_oggenc' because it's not yet possible to attach objects inside other attached directories, such as `/bin/oggenc' inside `/bin', which is attached implicitly.]
This runs Bash with the pathname `/my-bin/oggenc' mapped to `my_oggenc'. You can then run `my_oggenc' from inside Bash. This is a good way in general to test out the file namespaces that Plash creates.