Filed under Code

Announcing Fitbit Accomplishments for the Ubuntu Accomplishments System!

For my birthday in October, I received a Fitbit One. The reason that I wanted it is that I thought with better data tracking I could push myself to be more active during the day. The Fitbit One is a “fitness tracker”, essentially a technologically enhanced pedometer, that can also measure elevation gain (steps climbed they call it), and even track your sleep patterns. The device, which is slightly larger than a large paperclip, syncs wirelessly  to iPhones or to a computer. It uploads all your statistics to Fitbit.com, which provides a cool dashboard which you can use to track your steps, floors climbed, calories burned, etc. Here’s my dashboard from yesterday:

My Fitbit dashboard from yesterday

Like most geeks, I love data, and nice charts and graphs too, so I’ve really enjoyed the dashboard. I’ve also found that the maxim, “What gets measured gets done” really applies here. Two nights ago at 11:30PM, I noticed I was 300 steps short of 10000 steps, so I made sure to walk around while brushing my teeth, took the trash out, and generally wandered until I got past 10000 steps. That was only 300 steps, but I’ve also found myself walking the dog more, walking to the library more, etc.

So what does this have to do with Ubuntu? Well you can see at the bottom of that dashboard that Fitbit gives “badges”, which Chris Wayne thought would be a perfect fit for the Ubuntu Accomplishments system.  So Chris hacked all weekend and created an online account plugin for Fitbit. On Monday we hooked the oauth account created by Chris’s plugin into Fitbit’s web API and now we had Fitbit accomplishments!

Badges I can earn

My Trophies

You need a Fitbit to use it, and if you buy one, use this link so that Chris and I can support our daily beer and daily steps habits. The same link is also in the collection itself.

Installing

Note: That this requires Quantal or Raring because it uses Online Accounts. The raring build broke for some reason earlier but it should be ready an hour from the time this posts.

Installing is easy, although if you don’t already have Ubuntu Accomplishments installed it’s a two step process.

First, install Ubuntu Accomplishments if you’ve not already done so:

sudo add-apt-repository ppa:ubuntu-accomplishments/releases
sudo apt-get update
sudo apt-get install accomplishments-daemon accomplishments-viewer ubuntu-community-accomplishments ubuntu-desktop-accomplishments accomplishments-lens

Then install the Fitbit Accomplishments collection:

sudo add-apt-repository ppa:fitbit-accomplishment-maintainers/daily
sudo apt-get update
sudo apt-get install account-plugin-fitbit ubuntu-fitbit-accomplishments

If you’re already running Ubuntu Accomplishments, you’ll need to close the viewer and restart the Accomplishments Daemon to get the new collection to show up.  You can restart the daemon by doing accomplishments-daemon –restart.  A simple logout/login will also work.

The first accomplishment you need to get is connecting to your Fitbit account. Chris also wrote a post with some screenshots if you get stuck.

You need to setup your Fitbit Online Account before you can get any Fitbit badges, follow the steps in the Accomplishment to do so.

Follow the directions in the accomplishment to set this up. Once you do that, the other fitbit accomplishments will unlock in a logical progression as you achieve things (for example, the 10000 steps in a day accomplishment requires you to complete the 5000 steps in a day accomplishment first).

Note that Fitbit admits that the Badge API is still new and there are some quirks, for example, Fitbit provides badges for 50 and 250 lifetime kilometers, but for lifetime miles, they offer 50, 250, 1000, and 5000. Also some badges are transparent, some are not, which I know we could fix, but I haven’t had time yet. As this API improved and is expanded, we’ll add more accomplishments, or better yet, you can add more by sending us a merge proposal (the code is here).

Why?

Fitbit accomplishments, like walking 10000 steps in a day, obviously have nothing to do with Ubuntu, but this collection highlights the flexibility of the Ubuntu Accomplishments system. Anything that can be tested via script can be an accomplishment. I’m sure there are lots of other websites that people use that could be added as collections like this one. If you’re interested and you need help setting one up, you can find me (mfisch) in #ubuntu-accomplishments on Freenode.

About the Accomplishments Code

The code for checking these accomplishments in the accomplishments scripts is very very simple:

    badgeid = "10000 DAILY_STEPS"
    me = FitBit.fetch(None)
    if badgeid in me.badges:
        sys.exit(0)
    else:
        sys.exit(1)

This is because all the hard logic is in helpers.py, which provides the FitBit class and handles caching for us. Since the way accomplishments work is that each accomplishment has a script associated with it, we want to cache the info so that we don’t hammer the Fitbit web API once per script every 15 minutes (all unlocked accomplishments are checked every 15 minutes). The caching solution in helpers.py, was copied from the model used by AskUbuntu and Launchpad in the Ubuntu Community Accomplishments package. helpers.py also is how we interact with the Online Accounts plugin and Fitbit’s web API, so if you want to see the “interesting code”, look there.

Note: Expect a follow-up blog post from Chris Wayne on how to write an online accounts plugin in the next couple of weeks.

Help Needed

If you live outside of the US and you have a Fitbit and are willing to help, I need some assistance to see what happens if the Fitbit API returns localized Badge info. I also need to see if what it looks like when you get a badge marked in Kilometers. I don’t think I get these because of where I live (the US). Drop me an email to matt@<this_domain>.com if you can assist or find me in #ubuntu-accomplishments on freenode, I’m mfisch. I think I’ll only need a few minutes of your time.

Tagged , ,

Soon

According to Alex Chiang, being in Copenhagen just means “more work while being tired”. Well we’ve been hard at work, and so this will be coming soon…

Tagged , ,

ubuntu-bug is for lightdm, Soon!

A bug triager’s best friend is ubuntu-bug. Special per-package hooks in /usr/share/apport/package-hooks collect logs and config files that make bug triage and analysis much easier. The scripts also help to avoid the “Can you please post the … file?” back and forth that tends to waste time and many time ends up in an incomplete bug being marked expired. (As an aside when a bug submitter is asked for more info, the bug is marked Incomplete until it is updated, if it’s not updated in 60 days, it expires).

What does this have to do with lightdm? So tonight I shared my lightdm blog post with some people and Bryce Harrington suggested that I write an apport script for lightdm. I mentioned this brilliant plan to Robert Ancell and it turns out that lightdm has one already! Well my work is done!  Except…  The existing script wasn’t being packaged or installed, was collecting logs with the wrong (old) names, and didn’t collect the configs. All of that is fixed tonight with a MP I made, based on the code that Xorg’s apport script uses. Once it’s merged and released (probably not until R opens) we will have an apport script for lightdm. Once that happens, you can file a bug by simply typing:

ubuntu-bug lightdm

There’s no apport script for any of the the greeters, at least not that I know of for unity-greeter or lightdm-gtk-greeter, but they’d need the same data. The only thing that this script doesn’t do is get a screencap/picture, that’s up to you guys.

Tagged , , ,

How Does udev Know What’s a Keyboard or Mouse?

Last week, I found myself having to dive into the udev code to figure out how it determines whether what is a mouse or a keyboard. To solve the problem I was working on, I ended up having to replicate some of that logic in python, which is posted at the bottom. Let me explain how it works.

Device Info in /sys

To start, let’s look in /sys and see what input devices we have. If you look in /sys/class/input on your system, you’ll see many symlinks to devices. I’ll pick /sys/class/input/event12 to look at in more detail, so cd to event12/device

mfisch@caprica:/sys/class/input/event12/device$ ls -al
total 0
drwxr-xr-x 7 root root 0 Jul 8 16:05 .
drwxr-xr-x 3 root root 0 Jul 8 16:05 ..
drwxr-xr-x 2 root root 0 Jul 8 16:05 capabilities
lrwxrwxrwx 1 root root 0 Jul 8 20:50 device -> ../../../serio1
drwxr-xr-x 3 root root 0 Jul 8 16:05 event12
drwxr-xr-x 2 root root 0 Jul 8 16:05 id
-r--r--r-- 1 root root 4096 Jul 8 20:50 modalias
drwxr-xr-x 3 root root 0 Jul 8 16:05 mouse0
-r--r--r-- 1 root root 4096 Jul 8 16:05 name
-r--r--r-- 1 root root 4096 Jul 8 20:50 phys
drwxr-xr-x 2 root root 0 Jul 8 20:50 power
-r--r--r-- 1 root root 4096 Jul 8 20:50 properties
lrwxrwxrwx 1 root root 0 Jul 8 16:05 subsystem -> ../../../../../../class/input
-rw-r--r-- 1 root root 4096 Jul 8 16:05 uevent
-r--r--r-- 1 root root 4096 Jul 8 20:50 uniq

There are some interesting things in here, but the two I’m interested in are “name” and the “capabilities” directory. Let’s look at name first.

mfisch@caprica:/sys/class/input/event12/device$ cat name
PS/2 Generic Mouse

Using Capabilities

Okay, so this looks like a mouse, but udev doesn’t use the name to figure this out, it uses the capabilities directory. Let’s look there:

mfisch@caprica:/sys/class/input/event12/device/capabilities$ ls -al
total 0
drwxr-xr-x 2 root root 0 Jul 8 16:05 .
drwxr-xr-x 6 root root 0 Jul 8 16:05 ..
-r--r--r-- 1 root root 4096 Jul 8 16:05 abs
-r--r--r-- 1 root root 4096 Jul 8 16:05 ev
-r--r--r-- 1 root root 4096 Jul 8 20:43 ff
-r--r--r-- 1 root root 4096 Jul 8 16:05 key
-r--r--r-- 1 root root 4096 Jul 8 20:39 led
-r--r--r-- 1 root root 4096 Jul 8 20:43 msc
-r--r--r-- 1 root root 4096 Jul 8 16:05 rel
-r--r--r-- 1 root root 4096 Jul 8 20:43 snd
-r--r--r-- 1 root root 4096 Jul 8 16:05 sw

The aptly named capabilities provide information to udev on what “capabilities” the device has. udev is specifically interested in the following ones: abs (Absolute axes), ev (Event types), key (Keys and Buttons), and rel (Relative axes). Let’s examine one of these to determine what data it contains:

mfisch@caprica:/sys/class/input/event12/device/capabilities$ cat ev
7

So what does 7 mean? This is a bitmask (111) who’s bits are defined in /usr/include/linux/input.h. That file has bits defined for each of the types listed in the capabilities directory, the #define prefix matches the name in the capabilities directory, so we’re looking at EV_ (Note: key is represented by defines KEY_ and BTN_, since it’s for keys and buttons).

Since this mask is 0×7 or 111, we have bits 0, 1, and 2 set and the rest are false. Judging from the #defines, the following bits are set for this device:

#define EV_SYN 0x00
#define EV_KEY 0x01
#define EV_REL 0x02

This tells us that our device gets key events, relative movement event, and synchronization events. (Figuring this out required poking around the header a bit more).

A Quick Note About the ‘key’ Capability

The entry ‘key’ is much longer than ev and requires more explanation. Let’s look at what the ‘key’ capabilities shows for my keyboard:

20000 20000000020 0 0 500f02100002 3803078f900d401 feffffdfffefffff fffffffffffffffe

So that’s much larger than ev was. It’s still a bitmask, but it’s been split into words, using the word-size sense, so 8 bytes on my system. Since this mask is little endian, the ’20000′ represents the highest set bits. This makes processing need to take place in word-size chunks, starting at the right. Another way to think of the output above is to think about it as one large mask, as long as you pad each “word” to be 16 digits, for example, what you really see above is this:

00000000000200000000020000000020000000000000000000000000000000000000500f02100002 03803078f900d401feffffdfffeffffffffffffffffffffe

To solve my problem, I split the output up on the spaces and set 64-bits at a time in my mask starting from the right-most word.

Now that we’ve read all these bitmasks, we need to do something with it.

What udev Does with this Info

If you look at the udev source code, specifically udev/udev-builtin-input_id.c, you can see how this data is used.

For mice, the code is fairly straightforward, look at the code for the test_pointers function. We really want to know where they set “is_mouse”. The code is also looking for other stuff like touchpads, so let’s ignore that. It basically boils down to this:

if EV_REL and REL_X and REL_Y and BTN_MOUSE:
you have a mouse

There’s also a similar block where you can replace REL above with ABS, which the comment claims is for VMWare’s USB mouse, but I kept it in my solution because I didn’t quite trust the comment and it seemed harmless otherwise.

For keyboards, the code is a bit more complex because it’s basically checking to see if you have a bunch of keys defined, this code is in the function test_key. then test_key looks for a keyboard it boils down to this:

If any bits are set in 'ev' and
if bits 1-31 (but not 0) are set:
you have a keyboard

Note: bits 1-31 represent the Escape key, numbers, and Q through D.

Putting it All Together

So now that we know how it works, let’s see it in action! Here it is run on my dev box:

mfisch@caprica:~/tmp/find_input$ ./find_input.py
INFO:root:/dev/input/event3 is a keyboard (AT Translated Set 2 keyboard)
INFO:root:/dev/input/event12 is a mouse (PS/2 Generic Mouse)

Here it is run on another system with more devices attached:

INFO:root: /dev/input/event3 is a keyboard (AT Translated Set 2 keyboard)
INFO:root: /dev/input/event13 is a mouse (PS/2 Generic Mouse)
INFO:root: /dev/input/event15 is a keyboard (BTC USB Keyboard)
INFO:root: /dev/input/event16 is a mouse (Primax HP Wireless Laser Mini Mouse)

My python code including unit tests! is avaialble in bzr at lp:~mfisch/+junk/find_input

Comments, bugs, and fixes all gladly accepted.

Tagged , , ,

Forcing lightdm to Use a Specific Resolution

This morning I was debugging a problem in unity-greeter and I suspected that it might be related to the resolution of the system I was using. Since I was live booting it was a pain to make changes, mainly because I didn’t have all my standard tools (or web access). I ended up messing around with xorg.conf files for awhile, without much success (this reminded me of college, but not pleasantly). After some other searches, I ended up settling on this method:

For this example, I wanted a resolution of 1024×600.

  1. Login to my system
  2. Open a terminal and run cvt 1024 600
  3. Copy the resulting output, minus ModeLine portion
  4. mfisch@caprica:~$ cvt 1024 600
    # 1024x600 59.85 Hz (CVT) hsync: 37.35 kHz; pclk: 49.00 MHz
    Modeline "1024x600_60.00"   49.00  1024 1072 1168 1312  600 603 613 624 -hsync +vsync
    
  5. Run xrandr –newmode
  6. mfisch@caprica:~$ xrandr --newmode "1024x600_60.00"   49.00  1024 1072 1168 1312  600 603 613 624 -hsync +vsync
    
  7. Run xrandr –addmode LVDS 1024x600_60.00. (Note that there are several displays that xrandr shows, I wanted to change my main laptop display, LVDS, you may want to change another. Run xrandr without arguments to see all the displays.)
  8. Run xrandr –output LVDS –mode 1024x600_60.00
  9. If that switches your resolution and looks right, then lets make it permanent.
  10. Edit: /etc/lightdm/lightdm.conf, add this line (note you can choose a different path:
  11. display-setup-script=/usr/bin/lightdmxrandr
  12. Make a new file called /usr/bin/lightdmxrandr, make it a shell script that just executes lines 4, 5, and 6 from above
  13. #!/bin/sh
    xrandr --newmode "1024x600_60.00"   49.00  1024 1072 1168 1312  600 603 613 624 -hsync +vsync
    xrandr --addmode LVDS 1024x600_60.00
    xrandr --output LVDS --mode 1024x600_60.00
    
  14. Make /usr/bin/lightdmxrandr executable, logout, and restart lightdm (sudo initctl restart lighthdm)
  15. Enjoy your new resolution!

Here are the two links that helped me piece this together:

How change display resolution settings using xrandr
How can I make xrandr customization permanent?

Tagged , ,

Sending Custom Data with DHCP Option Tags … and Then Doing Something with the Data

Over the past week I’ve been playing with DHCP option tags, and using them to send data to DHCP client systems, and then do something with the data. I didn’t know much about DHCP option tags before doing this, I figure DHCP just gave you an IP address and maybe a nameserver. In reality, DHCP can be customized to send a couple hundred different settings, including where the IRC or SMTP servers are, VOIP settings, and lots more. What makes it really powerful is that you can use the unassigned options to create your own settings, and then do actions on the client when they get set. In this simple example, I’m going to send and set a new “message of the day” via dhcp. The message of the day is a (usually) short text message shown when you login via the terminal and is stored in /etc/motd. If you are trying this on a production system, you should back-up your /etc/motd first.

Server Setup

On the server, after installing dhcpd, edit /etc/dhcp/dhcpd.conf, create a valid subnet and then in the global space, add your new option. Since options 151-174 are unassigned, I will use option 151. You could also use a ‘site-local’ option between 224-254.

Add the following outside any subnet sections:
option motd code 151 = text;
option motd "Happy Easter from your DHCP server team";

Client Setup

On the client, manually run dhclient -d and make sure you’re getting a valid IP from the server. Once you know that is working, edit /etc/dhcp/dhclient.conf and add two parts, first you need to name the new option and second you need to request it:

Add this to the top, you may see another option code there, add it near that:
option motd code 151 = text;

Then look for the big request block, add your new option into that block:
request subnet-mask, broadcast-address, time-offset, routers,
domain-name, domain-name-servers, domain-search, host-name,
netbios-name-servers, netbios-scope, interface-mtu,
rfc3442-classless-static-routes, ntp-servers,
dhcp6.domain-search, dhcp6.fqdn,
dhcp6.name-servers, dhcp6.sntp-servers,
motd;

Now, let’s see if we get it. Edit /etc/dhcp/dhclient-exit-hooks.d/debug and switch RUN=”no” to RUN=”yes”. Then edit the large block of variables and add your new option, motd.

Re-run dhclient -d and then check /tmp/dhclient-script.debug, do you see your motd option? If so, your setup is correct. It should look liks this:

new_motd='Happy Easter from your DHCP server team'

Doing Something When You Get an Option

dhclient defines exit-hooks, one of which is the debug script you edited above. You can create your own shell script in /etc/dhcp/dhclient-exit-hooks.d/ and follow some of the other examples in there. DHCP options come in as variables, so in this case, the script would use ${new_motd}. From what I can tell everything in here has to be a shell script, a python script I tried did not work. Here is my script to set motd:

if [ -n "${new_motd+x} ]; then
echo ${new_motd} > /etc/motd
fi

Re-run dhclient -d and you should see that the script has run and your message of the day is now a Happy Easter message.

What About NetworkManager?

If you are running NetworkManager on your system, and you probably are, then NetworkManager starts dhclient during boot and subverts the running of dhclient’s exit hook scripts. You can use NetworkManager’s own version of “exit hooks” which live in /etc/NetworkManager/dispatcher.d to respond to DHCP events. Scripts in that directory can be written directly in python. The scripts run from NetworkManager’s dispatcher use slightly different environment variables for DHCP options, they’re in all caps and are pre-pended with DHCP4_ (and presumably DHCP6_ if you’re serving IPv6 addresses).

if [ -n "${DHCP4_MOTD+x} ]; then
echo ${DHCP4_MOTD} > /etc/motd
fi

See the man NetworkManager(8) for more details on actions that your scripts can respond to.

Options as Expressions

There are more advanced things you can do. For example, you set a setting on the server based on an expression, for example:

if option dhcp-user-class = "fortcollins-office" {
option smtp-server "smtp.ftc-office.net";
}

You then configure the client to send up a dhcp-user-class of “fortcollins-office” for all your PCs in Fort Collins.

Using my example, you could change the holiday in the message based on the date that the lease was given out.

See man dhcp-eval(5) for more info on expressions.

Encapsulated Options

Another thing you can do is vendor encapsulated options. Vendor encapsulated options take a bunch of vendor specific info and encapsulate it into one option. This means that you can pack a bunch of data into one option. I am not sure if all dhcpd servers support this construct however.

Testing This
If you want to try DHCP options without messing up your network, the quickest way is to create two virtual machines, a client and a server, and give them each two NICs. One NIC on each machine should be on a private network, and the server should have a static IP on this private network NIC and serve DHCP on that interface. The other non-private NIC is used by you to copy files to and from your virtual machines, but is otherwise not specifically required. I did this setup using Ubuntu 12.04 precise and qemu and it works great.

Conclusion
Customized DHCP options let you transmit data to your DHCP clients and then write scripts to act on that data. Although my example is contrived, you could use DHCP to transmit useful system settings and then configure them via script.

References

Tagged , , ,

A Lens for Stock Prices/News

Fun With Lenses and Scopes

I’ve been fooling around with lenses over the past couple of weeks in my spare time. My idea was to write a stock ticker lens/scope. I wrote a quick backend in python in an hour or so, but the lens/scope proved more difficult. The main issue was that as I made progress, Michael Hall announced Singlet and then Singlet 0.2. These were compelling enough to drop my work and start over. Initially I had some issues getting singlet based scopes to work (some of which I fixed), but then I got better at debugging them and figuring out file placement. (Of course after I was well into this project, Michael Hall announced another easier way of doing lenses and scopes!)

Announcing the Stock Quote Lens

The lens I’m pushing today is a stock quote & news lens and scope combo. It relies on Michael Hall’s singlet 0.2 and will only work for precise.

Here are some screenshots:

As you can see it has two modes. If you enter one symbol, you get a quote and news. If you enter more than one, you get quotes. I could not figure out a good wait to show news for multiple symbols in the constraints of a lens. However, all the icons are clickable and direct you to a page with more quote info and news for each ticker, so use that method to get more info. The icons themselves are static, they’re not real charts.

For now, this is US markets only and it’s only in English. Given the architecture, you could write your own scope back-end for this and populate results for the stock markets in whatever country you’re in.

Where to Get It

I pushed the code up to launchpad today. You can package it yourself, but I have not published the package. I will be doing so next week when I can get into the scopes packagers PPA. The package is published in the Scopes Packagers PPA. You can install it by doing:

sudo add-apt-repository ppa:scopes-packagers/ppa
sudo apt-get update
sudo apt-get install unity-stock-ticker-lens

You can also pull the code from bzr directly:

bzr branch lp:~mfisch/onehundredscopes/unity-stock-ticker-lens

Some tricks to debugging Unity scopes & lenses

These are some methods I used during development.

  1. Remember to run setsid unity and watch the terminal output. This is where you’ll see all your missing icons and broken .scope and .lens issues. You’ll need to re-run this when you change a .scope or .lens file IIRC.
  2. When debugging, fire up your scope in a terminal, then your lens in another terminal. Any errors or prints will dump to the terminal. Seeing a dumb error like a python indent error here is much more helpful than trying to use the scope and wondering why you have no output.
  3. If you want to run the scope/lens manually, be sure to kill the old processes first.
  4. Check your paths and dbus info. If the stuff runs fine manually, but won’t autostart, then something is wrong in your dbus service files.

PS – Thanks to Bob Davis for help with sourcing those icons.

Tagged , , , ,

So You Want to Write a LightDM Greeter…

What is a LightDM Greeter?

LightDM is a lightweight display manager that splits the responsibilities of a display manager between the server and the UI, which is known as a greeter.  When you login to Ubuntu you are shown the Unity Greeter, but all the back-end session management and authentication is provided by the LightDM daemon, and functionality that a greeter needs is provided by liblightdm. LightDM exposes a set of APIs that allow you easily and quickly write your own greeter using your choice of languages.  As long as you can interact with LightDMs GObject API, you can use any language you want.

Why Write a Greeter?

Given all that, why would you want to write a greeter?  The Unity Greeter looks pretty nice after all.  In my case, I had special requirements that necessitated a change to the UI and some extra processing for the session.  During the course of this work, I wrote a greeter based on the sample GTK greeter that comes with LightDM and then applied my modifications.  I originally wrote it in C, but then moved to Python for easier integration into my other code.  My hope is that you can re-use some of my learnings below and write your own greeter that suits the needs of your distro or project.

The Unity Greeter

Let’s Get Started

This post is designed to give you an overview of the greeter and how it works. The code for my very simple example greeter, which is available here, is commented to the point where I hope it will answer your code questions. After reading this post, walk through the code and I think the process will make sense. Once you understand this code and want to do more advanced things, you should check out the GTK example greeter, which is part of the LightDM GTK+ Greeter project.

Note: You can also get my code from launchpad here, or via bzr with bzr branch lp:~mfisch/+junk/example-greeter

How LightDM Authentication Works

The first thing to understand is the way LightDM authenticates is a multi-step process and that this process will guide how we write our greeter.

  1. User enters a username and clicks the Login button.
  2. LightDM passes the username to PAM and responds back to the greeter with a prompt, such as “Password:”.
  3. User enters the password and the greeter passes it back to LightDM.
  4. LightDM authenticates with PAM and returns success or failure back to the greeter.
  5. LightDM may also return a message, such as “Login failed”, which needs to be displayed to the user.
  6. If the authentication succeeds, the greeter starts a session.

Program Flow

The diagram below shows the flow between the greeter, the top row, and the LightDM backend, the bottom section. This flow assumes a successful login as we don’t show the failure case for simplicity’s sake.

Flow of the greeter during login

Looking at the Code

At this point you probably want to look at the code and compare it to the flow in the diagram. The code is heavily commented and hopefully the diagram makes it easy to follow.

Screenshots of the Example Greeter

As you can see our greeter isn’t pretty, but it works. Hopefully this gives you an idea of the prompt re-use, which is pretty standard in greeters I’ve seen. Making this look nice is left as an exercise to the artistic reader.

Installing Your Greeter

Your greeter will minimally include an executable and a desktop file. The executable is usually installed somewhere in the path, like /usr/bin. The .desktop file goes into /usr/share/xgreeters and tells LightDM how to launch your greeter. The name of the .desktop file defines what you need to put in LightDM’s conf file. Additionally your greeter may install a configuration file, typically in /etc/lightdm and a Gtk UI file, if you’re using GTK. Once your greeter is in place you need to tell LightDM to use it. If you’re just testing things, you can edit /etc/lightdm/lightdm.conf and set greeter-session=example-greeter. What you set here has to match the name of your desktop file as mentioned above. If you plan to do this in a package install, you need to use /usr/lib/lightdm-set-defaults, which allows you to set a few defaults via a script. In this case we’d call /usr/lib/lightdm/lightdm-set-defaults –greeter=example-greeter. You can run that script with no arguments to see it’s full capabilities and options.

Once you install your greeter, you need to restart LightDM with sudo initctl restart lightdm. If it works, great! If not, check out the Debugging section below.

Debugging

A few hints on debugging your greeter. Anything you print to stderr in your greeter ends up in /var/log/lightdm/x-0-greeter.log. I find that keeping that open in a separate console dedicated to looking at this log always helps. Since the logs get deleted every time the greeter restarts, this is your best option: sudo tail -f /var/log/lightdm/x-0-greeter.log

LightDM itself may have useful logs if your configuration is broken, they’re in /var/log/lightdm/lightdm.log, and again you need sudo to view the logs.

Debugging a greeter with a debugger can be tricky, so I’ve found that printing debug into to stderr is your best option. However, the greeter seems simple enough that this method usually works fine.

Also you almost always want to do your development of a greeter in a VM. Alternatively, you can run lightdm in test mode with lightdm –test-mode, which allows you to run lightdm from the command-line as an unprivileged user. This will start your greeter in a window inside your existing session.

Finally you may get your greeter into a state where it keeps trying to launch and keeps failing. In a VM this will result in the window popping between X and a text terminal as it keeps failing. Just wait a bit and LightDM will eventually give up on your greeter and you can drop into a console and see what’s wrong.

Documentation

LightDM’s API is defined here. Everything I used in the example greeter focuses on the Greeter section. If you want to do more advanced things like allow the user to shutdown the system from the greeter, or query the list of available sessions, please check the other sections.

I also highly recommend walking through the GTK Greeter for seeing how some of these more advanced options work.

A word of caution if you’re going to use Python, in version 1.0.6 of LightDM, the introspection bindings for Python were broken. They’re fixed on trunk, but I don’t think a fix has made it into a release yet (as of Feb 6, 2012).

Summary

This example code barely covers the surface of what you need to do in order to make a good greeter, but it should help you get started. I recommend for follow-up that you read through example the GTK example greeter, which comes in the source for the LightDM package. You can use that knowledge to make your greeter look much nicer and add features like accessibility (a11y) and options like shutdown/restart/etc from your greeter.

Tagged , , ,