Experiments with runtime-less app-bundles

I’ve always admired the klik
project. At least the idea behind it, because the implementation was
(by necessity) a bit ugly. There are two main problems, first it uses
loopback mounts (requires root/setuid, has weird limitations, etc) and
secondly it does relocation of packages (so that they can find their
files) by patching the binaries.

Thats why I was so happy when i saw this blog
entry
. The klik developers are working on klik2, which is working
to clean up these issues. I took a quick look at it, and they seem to
be using fuse instead of loopback, which is very good. To solve the
relocation problem they seem to be using fuse to implement a full
union filesystem.

I wasn’t completely sold on the union filesystem thing, as I thought
that was a bit overkill. All that really needs to be virtualized is one
directory, accessible via a fixed name for the running
application. Then you can install things in that given prefix, make a bundle of it and it
will run fine.

A few days later when I was lying on the beach I came up with various ways this
could be done. After a while I realized that it would be
possible to use fuse to do a runtime-less bundle system. In other
words, all you needed to install was a standard fuse, and then you can download the
app as one file and just run it. I’m on vacation and shouldn’t even be
touching my computer, but I was so exited about this idea I just had
to see if I could do it. After a day or so of hacking, i was able to make it work.

Introducing glick

glick bundles are standard ELF files that you can run like any other
binary, but they contain all the files, libs and binaries needed for
the application in that one file. glick files embedd a filesystem in the
rodata section of the elf file, and the startup code contains a fuse
filesystem to mount this filesystem, and some magic glue code to make
it all work together.

Here are two example bundles: fancy
greeter
, katach.

I’ve tested these on F7, and it seems to work. (fuse-lib and fuse must be
installed, and due to a broken Fedora packaging of fuse the user must be a
member of the fuse group or fusermount needs to be chmod:ed).

Implementation details

On application launch the glick code is started. It then creates a
temporary directory where the filesytem will be mounted. Then it creates a pipe to
talk to a child process and forks. The child process starts up the
fuse filesystem (reading from the automatically mmap:ed rodata section) and mounts that on the temp directory. Then it talks
to the parent by writing to the pipe.

The parent code opens the directory, and uses dup2() to store the file
descriptor to it as fd number 1023. Then it creates (or ensures that
there already exists) a symlink called “/tmp/glick_root” that points
to “/proc/self/fd/1023”. This is the magic part. Since we have the
target directory opened at fd 1023 the /tmp/glick_root symlink will
point to the mounted fuse filesystem for this process and all its children
(that don’t close fd 1023). This means the code in the filesystem can
hardcode /tmp/glick_root to access their files. (Having this in /tmp
is slightly non-ideal. A better place would be in say /var/glick_root,
but then it wouldn’t be a runtime-less system
).

Then it reads from the pipe, waiting for the mount to finish. When the
mount is up it it execs /tmp/glick_root/start which is a script or a
symlink that starts up the real application inside the fuse mount.
When the application dies the pipe to the fuse child is automatically
closed. The child notices this, unmounts the mount, removes the
temporary mountpoint and exits.

Licensing issues?

The implementation uses e2fsprogs-libs to implement the fuse
filesystem (based on fuse-ext2 by Jeff Garzik). This library is
unfortunately GPL, which is not ideal for maximal reuse (my code in glick is X-style
BSD). Does embedding an ext2 filesystem inside an elf
file that contains GPL code make the files in the filesystem a derived
work of the GPL code, or is this a “mere aggregation” in the terms of
the GPL? License questions abound… Anyway, perhaps using fuse-cramfs
that the klik people are working on is a better idea anyway.

The code

The code is availible via git here. Play
around with it, experiment, have fun. I did.

Note: The name “glick” is a pun on the kde:ish name of the the klik
project. This is not ment to be a slight or anything like that. I
greatly appreciate the work of the klik project (although I do believe
that their choice of name unfortunately is costing them mind-share
amongs gnome users). Hopefully the klik people will look at my work and steal the appropriate ideas.

31 thoughts on “Experiments with runtime-less app-bundles”

  1. The remaining problem is that the icon is the standard executable icon, and the label is “katachi”. Next step embedding desktop files directly into the binary? 🙂

  2. I was just wondering whether there was any trouble running two glick apps simultaneously. (I cannot test the two above right now 🙂

  3. This is cool stuff, but here are some issues with your approach

    – there’s no way to discover glick apps stored in, say, ~/Applications, /media/*/Applications or /Applications. Apps stored here should show up in the menus (e.g. desktop files)
    – setuid binaries won’t work
    – apps providing D-Bus services won’t work
    – apps providing plug-ins won’t work
    – apps consuming plug-ins won’t work

    I think the latter three is the reason that you need union mounts; e.g have some union of the payload of the files in /Applications, /media/*/Applications and ~/Applications appear in some per-user directory for example ~/.local/. Obviously you get into issues with potential file conflicts here.

    Then you need to fix things consuming glick apps to look there, e.g. for D-Bus session bus services it needs to look in ~/.local/share/dbus-1/services (I think D-Bus already does, but there are plenty other examples)

    And even when you done this, it won’t work for things providing system-wide things like (not exhaustive)

    – kernel modules (/lib/modules/`uname -r`)
    – D-Bus system services (/usr/share/dbus-1/system-services)

    For this you can do crazy symlinks but I bet any solution has security issues.

    Of course you can just punt all these issues saying “it’s not for platform style stuff, a fine platform is provided by the OS already” but I’m not sure that’s an adequate answer for Desktop Linux just yet.

  4. davidz:
    All your comments are true, but there are still many applications for which it is good enough.

    The most important issue you mention is imho getting glick apps into the desktop menu. If we did that then the other issues can be punted to distro packages.

  5. Pretty sweet, dude!

    Sadly, there is still a little ways to go before we get distribution independence. That is, your shit does not work on SUSE. 😉

    [joe@posthaste ~]$ ./fancy_greeter
    ./fancy-greeter: symbol lookup error: /tmp/glick_root/lib/libclutter-cairo-0.3.so.0: undefined symbol: cairo_set_user_data

    [joe@posthaste ~]$ ./katachi
    /tmp/glick_root/bin/katachi: error while loading shared libraries: libselinux.so.1: cannot open shared object file: No such file or directory

    selinux? I mean, seriously. 😉

  6. Hi, Alex,

    thanks a lot for picking up the ball. 🙂

    Your idea is stunning, and the fact that you came up with some working code after such a short time even more so.

    I’ve seen your blog a few hours ago when my notebook picked it up while I was on an GPRS mobile connection. The connection broke, and I saved the story to my disk… but it wasn’t complete. I only now can see your last few paragraphs and the comments section.

    Do you wanna come talk to us on IRC? (Next klik developer meeting on #klik on Freenode, Saturday 21:00 h CEST — that’s UTC +2:00 h)

  7. alexl: oh, yeah, I agree this is good enough. It’s super awesome too. And at some point we’ll have a “good enough” platform such that apps won’t need to use many of the things I mention. At least not the system-wide stuff. That should be part of the platform.

    For session-wide stuff.. I’m still not convinced.. I still think it would be interesting to explore having ~/.local (maybe rather, ~/.glick-local) being a union mount of some of the payload in the glick archives stored in /Applications, /media/*/Applications and ~/Applications. Maybe explode everything in ./local/ inside the glick fs to ~/.local? Then suddenly a lot of things just start working… desktop files, dbus session bus services, icon theming, basically anything that honors the XDG Base Directory Specification.

  8. /tmp probably isn’t the best place… perhaps attractive from the “needs no setup” angle, but very painful from a security one

  9. I feel so dirty. But so proud.

    Sounds a tad like what we’re doing with bundles on OLPC but a lot more contained.

    I worry that all of these approaches end up spending all of their time working around busted platforms and trying to wedge single-binary or single-tarball approaches mapped onto a classic filesystem. Instead of just taking the leap and making a sane platform decision.

  10. It seems a bit silly hard-coding filesystem paths for speed, but then mounting over FUSE to make it relocatable again! Any plans to make GNOME apps just use relative paths in the first place?

    A similar, but lighter-weight, alternative is this one:

    http://thread.gmane.org/gmane.comp.file-systems.zero-install.devel/1683/focus=1684

    It uses LD_PRELOAD to map all /tmp/PKG paths to wherever the package actually is. The actual packages are installed using Zero Install, although the mapping only works for the main program; libraries have to be relocatable otherwise they can’t be shared between programs. I guess you could fix that by using a different hard-coded path for each library.

  11. Thomas:
    Where in the world did you get the idea that hard-coding paths is for speed? Hard coding paths is to avoid having to change applications and libraries so that they are relocatable (since almost none are). (And glick is not just about gnome programs!)

    LD_PRELOAD doesn’t feel very solid to me. I’m sure it can break in a bunch of ways. I prefer the real syscall interface fuse gives. I doubt there is a performance issue anyway.

  12. Did you ever look into Zero Install? I quite like their decentralized approach, and been following them for a while.

    I like their idea that software on your harddrive should just be a cache for what’s on the internet. (I like that idea so much that I’d like to see it generalized, for software and data)

    I’m ashamed to admit I haven’t looked into Klik properly before, but will do that now.

    Here’s some links:
    http://0install.net/goals.html

    Basic comparison with Klik and others:
    http://0install.net/matrix.html

  13. OK, apparently I’ve missed something important here, because I don’t see the point. Distributions have perfectly good package management systems for precisely this reason. I don’t *want* some random program coming with its own versions of libraries. I want a nice packaged program that I can install with $PKG_MANAGER_OF_CHOICE (aptitude in my case).

    Please, help me understand why I would want this. What makes a glick bundle better than a package in my distribution?

  14. nona:
    Zero install isn’t really an app-bundles approach. What i like about app bundles is that you have only one file, you can move it around and put it where you want, delete it, rename it, store it on a usb stick or whatever. There is no hidden cache dir and complicated “automatic setup” going on in the background.

    Anon:
    I don’t see glick as totally replacing package management. But it is a very useful alternatives for some applications. Be it beta versions of apps (that use some unstable version of libs you don’t want to install on your system), apps you just want to test out, or unpackaged third party applications.

    For instance, you can (ideally) just download and run the two bundles i linked to above. But there is little chance you would find them in debian, since they are basically built from svn/git/bzr of various unreleased and unstable modules. The only other way you could try these is to build them yourself in a separate prefix. Thats not something most people will do.

  15. alexl: Have you thought of generating glick files from the Zero Install feeds? The feeds are XML, and tell you exactly where to download each component. e.g. example feed:

    http://rox.sourceforge.net/2005/interfaces/ROX-Filer

    There’s already a Zero2Bundle program that does this, but creating ROX application directories rather than ELF files:

    http://rox.sourceforge.net/desktop/Zero2Bundle

    It would be good to standardise on a format for programs requiring mappings from hard-coded paths. Currently, the few Zero Install programs that need this explicitly depend on a specific mapping library (e.g. the LD_PRELOAD one), but a more general way to state “I need all access to /some/path/… mapped to wherever I really am” would let different tools be used.

    I assumed that GNOME apps hard code paths for speed because the source code already allows any location to be used (e.g. load(PREFIX “/MyData”)); it’s only at compile time that this flexibility is lost. Maybe there is some other reason. It could just be that joining strings in C was difficult, so people didn’t bother.

    nona: a better comparison with Klik (with text rather than just boxes) is here:

    http://0install.net/links.html

    However, that’s still only for the current Klik version 1.

  16. Thomas:
    I haven’t thought much about it at all. This is mainly an experiment to see if it would work.

    I think in general apps are not relacatable for two reasons:
    1) Its more work
    2) No many users require it (or test it)

    Its also not standardized how you’d set up the prefix for running the app, such that all libs could theoretically pick it up.

  17. Very interesting alexl!

    Another way to make an application relocatable is to use our binreloc code (http://www.autopackage.org/docs/binreloc/). We have two versions, one that require some code changes, and one that uses a dynamic prefix through the proc filesystem. Linux only though.

    As Joe Shaw experienced, there are more problems with shipping distribution neutral packages than wrapping them in a container and making them relocatable. The binary incompatibilities between distributions, and even versions of distributions, requires compilation tweaks to make it work. libc and gcc does not support “build once, run everywhere” out of the box, and there’s tons of “Oh-by-the-way”‘s to consider. For Autopackage, we have developed and use APBuild (http://www.autopackage.org/aptools.html) which is a perl script around gcc/g++ that addresses many of the issues.

    For self contained apps, the appfolder style bundle is quite useful, but as soon as the apps depend on each other (e.g., plugins) or it contains files used by the system such as dbus session files, fonts, images/icons, desktop files) it needs to go to a location where the system can find it.

    That’s enough pessimism for now, and all things considered, I think this is a very cool investigation project! 🙂

  18. Awesome idea Alex. There’s some talk internally about trying to get VMware WS or Player running as a glick package. We already run very well with relocatable install locations, but it’ll stress glick viability given a large, complex app. I’ll keep you posted 🙂

  19. Hats off, Alexander. From what I read, your implementation is way cleaner than ours. Will it work with unmodified binaries? (E.g. RealPlayer, Skype, Picasa,….) These are the most frequently downloaded applications from klik, since they are not frequently distributed due to their closed-source nature. Also, “klik2” is centered around “application virtualization” (also known as “Portable Apps” in the Windows world), so that you can take the application and all its related data with you, e.g., on a USB stick.

    “Hopefully the klik people will look at my work and steal the appropriate ideas.” –> We’d definitely like to invite you to our next developer meeting (http://klik.atekon.de/wiki/index.php/Developer_meetings), and to #klik on freenode. This invitation also extends to other interested developers (= all frequent readers here).

    As for the “k” in klik, it is no longer specifically related to KDE. We do have GNOME and CLI interfaces Today, “klik” has no special meaning besides its resemblance to “click”.

    Again, “hats off”. I’m going to test it now…

  20. Now this is the first implementation of app bundles on linux that I would seriously recommend to others if you continue it. With fuse being the only hard requirement and possibly enhancing the portability with help of the already mentioned autopackage tools, this would be perfect for testing versions of applications or for trying out new apps very quickly.

    Imho the only unclear point is the desktop file/ icon issue. Maybe a workaround is an option, for example the app provides an option to install the desktop file and icon to ~/.local/share/ along with a subtle uninstall option, similar to what autopackage does. In any case this is a minor problem imho, since we don’t want long-time users anyway but rather encourage users to use the distribution package of a stable version if they are going to use it more often.

  21. Hello,

    this is one of my old way to solve thinks taking into account an evolution starting from ROX AppDir, OSX Bundles/DMG handling, Klik gateways and others.
    I explored also an os centric mode with mount bind into a live tmp filesystem but everything pass through the root…and it’s bad for me.
    I’m working now into a solution that does not take into account of oscentric and esoteric out of rootless peoples control.
    My vision is 100% user centric.
    To let you understand what I’m going to do just point to some of my experimental packages here:
    Oldest one: http://sourceforge.net/project/showfiles.php?group_id=43393&package_id=151348
    Newest one:
    http://sourceforge.net/project/showfiles.php?group_id=199098
    Whith what I’ve renamed as “SpatialBundles” I’ve no requirements other than: Bash.
    So no root, no fuse, no tmpfs, no mount.
    Nash it’s not a striclty requiremente in the future because for performance reasons I can choose to use and ELF file directly like glik but my point of view in terms of usability take into account to let better developers to manipulate directly the Bundles using the standard bash shell.
    My technology now use directly Makeself to make a single file runnable where there is embedded al what I call as “CrossBundles” that let me run the bundle on a Linux/BSD/OSX platform depends on how I ‘bundled’ the runnable objects.
    This days I’m solving newer issues to permit a good mime interaction bundling Java6 with very good and promising results.
    My technology it’s not so publicy available because I’m trying to develope a tool to permit developers to freely build their own packages without lost into the developing tools world as you can do into a generic tool like *pkg*, xcode, and so on…trying to reduce to the max complexity and offer to developer something so banal tha should be 100% transparent…
    The skeleton of my SpatialBundles are made by a nativa recompilation against:
    /tmp/Programs/
    than repackaged with Makeself to make single file runnable where it acts as a gateway if there where a previous run (speeding up the run) or,as Makeself do, extract into /tmp/Programs and then run the main binary.
    Peoples manipulate spatially the original Bundle doing thinks like, duplicate, sending via mail/bluetooth etc etc, renaming, trashing…nothing is installed, nothing require installation and a two fase turbo cache (disk and ram) could be activated to improve future runs into a os restart session.
    After the first run the Bundle is shared with al the system user so they could feel and improvement of speed for their first run…
    I’m working to better thinks and include features…it’s just a matter of time 😉

    In the mean time you can simply reverse packages due to it’s native Makeself-ish
    Hope next day could give you more.
    Tnx to Alexander for your work,

    Luca Cappelletti

  22. I would say also that when I was in Dapper I made a little experiment embedding a .desktop file into the header of a Makeself runnable file and Nautilus parses it well showing me and Icon!!! like in a classical .exe windows file…
    But everything goes away from future nautilus (a bug? a feature? I never know what happen to nautilus to stop to parse that files..).
    Another side of the architecture I made is that it use ROX AppDir as Bundle format so when the Makeself extract the Bundle into /tmp/Programs you canfly with Rox Filer and click like a natural AppDir due to it’s /tmp/Programs/TEST/Linux-ix86/bin structure and an AppRun, .DirIcon etc etc gateway objects that let ROX an easy parsing.
    The word Programs was taken from Gobo Linux world and I think it’s better than the word Application used into OSX because “programs” is a word that everyone easy understand today (my mother too ;-).

    Luca Cappelletti

  23. Hello to all “packaging gurus” around,
    Everybody is talking about how (k/g)lick cannot replace centralized package managament. While I agree that the “base system” must be managed with something like apt, is there a reason why a distribution could not be built using apt for the “system” and something like klik for the applications?

Leave a Reply to Joe Shaw Cancel reply

Your email address will not be published. Required fields are marked *