Not Really a Blog

December 19, 2007

Modifying a live linux kernel

Filed under: Linux, Programming, System Administration — Tags: , , — jesus @ 17:22

Before reading this, I just need to say something:

I’ve no idea of linux, I’ve no idea of programming, I’ve no idea of computers… Everything you read here might have been invented, so, please, do not reproduce what I write here. If you do, bear in mind that you do it under your own responsibility. In fact, what is a computer anyway?

The other day we were having issues with a box that was used as a NFS box among other things. These issues appeared on upgrading this box from kernel 2.4 to kernel 2.6.22.1. These issues were related to locking on the NFS server, because of changing the behaviour of the flock system call on linux 2.6.11-18. From the NFS FAQ:

The NFS client in 2.6.12 provides support for flock()/BSD locks on NFS files by emulating the BSD-style locks in terms of POSIX byte range locks. Other NFS clients that use the same emulation mechanism, or that use fcntl()/POSIX locks, will then see the same locks that the Linux NFS client sees.

The problems we had are related to using NFS and Samba for exporting the same file system and locking not working properly.

SMB supports two types of locks – file-wide locks and byte-range locks.

  • File-wide locks
    • Called ‘share modes’ in SMB parlance
    • Also known as ‘BSD-style locks’
    • provided by flock() in Linux
    • provided by a ‘share mode’ flag when opening a file under Win32
    • Supported primarily by samba within samba itself by storing in a TDB – get listed under ‘Locked Files’ at the bottom of smbstatus
    • May also be enforced in the kernel using flock() if HAVE_KERNEL_SHARE_MODES is 1.
  • Byte-range locks
    • Called ‘POSIX-style’ locks.
    • provided by fcntl(fd, F_GETLK) in POSIX.
    • provided by _locking() in Win32
    • lockf() is a Linux wrapper around fcntl() for locking the whole file.
    • Supported by samba by a ‘Windows to POSIX byte-range overlap conversion layer’ and then fcntl().

Windows applications appear to use both share modes and byterange locks.

In Linux, flock() and fcntl() locks are oblivious to each other, as per http://lxr.linux.no/source/Documentation/locks.txt.

NFSv3 (as a protocol) only supports byte-range locks. However, nfsd does flock() locks on files on the server taken out by other processes – although clients cannot set them themselves. See http://nfs.sourceforge.net/#faq_d10

Unfortunately, linux 2.6.12 adds flock() emulation to the Linux NFS client by translating it into a file-wide fcntl(). This means that flock()s and fcntl()s *do collide* on remote NFS shares, which introduces all the potential application race conditions which Linux avoided by having them oblivious to each other locally. The practical upshot of this is that if you re-share an NFS share via samba, then if a Windows client (e.g. Outlook opening a PST file) opens a file with a share mode, then byte-range locking operations will fail as the lock has already been acquired. (The fact that NFS doesn’t realise the same PID has both locks and allow them both is probably an even bigger problem). The solution for this is to revert bits of the patch responsible: http://www.linux-nfs.org/Linux-2.6.x/2.6.11/linux-2.6.11-18-flock.dif. Disabling share modes in samba is not an option, as it also disables the application-layer TDB support for them – and disabling HAVE_KERNEL_SHARE_MODES will stop other programs (e.g. nfsd) on dump being aware of what’s been flock()ed.

So our solution for our server was reverting this patch on 2.6.11-18 and apply this patch:

--- fs/nfs/file.c       2007-07-10 19:56:30.000000000 +0100
+++ fs/nfs/file.c.nfs_flock_fix 2007-11-13 13:40:06.000000000 +0000
@@ -543,10 +543,24 @@
         * Not sure whether that would be unique, though, or whether
         * that would break in other places.
         */
-       if (!(fl->fl_flags & FL_FLOCK))
+
+       /**
+        * Don't simulate flock() using posix locks, as they appear to collide with
+        * legitimate posix locks from the same process.
+        */
+       if (fl->fl_flags & FL_FLOCK)
                return -ENOLCK;

        /* We're simulating flock() locks using posix locks on the server */
+       /* ...except we shouldn't get here, due to the above patch. */
        fl->fl_owner = (fl_owner_t)filp;
        fl->fl_start = 0;
        fl->fl_end = OFFSET_MAX;

So, for us, recompiling the kernel with such patch on the production server fixes all our problems. But, what if we wanted to do this live, could such a subtle change be done without rebooting? You might be thinking right now about the different options that you have on /proc about changing the behaviour of the kernel live, but what if you don’t have such option? What if we wanted to change something and there was no way to do this because it is not implemented or not possible?

Let’s see.

So, from an academic point of view we wanted to see if this could really be done. If the linux kernel would let us do that, if it was feasible. So, we set up a testing box in which we would try to modify the running kernel. How hard would be to do it on the testing box?

It seems there’s a way to do it which my colleague Matthew came up with (all credit to him, I’m just telling the story). Let’s examine the piece of code that we want to change. The offending code lives on the file fs/nfs/file.c of the linux kernel

*
 * Lock a (portion of) a file
 */
static int nfs_flock(struct file *filp, int cmd, struct file_lock *fl)
{
        dprintk("NFS: nfs_flock(f=%s/%ld, t=%x, fl=%x)n",
                        filp->f_path.dentry->d_inode->i_sb->s_id,
                        filp->f_path.dentry->d_inode->i_ino,
                        fl->fl_type, fl->fl_flags);
         /*
         * No BSD flocks over NFS allowed.
         * Note: we could try to fake a POSIX lock request here by
         * using ((u32) filp | 0x80000000) or some such as the pid.
         * Not sure whether that would be unique, though, or whether
         * that would break in other places.
         */

        if (!(fl->fl_flags & FL_FLOCK))
                return -ENOLCK;

        /* We're simulating flock() locks using posix locks on the server */
        fl->fl_owner = (fl_owner_t)filp;
        fl->fl_start = 0;
        fl->fl_end = OFFSET_MAX;

        if (fl->fl_type == F_UNLCK)
                return do_unlk(filp, cmd, fl);

        return do_setlk(filp, cmd, fl);

}

What we wanted to do is to change the behaviour of the previous function such that the if condition would be:

        if (fl->fl_flags & FL_FLOCK)
                return -ENOLCK;</pre>

That means that the change is fairly trivial and that it would result in switching an operation, basically changing the way the branching (in the machine code) is done on the if instruction. If we disassemble such object file, file.o, we get something like

# objdump -d fs/nfs/file.o
...
00000885 :
 885:   57                      push   %edi
 886:   89 d7                   mov    %edx,%edi
 888:   56                      push   %esi
 889:   89 c6                   mov    %eax,%esi
 88b:   53                      push   %ebx
 88c:   89 cb                   mov    %ecx,%ebx
 88e:   83 ec 14                sub    $0x14,%esp
 891:   f6 05 00 00 00 00 40    testb  $0x40,0x0
 898:   74 3b                   je     8d5
 89a:   0f b6 41 2c             movzbl 0x2c(%ecx),%eax
 89e:   89 44 24 10             mov    %eax,0x10(%esp)
 8a2:   0f b6 41 2d             movzbl 0x2d(%ecx),%eax
 8a6:   89 44 24 0c             mov    %eax,0xc(%esp)
 8aa:   8b 56 0c                mov    0xc(%esi),%edx
 8ad:   8b 42 0c                mov    0xc(%edx),%eax
 8b0:   8b 40 20                mov    0x20(%eax),%eax
 8b3:   89 44 24 08             mov    %eax,0x8(%esp)
 8b7:   8b 42 0c                mov    0xc(%edx),%eax
 8ba:   8b 80 9c 00 00 00       mov    0x9c(%eax),%eax
 8c0:   c7 04 24 20 01 00 00    movl   $0x120,(%esp)
 8c7:   05 40 01 00 00          add    $0x140,%eax
 8cc:   89 44 24 04             mov    %eax,0x4(%esp)
 8d0:   e8 fc ff ff ff          call   8d1
 8d5:   f6 43 2c 02             testb  $0x2,0x2c(%ebx)
 8d9:   74 47                   je     922
 8db:   80 7b 2d 02             cmpb   $0x2,0x2d(%ebx)
 8df:   89 73 14                mov    %esi,0x14(%ebx)
 8e2:   c7 43 30 00 00 00 00    movl   $0x0,0x30(%ebx)
 8e9:   c7 43 34 00 00 00 00    movl   $0x0,0x34(%ebx)
 8f0:   c7 43 38 ff ff ff ff    movl   $0xffffffff,0x38(%ebx)
 8f7:   c7 43 3c ff ff ff 7f    movl   $0x7fffffff,0x3c(%ebx)
 8fe:   75 11                   jne    911
 900:   83 c4 14                add    $0x14,%esp
 903:   89 d9                   mov    %ebx,%ecx
 905:   89 f0                   mov    %esi,%eax
...

We can actually have a look at the disassembled code of the function flock(). If you have a look at address 0x8d9, there’s an instruction that looks suspiciously similar to the test carried on the if instructions. If you know assembly and know how a compiler works, you could find out that this jump instruction (JE) is just the one we want to change, exactly to a JNE instruction (I am not going to extend here on assembly and compilers and the like. I guess that if you wanted to do this, you should already know this. And, by the way, I’ve no idea of those concepts either, seriously).

If you are not sure if that’s the right instruction, you could recompile the kernel, get the assembly out of the same file.o object and compare it to see what is what changed.

Also, if you look at address 0×898, there another JE instruction which may look like the one we are looking, but this belongs to dprintk as we have debug enabled on that kernel.

If we have a look at the instructions on the IA32 manual, we see that the opcodes for the interesting instructions are:

  • JE: 74
  • JNE: 75

Ok, so right now, we know that we want to change an JE (with opcode 74) instruction for a JNE instruction (with opcode 75) on address 0x8d9 of the object file “file.o”.

The problem now is to find out where on the kernel memory this piece of code lives. One approach that you might think of doing is grepping the whole memory for a particular sequence of instructions. This is not recommended and I will explain why later on. First, let’s see how we can have access to the kernel memory, where we could possibly modify the data…

If you have a look at your unix system, you’ll see that you have a /dev/kmem special file, with which you can access the memory from the kernel’s point of view. This is quite useful as you can access it in read mode and, more interestingly, write mode. However, doing stuff with it might be a bit dangerous, as you might have guessed. It even seems that some vendors will disable this special file.

Anyway, as I said before, you cannot and don’t want to read or grep the whole memory at /dev/kmem. It seems that the reason is having write-only registers mapped into memory, so a read would crash the system. (You might think that that message is very old, from a distant time, but believe me, it crashes linux if you do so. We tried reading the whole of /dev/mem and the network driver crashed among other little pieces, so don’t bother).

So basically the thing boils down to:

  1. Finding out exactly where we have to change the kernel (ie, at which memory address)
  2. Open /dev/kmem and changing it with the right tool
  3. Hope everything went fine :-)

Finding out where

We need to find out where on the kernel we want to change it. This can be easily do by using tools and information the kernel provides us.

First, we need to find out where the kernel function nfs_flock starts, and that can be done by having a look at the System.map file that is generated every time we compile a kernel. The System.map file is a file that helps kernel developers to debug their code by mapping kernel functions to memory addresses, so it is actually much easier to find stuff. It contains the kernel symbol table with all symbols.

Ours looks like:

...
c01a9b3f t do_unlk
c01a9b97 t do_setlk
c01a9c2b t nfs_lock
c01a9d21 t nfs_flock
c01a9dcc T nfs_get_root
c01a9f3c T nfs_write_inode
...

So now, we know that nfs_flock is located at 0xc01a9d21. We’ll use this in a minute.

We saw that we were having a look at the instruction located at 0x8D9 on the object file file.o (got by using objdump before). We also know that, on such object file, nfs_flock starts at 0×885, right?. That means that, the byte we want to change, is located exactly:

0x8D9 - 0x885 = 0x54

at nfs_flock + 0×54.

Well, as you might know, those adresses (on the object file) are relative to such file, and that, when being linked into the actual kernel, the addresses are all relocated and recalculated. So, basically the right point is on

0xc01a9d21 + 0x54

Opening /dev/kmem and modifying it

Opening /dev/kmem needs to be done with the right tools. This is basically because we need LARGE_FILE support on whichever tool we use to modify it, as the /dev/kmem special file is a representation of the kernel memory and we need access it with a tool that supports large files.

In our system, the easiest way to do it is with perl. First, we double check that perl was compiled with LARGE_FILE support:

root@devbox:~# perl -V  | grep LARGE
    cc='cc', ccflags ='-fno-strict-aliasing -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64',
  Compile-time options: USE_LARGE_FILES
root@devbox:~#

and, now, we can modify the kernel in a oneliner such as:

root@devbox:~# perl -e 'open (KERNEL, "+</dev/kmem") || die $!; seek(KERNEL, 0xc01a9d21 + 0x54, 0); syswrite(KERNEL, chr(0x75)); close(KERNEL);'
root@devbox:~#

where we are writing 0×75 in the right position (calculated previously).

And that’s all there is to it. If everthing went fine, the kernel behavior has been modified and nothing has crashed.

Again, only do this under a dev box, under your responsibility and if you really really know what you are doing. And only only only if you want to play with a live kernel. Remember, I know nothing about computers.

July 23, 2007

Multitail

Filed under: Programming, System Administration — Tags: — jesus @ 13:24

Some time ago I discovered Multitail, a tool for displaying in a tail-like fashion any kind of information. I works by splitting the console multitail window in many parts and displaying the info you want on each of those screens, whether it is tailing a file or the output of a command via a ssh session. It also has coloring support (which you can extend using regular expressions) to tailor your needs.

I found it really handy when I have to monitor many servers. Just by using some bash power, you can get very nice outputs just by using something like this:

#!/bin/bash
rest=$*
if [[ -z $rest ]] ;then
  echo "You need to specify at least one server"
  exit 1
fi
command="multitail -s 2 "
for server in $rest
do
  command="$command -CS vmstat -t $server -l  \"ssh $server vmstat 1 \" "
done
eval $command

December 13, 2006

More on setting a subversion mirror repository

Filed under: Internet, Programming — Tags: , , , — jesus @ 22:34

A few days ago I wrote about setting up a subversion repository using svnsync. On that entry I was using svn+ssh authentication, but there are some gotchas to take into account to avoid having a security issue.If you recall correctly, we had a unix user with which people will access the repository. If we haven’t taken any extra protection, that means that anyone that has their ssh key on such a user’s ~/.ssh/authorized_keys file can log into the system. If such user uses public authentication to access the remote account used for the remote repository, any committer can have access to the private key and thus access the remote machine and have write permissions on it.

To avoid this scenario we need to set things up in a way that users can commit to the main repository but cannot have access to the remote repository. And that, again, can be done by creating a different user (Remember, all this applies to the main repository box, not the remote box).
Say:

  • svn: The user used for the repository. It will let any user have write permission on the repository. It will also have all the public keys of all the committers, to allow them to access the server using the svn+ssh authentication under the generic user svn, in case it needs to be retrieved from a remote location, which can be easily used by forwarding your credentials.
  • svnsync: A user which will have access to the repository (read access is enough) and will have a couple of wrapper scripts to svnsync. It will use a ssh key to access the remote repository. The rest of the users will have no read permissions on this user’s home directory.
  • The rest of the users for the committers. They will have write access to the repository. As you may know, if you use svn+ssh authentication, whenever you do a commit, you basically are executing svnserve on the repositoy under your user ID (That’s why you need write permissions).

This way, committers won’t have access to the svnsync private key. But we must grant them access to the wrapper scripts, so whenever they do a commit, svnserve is able to execute those scripts by triggering a post-commit hook.

All these can be achieved by using sudo. So, say that we have two different wrapper scripts:

  • /home/svnsync/bin/synccommit.sh
    #!/bin/bash
    # this is to synchronize the repostory
    # $1 will be the repository
    if [[ -z $1 ]]
      then
        # The repository should be a parameter
        exit
    fi
    
    DATE=`date +%Y%m%d-%H%M `
    
    echo "[$DATE] Commiting to repository $1 . (output below if any)" >> /home/svnsync/svnsync-commit.log 2>&1
    /usr/local/bin/svnsync synchronize --username svnsync "$1" >> /home/svnsync/svnsync-commit.log 2>&1
    
  • /home/svnsync/bin/syncprop.sh
    #!/bin/bash
    # this is to synchronize the a property
    
    # $1 will be the repository
    # $2 will be the revision number
    
    if [[ -z $1 || -z $2 ]]
      then
        # The repository should be a parameter
        exit
    fi
    
    DATE=`date +%Y%m%d-%H%M `
    echo "[$DATE] Updating property (Revision $2) to repository $1 . (output below if any)" >> /home/svnsync/svnsync-properties.log 2>&1
    /usr/local/bin/svnsync copy-revprops --username svnsync "$1" "$2"  >> /home/svnsync/svnsync-properties.log 2>&1
    

We can call them by setting up the hook scripts as:

  • post-commit hook:
    # Propagate the data to the remote repository
    sudo -u svnsync /home/svnsync/bin/synccommit.sh svn+ssh://svnsync@remote/home/svnsync/svn &
    
  • post-rev-change:
    # Propagating changes to the remote repository. Putting it to the background
    sudo -u svnsync /home/svnsync/bin/syncprop.sh svn+ssh://svnsync@remote/home/svnsync/svn $REV  &amp;
    

    All we need to do now is grant access to the committers to execute both wrapper scripts by adding the following to the /etc/sudoers file (remember, by using visudo)

    %commiters  ALL= (svnsync) NOPASSWD: /home/svnsync/bin/syncprop.sh
    svn         ALL= (svnsync) NOPASSWD: /home/svnsync/bin/syncprop.sh
    
  • Where committers is a common group where all committers belong to.

    December 4, 2006

    Speeding up trac’s response time

    Filed under: Internet, Programming — Tags: , , , , — jesus @ 18:30

    I’ve been trying to speed up an installation of trac over the last few days. The web interface took ages to display each of the directories or files within the subversion repository. But this one wasn’t too big. The only change to the subversion repository is that we started using a vendor branch imported into our main repository using svm

    So, after a few hours trying different solutions, and reading trac’s source code, I think I got where the bottleneck was.Well, it was http://www.sqlite.org/download.html which was causing the bottleneck. Trac uses an object CachedRepository to access the repositories. Whenever we want to get the chagesets, a function to synchronize the repository is called:

    class CachedRepository(Repository):
      def get_changeset(self, rev):
        if not self.synced:
          self.sync()
          self.synced = 1
          return CachedChangeset(self.repos.normalize_rev(rev), self.db, self.authz)
    

    and such method, sync(), makes a call to:

    youngest_stored = self.repos.get_youngest_rev_in_cache(self.db)
    

    which is all this:

    def get_youngest_rev_in_cache(self, db):
        """Get the latest stored revision by sorting the revision strings
        numerically
        """
        cursor = db.cursor()
        cursor.execute("SELECT rev FROM revision ORDER BY -LENGTH(rev), rev DESC LIMIT 1")
        row = cursor.fetchone()
        return row and row[0] or None
    

    And that SQL query was taking around 1-2 seconds each time it was executed. It happened that we were running an old version of sqlite and pysqlite, so a ./cofigure && make && make install using the recommended installation saved my day :-)

    Hope it is useful to anybody if it gets indexed by Google.

    November 30, 2006

    Setting up a subversion mirror repository using svnsync

    Filed under: Internet, Programming — Tags: , , , — jesus @ 01:48

    With the new version of subversion 1.4 you have a new tool called svnsync with which you can maintain mirror repositories quite easily. I’ve been working on one at work and would like to share with you my findings in case it interests anyone :-)

    Understanding the problem

    In order to have a mirror repository, it is important that commits only happen on the master and then they are synchronized to the mirror using the svnsync program. Then, the mirror repository can be used for whatever you may think of but for committing: backup, high speed checkouts, web front-ends, etc.

    So, svnsync must be the only “one” able to commit to the repository. If we use the apache integration, there are various ways to do this. Let’s say we are using svn+ssh for authentication, in which case it is more complicated as ssh access usually grants writing access to the file system. So creating a different user is going to be handy.

    Creating and populating the repository

    So, let’s say that we created a user called svnsync on the target machine and that we are going to create a new subversion repository in its home directory:

    svnadmin create /home/svnsync/svn

    Now, we need to set up a hook to let svnsync change the properties. For this, we create /home/svnsync/svn/hooks/pre-revprop-change with:

    #!/bin/sh
    USER="$3"
    
    if [ "$USER" = "svnsync" ]; then exit 0; fi
    
    echo "Only the svnsync user can change revprops" >&2
    exit 1
    

    We will grant access to the user running svnsync on the main machine by copying its ssh key to .ssh/authorized_keys. And now, we only need to initialize the remote repository. Note that we can run svnsync from where ever we want, but for the sake of simplicity, we will run it on the main machine, where the original repository resides.

    $ svnsync init --username svnsync \
          svn+ssh://svnsync@remote/home/svnsync/svn \
          file:///source-svn
    

    Note:

    • The syntax is
      svnsync init DEST SOURCE

      That’s it, the destination repository goes before the source repository.

    • There is no “:” between the host name and the path to the remote repository.

    With this command, we will have initialized the destination repository and now we are ready to populate the destination repository. We can do it with this command:

    svnsync synchronize --username svnsync \
           svn+ssh://svnsync@remote/home/svnsync/svn
    

    And, as we already initialized the repository, there is no need to specify the source repository. This will take more or less time depending on how big you repository is and how fast your network connection is. Hopefully it will have finished after you take a coffee :-)

    Creating another user to let users access the repository

    So, we will now create an user called svn which will be used to access the repository using the subversion client. As we are using svn+ssh, all we need is to grant access to such user to all the users that have access to the main repository. If we are using ssh keys it’s as easy as copying all the allowed keys to the /home/svn/.ssh/authorized_keys file.
    Also, if we change the permissions to the repository at /home/svnsync/svn (and its parent) to be something like

    drwxr-xr-x  7 svnsync users 4096 2006-11-28 17:30 svn/

    we will let svnsync (and the svnsync user) be the owner and have write permissions to the repository and let svn (and all the users ssh’ing) have read access only to the repository (provided both belong to the users group).

    $ svn co svn+ssh://svn@remote/home/svnsync/svn/test
    A    test/trunk
    [...]
    A    test/trunk/readme
    Checked out revision 2.
    $ echo "test" >> test/trunk/readme
    $ cd test/
    $ svn -m "test commit" ci
    Sending        trunk/readme
    Transmitting file data .svn: Commit failed (details follow):
    svn: Can't create directory '/home/svnsync/svn/db/transactions/2-1.txn':
    Permission denied
    

    And that’s all.

    Commiting to the master respository

    In case you want to commit back to the master respository, you need to do a “svn switch –relocate” to point to the master repository, but for that to work, it needs to have the same UUID if we don’t want it to fail.

    1. To get the UUID on the main machine:
      svnadmin dump -r0 /source-svn | head -n 3 > saved-uuid
      
    2. Copy the file saved-uuid to the remote machine and do a
      svnadmin load --force-uuid /home/svnsynd/svn < saved-uuid
      

    So, thins to take into account:

    1. When populating the repository, we use the svnsync user who has write permissions to the repository (svn+ssh://svnsync@…)
    2. When checking out, we use the svn user (svn+ssh://svn@…)
    3. Automatization

      If we want to keep both repositories synchronized, we need to set up a couple of hooks on the source repository.

      post-commit

      Add this to such hook (it needs to be executable and not have the .tmpl extension)

      # Propagate the data to the remote repository
      /usr/local/bin/svnsync synchronize --username svnsync
              svn+ssh://svnsync@remote/home/svnsync/svn &
      

      post-rev-changes

      We also want the properties changes to be present at the remote repository:

      # Propagating changes to the remote repository.
      /usr/local/bin/svnsync copy-revprops --username svnsync
             svn+ssh://svnsync@remote/home/svnsync/svn $REV  &
      

      Note that we put them to the background to let the user go on with what he was doing.

      Final notes

      This is a quick guide on how to set things to have a remote repository. There is much more than this and I recommend you to read the documentation and, obviously, do a backup. Doing a “svnadmin dump” only takes a while a it´s really worth it.

      In any case, just let me know if you find any errors or typos.

    October 3, 2006

    Mini tutorial on gdb

    Filed under: Programming — Tags: , , , — jesus @ 02:43

    Thanks to Matthew:

    gdb --args program/to/debug --whatever
    break Foo.c:532
    # to put a breakpoint at line 532 of Foo.c
    run # to run it, until it hits that line
    bt # to display the stacktrace (backtrace)
    frame 2 # to change the stack frame you're looking at
    list # to list the sourcecode for that stack frame
    step # to step into the next command for that frame
    next # to run the code to the next command
    continue # to continue onwards
    print bar # to print the contents of variable bar
    printf  "%08x", bar # standard printf for bar
    delete 2 # to remove the 2nd breakpoint
    

    Obviously, you need to compile things with -g as an argument to GCC.

    August 16, 2006

    Playing with web services

    Filed under: Internet, Programming — Tags: , , — jesus @ 03:25

    Some of you may know that I run a website for displaying the temperature in Seville which has many visitors, mainly from Seville. I’ve run that website since 2001 and it is actually number one when you perform a google search on temperatura en sevilla. I find it quite useful.

    Eversince I have implemented various related services, basically playing around in my free time. Some of them are a RSS service and a jabber service (which you can subscribe by adding sevillatemp@jabber.org to your jabber application – even Gtalk -). There are people that use it on a daily basis and I am glad that they do :)

    So, I had this idea in mind since some time, I just wanted to create a web service so other people could use it on their applications, be it web or desktop application. that would me let me play with webservices and, also, with Ruby.

    So, I did some coding and implemented something that I would like you to know.

    The web service

    The temperature is read from a couple of sensors from Dallas Semiconductor using a program called digitemp. It is a unix command line program that is used by a set of shell scripts that create all the fancy graphics you can see at http://temp.roncero.org using rrdtool. These scripts are executed every five minutes to both insert the data into the database and generate the graphs. The system usually works ok, but it has some flaws, mainly because of old hardware and by the fact that I am living in London and the machine is in Seville.

    In order to avoid problems with the actual system, I added a ruby script to the previous shell scripts to generate an XML file that would be later on used by the web service. The file content is

        1030AC15000800CB
        27.38
        Wed Aug 16 03:00:05 CEST 2006
        Celsius
    
        10952F1A00080063
        21.44
        Wed Aug 16 03:00:06 CEST 2006
        Celsius
    

    This is what I am using to feed the web service. Notice that each of the sensors has a unique serial number and that would be used by the calls to the webservice. I created a small ruby program mapped to the url http://temp.roncero.org/temperature in order to work. There are two different methods now:

    • list_sn* used to discover all the S/N available.
    • read_temp(sn)* used to read the data from a sensor. *sn* is the serial number of the desired sensor.

    Client programs

    So here are two different client little programs that you can use to access the data. One in ruby and another one in python. Very simple, no error check.

    Ruby

    #!/usr/bin/env ruby
    
    require 'soap/rpc/driver'
    proxy = SOAP::RPC::Driver.new("http://temp.roncero.org/temperature",
    "http://temp.roncero.org/temperature")
    proxy.add_method('read_temp', 'sn')
    proxy.add_method('list_sn')
    puts "List S/N: #{proxy.list_sn}"
    puts "reading from s/n  1030AC15000800CB"
    puts "#{proxy.read_temp("1030AC15000800CB")}"
    puts "reading from s/n  10952F1A00080063"
    puts "#{proxy.read_temp("10952F1A00080063")}"
    

    Python

    #!/usr/bin/env python
    
    from SOAPpy import SOAPProxy
    
    url = 'http://temp.roncero.org/temperature'
    namespace = 'http://temp.roncero.org/temperature'
    server = SOAPProxy(url, namespace)
    print "Printing S/N"
    server.list_sn()
    print "Temperature for 1030AC15000800CB"
    print server.read_temp("1030AC15000800CB")
    print "Temperature for 10952F1A00080063"
    print server.read_temp("10952F1A00080063")
    

    you can test both right now or come up with a new version in another language or improve these ones. Here is a typical output:

    $ ./client.rb
    List S/N: 1030AC15000800CB, 10952F1A00080063
    reading from s/n  1030AC15000800CB
    27.31 degrees at Wed Aug 16 03:20:05 CEST 2006
    reading from s/n  10952F1A00080063
    21.25 degrees at Wed Aug 16 03:20:06 CEST 2006
    

    So

    Yeah, this is a work-in-progress thing. I have no idea if this is the way to do it. No WSDL, not yet. Expect it to fail at some time, as I have yet to implement more error checking, etc.

    So, I would like all of you, if you have an opinion on these kind of things, to comment or suggest, say if you would like it to do something different or better.

    I have done some assumptions basically because my lack of knowledge on the matter.

    1. I have created the webservice using ruby’s webrick own webserver on a high port and mapping it to an apache url using proxypass directives. Is this the way to do it?
    2. I only created two methods. Would make sense to have more methods? Like one that would only return the temperature value?

    Have something to say? Go ahead :)

    March 9, 2006

    Chord

    Filed under: Internet, Programming, Sussex University — Tags: , — jesus @ 18:01

    As part of another assignment for the Distributed System course at the University of Sussex we have been doing some research on Chord, a distributed hash lookup primitive, and, in the end, implement part of what is called a chord ring in java.

    The idea behind Chord is a research paper in which a distributed protocol for adding nodes to the system, insert data into it, retreive it and drop from the system is described. The way in which nodes lookups and inserts are done is O(log n), so it’s quite efficient. Chord could be look as a layer over which p2p-like applications could be built. One example of this is CFS, the Cooperative File System, a distributed read-only file system.

    What we had to do is, basically, implement a simple Ring protocol and a Chord protocol:

    • Ring protocol: Each node in the system has a pointer to the next one in the ring, so, for instance, if any node wants to do a lookup on some data, it would ask the next one in the ring. If such node does not have it, it would forward that question to the next node in the ring and so forth. This would be O(n)
    • Chord Protocol: In this version of the protocol, each node not only has a pointer to the next node in the ring, but it also has a pointer to a number of other nodes, not necessarily in a row, so when any node wants to do a lookup, depending on a hash function and the list of pointers to other nodes, it would forward the question to a closer node, where the information is more likely to be stored. This is the way we get a O(log n) algorithm. In fact, there is more than this going on, so I recommend you to have a look at the chord paper again if you are interested.

    So, all we had to do is implement both protocols in java and run it in a
    simulator, as implementing a working solution was too much for an university assignment due in two weeks.

    What happens is that using the simulator and programming the whole thing is harder than it sounds, as the simulator itself is lacking some good documentation and have some bugs. In any case, doing it has been fun and interesting as well, as it has let me understand how a distributed system, such as a p2p system, works. So, I recommend anyone interested to have a look at how the protocol works (ie, by reading the Chord paper ;) )

    The idea behind Chord is a research paper in which a distributed protocol for adding nodes to the system, insert data into it, retreive it and drop from the system is described. The way in which nodes lookups and inserts are done is O(log n), so it’s quite efficient. Chord could be look as a layer over which p2p-like applications could be built. One example of this is CFS, the Cooperative File System, a distributed read-only file system.

    What we had to do is, basically, implement a simple Ring protocol and a Chord protocol:

    • Ring protocol: Each node in the system has a pointer to the next one in the ring, so, for instance, if any node wants to do a lookup on some data, it would ask the next one in the ring. If such node does not have it, it would forward that question to the next node in the ring and so forth. This would be O(n)
    • Chord Protocol: In this version of the protocol, each node not only has a pointer to the next node in the ring, but it also has a pointer to a number of other nodes, not necessarily in a row, so when any node wants to do a lookup, depending on a hash function and the list of pointers to other nodes, it would forward the question to a closer node, where the information is more likely to be stored. This is the way we get a O(log n) algorithm. In fact, there is more than this going on, so I recommend you to have a look at the chord paper again if you are interested.

    So, all we had to do is implement both protocols in java and run it in a
    simulator, as implementing a working solution was too much for an university assignment due in two weeks.

    What happens is that using the simulator and programming the whole thing is harder than it sounds, as the simulator itself is lacking some good documentation and have some bugs. In any case, doing it has been fun and interesting as well, as it has let me understand how a distributed system, such as a p2p system, works. So, I recommend anyone interested to have a look at how the protocol works (ie, by reading the Chord paper ;) )

     

    January 28, 2006

    On Distributed Computing using RMI

    Filed under: Programming, Sussex University — Tags: , — jesus @ 17:40

    Ok, so I have this assignment in one of my courses at the University of Sussex, Distributed Systems. The thing is that we need to implement a distributed event notification system, in which clients subscribe themselves to the server to which some event generators sends their events.

    All of this, using RMI (Remote Method Invocation) in Java.

    The assignment is an introduction to distributed objects in java by implementing this little system. In it, each Event Generator, sends events to the server, made of an identification string and a message (in fact, it could be any kind of object).

    On the other hand, the server get subscriptions of clients. These send the server a regular expression and a timeout, each of which would be used to choose among the different events received and to set a maximu time in which the subscription is valid. When doing so, the server sets up a callback function to the clients so, when a matching event is received, the server has a way to send the clients the object (event). I think it can be better understood by looking at the graph above.

    So far, it’s been quite interesting to learn this kind of distributed framework, although java is not my kind of language. I want to do some more researh on how to do this kind of things with other languages like ruby o r python. Seems to me interesting.

    Here is a little bit of the code used, just for curiosity:

    public static void main(String[] args) {
    	if (System.getSecurityManager() == null) {
    	System.setSecurityManager(new RMISecurityManager());
    	}
    	try {
    		EventNotificationServer server = new EventNotificationServerImpl();
    		String hostName = InetAddress.getLocalHost().getHostName();
    		Naming.rebind("//" + hostName + "/"
    				+ EventNotificationServer.rmiName, server);
    		System.out.println("EventNotificationServerImpl bound to "
    				+ hostName);
    
    	} catch (Exception e) {
    		System.err.println("EventNotificationServerImpl exception: "
    				+ e.getMessage());
    		e.printStackTrace();
    	}
    
    }
    

    The assignment is an introduction to distributed objects in java by implementing this little system. In it, each Event Generator, sends events to the server, made of an identification string and a message (in fact, it could be any kind of object).

    On the other hand, the server get subscriptions of clients. These send the server a regular expression and a timeout, each of which would be used to choose among the different events received and to set a maximum time in which the subscription is valid. When doing so, the server sets up a callback function to the clients so, when a matching event is received, the server has a way to send the clients the object (event). I think it can be better understood by looking at the graph above.

    So far, it’s been quite interesting to learn this kind of distributed framework, although java is not my kind of language. I want to do some more researh on how to do this kind of things with other languages like ruby o r python. Seems to me interesting.

    Here is a little bit of the code used, just for curiosity:

    public static void main(String[] args) {
        if (System.getSecurityManager() == null) {
        System.setSecurityManager(new RMISecurityManager());
        }
        try {
            EventNotificationServer server = new EventNotificationServerImpl();
            String hostName = InetAddress.getLocalHost().getHostName();
            Naming.rebind("//" + hostName + "/"
                    + EventNotificationServer.rmiName, server);
            System.out.println("EventNotificationServerImpl bound to "
                    + hostName);
    
        } catch (Exception e) {
            System.err.println("EventNotificationServerImpl exception: "
                    + e.getMessage());
            e.printStackTrace();
        }
    }
    

    January 11, 2006

    On XML and Java

    Filed under: Programming, Random — Tags: , — jesus @ 20:40

    As part of an
    assignment
    for a course at Sussex University I have to create a Servlet to run a small application, one to deal with a wedding list. It involves quite a few technologies, including XML, javascript, CSS and JAVA.So far I am not specially happy with the course as I haven’t enjoyed it as much as I thought initially.

    I think it is because there are so many technologies considered in such a short period of time, but I think that Java itself made me feel very uncomfortable from the very beginning (As an example, I found the Java DOM API to be quite awkward to use). Anyway.

    But I must admit that I have learned some topics I was not proficient in, such as javascript, css and XML.

    Probably I would feel much better doing these things in a scripting language such as python or ruby, or even perl…

    So far I am not specially happy with the course as I haven’t enjoyed it as much as I thought initially.

    Older Posts »

    Customized Shocking Blue Green Theme Blog at WordPress.com.

    Follow

    Get every new post delivered to your Inbox.

    Join 2,886 other followers