How to check for a package/binary in a disk-friendly way? PackageKit — but how?

The nautilus-open-terminal extensions appends a menu item for opening Midnight Commander, but only if it is installed and the functionality is activated via GConf. Whether it is installed is currently checked via

g_find_program_in_path ("mc")

which does synchronous I/O and therefore blocks the UI when the disk spins up after standby (bug report). I see two obvious ways of fixing this:

  1. Only do the check once at startup. As a user, I’d hate this because if I installed Midnight Commander during runtime, I’d expect the menu item to show up immediatly.
  2. Use PackageKit instead of the above call for checking whether the midnight commander is installed

Clearly, it is preferrable to use PackageKit for such a task due to its caching facilities. But I need some help from the PackageKit experts out there: How are the names of the packages standardized, especially those not shipped with GNOME? The libpackagekit API description seems to suggest cryptic names like “hal;0.0.1;i386;fedora”. How can I specify the package name in a cross-distribution way? Wouldn’t we need a package-naming-spec, similar to the icon-naming-spec?

Update

Some interesting comments:

  1. Oliver asks whether MC installs any .desktop file and proposes to check its extistence. hron84 points out that MC doesn’t.
  2. Richard Hughes who started PackageKit provides sample code that synchronously calls a certain PackageKit D-Bus method for checking whether a certain package is installed, and says that package naming is inconsistent between distros which I shouldn’t care about because they can patch my code.
  3. Xavier Claessens points out that PackageKit reads from disk too, and proposes to use an async. version of g_find_program_in_path().
  4. foo proposes to call a threaded version of g_find_program_in_path() and wait with the UI addition until it returns.
  5. Morten Wellinder says that package checking through the distro does not answer the question whether the “mc” binary is present, since a user could have installed it manually as well.

Regarding comments 3.+4.: The idea of an asynchronous version seems to be attractive, but the way Nautilus currently manages its extensions does not allow that easily because all extension menu items are added synchronously as Nautilus notices that the displayed files or the selection changed.

Regarding comment 5.: I agree that it seems to be somewhat wrong to ask the package management system whether a binary is present, but the idea behind it was that the package management can have an in-memory cache and track whether any packages have been installed since the last access such that no disk access is required at all. At least if the package management has been written with efficient file monitoring. On the other hand, the $PATHs are not cached and monitored. I also agree that manual installations of  packages are possible, but this is somewhat rare for a “low-level” package like mc.

That said, an interesting third solution may be: Add a GIO-powered g_find_program_in_path() variant that uses file monitoring to track changes to all directories in the $PATH, and only does as few disk accesses as possible. Of course, in general it does not make sense to keep the entire contents of the $PATHs in memory, but just add a hash table of all programs that were requested and whether they were present or not. I don’t know how reliable file monitoring is, though.

13 Responses to “How to check for a package/binary in a disk-friendly way? PackageKit — but how?”

  1. oliver says:

    Does MC install an item in the “start menu” (Applications menu on Gnome, K menu on KDE)? If it does, maybe you could get a notification when the .desktop file is installed. Have a look at the FDO specs related to .desktop files…

  2. hughsie says:

    Depends, if you just want to check if a package is installed, just use a session method like http://www.packagekit.org/files/session.c — just use the org.freedesktop.PackageKit.Query.IsPackageInstalled method. Don’t worry about the package name, if a distro changed it from the default, then the distro can patch the application checking the package name. Trying to unite package names across distros is a project destined to fail. Jump on the PackageKit mailing list and we’ll do our best to help.

  3. Claessens Xavier says:

    I don’t see how packagekit can help, it will need to read on disk too… All you need is an async version of g_find_program_in_path. Maybe it exists in GIO?

  4. hron84 says:

    @oliver:
    mc is a console-based app, and i don’t think it has a desktop entry. But this is totally unrelated. The main question IMHO how can you find any binary in system.

  5. M Welinder says:

    I don’t get it. Nautilus wants to know if it can execute the “mc” command.
    (And, perhaps, if that “mc” is really midnight commander.) That is not a
    question that packagekit can answer. If I install mc from a tarball then
    packagekit won’t even know it.

    g_find_program_in_path, on the other hand, can answer the question.

  6. foo says:

    Package names are not standardised, but there is an effort to map between distros (see the Gentoo PackageMap Google Summer of Code project).

    Anyway, just run the above method in a thread and add the mc menu item when the thread returns instead of blocking the UI. You can have mc installed, but not in your path in strange situations. Or have mc in your path but not installed (built from source) What matters is whether or not mc is in the path, not that an mc package is installed.

  7. foo says:

    Obviously caching the return is a good idea, you should also listen for inotify and or PackageKit removal events in case mc gets uninstalled/reinstalled etc so that the UI is consistent with the filesystem state.

  8. djcb says:

    it might also be side to have the program still in the menu (maybe greyed-out), and upon selection offer to install it.

    but, the easiest solution may be to simply trigger a spin-up when you’re returning from suspend; at least easy enough to use before implementing something really smart. anything but checking for the binary in path is rather fragile, as are caches.

  9. Josh Stone says:

    > On the other hand, the $PATHs are not cached and monitored.

    The kernel’s page cache will almost surely have the PATH contents in memory. Are you sure that g_find_program_in_path is your culprit? I would try hard-coding just that line to the mc on your system, to see if the spin-up delays go away.

  10. […] here to see the original: How to check for a package/binary in a disk-friendly way … Leave a […]

  11. Calvin Walton says:

    One thing to note is that your shell has had in-memory caching of executables which are installed and where they are located for ages.
    The first time you run “mc” in a shell, it’ll do the full synchronous PATH lookup, but every following time it will just immediately run the executable from the cached location.
    There’s problems with the shell implementation – if you delete or move a program, you have to run “hash -r” (in bash) to reset the cache.
    But still, it wouldn’t be that hard to make a simple shared path-lookup cache. Make it add monitors on the executables it finds, and it can invalidate itself.

  12. Tollef Fog Heen says:

    It sounds suboptimal to check this using packagekit. A user might well have compiled mc locally, or it might be in /usr/local for whatever reason.