An often-overlooked Linux distribution (likely due to being purchased by Novell, whom many self-professed Linux users find a reason to detest) is openSUSE; a free distribution, supported and partially developed by Novell.

I tend to be fairly software-eclectic: I’ll try anything once, because chances are – I will see it again. I can hold my own with RedHat distributions, Debian distributions, various forms of BSD, and, now, I have finally cut my teeth on SUSE. The last time I looked at SUSE, it was Slackware based, only, in German.

The times have truly changed. It’s certainly no Slackware.

I burned a Live CD of openSUSE 10.3, and stuck it into my Vostro 1400. It just.. worked. It booted, and I was in a KDE menu with my wireless online within 20 seconds.

(Not even the famed “easy to use” Ubuntu worked properly on the initial boot/install of this machine: Ubuntu screwed up the video settings, installing a VESA driver, and somehow managed to wedge itself when trying to load the ipw3945 wireless driver. Then, when I decided I wanted to be able to ‘sleep’ the laptop, I had to make my own ACPI scripts; none of those included with Ubuntu would work.)

Feeling a bit cocky, I took it upstairs to boot on my “brand new” (six months old) Inspiron 530 Quad Core based desktop. Not only did it work (although for obvious reasons, it used the open source driver rather than the proprietary NVidia one by default), but it saw that I had placed a $20 TV card into a PC slot, so it automatically put KDETV in my applications menu. How cool is that? I had to reload the module with the proper card, and tuner id – which took about 5 seconds in YaST2. I spent about two hours figuring all of the system settings, V4L2 drivers, etc, prior to it to work before. Wow. What else can this do?

I have not used the GNOME release of openSUSE, as I love the elegance – and the fact that KDE applications are closely tied together, not only with the desktop, but themselves. openSUSE has it’s own menu that’s fired off of kicker (one right click away from the usual KDE menu), which is pretty damn useful when you get used to it. It’s like having QuickSilver built into kicker.

Now, the bad side of openSUSE:

The package management system is so overengineered, it’s pathetic. Each subsystem can have it’s own repository for any purpose; I have one specifically for ‘Wine’, and another for ‘Mutt’. That seems a bit much. Your package management system doesn’t need to be a gateway to SVN, nor should it be. It’s slow. Zypper is so damn slow, it took 30 seconds to look for new patches with a very modest set of repositories. There is a secondary tool that can be installed, and used, called ‘smart’, which is somewhat similar to ‘apt-get’ on Debian systems. It is much, much faster, but non standardized upon, yet.

There’s no real way to ‘upgrade’ between versions. The official stance of many openSUSE users is to ‘Have /home on a new partition, format the rest, and start over.’ It’s not 1996; there’s no reason we should need to do this.

Everything is customized, sometimes it’s in an odd way. The Apache2 distribution in openSUSE is beyond pedantic: It’s obnoxious to configure with their distribution layout.

Everything important is controlled by a combination of shell scripts, and messy GUI utilities. If you don’t do it from YaST, you’ll do it in YaST2. There is no other option. You are now part of the YaST borg.

The polished applications and care put into the system still outshine the above issues; if openSUSE gets a managable packaging system that mortals can use – it might just make it. If you haven’t already, try it out. You might just like it. I did, and I am now without a Debian based system in my office for the first time in years.

I remembered this being a ‘never heard more than twice’ song from the 70s. However, it’s become incredibly popular. First, it was remixed for a GAP commercial, then for a commercial advertising anti-allergens.

For the rest of you who just wanted the song, and can live with generic jeans while taking an over the counter Sudafed, it’s by Bill Withers:

This was originally posted by Bret Hubert nearly 6 months ago, but it’s saved me a LOT of time, since he’s rehashed, well, common sense:

“…
Additionally, our database interface needed to offer an extra feature: every once in a while a query comes along that we DO need to wait for, and because of coherency issues, such a query can only be executed once all queries ‘in flight’ have finished.
So we spent some time pondering this, and suddenly it dawned on me that many of the features we needed exactly match the semantics of the venerable UNIX ‘pipe’.
A pipe is normally used to communicate between two processes, as exemplified by this sample shell script command, which shows us the largest directories on a disk:
$ du | sort -n
The program ‘du’ generates a list of directories and their sizes, which is then fed to sort which outputs this in ascending order. However, nothing prohibits us from using a pipe to communicate with ourselves – and as such it might be a might fine conduit to pass database queries through to our database worker thread.
This has some very nice benefits. Pipes are incredibly efficient, since a lot of UNIX performance depends on them. Additionally, they implement sane blocking behaviour: if too much data is stuck in the pipe, because the other process does not take it out again quickly enough, the sending process automatically blocks. The operating system implements high and low water marks to make this (un)blocking happen efficiently.
Furthermore, pipes guarantee that data up to a certain size can either be written as a whole, or not written at all – making sure we don’t have to deal with partial messages.
Finally, pipes automatically detect when the process on the other end of them has gone away, or has closed its end of the pipe.
However, not all is good. In order to transmit something over a pipe, it must be serialised into bytes – we can’t transmit ready to use objects over them. Additionally, because pipes implement ‘stream’ behaviour, we need to delineate one message from the other, because the pipe itself does not say where a message begins and ends – unlike datagram sockets for example.
And this is the clever bit of our idea. As stated above, pipes are usually employed to transmit data from one process to the other. In our case, the pipe goes from one thread of execution to the other – within the same process, and thus within the same memory space. So we don’t need to send serialized objects at all, and can get away with transmitting pointers to objects. And the nice thing is, pointers all have the same (known) length – so we can do away with both delineation and serialisation.
Additionally, pointers are a lot smaller than most messages, which means we can stuff more messages in the same (fixed) size of the pipe buffer.
So, are we done now? Sadly no – we have the additional need to be able to ‘flush the pipe’ in order to perform synchronous queries that we do need to wait for.
This is where things get complicated, but for those who really want to know, I’ll explain it here. It took almost a day of hacking to get it right however, and I’m explaining it for my own benefit as much as for that of the reader, since I’m bound to forget the details otherwise.
If a synchronous query comes along, we need to flush the pipe, but UNIX offers no such ability. Once we’ve written something to a pipe, all the kernel guarantees us is that it will endeavour to deliver it, but there is no system call that allows us to wait for all data to actually be delivered.
So we need to find a way to signal a ‘write barrier’, and the obvious way to do so is to send a NULL pointer over the pipe, which tells the other end we want to perform a synchronous query. Once the worker thread has seen the NULL pointer, it unlocks the single controlling mutex (which is the return signal that says “got you -the pipe is empty”), and then waits for further pointers to arrive.
Meanwhile, the sending thread tries to lock that same mutex immediately after sending the NULL pointer, which blocks since the receiving thread normally holds the lock. Once the lock succeeds, this tells us the worker thread has indeed exhausted all queries that were in flight.
The sending thread now performs its synchronous database work, knowing the database is fully coherent with all queries it sent out previously, and also knowing the worker thread is not simultaneously accessing the connection – since it is instead waiting for a new pointer to arrive.
If our program now wants to perform further asynchronous queries it can simply transmit further pointers to the worker thread – which oddly enough does not need to retake the mutex. This is what caused us many hours of delay, because intuitively it seems obvious that once the sending thread is done, it must release the mutex so the worker thread can retake it.
As it turns out, doing so opens a whole world of nasty race conditions which allow synchronous queries to ‘jump the queue’ of asynchronous queries that are in flight and have not yet arrived.
So, the sequence is that the worker thread only unlocks the mutex, while the sending thread only locks it.
And this basically is it! So how much lines of code did we save by using the magic of UNIX pipes? The pipe handling code takes all of 90 lines, whereas the Distributor code of PowerDNS takes a round 300, even though it does not offer synchronous queries, does not automatically block if too many queries are outstanding, and most certainly couldn’t implement the sensible wakeup ability that UNIX pipes do offer.
Oh, and you might be wondering by now, did it help? Indeed it did – our program is now at least 20 times faster than it used to be, and there was much rejoicing.”

MaxMind’s mod_geoip directly inserted into your webserver would be the easiest for external/programmable GeoIP support.

It’d make more sense to check the browser’s language(s) of choice, than force a user of a region into a language they don’t want. Let Apache handle the language, itself, then just display the runtime code (if that’s what you want to do) independently with mod_geoip.

First, setup your supported languages, and priority in Apache:

AddLanguage en .en
AddLanguage fr .fr
AddLanguage es .es
AddLanguage de .de

Then, set your defaults you want to support in order of priority:

LanguagePriority en fr es de

Create multiple pages with the different languages embedded. “index.html” would be removed, and you would have “index.html.en”, “index.html.fr”, “index.html.es”, and “index.html.de” (This also works for .php, .shtml, etc.)

Now, GeoIP’s database for the country name database part, which almost mimmics the above, but uses a different format, ISO3166.

Here’s some tiny little throwaway GeoIP code. It displays those tiny flags in my sig. It has very little overhead with mod_geoip, marginal with the runtime PHP library. This is an old version without eTags (caching) support. It’d be smarter to cache these into RAM than to continually read them from the disk, if you have the choice. You may use this code, if you want.

<?php
// Simple little example utlity to display a small flag based upon GeoIP data.
// by Shawn Holwegner <shawn/at/holwegner/dot/com>
// You may reuse this code, if you want.  I stake no claims to it.
//
// This example supports both mod_geoip, and the external PHP API.
// If mod_geoip is installed:
  $country = "";
  // Do we have apache_note()?
  if (function_exists('apache_note'))
    $country = strtolower(apache_note("GEOIP_COUNTRY_CODE"));
  if (empty($country)) {
    // external MaxMind PHP support
    @require_once("./geoip.inc");
    $gi = geoip_open("./GeoIP.dat", GEOIP_STANDARD);
    $country = strtolower(geoip_country_code_by_addr($gi, $_SERVER["REMOTE_ADDR"
]));
    geoip_close($gi);
    if ($country) {
      header ("Pragma: no-cache");
      header("Expires: Mon, 26 Jul 1999 04:20:00 GMT");
      header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
      header('Cache-Control: no-store, no-cache, must-revalidate');
      header('Cache-Control: pre-check=0, post-check=0, max-age=0');
      header("Content-type: image/gif");
      if (file_exists('./'.$country.'.gif')) {
         readfile('./'.$country.'.gif');
      } else {
         // Generic 'empty' country banner.
         readfile("./00.gif");
      }
    }
  }
?>

I hope this information has been helpful as to easily, and properly implement GeoIP support. Stop forcing me to read Spanish when I’m in Latin America; I get enough of that when I’m in the states!