Search Results: "tar"

15 July 2010

Uwe Hermann: Using the HP Pavilion dv7-3127eg laptop with Debian GNU/Linux

HP Pavilion dv7-3127eg Yep, so I bought a new laptop recently, my IBM/Lenovo Thinkpad T40p was slowly getting really unbearably sloooow (Celeron 1.5 GHz, 2 GB RAM max). After comparing some models I set out to buy a certain laptop in a local store, which they didn't have in stock, so I spontaneously got another model, the HP Pavilion dv7-3127eg (HP product number VY554EA). Why this one? Well, the killer feature for me was that it has two SATA disks, hence allows me to run a RAID-1 in my laptop. This allows me to sleep better at night, knowing that the next dying disk will not necessarily lead to data loss (yes, I do still perform regular backups, of course). Other pros: Much faster than the old notebook, this one is an AMD Turion II Dual-Core Mobile M520 at 2.3 GHz per core, it has 4 GB RAM (8 GB max), and uses an AMD RS780 / SB700 chipset which is supported by the Free-Software / Open-Source BIOS / firmware project coreboot, so this might make the laptop a good coreboot-target on the long run. I'll probably start working on that when I'm willing to open / dissect it or when the warranty expires, whichever happens first. Anyway, I set up a page at randomprojects.org which contains lots more details about using Linux on this laptop:
http://randomprojects.org/wiki/HP_Pavilion_dv7-3127eg
Most of the hardware is supported out of the box, though I haven't yet tested everything. There may be issues with suspend-to-disk / suspend-to-RAM, sometimes it seems to hang (may be just a simple config change is needed in /etc/hibernate/disk.cfg). Cons: Pretty big and heavy (but that's OK, I use it mostly as "semi-mobile desktop replacement"), glossy screen, loud fans (probably due to the two disks). For reference, here's an lspci of the box:
  $ lspci -tvnn
  -[0000:00]-+-00.0  Advanced Micro Devices [AMD] RS780 Host Bridge Alternate [1022:9601]
           +-02.0-[01]--+-00.0  ATI Technologies Inc M96 [Mobility Radeon HD 4650] [1002:9480]
                        \-00.1  ATI Technologies Inc RV710/730 [1002:aa38]
           +-04.0-[02-07]--
           +-05.0-[08]----00.0  Atheros Communications Inc. AR9285 Wireless Network Adapter (PCI-Express) [168c:002b]
           +-06.0-[09]----00.0  Realtek Semiconductor Co., Ltd. RTL8111/8168B PCI Express Gigabit Ethernet controller [10ec:8168]
           +-0a.0-[0a]--
           +-11.0  ATI Technologies Inc SB700/SB800 SATA Controller [AHCI mode] [1002:4391]
           +-12.0  ATI Technologies Inc SB700/SB800 USB OHCI0 Controller [1002:4397]
           +-12.1  ATI Technologies Inc SB700 USB OHCI1 Controller [1002:4398]
           +-12.2  ATI Technologies Inc SB700/SB800 USB EHCI Controller [1002:4396]
           +-13.0  ATI Technologies Inc SB700/SB800 USB OHCI0 Controller [1002:4397]
           +-13.1  ATI Technologies Inc SB700 USB OHCI1 Controller [1002:4398]
           +-13.2  ATI Technologies Inc SB700/SB800 USB EHCI Controller [1002:4396]
           +-14.0  ATI Technologies Inc SBx00 SMBus Controller [1002:4385]
           +-14.2  ATI Technologies Inc SBx00 Azalia (Intel HDA) [1002:4383]
           +-14.3  ATI Technologies Inc SB700/SB800 LPC host controller [1002:439d]
           +-14.4-[0b]--
           +-18.0  Advanced Micro Devices [AMD] K10 [Opteron, Athlon64, Sempron] HyperTransport Configuration [1022:1200]
           +-18.1  Advanced Micro Devices [AMD] K10 [Opteron, Athlon64, Sempron] Address Map [1022:1201]
           +-18.2  Advanced Micro Devices [AMD] K10 [Opteron, Athlon64, Sempron] DRAM Controller [1022:1202]
           +-18.3  Advanced Micro Devices [AMD] K10 [Opteron, Athlon64, Sempron] Miscellaneous Control [1022:1203]
           \-18.4  Advanced Micro Devices [AMD] K10 [Opteron, Athlon64, Sempron] Link Control [1022:1204]
Full lspci -vvvxxxxnnn, lsusb -vvv, and a much more detailed list of tested hardware components is available in the wiki.

14 July 2010

Mike Hommey: I/O patterns on ELF binary initialization

[ I meant to finish this article much earlier, but i had to leave it in a half-written draft state for quite some time due to other activities ] One particular issue that arises with big binary files is that of I/O patterns. It looks like it is not something under the usual scope for both ld.so and programs/libraries, but it can have a dramatic influence on startup times. My analysis is far from complete, would need to be improved by actually investigating ld.so code further, and so far has been limited to libxul.so built from mozilla-central, with the help of icegrind, on x86-64 linux. As I wrote recently, icegrind allows to track memory accesses within mmap()ed memory ranges. As ld.so does mmap() the program and library binaries, tracking the memory accesses within these mmap()s allows to get a better idea at I/O patterns when an ELF binary is initialized. I only focused on local accesses within a single given binary file (libxul.so) because the case of Firefox loading many files at startup is already being scrutinized, and because most of the non-Firefox files being read at Firefox startup (system and GNOME libraries) are most likely already in the page cache. I also focused on that because it was an interesting case that could be helpful to understand how big (and maybe less big) binaries may be affected by the toolchain (compiler, linker and dynamic linker) and possibly by some coding practices. For the record, what I did to get these results is to use icegrind and elflog, with further additions to the sections file with some output from objdump -h : I added sections that elflog wouldn t give (such as .plt, .hash, .dynsym, etc.), and even down to the entry level for the .rela.dyn section. The latter is particularly interesting because icegrind only outputs the first access for any given section. To see sequential accesses within that section, you need to split it in smaller pieces, which I did for .rela.dyn entries (each one being 24 bytes on x86-64). Uncommenting some parts of the icegrind code was also useful to track where some accesses were made from, code-wise. Now, the interesting data: One of the first accesses is a nullification of a few variables within the .bss section. The .bss section is usually an anonymous piece of memory, that is, a range of mmap()ed memory that is not backed by a file, and filled with zeroes by the kernel (I think it even does that lazily). It is used for e.g. variables that are initialized with zeroes in the code, and obviously, any code accessing these variables would be addressing at some offset of the .bss section. It means the section needs to start in memory at the offset it has been assigned by the linker at build time. This is actually where problems begin. When the .bss section offset as assigned by the linker doesn t align on a page (usually 4KB), the mmap()ed .bss can t be at that location, and really starts on the next page. The remainder is still mmap()ed from the binary file, and ld.so will itself fill that part. As the .bss section doesn t start on a page boundary, any write at this location will trigger the kernel reading the entire page. This means one of the first set of data being read from the file is the end of the section preceding .bss, and the beginning of the following one. Most likely, respectively the .data and .comment sections. While this probably doesn t matter much when the binary file is small, when it is big enough, reading in a non sequential manner will trigger hard disk seeks, and we all know how they can hurt performance. Although thankfully, cheap SSDs should be coming some day, in the meanwhile, we still need to cope with the bad performance. The interesting part is, the .bss section is really empty in the binary file, so its virtual memory address could be anywhere. Why the linker wouldn t align it at a page boundary without having to resort to a linker script is beyond me. The next accesses go back and forth between .dynamic, .gnu.hash, .dynstr, .gnu.version_r, .gnu.version and .dynsym. These are probably all related to symbol version resolution and DT_NEEDED library loading. While most of these sections are at the beginning of the file (not necessarily in the order they are read from), the .dynamic section is much nearer to the end, but way before .data, so that it won t even have been loaded as a by-product of the .bss section loading. After that, the .rela.dyn section is read, and for each relocation entry it contains, the relocations are being applied. Relocations is one of the mechanisms by which position independent code (PIC) is made possible. When a library is loaded in memory, it is not necessarily loaded at the same address every time. The code and data contained in the library thus need to cope with that constraint. Fortunately, while the base address where the library is mmap()ed in memory is not necessary constant, the offsets of the various sections are still as codified in the binary. The library code can thus directly access data at the right offset if it knows the base offset of the library (which is what is done on the x86 ABI), or if it knows where the current instruction is located (which is what is done on the x86-64 ABI). Static data (initialized in e.g. a const or a global variable, or, in C++ case, vtables), on the other hand, may contain pointers to other locations. This is where relocation enters the scene. The .rela.dyn section contains a set of rules describing where in the binary some pointers need to be adjusted depending on the base library offset (or some other information), and how they should be updated. ld.so thus reads all .rela.dyn entries, and applies each relocation, which means that while .rela.dyn is being read sequentially, reads and writes are also performed at various places of the binary, depending on the content of the .rela.dyn entries. This is where this gets ugly for Firefox: there are near 200000 such relocations. On x86-64 an entry is 24 bytes (12 on x86), and each of these is going to read/write a pointer (8 bytes on x86-64, 4 on x86) at some random (though mostly ordered) location. The whole .rela.dyn section not being read ahead, what actually happens is that it is read in small batches, with seeks and reads of other data at various locations in between. In libxul.so case, this spreads over .ctors, .data.rel.ro, .got, and .data. The relocation entries are somehow ordered by address to be rewritten, though they occasionally jump backwards. Some of these relocations also appear to be touching to .gnu.version, .dynsym and .dynstr, because their type involves a symbol resolution. Once .rela.dyn relocation have been dealt with comes .rela.plt s turn. The principle is the same for this section: entries describe what kind of relocation must be done where, and how the result must be calculated. The scope of this section, though, is apparently limited to .got.plt. But before explaining these relocations, I ll explain what happens with the PLT. The PLT (Procedure Linkage Table) is used when calling functions from external libraries. For example, in a Hello World example, the PLT would be used for calls to the puts function. The function making the call would in fact call the corresponding PLT location. The PLT itself, on x86 and x86-64, at least, consists of 3 instructions (I ll skip the gory details, especially for x86, where the caller needs to also set some register before calling the PLT). The first instruction is the only one to be called most of the time: it reads the final destination in the .got.plt section, and jumps there. That final destination, obviously, is not fixed in the library file, since it needs to be resolved by its symbol. This is why the two subsequent instructions exist : originally, the destination stored in the .got.plt section points back to the second instruction ; the first instruction will effectively be a nop (no operation), and the following instructions will be executed. They will jump into code responsible for symbol resolution, update of the .got.plt entry for the next call, and call of the real function. But pointing back to the second instruction is like the pointers in static data we saw above : it s not possible in position independent code. So, the .rela.plt relocations are actually filling the .got.plt section with these pointers back to the PLT. There are a tad more than 3000 such relocations. All these relocations should be going away when prelinking the binaries, but from my several experimentations, it looks like prelinking only avoids relative relocations, and not the others, while it technically could skip all of them. prelink even properly applies all the relocations in the binary, but executing the binary rewrites the same information at startup for all but relative relocations. That could well be a bug in either ld.so not skipping enough relocations or prelink not marking enough relocations to be skipped. I haven t dug deep enough in the code to know how prelinking works exactly. Anyways, prelinking is not a perfect solution, as it also breaks ASLR. Sure, prelink can randomize library locations, but it relies on the user or a cron job doing so at arbitrary times, but that s far from satisfying. An interesting thing to note, though, is that a good part of the relocations prelinking doesn t rid us of in libxul.so (more than 25000) are due to the __cxa_pure_virtual symbol, which is used for, well, pure virtual methods. In other words, virtual methods that don t have an implementation in a given class. The __cxa_pure_virtual function is set as method in the corresponding field(s) of the class VTABLE in the .data.rel.ro section. This function is provided by libstdc++, and as such, is dynamically linked. But this function is just a dummy function, doing nothing. Defining an empty __cxa_pure_virtual function to be included in libxul.so makes these relocations become relative, thus taken care of by prelinking. After all relocations occur, the library initialization itself can begin, and the content of the .init section is executed. That section, indirectly, executes all functions stored in the .ctors section. This includes static initializers, which are unfortunately called backwards, as Taras pointed out already. Each of these static initializers are also accessing various locations in the .data or .bss sections, which may or may not have already been loaded during the relocation phase. The execution of these initializers will also (obviously) read various pieces of the .text section (despite its name, it contains executable sections, i.e. functions code). The initialization of the library ends there, and no access should happen until a function from the library is called. In libxul.so case, XRE_main is first called, then many other functions, but that s another story. All that needs to be remembered about the startup process past this point is that the .text section will be read heavily, as well as the various data, got, plt and dynamic symbol sections, in a very scattered way. While most of these sections may have been retrieved in memory already, as a byproduct of the various initialization processes described above, some may have not, increasing even more the need to seek at all places in the binary file. Now the main problem with all these I/O patterns at startup, is that it seems the only way reorganizing the binary layout may have a visible impact is by considering all the above, and not only a part of it, because only addressing a part of it is very likely to only move part of the problem to a different layer. All in all, making sure the relevant sections of libxul.so are read by the kernel before ld.so enters the game is a good short-term solution to avoid many seeks at startup.

Julien Valroff: Back to Adobe Flash Player

After 4 weeks using exclusively Gnash as flash player, I have switched back to Adobe Flash Player. Though I had found workarounds for major video sites (including Youtube and DailyMotion), I have had a lot of issues with musical websites, most of them being still designed in Flash . To use Adobe Flash Player on my amd64 desktops, I have used Bart Marten s workaround described in the Debian wiki. It works fine though the player randomly crashes on some websites, and the browser needs to be restarted so that it works again. This 4-week experience was fine, but I am a little bit puzzled that there is still no usable free flash player. Hopefuly, things will improve over time the sooner, the better!

13 July 2010

Fathi Boudra: What s going on in Debian Qt world?

Qt 4.7.0 beta 2 and Qt Creator 2.0.0 have been released upstream. There s some
noticeable changes in our Qt packages compared to Qt 4.6.x series:
- legacy Qt Assistant (ADP) is removed
- new Qt declarative module is introduced
- Qt Multimedia and Qt Webkit aren t being built anymore In case your package depends on the deprecated Qt Assistant, it is still being
shipped in a separated Qt Assistant compatibility package called
qt-assistant-compat. Qt Multimedia is now shipped from Qt Mobility API instead of Qt. The module has
been renamed to Qt Mobility MultimediaKit. Qt Mobility delivers a set of APIs
for mobile device functionality. Qt WebKit is shipped standalone, separated from Qt. Hopefully, it will make
Qt WebKit security support easier. There are also a new component in the family: Qt Messaging Framework (QMF).
The Qt Messaging Framework, QMF, consists of a C++ library and daemon server
process that can be used to build email clients, and more generally software
that interacts with email and mail servers. Finally, I have started to ship a snapshot of a new Phonon backend: Phonon VLC,
one backend to rule them all ! All the packages can be found in Debian Experimental repository, except
Qt Simulator, a simulator for Qt applications running on Nokia devices.
This package should land soon in Debian archives. I encourage you to try building your Qt applications against these packages and
please, report any bugs you encounter. Enjoy. http://packages.qa.debian.org/q/qt4-x11.html
http://packages.qa.debian.org/q/qtcreator.html
http://packages.qa.debian.org/q/qt-assistant-compat.html
http://packages.qa.debian.org/q/qtmobility.html
http://packages.qa.debian.org/q/qtwebkit.html
http://packages.qa.debian.org/q/qmf.html
http://packages.qa.debian.org/p/phonon-backend-vlc.html
http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=587759

12 July 2010

Ross Burton: Tasks 0.18 (and 0.17)

Whilst Tasks isn't exactly under active development, I'm still maintaining it because I actually use it (unlike certain other projects, ahem). So, Tasks 0.18 is released. Tarballs and more information as usual are available at the Pimlico Project web site. In related news, we're slowly migrating over to the GNOME infrastructure. We've migrated the source code, next up is the tarballs and bugzilla.

Christine Spang: downloading a directory via HTTP with wget

From the department of things-that-I-know-are-possible-but-can-never-remember-how-to-do-so-hey-I-read-the-manpage-and-now-I'm-blogging-it, I bring you "downloading a directory of photos from a website":
 wget --recursive http://example.com/photos/some-event/ --no-directories --directory-prefix <local-folder-name> --accept JPG,RW2
I always remember wget --recursive (or wget -r for short), but that produces an annoying tree of directories starting with the website's domain and working its way up to the directory you actually want. In the command above, --no-directories removes the tree, and --directory-prefix tells wget to put the downloaded files somewhere that's not the current working directory. The --accept option tells wget to discard files with extensions other than those mentioned, so your downloaded directory is not cluttered with webserver-generated files like index.html if you don't want it to be. Here's the short version, since the long version is nice to remember but not so nice to type:
wget -r http://example.com/photos/some-event/ -nd -P <local-folder-name> -A JPG,RW2

Matthew Garrett: Power management at Plumbers

I'm running a power management track at the Linux Plumbers Conference again this November. Unlike most conferences which focus on presenting completed work, Plumbers is an opportunity to focus on unsolved problems and throw around as many half-baked solutions as you want in order to try to find one that seems to stick. The suspend/resume problem in Linux is mostly solved[1], which means that it's time for us to focus on runtime power management and quality of service.

This has been an especially interesting year in the field. We've landed the infrastructure for generic runtime power management, glued that into PCI and started implementing that at the driver level. pm_qos is being reworked to improve performance and scalability as we start seeing more drivers that need to express their own constraints. And, of course, we had the wakelock/suspend blockers conversation that didn't end in a terribly satisfactory manner, although Rafael is now working on an implementation that presents equivalent functionality with a different userspace API. Runtime full-system suspend isn't solved yet either - the current cpuidle-based solution doesn't work well on multicore systems. And maybe we could be more aggressive still by looking at reclocking more system components on the fly even if the existing interfaces don't allow that. Do we have all the hooks we need to identify which system resources are being used? Are we doing the best we can in terms of avoiding trading off performance for power savings?

So if you'd like to talk about any of these things, or if there's any other problems that you don't think have been solved yet, head on over to the call for submissions and help make sure that we can make Linux the most power-efficient OS possible.

[1] Yes, some machines are broken, but those tend to be individual weird bugs which we're gradually tracking down rather than fundamental issues in our core code, so they're not really in the scope of Plumbers

Craig Small: Manually calculating process times

Most of the time you can trust the times you see in programs such as ps and top within Linux, but there are those other times where you just want to check what is going on.

First up, if you're using a FreeBSD system and the procps Linux tools, you're out of luck because a lot of the procfs is broken. You're almost guaranteed to get wierd results.

Jiffies and Hertz
The first thing to find is your jiffies. This is the number of clock ticks per second. There are two main ways of doing it.

The first, and more difficult way is to add up the cpu numbers in /proc/stat, divide it by the first number in /proc/uptime and divide it by the number of cpus.

My cpu line in /proc/stat looks like:
cpu 1267430 145826 552700 137029699 905086 8295 17885 0 0

and my /proc/uptime is:
695317.99 1370303.58

I've got 2 cpus on this system so the calculation is:

(1267430 + 145826 + 552700 + 137029699 + 905086 + 8295 + 17885 + 0 + 0 ) / 695317.99 / 2
which equals 100.

The second way is to use getconf, with the command "getconf CLK_TCK" which again on this system gives you 100.

We call this value "Hertz" and is usually 100 or 1024 though for some architectures it is other values.

Start Time
For each process the procfs has the start time, but it is expressed as the number of jiffes since the computer was booted. If we want to know the real "wall clock" time a process is started we have to start with number of seconds since epoch (for example using the time() function) subtract the number of seconds since the computer was booted and add the process start time from procfs.

The seconds since boot is the first value in /proc/uptime:
696141.85 1371857.53

The 22nd item in a procfs PID stat file is the number of jiffes since boot when the process started. We have to divide it by Hertz to calculate the number of seconds.
27139 (bash) S 11942 27139 27139 34821 5200 4202496 5500 25537 0 14 40 16 18 15 20 0 1 0 69446639 23433216 1378 18446744073709551615 4194304 5082140 14073724008 6512 140737240085456 140127945249678 0 65536 3686404 1266761467 0 0 0 17 1 0 0 0 0 0

To get the start time, take the current time, subtract the boot time and add the process start time.
perl -e '$nowstr = localtime(time() - 696141.85 + 69446639 / 100); print $nowstr, "\n";
Mon Jul 12 22:20:07 2010

Unless you are very quick, you will have to take a few seconds off because the time() function will be now, while the values from the proc files will be a few seconds earlier as it takes time for you to cut and paste.


<script defer="defer" src="http://static.zemanta.com/readside/loader.js" type="text/javascript"></script>
1

Gerfried Fuchs: On Becoming a MOTU

Working on and for Debian for the whole millennium already it would had been hard to not notice Ubuntu through the times. Given that fixing bugs in the package is in the interest of all involved parties I started to get curious for the packages I maintain in Debian what users of Ubuntu might have filed against them. Given that a fair amount of those actually do also apply to Debian I started to fix them too. Though, some bug status isn't able to use as an outsider, and given my approach to perfection not wanting to have packages in a bad shape in a release I started to dig a bit further into the procedures and applied to be accepted as a Ubuntu Developer, more specifically as a MOTU, which gives me the possibility to directly ask for syncrequest instead of having to go through a sponsor, or set wontfix status for bugreports in my packages that simply doesn't make sense to get implemented. Last week I got accepted into that state and I'd like to thank for all the nice and encouraging feedback along the path (including some "What? I thought you were MOTU since ages already??" responses). Let's see how much I really need it, my approach is rather to reduce Ubuntu diffs instead of having to work on them. I though understand that at times close to the releases there can be a need for them, as can be seen in that the package I have to put most effort into (wesnoth-1.8) has a Ubuntu diff in the last lucid release. And because Ubuntu already is in DebianImportFreeze I did a syncrequest for gitolite. Thanks for accepting me so quickly and rather bureaucratic! And no, Laney, I won't give the talk tomorrow because of this just on my own. ;)

Chris Lamb: 10K Challenge: Week 1

Wasn't the best start this week, unfortunately.
http://chris-lamb.co.uk/wp-content/2010/10k_schedule_1.png

11 July 2010

Thibaut Girka: Fifth GSoC report

Ok, here is the (rather short) fifth report! First, and most important (but not so interesting, as I haven't had any review yet): I've submitted the glamo drivers usptream! There is still a long way to go before having a working Debian kernel on the FR, but I think it's still an important step (well, even more important would be its acceptance in mainline). Another thing is the uboot-envtools-udeb/uboot-installer thing. I haven't started it yet, but I have a rough idea of how to do that. Last but not least, the installation "script". It is meant to run on the host, and takes care of pushing necessary files to the FR and boot it, working around any issue ("slow typing" u-boot for instance). I've designed it in an extensible way, but now, I wonder if I've overdone it. However, it's here, and it's easy to strip down if we want something simpler.

Enrico Zini: Computing time offsets between EXIF and GPS

Computing time offsets between EXIF and GPS I like the idea of matching photos to GPS traces. In Debian there is gpscorrelate but it's almost unusable to me because of bug #473362 and it has an awkward way of specifying time offsets. Here at SoTM10 someone told me that exiftool gained -geosync and -geotag options. So it's just a matter of creating a little tool that shows a photo and asks you to type the GPS time you see in it. Apparently there are no bindings or GIR files for gtkimageview in Debian, so I'll have to use C. Here is a C prototype:
/*
 * gpsoffset - Compute EXIF time offset from a photo of a gps display
 *
 * Use with exiftool -geosync=... -geotag trace.gpx DIR
 *
 * Copyright (C) 2009--2010  Enrico Zini <enrico@enricozini.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
#define _XOPEN_SOURCE /* glibc2 needs this */
#include <time.h>
#include <gtkimageview/gtkimageview.h>
#include <libexif/exif-data.h>
#include <stdio.h>
#include <stdlib.h>
static int load_time(const char* fname, struct tm* tm)
 
    ExifData* exif_data = exif_data_new_from_file(fname);
    ExifEntry* exif_time = exif_data_get_entry(exif_data, EXIF_TAG_DATE_TIME);
    if (exif_time == NULL)
     
        fprintf(stderr, "Cannot find EXIF timetamp\n");
        return -1;
     
    char buf[1024];
    exif_entry_get_value(exif_time, buf, 1024);
    //printf("val2: %s\n", exif_entry_get_value(t2, buf, 1024));
    if (strptime(buf, "%Y:%m:%d %H:%M:%S", tm) == NULL)
     
        fprintf(stderr, "Cannot match EXIF timetamp\n");
        return -1;
     
    return 0;
 
static time_t exif_ts;
static GtkWidget* res_lbl;
void date_entry_changed(GtkEditable *editable, gpointer user_data)
 
    const gchar* text = gtk_entry_get_text(GTK_ENTRY(editable));
    struct tm parsed;
    if (strptime(text, "%Y-%m-%d %H:%M:%S", &parsed) == NULL)
     
        gtk_label_set_text(GTK_LABEL(res_lbl), "Please enter a date as YYYY-MM-DD HH:MM:SS");
      else  
        time_t img_ts = mktime(&parsed);
        int c;
        int res;
        if (exif_ts < img_ts)
         
            c = '+';
            res = img_ts - exif_ts;
         
        else
         
            c = '-';
            res = exif_ts - img_ts;
         
        char buf[1024];
        if (res > 3600)
            snprintf(buf, 1024, "Result: %c%ds -geosync=%c%d:%02d:%02d",
                    c, res, c, res / 3600, (res / 60) % 60, res % 60);
        else if (res > 60)
            snprintf(buf, 1024, "Result: %c%ds -geosync=%c%02d:%02d",
                    c, res, c, (res / 60) % 60, res % 60);
        else
            snprintf(buf, 1024, "Result: %c%ds -geosync=%c%d",
                    c, res, c, res);
        gtk_label_set_text(GTK_LABEL(res_lbl), buf);
     
 
int main (int argc, char *argv[])
 
    // Work in UTC to avoid mktime applying DST or timezones
    setenv("TZ", "UTC");
    const char* filename = "/home/enrico/web-eddie/galleries/2010/04-05-Uppermill/P1080932.jpg";
    gtk_init (&argc, &argv);
    struct tm exif_time;
    if (load_time(filename, &exif_time) != 0)
        return 1;
    printf("EXIF time: %s\n", asctime(&exif_time));
    exif_ts = mktime(&exif_time);
    GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    GtkWidget* vb = gtk_vbox_new(FALSE, 0);
    GtkWidget* hb = gtk_hbox_new(FALSE, 0);
    GtkWidget* lbl = gtk_label_new("Timestamp:");
    GtkWidget* exif_lbl;
     
        char buf[1024];
        strftime(buf, 1024, "EXIF time: %Y-%m-%d %H:%M:%S", &exif_time);
        exif_lbl = gtk_label_new(buf);
     
    GtkWidget* date_ent = gtk_entry_new();
    res_lbl = gtk_label_new("Result:");
    GtkWidget* view = gtk_image_view_new();
    GdkPixbuf* pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
    gtk_box_pack_start(GTK_BOX(hb), lbl, FALSE, TRUE, 0);
    gtk_box_pack_start(GTK_BOX(hb), date_ent, TRUE, TRUE, 0);
    gtk_signal_connect(GTK_OBJECT(date_ent), "changed", (GCallback)date_entry_changed, NULL);
     
        char buf[1024];
        strftime(buf, 1024, "%Y-%m-%d %H:%M:%S", &exif_time);
        gtk_entry_set_text(GTK_ENTRY(date_ent), buf);
     
    gtk_widget_set_size_request(view, 500, 400);
    gtk_image_view_set_pixbuf(GTK_IMAGE_VIEW(view), pixbuf, TRUE);
    gtk_container_add(GTK_CONTAINER(window), vb);
    gtk_box_pack_start(GTK_BOX(vb), view, TRUE, TRUE, 0);
    gtk_box_pack_start(GTK_BOX(vb), hb, FALSE, TRUE, 0);
    gtk_box_pack_start(GTK_BOX(vb), exif_lbl, FALSE, TRUE, 0);
    gtk_box_pack_start(GTK_BOX(vb), res_lbl, FALSE, TRUE, 0);
    gtk_widget_show_all(window);
    gtk_main ();
    return 0;
 
And here is its simple makefile:
CFLAGS=$(shell pkg-config --cflags gtkimageview libexif)
LDFLAGS=$(shell pkg-config --libs gtkimageview libexif)
gpsoffset: gpsoffset.c
It's a simple prototype but it's a working prototype and seems to do the job for me. I currently cannot find out why after I click on the text box, there seems to be no way to give the focus back to the image viewer so I can control it with keys. There is another nice algorithm to compute time offsets to be implemented: you choose a photo taken from a known place and drag it on that place on a map: you can then look for the nearest point on your GPX trace and compute the time offset from that. I have seen that there are programs for geotagging photos that implement all such algorithms, and have a nice UI, but I haven't seen any in Debian. Are there any such softwares that can be packaged? If not, the interpolation and annotation tasks can now already be performed by exiftool, so it's just a matter of building a good UI, and I would love to see someone picking up the task.

10 July 2010

John Goerzen: Radios

Those of you that follow me on twitter or identi.ca know that I ve been working on my amateur radio license. This started a few weeks ago when Jacob got excited about radios, and must have infected me too. I ve been studying and learning a bit. I had called a local ham (amateur radio operator) I know from church. He gave me one of his apparently newer radios, a 1981 2m Icom IC-22U 10W unit. That was great and got me all the more interested. Then last Friday, a couple of local hams came over to check out that radio and see what repeaters I could hit with it. They discovered a handheld 5W unit could hit at least one local repeater, and this 10W unit could too. One of them also had an HF rig in his pickup, and it was fun to stand out in the driveway and listen to conversations in Michigan and Utah. That evening erased any doubt in my mind about whether or not I would become a ham and it s a bit hard to believe it was only a week ago. I had been studying for the Technician-class amateur radio license, the lowest of three levels of licensing. One of the guys that came over Friday gave me a book to study for the General license, the intermediate level. It warned me to allow a month to prepare, and here I was planning to take the exams Tuesday. I picked it up Sunday, so really got 2 days of studying in. Tuesday I drove to Independence, KS, about 2.5 hours away for the exams. I went so far primarily because the Wichita exams weren t going to be offered until mid-August and I didn t want to wait that long. I found a fun group of people in Independence. I hadn t expected to have fun taking exams, but really, I did. We visited before the exams, and they wanted to know where I was from, of course. When it was time to take the exams, I guess I sort of surprised them by already having my FRN (FCC registration number) and photo copies of my IDs with me. One of them said, You re going to have no trouble with this, are you? I took the Technician exam, checked my work carefully, and turned it in to be graded. They checked it twice and had two people looking over it before they announced I passed with all 35 questions correct. I should note at this point that I was the only person taking the exams that night. Anyhow, on to the General exam. Same drill. It was harder, of course, and I turned it in to be graded. One of them looked at the test, looked at me, put on a grave face and said, Uh oh, not one right. I knew he was joking and they announced I passed THAT one with 100% correct as well. I hadn t expected that, and neither had they. That was what I planned to do, but they said that nobody had ever walked in, taken two tests, and had a score like that and pretty much insisted I try taking the Amateur Extra exam as well. I said I haven t studied for it at all and really doubt I could pass, but eventually went ahead and took that one too. I didn t pass, but I don t think they d have wanted to let me leave without trying. After the paperwork was done, they invited me to hang around and chat with their group that was meeting next door for a bit. I did, and drove home. Then was the frustrating part of the week: waiting for my license. I can t transmit until the FCC issues my license, even though I had passed the exam. My handheld radio arrived later in the week, and of course I still couldn t do much with it. Finally the FCC posted my license late yesterday so I was able to talk to people. It s been fun and I look forward to doing more. I was able to talk to people 55 miles away while driving, and with suitable equipment at home should be able to do much more than that. Several of the people I talked to were offering me tips. I ve never seen a group of people so eager to help out someone new. It s an amazing community and I think Jacob and Oliver will enjoy it one day too.

Jonathan McDowell: SPI 2010 AGM & Board Election

As SPI secretary I announced that nominations for the SPI board were open at the start of the month. The nomination period closes this Tuesday (13th July) with voting opening up on Thursday 15th. This year over half the board is up for election - 5 seats (currently held by Luk Claes, Joshua D. Drake, Bdale Garbee, Joerg Jaspert & Martin Zobel-Helas). So far I've received only 2 nominations, though I'm aware these things are often left to the last minute, so hopefully more will appear in the next few days. All anyone who wants to stand needs to do is drop secretary@spi-inc.org a (preferably PGP signed) email nominating yourself and providing a position statement (which will all be published once the nomination period is over).

Oh, and if you're a contributing SPI member please do remember to vote once voting is open!

Enrico Zini: Importing OSM nodes into Spatialite

Importing OSM nodes into Spatialite Second step of my SoTM10 pet project: creating a searchable database with the points. What a fantastic opportunity to learn Spatialite. Learning Spatialite is easy. For example, you can use the two tutorials with catchy titles that assume your best wish in life is to create databases out of shapefiles using a pre-built, i386-only executable GUI binary downloaded over an insecure HTTP connection. To be fair, the second of those tutorials is called "An almost Idiot's Guide", thus expliciting the requirement of being an almost idiot in order to happily acquire and run software in that way. Alternatively, you can use A quick tutorial to SpatiaLite which is so quick it has examples that lead you to write SQL queries that trigger all sorts of vague exceptions at insert time. But at least it brought me a long way forward, at which point I could just cross reference things with PostGIS documentation to find out the right way of doing things. So, here's the importer script, which will probably become my reference example for how to get started with Spatialite, and how to use Spatialite from Python:
#!/usr/bin/python
#
# poiimport - import nodes from OSM into a spatialite DB
#
# Copyright (C) 2010  Enrico Zini <enrico@enricozini.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
import xml.sax
import xml.sax.handler
from pysqlite2 import dbapi2 as sqlite
import simplejson
import sys
import os
class OSMPOIReader(xml.sax.handler.ContentHandler):
    '''
    Filter SAX events in a OSM XML file to keep only nodes with names
    '''
    def __init__(self, consumer):
        self.consumer = consumer
    def startElement(self, name, attrs):
        if name == "node":
            self.attrs = attrs
            self.tags = dict()
        elif name == "tag":
            self.tags[attrs["k"]] = attrs["v"]
    def endElement(self, name):
        if name == "node":
            lat = float(self.attrs["lat"])
            lon = float(self.attrs["lon"])
            id = int(self.attrs["id"])
            #dt = parse(self.attrs["timestamp"])
            uid = self.attrs.get("uid", None)
            uid = int(uid) if uid is not None else None
            user = self.attrs.get("user", None)
            self.consumer(lat, lon, id, self.tags, user=user, uid=uid)
class Importer(object):
    '''
    Create the spatialite database and populate it
    '''
    TAG_WHITELIST = set(["amenity", "shop", "tourism", "place"])
    def __init__(self, filename):
        self.db = sqlite.connect(filename)
        self.db.enable_load_extension(True)
        self.db.execute("SELECT load_extension('libspatialite.so')")
        self.db.execute("SELECT InitSpatialMetaData()")
        self.db.execute("INSERT INTO spatial_ref_sys (srid, auth_name, auth_srid,"
                        " ref_sys_name, proj4text) VALUES (4326, 'epsg', 4326,"
                        " 'WGS 84', '+proj=longlat +ellps=WGS84 +datum=WGS84"
                        " +no_defs')")
        self.db.execute("CREATE TABLE poi (id int not null unique primary key,"
                        " name char, data text)")
        self.db.execute("SELECT AddGeometryColumn('poi', 'geom', 4326, 'POINT', 2)")
        self.db.execute("SELECT CreateSpatialIndex('poi', 'geom')")
        self.db.execute("CREATE TABLE tag (id integer primary key autoincrement,"
                        " name char, value char)")
        self.db.execute("CREATE UNIQUE INDEX tagidx ON tag (name, value)")
        self.db.execute("CREATE TABLE poitag (poi int not null, tag int not null)")
        self.db.execute("CREATE UNIQUE INDEX poitagidx ON poitag (poi, tag)")
        self.tagid_cache = dict()
    def tagid(self, k, v):
        key = (k, v)
        res = self.tagid_cache.get(key, None)
        if res is None:
            c = self.db.cursor()
            c.execute("SELECT id FROM tag WHERE name=? AND value=?", key)
            for row in c:
                self.tagid_cache[key] = row[0]
                return row[0]
            self.db.execute("INSERT INTO tag (id, name, value) VALUES (NULL, ?, ?)", key)
            c.execute("SELECT last_insert_rowid()")
            for row in c:
                res = row[0]
            self.tagid_cache[key] = res
        return res
    def __call__(self, lat, lon, id, tags, user=None, uid=None):
        # Acquire tag IDs
        tagids = []
        for k, v in tags.iteritems():
            if k not in self.TAG_WHITELIST: continue
            for val in v.split(";"):
                tagids.append(self.tagid(k, val))
        # Skip elements that don't have the tags we want
        if not tagids: return
        geom = "POINT(%f %f)" % (lon, lat)
        self.db.execute("INSERT INTO poi (id, geom, name, data)"
                        "     VALUES (?, GeomFromText(?, 4326), ?, ?)",
                (id, geom, tags["name"], simplejson.dumps(tags)))
        for tid in tagids:
            self.db.execute("INSERT INTO poitag (poi, tag) VALUES (?, ?)", (id, tid))
    def done(self):
        self.db.commit()
# Get the output file name
filename = sys.argv[1]
# Ensure we start from scratch
if os.path.exists(filename):
    print >>sys.stderr, filename, "already exists"
    sys.exit(1)
# Import
parser = xml.sax.make_parser()
importer = Importer(filename)
handler = OSMPOIReader(importer)
parser.setContentHandler(handler)
parser.parse(sys.stdin)
importer.done()
Let's run it:
$ ./poiimport pois.db < pois.osm 
SpatiaLite version ..: 2.4.0    Supported Extensions:
        - 'VirtualShape'        [direct Shapefile access]
        - 'VirtualDbf'          [direct Dbf access]
        - 'VirtualText'         [direct CSV/TXT access]
        - 'VirtualNetwork'      [Dijkstra shortest path]
        - 'RTree'               [Spatial Index - R*Tree]
        - 'MbrCache'            [Spatial Index - MBR cache]
        - 'VirtualFDO'          [FDO-OGR interoperability]
        - 'SpatiaLite'          [Spatial SQL - OGC]
PROJ.4 Rel. 4.7.1, 23 September 2009
GEOS version 3.2.0-CAPI-1.6.0
$ ls -l --si pois*
-rw-r--r-- 1 enrico enrico 17M Jul  9 23:44 pois.db
-rw-r--r-- 1 enrico enrico 37M Jul  9 16:20 pois.osm
$ spatialite pois.db
SpatiaLite version ..: 2.4.0    Supported Extensions:
        - 'VirtualShape'        [direct Shapefile access]
        - 'VirtualDbf'          [direct DBF access]
        - 'VirtualText'         [direct CSV/TXT access]
        - 'VirtualNetwork'      [Dijkstra shortest path]
        - 'RTree'               [Spatial Index - R*Tree]
        - 'MbrCache'            [Spatial Index - MBR cache]
        - 'VirtualFDO'          [FDO-OGR interoperability]
        - 'SpatiaLite'          [Spatial SQL - OGC]
PROJ.4 version ......: Rel. 4.7.1, 23 September 2009
GEOS version ........: 3.2.0-CAPI-1.6.0
SQLite version ......: 3.6.23.1
Enter ".help" for instructions
spatialite> select id from tag where name="amenity" and value="fountain";
24
spatialite> SELECT poi.name, poi.data, X(poi.geom), Y(poi.geom) FROM poi, poitag WHERE poi.rowid IN (SELECT pkid FROM idx_poi_geom WHERE (xmin >= 2.56 AND xmax <= 2.90 AND ymin >= 41.84 AND ymax <= 42.00) ) AND poitag.tag = 24 AND poitag.poi = poi.id;
Font Picant de la Cellera "amenity": "fountain", "name": "Font Picant de la Cellera" 2.616045 41.952449
Font de Can Pla "amenity": "fountain", "name": "Font de Can Pla" 2.622354 41.974724
Font de Can Ribes "amenity": "fountain", "name": "Font de Can Ribes" 2.62311 41.979193
It's impressive: I've got all sort of useful information for the whole of Spain in just 17Mb! Let's put it to practice: I'm thirsty, is there any water fountain nearby?
spatialite> SELECT count(1) FROM poi, poitag WHERE poi.rowid IN (SELECT pkid FROM idx_poi_geom WHERE (xmin >= 2.80 AND xmax <= 2.85 AND ymin >= 41.97 AND ymax <= 42.00) ) AND poitag.tag = 24 AND poitag.poi = poi.id;
0
Ouch! No water fountains mapped in Girona... yet. Problem 2 solved: now on to the next step, trying to show the results in some usable way.

9 July 2010

Benjamin Mako Hill: Memory

Today I started to tell a friend about something from dinner the night before. Except that she was at the dinner. And sitting at the same table! Even when prompted, I couldn't really remember! This does not warrant a blog post. Anybody who knows me well knows that my memory for these kinds of more mundane details is pretty porous. This kind of thing happens all the time. I'm writing this so that when I'm much older, and still forgetting things all the time, folks can use this as a reference point before concluding that senility is setting in. I'm afraid that everyone else will forget my forgetfulness!

Guido G nther: krb5-auth-dialog: Simple AFS plugin

I've just added a simple afs plugin to krb5-auth-dialog. It just calls either aklog or afslog after acquiring or renewing your Kerberos ticket to get you into your AFS cell. To activate it use
gconftool-2 --set --list-type=string --type=list /apps/krb5-auth-dialog/plugins/enabled [afs]
and restart krb5-auth-dialog. A package with the above enabled has been uploaded to Debian. Thanks to the test account in the global2000.at cell provided by J rg Herzinger I could test this easily and I have to say that AFS looks like a cool thing.

Mike Hommey: Iceweasel 4.0 beta 1 preliminary packages for Debian, and a nice trick

Since this blog is now syndicated on Planet Mozilla, a little background for its readers: Iceweasel is the name under which Firefox is distributed in Debian. This is an unfortunate detail due to copyright issues with the Firefox logos that have been solved recently. Work is under progress to get Firefox back in Debian (I do have good hope this will be possible). Anyways, I ve started to work on getting Iceweasel in shape for the 4.0 release in a few months. The packaging being in a good enough shape, I am hereby making available some preliminary packages (meaning there is still some more work needed for proper packaging) for the first beta release. The packages are available in a separate repository, which you need to add to your sources.list:
deb http://mozilla.debian.net/packages/ ./
Only x86 and x86-64 (amd64) packages are available for now. As far as I tested it, it works as well as the official Firefox 4.0beta1 binaries, though some xpcshell tests failed, and I haven t got reftests working yet. If you still wish to use a current version of iceweasel (i.e. non-beta), but want to try this beta, here is the nice trick: Instead of installing iceweasel from the above mentioned repository, Now you can start the Iceweasel beta with /some/directory/usr/bin/iceweasel. This trick should work with older versions of iceweasel, too.

Enrico Zini: Filtering nodes out of OSM files

Filtering nodes out of OSM files I have a pet project here at SoTM10: create a tool for searching nearby POIs while offline. The idea is to have something in my pocket (FreeRunner or N900), which doesn't require an internet connection, and which can point me at the nearest fountains, post offices, atm machines, bars and so on. The first step is to obtain a list of POIs. In theory one can use Xapi but all the known Xapi servers appear to be down at the moment. Another attempt is to obtain it by filtering all nodes with the tags we want out of a planet OSM extract. I downloaded the Spanish one and set to work. First I tried with xmlstarlet, but it ate all the RAM and crashed my laptop, because for some reason, on my laptop the Linux kernels up to 2.6.32 (don't now about later ones) like to swap out ALL running apps to cache I/O operations, which mean that heavy I/O operations swap out the very programs performing them, so the system gets caught in some infinite I/O loop and dies. Or at least this is what I've figured out so far. So, we need SAX. I put together this prototype in Python, which can process a nice 8MB/s of OSM data for quite some time with a constant, low RAM usage:
#!/usr/bin/python
#
# poifilter - extract interesting nodes from OSM XML files
#
# Copyright (C) 2010  Enrico Zini <enrico@enricozini.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
import xml.sax
import xml.sax.handler
import xml.sax.saxutils
import sys
class XMLSAXFilter(xml.sax.handler.ContentHandler):
    '''
    A SAX filter that is a ContentHandler.

    There is xml.sax.saxutils.XMLFilterBase in the standard library but it is
    undocumented, and most of the examples using it you find online are wrong.
    You can look at its source code, and at that point you find out that it is
    an offensive practical joke.
    '''
    def __init__(self, downstream):
        self.downstream = downstream
    # ContentHandler methods
    def setDocumentLocator(self, locator):
        self.downstream.setDocumentLocator(locator)
    def startDocument(self):
        self.downstream.startDocument()
    def endDocument(self):
        self.downstream.endDocument()
    def startPrefixMapping(self, prefix, uri):
        self.downstream.startPrefixMapping(prefix, uri)
    def endPrefixMapping(self, prefix):
        self.downstream.endPrefixMapping(prefix)
    def startElement(self, name, attrs):
        self.downstream.startElement(name, attrs)
    def endElement(self, name):
        self.downstream.endElement(name)
    def startElementNS(self, name, qname, attrs):
        self.downstream.startElementNS(name, qname, attrs)
    def endElementNS(self, name, qname):
        self.downstream.endElementNS(name, qname)
    def characters(self, content):
        self.downstream.characters(content)
    def ignorableWhitespace(self, chars):
        self.downstream.ignorableWhitespace(chars)
    def processingInstruction(self, target, data):
        self.downstream.processingInstruction(target, data)
    def skippedEntity(self, name):
        self.downstream.skippedEntity(name)
class OSMPOIHandler(XMLSAXFilter):
    '''
    Filter SAX events in a OSM XML file to keep only nodes with names
    '''
    PASSTHROUGH = ["osm", "bound"]
    TAG_WHITELIST = set(["amenity", "shop", "tourism", "place"])
    def startElement(self, name, attrs):
        if name in self.PASSTHROUGH:
            self.downstream.startElement(name, attrs)
        elif name == "node":
            self.attrs = attrs
            self.tags = []
            self.propagate = False
        elif name == "tag":
            if self.tags is not None:
                self.tags.append(attrs)
                if attrs["k"] in self.TAG_WHITELIST:
                    self.propagate = True
        else:
            self.tags = None
            self.attrs = None
    def endElement(self, name):
        if name in self.PASSTHROUGH:
            self.downstream.endElement(name)
        elif name == "node":
            if self.propagate:
                self.downstream.startElement("node", self.attrs)
                for attrs in self.tags:
                    self.downstream.startElement("tag", attrs)
                    self.downstream.endElement("tag")
                self.downstream.endElement("node")
    def ignorableWhitespace(self, chars):
        pass
    def characters(self, content):
        pass
# Simple stdin->stdout XMl filter
parser = xml.sax.make_parser()
handler = OSMPOIHandler(xml.sax.saxutils.XMLGenerator(sys.stdout, "utf-8"))
parser.setContentHandler(handler)
parser.parse(sys.stdin)
Let's run it:
$ bzcat /store/osm/spain.osm.bz2   pv   ./poifilter > pois.osm
[...]
$ ls -l --si pois.osm
-rw-r--r-- 1 enrico enrico 19M Jul 10 23:56 pois.osm
$ xmlstarlet val pois.osm 
pois.osm - valid
Problem 1 solved: now on to the next step: importing the nodes in a database.

Phil Hands: Talking to my MP about the Digital Economy Act

I had a constructive meeting with my Member of Parliament yesterday, in which he agreed to push this critique of the Digital Economy Act (the dire nonsense that Lord Mandelson forced through in the dying seconds of the previous UK parliamentary session, apparently at the behest of the copyright exploitation industry, who will presumably now owe him a few lucrative consultancies)If you're wanting to do something similar, feel free to use the content, or use it for inspiration, or just use the one-page PDF version if that's helpful to you in explaining the worthlessness of this law.Note, I know there are many other points that could be made, but I was restricting myself to one side of A4 (just) -- MPs cannot be expected to read more it seems.

Next.

Previous.