Tag Archives: lirc

gnome-lirc-properties: Using PolicyKit to get sudo access

Can’t get PolicyKit’s ObtainAuthorization() to work.

gnome-lirc-properties, which I just mentioned needs to edit /etc/lirc/lircd.conf, which requires sudo/root access. Various APIs exist to get temporary sudo/root access but everyone now seems to agree that the new PolicyKit system is the way to go. I’d like to link to a website for it. All the system administration control panels in Ubuntu Hardy Heron seem to use PolicyKit already.

But I can’t get the thing to work, maybe because I’m using Python, and I haven’t found any help so far. I have defined the PolicyKit mechanism, but I can’t get even a simple test of the ObtainAuthorization() method to do anything interesting. This python code does not return anything other than None from ObtainAuthorization() and none of the callbacks are called:

#!/usr/bin/python

import pygtk
pygtk.require('2.0')
import gtk

import dbus
import os

class TestWindow:
    def on_button_clicked(self, widget, data=None):

	#Call the D-Bus method to request PolicyKit authorization:

        session_bus = dbus.SessionBus()
        policykit = session_bus.get_object('org.freedesktop.PolicyKit.AuthenticationAgent', '/', "org.gnome.PolicyKit.AuthorizationManager.SingleInstance")
        if(policykit == None):
           print("Error: Could not get PolicyKit D-Bus Interface\n")

        gdkwindow = self.window.window
        xid = gdkwindow.xid

        print "Calling ObtainAuthorization..."

        #This complains that no ObtainAuthorization(ssi) exists:
        #granted = policykit.ObtainAuthorization("test_action_id", xid, os.getpid())

        #TODO: Neither of the async callbacks are called, and how could the return value be useful if it is async?
        # Note: Other code (such as gnome-panel) seems to use ShowDialog instead of ObtainAuthorization, though
        # ShowDialog is apparently deprecated (I don't know when), but that also has no effect.
        granted = policykit.ObtainAuthorization("test_action_id", xid, os.getpid(), reply_handler=self.__handleAuthReply, error_handler=self.__handleAuthError)

        print "...Finished."

        print "granted=", granted

    def __handleAuthReply(self, granted):
        print "handleAuthReply: granted\n"

    def __handleAuthError(self, exception):
        print "handleAuthError: not granted: %s\n" % exception

    def on_delete_event(self, widget, event, data=None):
        # Close the window:
        return False

    def on_destroy(self, widget, data=None):
        gtk.main_quit()

    def show(self):
       self.window.show()

    def __init__(self):

        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.window.connect("delete_event", self.on_delete_event)
        self.window.connect("destroy", self.on_destroy)

        self.button = gtk.Button("Obtain Authorization")
        self.button.connect("clicked", self.on_button_clicked, None)
        self.window.add(self.button)
        self.button.show()

window = TestWindow()
window.show()
gtk.main()

gnome-lirc-properties has a real mechanism and tries to use ObtainAuthorization() for real, if someone wants to look at it properly. Here is the gnome-lirc-properties svn, and a tarball (it doesn’t do much yet).

How to use PolicyKit from an application

In return for asking the lazy web, I’ll try to describe how I think it should work, based mostly on David Zeuthen’s description. There are many opportunities to get something wrong:

  • You install a D-Bus service (a PolicyKit “mechanism”) that does the thing that needs sudo access. PolicyKit will control access to this mechanism. For instance, the Clock applet’s mechanism has a SetTimeZone method. gnome-lirc-properties uses the Python D-Bus bindings to implement its mechanism. I know that our WriteConfigFile() method is far too generic, but we will improve it when we get this working.
  • Update:: And this mechanism’s implementation should ask PolicyKit if it has authorization, and return an error if it doesn’t. Thanks mclasen. I’ll try to find out how to do this. Update: You can do this via the PolicyKit (on the session bus) object’s IsProcessAuthorized() method, now used in the gnome-lirc-properties PolicyKit mechanism.
  • As with other D-Bus services, you install a .service file, so that D-Bus activation can start it on demand. PolicyKit mechanisms use the System bus rather than the Session bus, so you install it in $(datadir)/dbus-1/system-services (/usr/share/dbus-1/system-services on Ubuntu). Here is the .service file for gnome-lirc-properties.
  • As with other D-Bus services, you install a .conf file, saying who can use the service. This is installed in $(sysconfdir)/dbus-1/system.d (/etc/dbus-1/system.d/ on Ubuntu). This is the .conf file for gnome-lirc-properties
  • A .policy file tells PolicyKit who should be allowed to use the mechanism. You install this in $(datadir)/PolicyKit/policy (/usr/share/PolicyKit on Ubuntu). Here is the .policy file for gnome-lirc-properties. I believe this allows system administrators to configure access to these features via these .policy files, and I believe that is the whole point of this D-Bus “mechanism” abstraction.
  • Applications that want to use this mechanism call the ObtainAuthorization() method on the “org.freedesktop.PolicyKit.AuthenticationAgent” D-Bus service, passing the action ID that is mentioned in the .policy file. If necessary, PolicyKit asks for a password (or whatever) for authentication, and presumably then gives the application the appropriate rights. The dialog uses (translatable) text from the .policy file, so it’s very specific to your application. Many applications still seem to be using the ShowDialog() method instead, but that has been deprecated.

gnome-lirc-properties: A GUI to configure Infra-Red Remote Controls

We at Openismus will be working on a little GUI control panel to configure remote controls, so you can more easily, for instance, control a media-center application such as Elisa. We are doing this for Fluendo.

It should end up looking something like this and it will basically just give you a working /etc/lirc/lircd.conf file for your remote control:

gnome-lirc-properties Screenshot

I’ll make a few posts about this, but first here’s some (opinionated) notes about how I think it works now on Ubuntu without this GUI:

Installing lirc

You select the remote control via a curses UI when you install lirc (or when you run sudo dpkg-reconfigure lirc). We would prefer it to install silently and let the user configure it when he wants to. I think the script for this is debian/lirc.config.in in the lirc debian package, though this doesn’t seem to be the whole story. It seems to use the prewritten configuration files in /usr/share/lirc/, listed in the /usr/share/lirc/lirc.hwdb file, which seem to be supplied only with Debian/Ubuntu.
This creates

  • /etc/lirc/lircd.conf, which maps the key names to the key codes supplied by your IR remote (usually assuming that you are using the IR receiver (if any) supplied with the IR remote control. It’s these key names that an application will receive via the liblirc_client library.
  • /etc/udev/rules.d/85-lirc.rules, which uses /etc/init.d/lirc, which starts “/usr/sbin/lircd –device=/dev/lirc0”.

Custom Configuration

When the lirc package does not know about your hardware, or if it has a bad configuration file, you can use the irrecord utility (installed with lirc) to generate a custom lircd.conf file, which you can move to /etc/lirc/lircd.conf. It checks for key codes and some other aspects of the remote control. Here is an example of an irrecord run. irrecord is a command-line utility which takes input on stdin. Doing this programatically is going to be difficult, probably requiring the creation of a libirrecord in the lirc sources.

Note that the default device for lirc is /dev/lirc, and it doesn’t check for other devices automatically, but applications don’t need to specify the device because they just communicate with lird via liblirc_client. However, the irrecord utility doesn’t use lircd so you’ll need to use the –device option with it too.

The application

The application then creates a .lircrc file (Elisa creates one in /tmp when it starts, and this seems the only sensible thing to do) which maps these key names to “command” names. This seems like a useless extra step because most applications will just invent “command” names which are identical (or near identical) to the “key” names. The entries in this .lircrc must use the same “prog” identifier string as the application has passed to the lirc_init() function – another annoying invitation to failure. This is presumably useful when using one .lircrc file to control multiple applications somehow, but I can’t see how that could ever be useful – a global .lircrc file could at the most be used to _start_ applications, not control them.

Update:: Of course, I do have the strong feeling that remote controls should be just another input device, like mice and keyboards, so the whole lirc daemon thing seems odd. But I’m not a kernel or X programmer. This might even be planned for the future, but I don’t know. We do need something to make lirc work easily now.