what to backup

to celebrate the release of fedora 15 and in the spirit of the “release early” part of “release early, release often” i’ve decided to post my latest script.

this one’s a bit more involved than the last ones but the task is to answer this question:

what files do i need to back up from my fedora system?

in particular there are a few problems to solve:

  1. i don’t want to waste space backing up unmodified files from software packages.
  2. i want to see which parts of the file system are using up the most backup space so i can see if i can do something about that.
  3. i need a concise summary of what needs to be backed up so i can review it.  if an entire directory tree needs to be backed up then i only want to see that mentioned once, not a line of output for each file!

if it wasn’t for 2 and 3 i could just rpm -Va and then maybe something like find / -exec rpm -q | grep “no package” but that’s all rather fiddly and produces vast, unmanageable lists of files.  so i turn to python.  🙂

what i’ve ended up with is a tiny app, a file/package scanning class and a helper class for showing progress – checking every file on the system takes hours so you want to know how you’re getting on.

so, first up, the progress display class.  i’ve saved this as cmdmsg.py.

# coding=utf-8

# a utility function taken from stackoverflow
def getTerminalSize():
    returns (lines:int, cols:int)
    import os, struct
    def ioctl_GWINSZ(fd):
        import fcntl, termios
        return struct.unpack("hh", fcntl.ioctl(fd, termios.TIOCGWINSZ, "1234"))
    # try stdin, stdout, stderr
    for fd in (0, 1, 2):
            return ioctl_GWINSZ(fd)
    # try os.ctermid()
        fd = os.open(os.ctermid(), os.O_RDONLY)
            return ioctl_GWINSZ(fd)
    # try `stty size`
        return tuple(int(x) for x in os.popen("stty size", "r").read().split())
    # try environment variables
        return tuple(int(os.getenv(var)) for var in ("LINES", "COLUMNS"))
    # i give up. return default.
    return (25, 80)

from datetime import datetime, timedelta
from os.path import commonprefix
from sys import stderr
class cmdmsg():
    def __init__(self, interval = timedelta(0, 1, 0)):
        self.msg = ""
        self.height, self.width = getTerminalSize()
        self.last = datetime.now()
        self.interval = interval

    def say(self, msg, interval = None):
        if interval == None: interval = self.interval
        if datetime.now() - self.last < interval: return
        self.last = datetime.now()
        # multi-byte characters really futz with this stuff
        msg = msg.replace("\t", " ").decode(
            "utf8", 'replace').encode("ascii", 'replace')
        if len(msg) > (self.width - 1):
            ends = self.width / 2 - 2
            msg = msg[:ends] + "..." + msg[-ends:]
        offset = len(commonprefix([self.msg, msg]))
        # BS moves cursor but doesn't appear to remove content - so print spaces
        if len(self.msg) > len(msg):
            extra = len(self.msg) - len(msg)
            stderr.write("\b" * extra + " " * extra)
        stderr.write("\b" * len(self.msg[offset:]) + msg[offset:])
        self.msg = msg

    def saynow(self, msg):
        self.say(msg, timedelta(0, 0, 0))

    def end(self):

    def spit(self, msg):
        stderr.write("\r" + " " * len(self.msg) + "\r" + msg + "\n" + self.msg)

this allows the scanning module to write and overwrite progress messages to the terminal without lots of annoying scrolling (which takes a lot of CPU and means you lose key messages).

next, the scanning module.  i didn’t want to maintain a complete list of all files on the system in a big array and i wanted to do the summarising as i went along, so this has got some tricksy fiddling around with ‘references’ into a big dictionary hierarchy.

(each level of filesystem hierarchy uses two levels of hierarchy in the dictionary.  this is because the dictionary entry for a folder doesn’t have the sub-folders as keys, it contains a set of metadata keys and a ‘dirs’ key for the sub-folders.)

but basically it allows you to maintain a list of files and directories with the required information – should they be backed up or not.

# coding=utf-8

# a utility function
def fileSize(bytes):
    suffix = [' bytes', 'K', 'M', 'G', 'T', 'P', 'E']
    size = float(bytes)
    index = 0
    while size >= 1000 and index < len(suffix) - 1:
        index += 1
        size /= 1024
    return str(int(round(size))) + suffix[index]

from pwd import getpwuid
import yum
from datetime import datetime, timedelta
from os.path import commonprefix
from cmdmsg import cmdmsg
from os import path, stat

class pkgScanner():
    def __init__(self):
        self._lastroot = ""
        self._results = {}
        self._rootpath = []
        self._mounts = []
        self._root = ""
        self._rpmva = {}
        self._cm = cmdmsg(timedelta(0, 0, 25000))
        self._cd = self._results

    def __str__(self):
        return "scanned:\n" + self._pprec() + "\nnot scanned:\n" + "\n".join(

# see if sub folders can be 'collapsed' into their parent
    def _check(self, folder, thisroot = None):
        removes = []
        if 'dirs' in folder:
            for sub in folder['dirs']:
# copy sizes to parent so it has totals for the whole tree
                if 'savesize' in folder['dirs'][sub] and\
                    if 'savesize' not in folder: folder['savesize'] = 0
                    folder['savesize'] += folder['dirs'][sub]['savesize']
                if 'unmodifiedsize' in folder['dirs'][sub] and\
                    if 'unmodifiedsize' not in folder: folder['unmodifiedsize'] = 0
                    folder['unmodifiedsize'] += folder['dirs'][sub]['unmodifiedsize']
                if not len(folder['dirs'][sub]):
                    if thisroot: self._cm.spit(
                        "removing empty " + sub + " from " + thisroot)
                elif 'dirs' not in folder['dirs'][sub] and\
                    'unmodified' not in folder['dirs'][sub] and\
                    'save' in folder['dirs'][sub]:
                    if 'save' not in folder: folder['save'] = []
                    if thisroot: self._cm.spit(
                        "removing all new/modified " + sub + " from " + thisroot)
                elif 'dirs' not in folder['dirs'][sub] and\
                    'unmodified' in folder['dirs'][sub] and\
                    'save' not in folder['dirs'][sub]:
                    if 'unmodified' not in folder: folder['unmodified'] = []
                    if thisroot: self._cm.spit(
                        "removing all unmodified " + sub + " from " + thisroot)
            for sub in removes:
                del folder['dirs'][sub]
            if not len(folder['dirs']): del folder['dirs']

# figure out which folders should be checked now
    def _checkpath(self):
        self._rootpath = self._root.split("/")
        if self._rootpath[-1] == "": self._rootpath = self._rootpath[:-1]
        lastrootpath = self._lastroot.split("/")
        if lastrootpath[-1] == "": lastrootpath = lastrootpath[:-1]
        if len(self._rootpath) <= len(lastrootpath):
            # find common path of root and lastroot
            n = 1 # skip leading blank before "/"
            folder = self._results
            while n < len(self._rootpath) and self._rootpath[n] == lastrootpath[n]:
                folder = folder['dirs'][self._rootpath[n]]
                n += 1
            checkpath = "/".join(lastrootpath[:n])
            tocheck = []
            checkpaths = []
            while n < len(lastrootpath):
                folder = folder['dirs'][lastrootpath[n]]
                checkpath += "/" + lastrootpath[n]
                n += 1
            for index, folder in enumerate(tocheck):

# nicely formatted 'pretty print' of hierarchy
# should probably just make this two levels per recursion
# rather than checking if depth % 2
    def _pprec(self, p = None, depth=0):
        if not p:
            p = self._results
            output = "/"
        else: output = ""
        if type(p) is dict:
            if not depth % 2:
                if 'unmodifiedsize' not in p and 'savesize' not in p:
                    output += " (empty)"
                elif 'savesize' not in p or not p['savesize']: output += " (none)"
                elif 'unmodifiedsize' not in p or not p['unmodifiedsize']:
                    output += " (all)"
                else: output += " save " + fileSize(p['savesize']) + "/" +\
                    fileSize(p['savesize'] + p['unmodifiedsize']) + "=" +\
                    str((100 * p['savesize']) / (p['savesize'] +\
                    p['unmodifiedsize'])) + "%"
                if 'save' in p:
                    names = ", ".join(p['save'])
                    if len(names) > 30: names = names[:30] + "..."
                    output += " (save " + str(len(p['save'])) + " local: " + names + ")"
                if 'unmodified' in p:
                    names = ", ".join(p['unmodified'])
                    if len(names) > 30: names = names[:30] + "..."
                    output += " (unmodified " + str(len(p['unmodified'])) + " local: " +\
                        names + ")"
                output += "\n"
                if 'dirs' in p: output += self._pprec(p['dirs'], depth + 1)
                output += ''.join("  " * depth + str(x) + self._pprec(p[x],
                     depth + 1) for x in sorted(p))
        else: output += "  " * depth + str(p) + "\n"
        return output

    def dump(self, p = None, path = "/", depth=0):
        if not p:
            p = self._results
        output = ""
        if type(p) is dict:
            if not depth % 2:
                if 'save' in p:
                    output += "".join(path + x + "\n" for x in p['save'])
                if 'dirs' in p: output += self.dump(p['dirs'], path, depth + 1)
                if 'save' not in p and 'dirs' not in p: output += path + "/\n"
                output += ''.join(self.dump(p[x], path + x + "/",
                    depth + 1) for x in sorted(p))
        return output

    def setRoot(self, root):
        self._root = root
        self._lastroot = self._root

        self._cd = self._results
        for folder in self._rootpath[1:]: # skip blank before leading "/"
            if 'dirs' not in self._cd: self._cd['dirs'] = {}
            if folder not in self._cd['dirs']: self._cd['dirs'][folder] = {}
            self._cd = self._cd['dirs'][folder]

    def processFiles(self, files):
        if not files: return
        locallinks = []
        for f in files:
            thispath = path.join(self._root, f)
            if path.islink(thispath): locallinks.append(f)
        for link in locallinks: files.remove(link)
        linenum = 0
        morepackagesthanfiles = False
        packages = {}
        newfiles = {}
        for doc in files:
            thispath = path.join(self._root, doc)
            thisdoc = {'size': 0, 'owner': 0}
                thisstat = stat(thispath)
            except OSError: # assume permission denied
            thisdoc['size'] = thisstat.st_size
                thisdoc['owner'] = getpwuid(thisstat.st_uid).pw_name
            except KeyError:
                thisdoc['owner'] = thisstat.st_uid
            self._cm.saynow(thispath + " - providers")
# get yum to ask rpm if this file is from a package
            pckgs = self._yb.rpmdb.whatProvides(thispath, None, (None, None, None))
            if not len(pckgs): newfiles[doc] = thisdoc
                package = pckgs[0] # assume first match will do
                if package not in packages: packages[package] = {}
                packages[package][doc] = thisdoc

        modified = {}
        unmodified = {}
        pk = packages.keys()
        for p in pk:
            if p not in self._rpmva:
                self._cm.say(self._root + " - checking " + str(p))
                self._cm.saynow(self._root + " - " + str(p) + " - checking")
# get yum to ask rpm to verify this package
                self._rpmva[p] = dict((f, ", ".join(list(x.message for x in m)))
                    for f,m in self._yb.rpmdb.searchNevra(p[0], p[2], p[3], p[4],
                self._cm.saynow(self._root + " - " + str(p))
            for f in packages[p]:
                if path.join(self._root, f) in self._rpmva[p]:
                    modified[f] = packages[p][f]
                else: unmodified[f] = packages[p][f]
        #rpmva = {} # trash the cache - trade speed for memory
        if modified or newfiles:
            self._cd['save'] = modified.keys() + newfiles.keys()
            self._cd['savesize'] = sum(modified[x]['size'] for x in modified) +\
                sum(newfiles[x]['size'] for x in newfiles)
        if unmodified:
            self._cd['unmodified'] = unmodified.keys()
            self._cd['unmodifiedsize'] = sum(
                unmodified[x]['size'] for x in unmodified)

    def processFolders(self, dirs):
        localmounts = []
        locallinks = []
        for folder in dirs:
            thispath = path.join(self._root, folder)
            if path.islink(thispath): locallinks.append(folder)
            elif path.ismount(thispath): localmounts.append(folder)
        for link in locallinks: dirs.remove(link)
        for mount in localmounts:
            self._mounts.append(path.join(self._root, mount))

    def close(self):
        self._root = "/"

    def getRootPath(self):
        return self._rootpath

and so the actual app is nice and small.  it prints those progress messages and the final summary to stderr and the flat list of files and whole directories to stdout.  and it takes hours so i usually run it like this: time backup.py > backup-datetime.out; paplay –volume 30000 /usr/share/sounds/gnome/default/alerts/sonar.ogg

# coding=utf-8

from os import walk
from sys import stdout, stderr
from pkgscan import pkgScanner

ps = pkgScanner()
for root, dirs, files in walk("/"):


and that’s that.  oh, sometimes it can use up an awful lot of memory.  keep an eye on it.

what’s next?  i’d like to specify a set of starting points for the scan on the command line, maybe pass in an exclusions file.  also i want to check that i actually have permission to read those files i want to back up.

it’d be nice to be able to generate a backup list for my non-admin user, then pass that list in to the scanner when run as root to generate a short list of stuff that has to be backed up by root.

looking at the output generated so far i’ll need to start writing some (possibly plugin-based) rules to handle/exclude certain files – some config files should be diffed rather than just saved, some files should be backed up by their application’s own backup system (e.g. databases), some files should only be backed up when the user isn’t logged in, some only on shutdown/startup, some only in single user mode.

python, hamster and zeitgeist FTW!

update: note that this turned out not to be all that useful – tracking active windows into hamster turned out better.

in response to gnome bug 639018 and my general desire to track automatically what i’ve done, i’ve made a python script which connects to the zeitgeist activity monitor and copies its messages to the hamster time tracker.  it goes like this:


# monitor zeitgeist and do stuff
from zeitgeist.client import ZeitgeistClient
from zeitgeist.datamodel import TimeRange, Event
from gobject import MainLoop

import hamster.client
class hamster_handler(hamster.client.Storage):
   def handler(self, tr, ev):
      # because the mainloop appears to catch exceptions
      from traceback import print_exc
      from urlparse import urlparse
         # FIXME insert clever rules here
         app = urlparse(ev[0].actor).netloc
         desk = open("/usr/share/applications/" + app)
         comments = filter(lambda x: x.startswith("Comment[en_GB]="), desk)
         comment = comments[0].split("=")[1].strip()
         self.add_fact(comment + " - " + ev[0].subjects[0].text)

hh = hamster_handler()
ml = MainLoop()

It never ends until it’s killed so you’ll probably want to run it in the background – i’ve added it to my session ‘startup applications’.  if it doesn’t appear to be working then run it from the command line instead – you should see some error messages if it’s failing to update hamster.

on my fedora 14 system i only get updates for local text files, images and videos opened in gedit, EoG and totem.  on ubuntu i imagine you’ll get a lot more updates.  OTOH, on ubuntu the script will probably need some tweaking for the hard-coded paths and locale.

under surveillance

i’ve decided not to link this to my hamster-to-empathy updater – i don’t really want to broadcast a stream of every little thing i do .. particularly if my IM accounts include twitter and facebook status feeds.  🙂

t-mobile 3g stickage

old skool phone modem

old skool phone modem

i’m not t-mobile’s biggest fan by any means but i do like the idea of having a selection of 3G providers to choose from.

so i’ve ended up with this 3g usb stick from t-mobile which “works on windows and osx” – *sigh*

still, i’ve seen a few of these on vodafone and you just plug them in and NetworkManager does the rest, right?

well, almost.  if you’re still back on fedora 10 (what’ve you been doing all week?!) then you’ll need to do something (called ‘usbmodeswitch’) to switch the stick from ‘zerocd’ mode into 3g modem mode – in fedora 11 that’s done for you (by hal i think).  but then when you try to connect it fails.

googling around i found lots of people saying it doesn’t work if the settings have a blank username and password – doesn’t matter what they are as long as they’re not blank.  well i tried that but it made no difference.

tried calling t-mobile support who were moderately helpful and did say that i should try “user” and “pass” but that didn’t  help either.

they also said that there’s some other software you can download that sorts it all out for you.  more googling found ‘hsoconnect’ hosted on pharscape.org which was, at that time, blacklisted by google for apparently hosting viruses!  eek.

went to the t-mobile shop today and got the same story from them.  also they can’t swap the stick for a different model and they can’t accept it back from me either – i have to call support again.

so, thought i’d have another try at hsoconnect. pharscape.org is no longer blacklisted!  bit of a problem with the latest release because it’s python2.5-based but fedora 11 has 2.6.  then i found a beta version which runs on 2.6.  hoorah!

once i’d found that you have to create folders for it before you run the installer it finally ran … and failed.  reading some more comments it turns out that hsoconnect only ever works if you configure it yourself with username, password and APN.  APN?  ah ha!

tracked down the APN here … success!  then i thought, maybe that’s what the problem was all along …. and now i’m typing this using the 3g stick connected by NetworkManager! (well, i was, right up until t-mobile blocked me from finding a picture on flickr – even though the chap who sold it to me assured me he’d removed the block.  grrr.)

so, if you’ve got fedora 11 and a new uk t-mobile 3g stick you need to know:

go to the ‘connection settings’ in NetworkManager.

go to the mobile broadband connection and edit it.

set the username and password to anything non-blank.

set the APN to general.t-mobile.uk

success!  😀

Fedora 10 beta on eee pc 901 – take 2

* before i forget – there’s an odd bug where if you press ctrl– to shrink the text in gnome-terminal it crashes.  i should track that down at some point but it’s certainly there in the f10beta livecd. … seems to be fixed after an update. – ah! actually i think it’s just that with compiz enabled the window resizes itself off the screen.

this time i set up /boot on RAID1, the rest of the 4G disk and a matching chunk of the 16G as a RAID0 physical volume  with /home on it and the rest as another physical volume with / followed by a 1G swap partition.

the 480 updates took 11.5 hours to complete.  no errors so far as i can see.  no new updates to apply.

so, let’s try rebooting from kernel-2.6.27-0.352.rc7.git1.fc10.i686 to kernel-

i’ll edit the grub boot line so it’s not quiet or graphical and i’ll press escape if the blue bars appear at the bottom of the boot screen so i can see what happens.  also, i’ll do a shutdown and power on rather than just a restart.


it’s absolutely fine.  no problems at all.  *sigh*

new things:

  • the add/remove software window doesn’t have a maximise button and appears maximised by default.  if you unmaximise it (by dragging) you can’t re-maximise or resize it.  with desktop effects enabled it appears small by default – so you can’t use it.
  • the first attempt to add more software produced an odd error.  just clicking apply again has worked with a message that “An untrusted package was installed”.
  • i installed google gadgets.  clicking Run when it finished either didn’t work or it crashed.  running it again from the menus worked.
    • another software install – another ‘run not working’ – so i don’t think it crashed.
  • i’ve discovered the neon theme for cairo-dock – it’s pretty!

    eee desktop screenshot

    eee desktop screenshot

  • installed the wireless drivers from the development part of the fusion repo.  success!  (you don’t need to reboot – just modprobe rt2860sta and wait a few seconds for NetworkManager to pick up some networks.)  of course, the moment it starts working a new kernel becomes available in rawhide.
  • also discovered that the black variant of the most excellent comix cursors have a super-nice little featurette – the ‘bar’ cursor that appears over text has a red bar.  super-nice.
  • i had intended to do lots of carefully controlled restarts to pin down the problem if it reoccurred .. but i wasn’t concentrating and accidentally did a default reboot.  i’ve done a couple more since then – no problems.

happy now.  🙂

eee 901 wireless on fedora 10

along with the 1.8.0 driver there’s an intriguing WebUI file.  not sure what it is but the install script builds some stuff (possibly including the driver) and then starts firefox pointing at a local server running on another port.

don’t forget to install make, gcc and kernel-devel before running the script!

Error unpacking rpm package glibc-common-2.8.90-14.i386
エラー: アーカイブの伸長に失敗: ファイル /usr/share/i18n/charmaps/ISO_6937-2-25.gz;490266ab: cpio: open

for the WebUI you also need openssl-devel

of course, the kernel-devel package it’s picked up isn’t the right one for the original kernel – that’s not available through yum.  so we’ll have to update the kernel and reboot…..

oooh, while i’m waiting for the kernel to install – watching videos with compiz enabled works fine now!  😀

although that reminds me – with a 5.1ch ogg movie playing in totem all i’m hearing is the rear channels no matter what sound options i select – at least that’s what happened  on F9.

had filesystem problems when i rebooted and it wouldn’t accept my root password.  booted with the old kernel and it still had the fs probs but accepted my password and fsck’d the problems.  now i’m back with the latest kernel.

looks like the driver compiled okay – and it’s done its mad “and copy the file to /tftpboot” thing as before.  let’s try that WebUI thing again…

the script uses zenity to pop up prompts – extremely slow if you’ve got input methods enabled.  lots and lots of compiling but it still doesn’t work.  whatever, back to the actual driver…

hmm.  it looks like it’s loaded okay.  there’s a ra0 device but networkmanager hasn’t spotted it and i can’t ifup it or rmmod the module.  time for another reboot i think….

didn’t help much.  i can rmmod and modprobe now but nothing happens.

tried adding “alias ra0 rt2860sta” to /etc/modprobe.conf …. no change.

running the webui thingy adds the ra0 interface and locks up the module.  doesn’t appear to have any other effect though. … oh, apart from allocating it an IP6 address.  hmm.

guess i’d better install these 460 updates!  good old rawhide.

on the plus side, fiddling around with this has revealed that there is a monitor mode.  iwconfig ra0 mode monitor works!

welcome to fedora 10 Beta

so, almost first impressions:

  • had to install from livecd again – no problems except that, as with f9, the window doesn’t fit until you reduce the font size from 10 to 6pt.  (the font properties window is too big too!)
  • i’ve accepted the default layout this time – no RAID weirdness.
  • like the funny little coloured  bars (white, light blue and blue) during the initial boot.
  • near the end of startup something crashes just before X starts.  i think it happened with the liveCD too.
  • starting X for firstboot takes forever but i think it did that for fedora 9 too.
  • enabling desktop effects for the first time leave a lot of the screen black – needs a redraw.  changing workspaces fixes that.
  • the adobe repo works as does flash (from that repo)
  • the wireless drivers still haven’t arrived in the kernel
  • SELinux is preventing alsactl (alsa_t) “getattr” to /usr/share/alsa/alsa.conf (usr_t).

now to try out the suspend stuff…

on AC:

selecting suspend from the battery status icon suspends (even though it looked like it was about to hibernate because it took ages and went into text mode and displayed some messages)

screen brightness Fn keys still work – there’s no OSD but ‘dim on idle’ seems to work.

suspend on idle works too just the same.

suspend on lid works fine too.  a lot faster but i think that’s just because some system stuff was busy thrashing the disk earlier.  opening the lid doesn’t wake it up – you have to press a key (same as in F9 – presumably it’s a eee thing).

on battery… (screen dims and OSD shows)

suspend from the icon works.

suspend on idle… hibernates!  (that’s probaby what was happening on F9 too) hmm.  seems to be a bit smarter than before – removes cache and “shrinks memory” before writing memory to disk – but it took a looooong time to start up again.

suspend on lid works.

so that’s still annoying – if you’re running on batteries you have to remember to shut the lid otherwise it’ll hibernate.  boo.

but they’ve fixed the screen brightness on resume – hooray!

fedora 9 on the eee pc 901

i need to make a note of all the behaviours of fedora 9 on my eee pc 901 before installing fedora 10 beta on it.

main points are:

  • have to build wireless drivers from source from here – currently version 1.8.0 – which is, hopefully, the same as the 1.7.0 i used plus the patches i had to apply!
  • the wireless driver doesn’t appear to have a monitor mode
  • without a usb dvd drive you have to boot from the SD slot or an external usb hard drive. this works with livecd (if you’ve got the livecd creator tools) but not with the DVD image.
  • after a suspend/resume the brightness control doesn’t work
  • suspend by closing the lid works fine but suspend on idle does a hibernate instead
  • there’s noise from the speakers if they’re not muted – which is a shame on an utterly silent system
  • occasionally the back-light switches off (possibly only after screensaver and screen blank when on AC) and it doesn’t come back on until you use a Fn brightness key. it’s still possible it’s actually any key and it’s just gone to sleep
  • 3D works pretty well for compiz but it goes a bit wrong when you try running a 3D or Xv (video) app at the same time – particularly troublesome when that app defaults to fullscreen mode – e.g. blender or miro. watching DVDs or using stellarium or blender is fine with compiz disabled
  • no middle mouse button (big problem in blender) and we’ve moved to xinput driver so it’s a bit of a pain enabling support again – i created /etc/hal/fdi/policy/3button.fdi with
    <match key="info.product" string="ImPS/2 Logitech Wheel Mouse">
     <merge key="input.x11_options.Emulate3Buttons" type="string">true</merge>

    in it.  can’t remember where i read that – i think it’s the sum of several hints and previous hal experience.

  • sometimes if you type too fast keystrokes (especially shift keys) are missed
  • when compiz is enabled you get a brief flash of the desktop before the password prompt is displayed when the system has been locked or suspended
  • sometimes networkmanager disables networking entirely – not sure why.  i think it only happens after a suspend/resume – but only occasionally.
  • 3D does have its limits – flightgear manages only a couple of frames per second (might not have anything to do with the 3D and might be fixable in the settings) – celestia doesn’t work at all and google-earth runs a several seconds per frame.  also i’ve switched off the compiz cube as there’s a little bit of tearing when it spins.  the left/right slide is much more understated anyway.
  • many bad apps assume you’ll have 600 vertical pixels to play with.  i think there might be a compiz plugin to run the entire screen like a virtual display scaled down – i’ll see if i can find that.
  • everything looks sooo pretty on it that  a) i’ve forgotten how hideous the ‘pearl white’ plastic is and b) i really don’t want to do a clean re-install.

on the plus side:

  • the screen is very nice
  • it plays DVDs – we’ll see how it copes with HD content soon
  • desktop effects, google gadgets and cairo-dock look great
  • it’s completely silent
  • battery life is good – 4-6 hours
  • i’ve installed all the good office, internet and graphics apps without problems
  • it fits in a small bag
  • i can afford it
  • it certainly runs F10 to some extent
  • you can jam a custom touch screen and 3G slot inside it!

happy days.

well, that’s as much as i can remember for now.  on with the show – let’s trash it and stick F10 beta on instead.

oh yes, the 901 actually has two SSDs – 4G + 16G for linux, 4G + 8G for windows.  nice.  so i tried partitioning them with some RAID stuff.  here’s some hdparm numbers for F9:

cached reads (memory speed) are around 550MB/s

the 4G disk goes at around 26-28MB/s

the 16G at around 24-27MB/s

RAID1 at around 24-26MB/s

RAID0 at around 30MB/s

with O_DIRECT (unbuffered):

4G 31MB/s

16G 28.5MB/s

RAID1 31MB/s

RAID0 30MB/s