Sunday, November 28, 2004

Impressions on Monotone

I'm amazed after having played with Monotone during the whole evening. Simply put, it is a distributed version control system, similar to CVS in the sense that it keeps track of changes across files and lets multiple people work at the same time with them. But, unlike CVS, it has many other cool features. The front page of its website contains a nice paragraph summarizing all available features, so I'm not repeating them here. But let me discuss here what has taken my attention.

First of all, its manual. It's great, a strange thing in free software projects. It starts by explaining a series of basic concepts, continuing with a quite complete tutorial, followed by several other chapters that I've not yet read. But at this point, I'm able to do basic stuff with Monotone, and even converted a project from an existing cvsroot. (Ok, ok, I know CVS very well, so this may have helped ;).

Then we have disconnected operation. Monotone works locally almost all the time; it only uses the network when you tell it to synchronize your local database with a server (or any other peer!). That is, you can start your daily work by pulling a fresh copy from the server, doing all your modifications in your system (even committing as much as you want), to later push your changes back to the server. In fact, this is like maintaining mini-branches all the time and merging them when you are ready to send your changes to someone else.

Another interesting point is its use of cryptography. All revisions are identified by SHA1 hashes, thus avoiding manual naming problems or meaningless version numbers (in case this seems problematic because of hash collisions, please read this). As regards to security, all changes are assigned to a key pair, thus being digitally signed by a RSA certificate.

At last, let me say that it's portable, written in C++ (yay! and it follows the standards!), and seems to be very stable already (it's self-hosting). If you are not happy with your version control system, give this utility a try! Looks very promising. (And I've left out several details that'd be worth mentioning too.)

Saturday, November 27, 2004

QT Parted experiences

A few days ago I gave QT Parted a try (from a Knoppix 3.6 disk): I shrinked my NTFS partition to leave more room for other OSes (not that space is a problem here, but I wanted to try it). The program is awesome: very easy to use (looks a lot like Partition Magic) and it did the job quickly and correctly.

However, yesterday's evening I tried the same but with a ReiserFS partition. In few minutes, the partition was shrinked to half of its original size. Afterwards I tried to mount it, still from within Knoppix. Everything seemed to be OK.

But it was not so nice as I expected. I rebooted into my Debian system... and fsck.reiserfs was started. Lots of errors flooded the screen, just to tell me a bit later to fix the errors manually.

Got to the console (which still worked), tried another fsck and got the same errors. Now I noticed a suggestion that told me to try the --rebuild-tree flag. Oh, it may fix it... so I tried. The process seemed to go fine, but things got only worse. After a reboot, nothing worked; even GNU GRUB failed to read its stage2 correctly.

So I gave up. I had nothing important in the partition aside apt's cache (not a problem since I've already downloaded everything again). From now on I'll continue to keep my Linux installations under Ext2 (or maybe Ext3), mainly because they can be read (and written!) without problems from NetBSD. And they can be safely resized (I can always go back to Partition Magic 5), in case I want to try another OS.

Thursday, November 25, 2004

Differences between a hub and a switch

A friend of mine asked me yesterday which are the differences between a hub and a switch. After giving her a quick explanation, I realized that I'd write something better and leave it here for anyone else interested. However, a detailed explanation could need a lot of background theory, so these are just the basic ideas that differentiate these network devices.

First of all: Ethernet's logical topology is a bus: a single cable carries all data in all directions, which reach all equipments connected to it. Just think about a network using 10Base2 or 10Base5 (coaxial cable) and you'll quickly understand this point.

Now suppose you collapse all the bus (the cable that reaches all computers) in a little box and then connect this little box to each computer using another cable. This box is basically a hub, a level 1 device in the OSI model. Note that the physical topology of the network has become a star (all cables go to a central point), but logically, it's still a bus (this last part is important, because Ethernet is always a bus).

More specifically, a hub processes data bit by bit. Whenever a bit arrives through a port, it's automatically replicated to all others. Aside replication, it also regenerates and amplifies the transmitted signal. But keep in mind that it doesn't process any of the data that travels through it (it's a level 1 device).

On the other hand, a switch is more complex. It's quite similar to a hub in its physical functionality, though a switch isolates connections between hosts: frames sent through a port are only replicated to the port that contains the target computer, not all ports. To do this, the switch stores the received frame into memory, gets its target MAC address, finds a match in its MAC-to-port translation table and transmits the frame to that port. Therefore, this is a level 2 device.

Because the switch handles level 2 frames (not raw bits), it can do much more than just replicate data. For example, it can avoid the replication of collisions and errors: the former is always present, but the later is only found in store and forward switches (not cut-through).

However, a good (and very expensive) switch can do much more than this; it can apply security policies to ports, replicate configurations to other switches, manage virtual LANs, etc.

Sunday, November 21, 2004

Linux's UFS support

The last time I tried Linux's UFS support was around the first 2.4 releases, and IIRC, it trashed my file-systems even in read only mode (sounds strange, I know, but this is what I remember). Today I decided try again to verify if the situation is better now.

The first step has been to install NetBSD on another disk, using UFSv2, to have a test partition with a bunch of files. Then, I've installed Linux. Which one? I went for Debian because this was my distribution of choice back to the days when I used Linux ;-) But, in fact, there is another reason behind this choice. It has been ages since I compiled a Linux kernel, and I didn't want to loose too much time relearning the process (yet). Debian makes compilations very easy (providing you a ready-to-install package with your custom kernel), and it comes with a default configuration that is suitable for most uses (extremely modularized). Note that I installed unstable with a 2.4.26 kernel.

Anyway, after all these installations, it was time for the test. Issued mount -t ufs -o ro,ufstype=44bsd /dev/hda7 /mnt and the command failed. Oh... what a pity. Of course it didn't work, because the 44bsd type is addressed to UFSv1 file-systems.

So "let's try with a kernel from the 2.6 series", I thought. All I needed was: apt-get install kernel-image-2.6.9-1-k7; isn't this easy? After rebooting, I wondered how could I check if UFSv2 was supported (and which could be the string to pass to the ufstype parameter). man mount didn't give me any clue (outdated manual pages, you know), so I used a more rudimentary method: strings ufs.ko and searched through all lines until I found one with the list of supported types. What a surprise! ufs2 is there! Attempted to mount... list directories... view files... all correct! I can safely read my files from Linux!

Hmm... but I would like to write to the file-system too. So I removed the ro option and got a warning saying that the kernel was built without UFS write support (because it's still marked as experimental). Ew, time to rebuild the kernel. Started by reading the README.gz file included in the kernel-package package, configured the kernel with only three custom changes changes and built it. Not very difficult :)

Unfortunately, after rebooting, I couldn't get read-write support. Simply put, the UFSv2 support is still read-only, and there is no write functionality (even with the experimental code compiled in). I hope that the ufs-linux project keeps up the good work and add this feature.

But still not happy, I repeated the same process, this time creating a UFSv1 file-system. To my surprise, Linux can read it without problems (using the 44bsd subtype), but write support is very frustrating. One of the tests just made the kernel enter an infinite loop. In another one, I was able to create directories, but they always appeared empty, no matter what I put in them.

Given the results, I'll keep UFSv2. I expect that write support will be added "soon" (I can wait), and I guess that 44bsd won't be fixed (it hasn't been for years). (And no, no time nor enough knowledge to "fix-it-myself".)

Oh, and in case you are wondering why I don't keep my home tree in a Ext2 partition (as I used to when I was a BSD newbie), which can be perfectly read from the *BSDs... well, there are basically two reasons. On the first hand, Ext2 performance under NetBSD is very poor. On the other hand, I don't trust Ext2 very much; I used to loose big amounts of data after power outages, a thing that has never happened to me with UFS.

Sunday, November 14, 2004

Dynamic open of libpthread considered harmful

Here goes another portability issue I've met multiple times while packaging software for NetBSD 2.x, where libpthread has some restrictions that other systems don't seem to have. Simply put, a non-threaded program cannot become threaded at run time, because, if it does, you get a nice "Abort trap". But, how can this happen?

Suppose you have an application that is not linked against libpthread, neither directly nor indirectly (through required libraries). The initialization code used to set up this program doesn't need to care about threads, because it's assumed that they won't be used. Hence you have a non-threaded program. Note that the important part here is the initialization code used to setup the program, not whether the program uses calls to pthread_* functions or not.

Now imagine that this non-thread program loads a shared object (be it a library or a plugin) using dlopen(3). This is not by itself a problem. The funny part comes when the shared object being opened is linked against libpthread; that is, threaded code. There you have the "Abort trap". You have loaded threaded code into a non-threaded application. Here is a little example for you to play with:

#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>

int
main(void)
{
void *handle;

(void)printf("Loading libpthread.so\n");
handle = dlopen("libpthread.so", RTLD_LAZY);
(void)printf("libpthread.so loaded\n");
dlclose(handle);

return EXIT_SUCCESS;
}

Can this problem be worked around? Sure. It's as simple as linking the non-threaded application against libpthread (even if it won't make any use of threads), because this library will override the initialization code used to set up the program. I've done this, for example, in the gtk-query-immodules-2.0 utility that comes with GTK+, because some plugins loaded by it are threaded.

Considering the previous example, doing: cc test.c ; ./a.out will produce a crash. But cc -lpthread test.c ; ./a.out will not. And even cc test.c ; LD_PRELOAD=/usr/lib/libpthread.so ./a.out will work too. Remember? It's the initialization code that matters, not the amount of threads used by the code.

Now, before thinking this is a bug in NetBSD, let me say that this was a design decision in the libpthread library. I don't remember the rationale behind it, but I guess you should be able to find it Googleing a bit ;-)

Saturday, November 06, 2004

Good user interfaces

First of all, let me apologize for not posting in several days. Unfortunately, this situation will continue as my time is quite limited because of university stuff...

Anyway, to the point of this post. A few days ago I found an interesting article that talks about free software and good user interfaces; you can read it here. It starts giving a summary of what a good UI should be, why free software can design good interfaces (comparing UI design to code design) and whether commercial companies help or hurt this process.

Later on, it describes the "too many preferences" problem. It explains why a bloat of costumization options leads to an unusable program, and what can be done to improve this situation. I wholeheartedly agree with the author's comments after having used GNOME 2.x for a while. Specially, Epiphany and Imendio Gossip are two excellent applications in the usability field. They Just Work (TM) and don't lack any important functionality.

An example of why too many customization makes programs difficult to use can be found in this analysis. It is a bit old (applies to GNOME 1.4), but is still worth reading.

So, if you are a developer, and your programs have a graphical interface, consider making their UI usable (and accessible). Read the GNOME Human Interface Guidelines for more information, and ask the UI experts (through the existent mailing lists) when in doubt.