Search Results: "ron"

18 July 2011

Jelmer Vernooij: Summer of Code 2011

The Samba team is once again participating in the Summer of Code this year. This year we have 4 students working on various projects related to Samba. This year I am mentoring Dhananjay Sathe, who is improving the GTK+ frontends for Samba. In particular, he is making it possible to manage shares and users of a remote Samba or Windows machine. Dhananjay is also blogging about his progress.

17 July 2011

Wouter Verhelst: Mechlin to Banja Luka in statistics

I decided to go to DebConf11 by car. Not willing to do this alone, I asked for volunteers to join me in my car trip. One such volunteer showed up, and the car trip happened. Now, since I remembered to push the correct buttons on my dashboard after I completely fueled up the car and checked the tire tension and other things, I am now proud to be able to present you with the following statistics on this trip:
  1. Total distance travelled: 1466.8 km
  2. Average driving speed: 95.9 km/h [0]
  3. Total number of kilometers wasted on 'oh, crap, we forgot something': 10 (approx) [1][2]
  4. Total amount of fuel used: 81.3l
  5. Average fuel consumption rate: 5.5l/100km [3]
  6. Total amount of kilometers spent behind the wheel by Jeremy: 50 (approx)
  7. Total amount of kilometers spent behind the wheel by Wouter: the rest.[4]
  8. Time of departure: 2011-07-16 21:00
  9. Time of departure after point three and making sure we had everything: 2011-07-16 22:00
  10. Time of arrival: 2011-07-17 15:15 (approx) [5]
  11. Outside temperature at departure: 12 celcius
  12. Outside temperature at arrival: 35 celcius
  13. Longest (voluntary) break: 1 hour (approx; breakfast, tanking, bathroom visit, watching the sun rise)
  14. Longest (involuntary) break: 45 minutes (approx; customs check) [6]
  15. Last fueling stop: 4 AM.
  16. Amount of kilometers left to go before the car said bleep, you need to tank soon: less than one.
  17. Most tense moment: on crossing the slovenia->croatia border, confusing the border police officer ("no, I'm Wouter, that is Jeremy" yes, we both had photo-ID), who then promptly holds on to our documents, asks the car's papers, and tells us to please park over there and wait a moment while he gets us to be handled by guy in a somewhat more military-looking uniform.[7]
  18. Biggest "uh-oh" moment: after crossing the croatia->bosnia and herzegovina border, watching the very, very, very, very, very[8] long line of trucks queueing to cross the border. In the other direction. And then realizing that you probably need to go through that very same queue on the way back
  19. Largest single toll fee: 15 [9]
  20. Last bit of time spent asleep before this trip: the minutes surrounding 2011-07-16 05:00:00 [10]
  21. Time wasted writing this blog post when I really should've gone to bed instead: 45 minutes
  22. Time now: 21:02:54
But it was fun. And that's all that matters. Those who've shown up at debconf already, or who will arrive soon: see you tomorrow. Those who will arrive later: see you soon. And those who won't arrive: better luck next time, I hope. [0] this includes times when the car isn't moving with the engine running, but not times when the car is turned off, locked, alone and forgotten, in the rain, while those idiots who were supposed to be its drivers were inside having a drink.
[1] Which is surprisingly little. Usually I manage to need more.
[2] Jeremy forgot his^Wa Debian banner. I forgot something, I can't recall what. I decided to bring some CD's to enjoy during the trip, and a 3.5mm audio connection cable which we end up not using
[3] According to the dash. I'm too tired right now to check it. Also see the next two points. [4] This could incorrectly be interpreted as a criticism on Jeremy. It's not his fault that I only told him a day before we left that I have a manual transmission. And in fact, if anything, I'm unhappy with the fact that I even had to hand him the wheel for those 50 km. I would've loved to be able to say "I went all the way from Mechlin to Banja Luka, all by myself".
[5] I forgot to check the clock, to be honest.
[6] What, you think I have a chronometer?
[7] The last four points could really have been written as two. Figuring out which goes with which is left as an exercise to the reader.
[8] very, very, very
[9] 7-day Slovenian road tax vignet
[10] Once I wake up, I hardly ever manage to fall asleep again. Esecially if it's light outside.

Lars Wirzenius: rm is too slow

Some weeks ago I noticed that one of my backup servers was not removing the old backup generations it was making. It had over 18 months worth of daily backups, done with rsync and "cp -al". This is not the world's greatest backup system, but that's entirely irrelevant for this rant. The cron job that would do that did not exist: I had forgotten to enable it. So I ran it by hand. Then fixed it, when I noticed it was buggy, and didn't remove any old generations prior to 2010. After that I removed years-old backups of machines that haven't existed for years. Disk usage:
       Size  Used Avail Use% 
before 5.4T  2.6T  2.6T  51% 
after  5.4T  892G  4.3T  18% 
Inodes:
          Inodes    IUsed     IFree IUse%
before 732577792 77645915 654931877   11%
after  732577792 16871789 715706003    3%
That's about 61 million files removed, over the course of three weeks, of which the machine was down due to crashes for about one week. It should not take two weeks to copy a couple of terabytes (it's less than two megabytes per second). It should definitely not take two weeks to delete it. Almost all the time it was up, it was removing files. There were a couple of times when it finished one run and was idle until I woke up and started the next run. Say a day in total. Why is "sudo rm -rf" on a large directory tree, or set of large directory trees, so slow? I suspect part of the problem is that the system call API is beautiful, but simplistic: you need to call unlink or rmdir on every file separately, and that means doing an inode lookup separately for each file. Having a system call to delete everything in a directory would have a chance of removing things more efficiently, I suspect. However, since I've not done any profiling, nor even analyzed the relevant source code in the kernel, I might be entirely wrong. I wonder if anyone else needs to remove large directory trees frequently? I do that a lot, because of my backup software development hobby. But I may be in a unique position, and nobody else would benefit from fast removal. (I'll note that my backup server is using ext3, and that ext4 is much faster at removing files, particularly large files, but it's still not fast enough.)

Daniel Baumann: Debian Live Autobuilds

Right now I am sitting in the train to the airport for going to DebCamp/DebConf 11 in Banja Luka, Bosnia and Herzegovina. During DebCamp amongst other things I am going to work on finishing up the last bits on getting daily/weekly Debian Live autobuilds back for wheezy. This spring Jimmy Kaplowitz offered to help with the task of overseeing the debian-live autobuilder and to coordinate necessary bits with cdimage.debian.org. Having them on cdimage.d.o became recently possible as Steve McIntyre offered to make a VM available for us (live-build requires root privileges, and naturally, you would not want to let that run on the host system itself, that is why in the past it was not possible to build them on cdimage.d.o). The remaining issues are, and this is why the autobuilds are not yet back since spring, that live-build still needs two things: a couple of tweeks for wheezy, and the autobuild stuff need to be properly integrated into live-build itself. That will happen as an additional binary package live-build-cron with a bunch of debconf questions, so that everyone and his dog can setup a debian-live autobuild server on his own machine. The initial upload (which will not yet work, do not bother to try) was uploaded to experimental this week and cleared the new queue, now looking forward to get this all finished up and resulting in a live-build uploaded to unstable by the end of the week. In the second week, Jimmy arrives, I will be introducing him to live-build and the various little things to watch for and hopefully manage to persuade him to take over the autobuilds maintenance. Having all images that are based on packages solely from the stable/testing/unstable archives moved to cdimage.d.o is freeing up ressources on live.debian.net which did the job in the past since etch. Ben Armstrong volunteered to help with overseeing the live.d.n instance of live-build-cron that will build more then once per day images for si, with the only difference that they will not use live-* from the archive but the latest snapshots from our git snapshots repository. This will be a big help for the development as eventually we will be autotesting these images for various things. Last but not least, thanks to both Jimmy and Ben for all your kindness, patience and help with this during the last couple of months. We have interesting times ahead.

Thorsten Glaser: mksh R40b; joe 3.1jupp16 and 2.8jupp2 released

mksh R40b (nowadays with filled in user s caveats (for R40, too!) and packager s upgrade hints) has just been released. This is a should-have upgrade, fixing a number of admittedly some obscure bugs, changing things begun in R40, improving upon others. Thanks to the PLD Linux guys for spotting all these errors; thanks to them and phpnet.org both for adopting mksh so well. I have also fixed a bug in nroff(1) which will lead to an even nicer looking HTML manpage mksh(1) (after the next rebuild and upload of a MirBSD snapshot scheduled RSN). jupp 3.1.16 took on the task of merging Debian joe changes (aiming at an upload). I also split the jupprc file into three versions (2.8 generic/DOS, 3.1+jupp and 3.7/Unix) because of the differences in the baseline executables making rc files partially mutually incompatible (think Insert key), annoyingly warning (think syntax, hmsg), or less usable (joe s new menu system). jupp 2.8.2 is a companion to jupp 3.1.16 mostly because of the new help window character map Binaries for jupp should be updated RSN too. Considering Banja Luka is arriving quickly, the r in RSN should be taken with a few grains of salt. I ve also scheduled working on the pcc Debian package for the next future; updating lynx and maybe others like OpenSSH in MirBSD is also due; cvs(1) will receive more of my time, but before the next Upload I d like to fix LP#12230 once verified. Builds for Debian/m68k are also still running. I note I did in fact not manage to make a new base image, yet (but 2.6.39 kernels miss a patch, anyway, so waiting for 3.0 is ok). It s still using gcc-4.4 because nobody tests gcc-4.6 and gcj-4.6 FTBFS due to SIGSEGV, but that s ok in my books. rsyslog is broken but sysklogd works. The #ksh Freenode page finally got a well-deserved link to Planet Commandline. Throw more my way! Acronyms and translations, too. (Got Norwegian and Rumanian covered in the meantime. No idea whether any RTL languages will work in that beast. But I m young and need the money) Since I m writing a wlog entry anyway let me thank Gunnar for a nice summary on the current Free Culture discussion; my comments on Nina s site seem to be eaten, but let me support it fully, although, of course, I normally use a copycenter style licence, which is specifically written for general works of authorship under copyright law, not limited to software. I did in fact have that in mind. Maybe some people will like it (it s less than one Kibibyte long) either generally or just for their everyday random musings (they can then keep CC-BY-SA for the big works if they so desire). Wouter, grass background makes green headlines illegible. I ve never liked, and never installed manually, cups either. (Benny tells me that Apple s new version refuses to talk with a non-Apple cups, kinda defeating the whole idea I think.) Port 9100 is JetDirect (probably with an HP in front and some subset of trailing) and just nice. (Being able to talk ESC/P with your printer like print '\033K\x07\0\x3E\x81\x99\xA5\xA5\x81\x3E' >/dev/lpa too rocks though, IMHO. Yes, mine can, and I still can. /dev/lpa is BSD.)

Kai, thanks for your vimrc lines:

	:highlight TrailWhitespace ctermbg=red guibg=red
	:match TrailWhitespace /\s\+$\  \+\ze\t/
 

Automatic removal is harmful, though I just fell into the trap since jupprc contains needed whitespace at EOL but manual removal (bound to ^K] in jupp) rocks. And I like that your solution uses such strong a colour vim users are the single most represented offender group for actually leaving the redundant whitespace at EOL there, and it should hurt their eyes. (Sadly there is some vehement disagreement preventing them from inclusion in grml-etc-core but that s why I re-post them here.) Ah, and jupp can of course display whitespace visibly (although it uses / , replacing the arrow with if no UTF-8, not ), accessible with ^Ov. Steve, want to put up a checklist for sites? We can crowdsource the testing to maybe get some interesting results Some other people would get more comments if they were idling in IRC (Freenode) or allow comments on their blog, specifically without too high an entrance barrier OpenID is ok, but many other things, and ECMAscript, are not; but I can t really say that loud because our wlog is static HTML compiled from a flat plaintext data source so it doesn t allow such either. I often forget what I wanted to add if I can t get it out quickly enough (especially at work). Sowwy Me like the cat picture postings (Amayita, Tiago, Gracias!).

16 July 2011

Steve Kemp: There is a reason why I test sites

Recently I was at a pub and there was an advert for pub tokens displayed on the window. Seemed like a cute idea: Perfect for friends, family, remote hackers/developers & similar. When I got home I checked out their site. Seemed simple and nice enough, with good coverage (in terms of local drinking establishments that would accept their tokens). I decided to sign up, with the intention of gifting my sister with some delicious beer. Unfortunately that's where it all went wrong. I tend to act the same on all new sites. Partly to amuse myself, partly to get a feel for how safe/secure/good the site is, I'll try to login with a few different values. You know you're in trouble when you see responses like this:
SELECT * FROM cms_module_pubtokens_users where email = '"'' AND
   password =''"' LIMIT 1
Fatal error: Call to a member function FetchRow() on a non-object in
  /home/pubtokens/U79P18WQ/htdocs/includes/functions.php on line 291
Suffice it to say I sent them an email, then poked them on twitter, but to no avail. In conclusion they don't get my money, and I couldn't recommend them to anybody else at this point either. As I'm not a customer at least I can rest easy knowing my details haven't been compromised at any point over the past few months. ObQuote: "It can't rain all the time" - The Crow

Matthew Palmer: MySQL replication and crash recovery

The question was recently asked, How do I perform crash recovery on a MySQL master/slave database cluster? Because replication is async, a transaction that has been committed to the master may not be able to leave the master before the crash happens. The short answer is, you don t. Asynchronous replication, by it s very nature, is prone to these sorts of issues, and MySQL doesn t make life easier in general by using query replay. Some of the issues aren t massive: for instance, if a slave crashes, the master will keep replication logs for the slave until it comes back up (as long as the slave comes back before the master decides some of the replogs are too old and starts deleting them, of course), so as long as you can live without a master for a while, you can recover and continue along your merry way. Promoting a slave to be a master, on the other hand, is a situation frought with peril. Here, you ve just got to hope hope that your chosen slave is consistent with the master s state at death (because it s async, you have no guarantees about that), and that all the other slaves have the same ideas about what counts as the current state of the data. If your newly-promoted slave managed to apply an update that another slave didn t, that slave will be out of sync with reality (until such time as the now-dead master comes back and replays all those queries hahahaha). To even guarantee that any slaves have a consistent view of the data as compared to the new master, you ve got to rereplicate everything from the new master, because MySQL s where are you up to? counter is master-specific. I ve heard people who have to deal with this sort of thing these days say that the Maatkit tools are handy for dealing with various issues of this type (monitoring, repairing the database when it gets inconsistent). However, I prefer to keep right the hell away from MySQL replication altogether, after a year of having to gently coax an insane MySQL tree replication setup to do the right thing for any extended period, and watching a group of DBAs at Engine Yard go slowly insane. My preferred alternatives are: Never discount the value of the Jaws approach to scaling ( we re gonna need a bigger boat^Wmachine ) hardware is so ridiculously cheap, relative to the clueful sysadmin time needed to make (and keep) MySQL replication running, that you really need to be at the huge end of town (where other scaling issues are going to bite you in the arse first) before spending too much time on your database is worth the investment. Even then, you ll get more bang for your buck from: than you ever will from the constant, mind-numbing grind that is keeping MySQL replication going.
  1. The term comes from a time when a colleague near-screamed This slave cluster keeps fucking me! at a particularly intransigent set of MySQL slaves.

15 July 2011

Jon Dowland: puppet revisited

In an earlier post, I asked a number of questions, most of which are now answered. Here's a recap: I wrote
I stumbled across some CF Engine-style definitions for puppet in a Debian Administration article (taken originally from puppet's own wiki, which have since been moved/deleted)
Perhaps it was removed with good reason This is a fairly horrid solution to the problem. Phil was the first to suggest investigating Augeas, a neat tool that abstracts away the nasty specifics of parsing the myriad configuration file formats and provides a unified, tree-like interface for adjusting configurations. The interface reminds me of manipulating XML via the DOM. More on Augeas in a later blog post I also wrote
package [foo,bar]: syntax worked fine, but is a little verbose and the package list is awkward to maintain buried inside the puppet syntax. I spent some time trying to see whether I could populate a puppet list based on the contents of a file: then I could list the packages one-per-line in a separate file. I haven't found a way of doing that yet.
I haven't found a more concise way of solving this problem, but as I have grown more familiar with puppet's syntax I have become less bothered by it. I igo suggested pre-processing the puppet configuration, but I'd rather not complicate my provisioning approach (VCS hooks) by attempting that. Finally I wrote
Using my laptop as a second node also exposed some interesting puppet client behaviour. Once I went off-site, the laptop tried to look up puppet on foreign networks (such as my home network). I would have assumed that, once the strong SSL association had been made, the client would try to connect to the FQDN of the server. This probably isn't a serious problem in practice, as we probably won't need to rely on nodes off-site talking to the configuration server, but I hope that the client doesn't trust any host that happens to answer to puppet...
I'm pleased to report that the client is at least this clever: it refused to talk to a local puppetmaster that I setup at home.

14 July 2011

Jamie McClelland: GnuCash with python bindings

Hats off to the GnuCash developers and the Parit Worker Collective for the python bindings to GnuCash. If there is any doubt that free software makes life easier it's being able to write your own accounting import scripts while reading the original source code. I had to re-compile Debian's version of GnuCash because it doesn't have the python bindings enabled by default. As root:
apt-get build-dep gnucash
apt-get install python-dev
As a non privileged user:
apt-get source gnucash
cd gnucash-N.N.N # varies depending on your version)
Then, edit debian/rules and add --enable-python-bindings to the dhautoconfigure command. I replaced the line:
--enable-locale-specific-tax
With the lines:
--enable-locale-specific-tax \
--enable-python-bindings
Then, update the change log with:
dch
Built the new package:
dpkg-buildpackage -rfakeroot -uc -b
I then wrote a python module. It logs into the server running our members database, runs a script to export tab delimited lists, and then imports them into our gnucash xml file. It's far from perfect (I'm fairly new to python). Also - I have sheepishly inserted code that searches for certain values by accessing the gnucash file directly (via python's xml handling modules). I couldn't figure out how to do the search I needed with they available python methods, although it may very well be there and I just couldn't figure it out. If anyone is interested in using it for a template:
#!/usr/bin/python
import sys 
import os
import time
import csv
import warnings
from xml.dom.minidom import parse
from gnucash import Session,  GncNumeric
from gnucash.gnucash_business import Customer, Invoice, Entry, BillTerm
from decimal import Decimal
import datetime
import tempfile
import subprocess
def main(argv = None):
  if argv is None:
    argv = sys.argv
  path = os.environ['MFPL_GNUCASH_PATH']
  if not os.path.exists(path):
    print """Path (%s) does not exist. Please set 
    MFPL_GNUCASH_PATH environment variable\n""" % path
    return 1 
  if os.path.exists(path + '.LCK'):
    print """Lock file exists. Is GNUCash running?\n"""
    return 1 
  command = argv[1]
  import_file = None
  if 2 in argv:
    if argv[2] == '--from-file':
      if argv[3]:
        if os.path.exists(argv[3]):
          file_path = argv[3]
          import_file = open(file_path,'r')
        else:
          print "Can't find file to import: %s" % argv[3]
          return 1
      else:
        print "Please pass path to file if using --from-file"
        return 1
    else:
      print "I didn't understand the argument: %s" %s
  # download import file if not provided
  if import_file == None:
    import_file = get_data(command)
  # The file might be gzipped, so
  # open and save it 
  s = get_gnucash_session(path) 
  s.save()
  s.end()
  # Wait a second, so we can create a backup
  # without a name collision
  time.sleep(1)
  # Open for real now...
  s = get_gnucash_session(path) 
  xml_dom = parse(path) 
  if command == "members":
    ret = import_members_from_file(s, xml_dom, import_file)
  if command == "invoices":
    ret = import_invoices_from_file(s, xml_dom, import_file)
  s.save()
  s.end()
  return ret
def get_data(command):
  import_file = tempfile.NamedTemporaryFile()   
  if command == 'members':
    remote_cmd = 'acc-member-list'
  else:
    remote_cmd = 'acc-invoice-list'
  args = [ '/usr/bin/ssh', 'mayfirst@hay.mayfirst.org', '/home/mayfirst/bin/' + remote_cmd ]
  subprocess.call(args,stdout = import_file)
  return import_file
def import_invoices_from_file(s, xml_dom, csvfile):
  dialect = 'excel-tab'
  csvfile.seek(0)
  reader = csv.reader(csvfile, dialect)
  nodes = xml_dom.getElementsByTagName('gnc:GncCustomer')
  already_entered = 0
  entered = 0
  for row in reader:
    member_id = row[0]
    invoice_id = row[1]
    invoice_date = row[2]
    invoice_amount = row[3]
    invoice_desc = row[4]
    if not member_id.isdigit():
      # header line or not coded to a member?
      continue
    member_id = int(member_id)
    invoice_id = int(invoice_id)
    if invoice_exists(xml_dom, invoice_id):
      # print "Already entered %s (%s)" % (invoice_date,invoice_id)
      already_entered += 1
    else:
      if not member_exists(xml_dom, "No Name McGoo - fix me" , member_id):
        print "Skipping member id %s, invoice id %s - member doesn't exist" % (member_id, invoice_id)
      else:
        invoice_enter(s, member_id, invoice_id, invoice_date, invoice_amount, invoice_desc )
        entered += 1
  print "Invoices entered from last two months: %s (%s already existed)" % (entered, already_entered)
  return True
def get_guid_from_member_id(nodes,member_id):
  member_id = pad_number(member_id)
  for node in nodes:
    for child in node.childNodes:
      if child.nodeName == 'cust:guid':
        guid = child.childNodes[0].data
      if child.nodeName == 'cust:id':
        if member_id == child.childNodes[0].data:
          return guid   
def import_members_from_file(s, dom, csvfile):
  dialect = 'excel-tab'
  csvfile.seek(0)
  reader = csv.reader(csvfile, dialect)
  already_entered = 0
  entered = 0
  for row in reader:
    member = row[0]
    member_id = row[1]
    if not member_id.isdigit():
      # header line?
      continue
    member_id = int(member_id)
    if member_exists(dom, member, member_id):
      #print "Already entered %s (%s)" % (member, member_id)
      already_entered += 1
    else:
      member_enter(s, member, member_id )
      entered += 1
  print "Members entered from last two months: %s (%s already existed)" % (entered, already_entered)
  return True
def get_gnucash_session(path):
  with warnings.catch_warnings():
    warnings.simplefilter("ignore")
    return Session(path, is_new = False)
def member_enter(s, member, member_id):
  print "Entering %s with id %s" % (member, member_id)
  book = s.book
  root = book.get_root_account()
  commod_table = book.get_table()
  USD = commod_table.lookup('CURRENCY', 'USD')
  member_id = pad_number(member_id)
  new_customer = Customer(book, member_id, USD, member)
def invoice_enter(s, member_id, invoice_id, invoice_date, invoice_amount, invoice_desc):
  book = s.book
  root = book.get_root_account()
  commod_table = book.get_table()
  USD = commod_table.lookup('CURRENCY', 'USD')
  invoice_id = pad_number(invoice_id)
  member_id = pad_number(member_id)
  invoice_date_object = datetime.datetime.strptime(invoice_date, "%Y-%m-%d %H:%M:%S") 
  member = book.CustomerLookupByID(member_id)
  assert( member != None )
  assert( isinstance(member, Customer) )
  assets = root.lookup_by_name("Assets")
  recievables = assets.lookup_by_name("Recievables")
  income = root.lookup_by_name("Membership Dues")
  invoice = Invoice(book, invoice_id, USD, member, invoice_date_object )
  invoice_value = gnc_numeric_from_decimal(Decimal(invoice_amount))
  invoice_entry = Entry(book, invoice)
  invoice_entry.SetDescription(invoice_desc)
  invoice_entry.SetQuantity( GncNumeric(1) )
  invoice_entry.SetInvAccount(income)
  invoice_entry.SetInvPrice(invoice_value)
  invoice_entry.SetDate(invoice_date_object)
  invoice_entry.SetDateEntered(invoice_date_object)
  print "Entering invoice %s %s %s" % (invoice_id, invoice_date, invoice_amount)
  invoice.PostToAccount(recievables, invoice_date_object, invoice_date_object, "", True)
def pad_number(num):
  return "%(number)06d" %   'number': num  
def member_exists(dom, member, member_id):
  member_id = pad_number(member_id)
  for node in dom.getElementsByTagName('gnc:GncCustomer'):
    for child in node.childNodes:
      if child.nodeName == 'cust:name':
        for grandchild in child.childNodes:
          if member == grandchild.data:
            return True
      if child.nodeName == 'cust:id':
        for grandchild in child.childNodes:
          if member_id == grandchild.data:
            return True
  return False
def invoice_exists(dom, invoice_id):
  invoice_id = pad_number(invoice_id)
  for node in dom.getElementsByTagName('gnc:GncInvoice'):
    for child in node.childNodes:
      if child.nodeName == 'invoice:id':
        for grandchild in child.childNodes:
          if invoice_id == grandchild.data:
            return True
  return False
def gnc_numeric_from_decimal(decimal_value):
    sign, digits, exponent = decimal_value.as_tuple()
    # convert decimal digits to a fractional numerator
    # equivlent to
    # numerator = int(''.join(digits))
    # but without the wated conversion to string and back,
    # this is probably the same algorithm int() uses
    numerator = 0
    TEN = int(Decimal(0).radix()) # this is always 10
    numerator_place_value = 1
    # add each digit to the final value multiplied by the place value
    # from least significant to most sigificant
    for i in xrange(len(digits)-1,-1,-1):
        numerator += digits[i] * numerator_place_value
        numerator_place_value *= TEN
    if decimal_value.is_signed():
        numerator = -numerator
    # if the exponent is negative, we use it to set the denominator
    if exponent < 0 :
        denominator = TEN ** (-exponent)
    # if the exponent isn't negative, we bump up the numerator
    # and set the denominator to 1
    else:
        numerator *= TEN ** exponent
        denominator = 1
    return GncNumeric(numerator, denominator)
if __name__ == "__main__":
  sys.exit(main())

13 July 2011

Daniel Baumann: Debian Live 6.0.2 Images

Two days before the 6.0.2 squeeze point release a user reported that usb-hdd images are not bootable for him. We could not reproduce that for several days but thanks to David Endler, we found out a few days ago that the reason for this is the following commit: Setting partition flags after creating the filesystem, workaround for bug in parted where we result in having the partition type set to linux otherways. The original problem was that in some situation the code resulted in having a wrong partition type and that can let to confusion for some bioses. For other reasons, the applied workaround has the side effect to cause parted to not set the bootable flat at all anymore and by that creating an even bigger problem - most crappy bioses these days require the bootable flag to be set and refuse to boot from a device otherwise (Missing Operating System). Or in other words, the fix solved the original problem but created another one that is affecting much more bioses. Rather than doing a hasty and suboptimal build of 6.0.2 with the commit reverted (and wasting resources for a 6.0.2a build some time later), we have looked into fixing the original problem properly without causing side effects. Images for the 6.0.2 squeeze point release will therefore be published in the next couple of days.

Kai Wasserb ch: Vim: How to prevent trailing whitespaces

As the local geek I get all sorts of (Linux) questions asked, like "How can you delete the nth line with Sed?" or "Is there a way to search for the following in a file?" (the latter being a request to construct a regular expression for grep). And while I'm pretty sure, you can find answers for such questions quickly with $SEARCH_ENGINE I find myself generally typing the answer into the IM session. This, and just seeing Bernd's post about Vim on Planet Debian, prompted me to start a little, irregular series of posts, to which I can point people, whenever I get asked such questions. I start this off with a tip for Vim, a very powerful text editor. The problem is simple: you get (source code) files with trailing whitespaces (sometimes accumulated in "empty" lines). This makes diffing (and merging) difficult. Thus the question is: how do I prevent that from happening? How do I notice, that I have whitespaces at the end of a line? The solution consists of two lines in your .vimrc:
:highlight TrailWhitespace ctermbg=red guibg=red
:match TrailWhitespace /\s\+$\  \+\ze\t/
If you just want this functionality if syntax highlighting is also active, then you should use
:autocmd Syntax * syn match TrailWhitespace /\s\+$\  \+\ze\t/
instead of the :match line. The regular expression used in both cases matches trailing whitespaces and whitespaces in front of tabs. Of course there are several other options on how to do this or what you might want to highlight, but that would be beyond the scope of this little post.

Jaldhar Vyas: A Dance With Dragons (No Spoilers)

Somehow, even though I'd know about it for the longest time, I had never get around to reading George R.R. Martins A Song of Ice and Fire series until I heard about the Game of Thrones HBO series. Then I thought I had better hurry up and read the books as TV adaptations tend to be inferior. (I must say HBO did a pretty good job though.) I was instantly hooked and finished all four books in a week. Today the long-awaited fifth installment called A Dance with Dragons and I immediately got myself a copy. (Nowadays I mostly do my recreational reading with the Kindle app on my Android smartphone.) As before, I'm spellbound. I'm already about 25% through it and loving it. What I love about Martin is that he really gets how traditional, "pagan", "feudal" societies work. Take religion for instance. A lot of Sci-Fi/Fantasy authors if they deal with the subject at all usually give a cartoon version of polytheism that owes more to Christian canards and Dungeons & Dragons than any existing human religion. But in Westeros, one can be simultaneously devout, amoral, superstitious, and cynical in a way that makes a lot of sense to me as a Hindu. Or class. Being a peasant in Martins' world is horrible. Your life is completely at the mercy of Lords and Ladies and can end in an instant. But the upper class despite its wealth and power is trapped in its own way too. They must constantly protect their status from jealous competitors and a reversal of political or military fortune can destroy an ancient noble family just as suddenly as a bad harvest can destroy a family of farmers. Like the other books in the series, this one has a huge cast of characters whose stories weave in and out with each chapter told mainly from one characters viewpoint. This is a tactic which I for one found helpful to keep my attention from flagging but it means you can't really enjoy it unless you've read the previous volumes in order first. I definitely think it's worth the effort though.

12 July 2011

Gustavo Franco: Google+ and your data

Let's make it clear: Your data is yours and in Google+ official announcement there's the following "You and over a billion others trust Google, and we don t take this lightly. In fact we ve focused on the user for over a decade: liberating data, ...". John Goerzen is wrong.

I can understand people having problems to use a product during its field trial and demanding features. I won't sit and watch people spreading FUD in bold before asking though.

Last, but not least, the following link is easily visible in the Settings and is also listed in the Help pages:
https://plus.google.com/settings/exportdata


11 July 2011

Michael Prokop: Creating 32bit Debian packages in 64bit environments

i386 systems are dying off and therefore building 32bit Debian packages in 64bit environments is important if you still need packages for i386. For some recent projects I had to set up 64bit-only environments with the need to provide 32bit packages for i386 as well as according i386 Linux kernel packages. 1) Common Debian packages Just prepend the linux32 command to the build commands and use the -a386 option of dpkg-buildpackage(1). So use linux32 dpkg-buildpackage -ai386 , linux32 git-buildpackage -ai386 , linux32 debuild -ai386 , or whatever build tool you prefer to work with. I personally like using git-buildpackage -S together with cowbuilder. When using cowbuilder it s easy to get i386 Debian packages:
# create initial cow base directory, needs to be executed just once:
cowbuilder --create --basepath /var/cache/pbuilder/wheezy32.cow \
   --architecture i386 --distribution wheezy --mirror http://ftp.de.debian.org/debian
# the "debbuildopts -b" says I want a binary-only build, see dpkg-buildpackage(1):
linux32 cowbuilder --build --basepath /var/cache/pbuilder/wheezy32.cow \
   ~/foobar_0.42.dsc --debbuildopts "-b"
2) Debian Kernel packages If you re building your own kernel images you might be aware of setting ARCH for the common make targets, like:
ARCH=i386 make oldconfig
To build a linux-image Debian package you can use upstream s deb-pkg target, like:
ARCH=i386 make deb-pkg
This should provide you according linux-headers, linux-firmware-image, linux-libc-dev and linux-image i386 Debian packages. But if you need the according source and doc packages as well you might prefer make-kpkg of kernel-package instead, using options like:
DEB_HOST_ARCH=i386 setarch i386 make-kpkg --revision "$KERNELVERSION" \
  --cross-compile - --arch=i386 --us --uc --initrd \
  --rootcmd fakeroot kernel-image kernel-headers kernel-doc kernel-source
3) Debian linux-2.6 kernel packages If you re interested in building Debian kernel images the same way as the Debian kernel team does (AKA linux-2.6) make sure to check out the kernel-handbook. After installing essential software packages I had to set up some symlinks for building:
apt-get install build-essential fakeroot devscripts fakeroot
apt-get build-dep linux-2.6
ln -s /usr/bin/gcc-4.4 /usr/bin/i486-linux-gnu-gcc-4.4
ln -s /usr/bin/ld /usr/bin/i486-linux-gnu-ld
ln -s /usr/bin/ar /usr/bin/i486-linux-gnu-ar
ln -s /usr/bin/objcopy /usr/bin/i486-linux-gnu-objcopy
ln -s /usr/bin/nm /usr/bin/i486-linux-gnu-nm
ln -s /usr/bin/objdump /usr/bin/i486-linux-gnu-objdump
ln -s /usr/bin/strip /usr/bin/i486-linux-gnu-strip
Then executing:
apt-get source linux-2.6
cd linux-2.6*
debuild -ai386 -us -uc
provided the according Debian packages (linux-doc, linux-headers, linux-image, linux-libc-dev, linux-manual, linux-patch-debian, linux-source, linux-support and linux-tools). 4) Out-of-tree kernel module packages If you want to build external modules which are available as $MODULENAME-source in Debian (e.g. tp-smapi-source) using module-assistant then use the DEB_HOST_ARCH environment variable, like:
DEB_HOST_ARCH=i386 m-a -v --text-mode -k /home/mika/linux-$KERNELVERSION -l $KERNELVERSION build tp-smapi
If the external module is not available for use with module-assistant then compile the module running:
make -C /lib/modules/$KERNELVERSION/build M=$(pwd)
whereas /lib/modules/$KERNELVERSION/build is pointing to the build directory of your 32bit kernel.

10 July 2011

NeuroDebian: NeuroDebian@HBM2011.ca

NeuroDebian@HBM2011.ca On June 26-30 the annual meeting of the Organization for Human Brain Mapping (HBM2011) took place in Quebec City, Canada. Encouraged by our positive experience at last year s SfN in San Diego and enthusiasm of our scientific adviser, James V. Haxby, we hosted another NeuroDebian booth. The setup was pretty much the same as last year: Some chairs and tables, lots of people, our tri-fold flyers, a Debian mirror and some virtual machine images to show Debian in action. This time we also had an LCD display attracting visitors with the package swarm, some demos, and our recent paper. We had many curious people have their first exposure to Debian, long-time users expressing their gratitude to Debian, and our upstream developers getting together to discuss various topics. Having registered the booth as NeuroDebian , we had the additional pleasure of explaining visitors the concept of a project inside Debian, in contrast to a derived distribution. But that is nothing new really, so let s talk about the differences from last year s booth. First of all, we had more people at the booth. Dominique Belhachemi volunteered to help us out and that was very much appreciated. Although HBM has only about a tenth of the attendees that SfN has, we had significantly more traffic. While last year people were primarily interested in knowing about the project, this time many of them wanted to give it a try immediately. People came with their laptops, got the VM images and started playing with Debian. After a day or so, some came back and asked for recommendations on particular software after having been exposed to the wealth of the Debian archive. What also had increased was the number of developers, or rather research labs developing neuroimaging software that came to the booth to discuss how to get their software into Debian and how to arrange ongoing maintenance of these future Debian packages. As we have our plates already quite full, we have been spending some time on mentoring interested developers to learn the art of Debian packaging and making them familiar with Debian s procedures and standards (e.g. working on #609820 with Yannick Schwartz, upstream, at the booth). ../../_images/BusyBooth216.jpg Two promising new developments need to be mentioned. First, we were approached by companies that develop hardware for brain-imaging and psychophysics research. They were curious to learn about Debian as an integrated platform that offers free software solutions that an increasing amount of their customers demands (e.g. PsychoPy). Apparently, the movement towards open research software has finally made it into the business plans of companies, as they seem to start perceiving compatibility with free software systems as a competitive advantage. We explained how software gets into Debian, and how its release cycle is managed. To foster their motivation we also pointed them to the existing open-source software that is already available or even present in Debian. Let s see whether we see more Debian-certified research products in the future. Lastly, we started talking with folks from the INCF to explore possibilities of collaborating on INCF projects using Debian as the integration and development platform. The INCF is an OECD-funded organization that develops collaborative neuroinformatics infrastructure and promotes the sharing of data and computing resources to the international research community. At least one INCF project is already relying on the efforts of the NeuroDebian project. We are going to continue this discussion during a workshop in September. A report will follow...
../../_images/DDs13.jpg

Debian people at the booth (f.l.t.r): Michael Hanke, Yaroslav Halchenko, Stephan Gerhard, Dominique Belhachemi. Not shown: Swaroop Guntupalli.

Acknowledgments This booth has been made possible by the generous support of Prof. James V. Haxby (Dartmouth College, New Hampshire, USA).

9 July 2011

Wouter Verhelst: Freedom, continued

Okay, so the story continues: Joe Brockmeier has posted a rebuttal to Nina Paley's post that I commented on earlier today. Basically, Joe says that Culture is not the same things as Software, and gives a lot of arguments as to why this would be true. But I can't agree with him. Let's see why. His first argument is "One of these things is not like the other". As in,
But it's far more reasonable and sensible to discuss Perl scripts and software written in assembler embedded on a chip together, and the rights that they come with, than to try to talk about rights to modify sculpture and essays.
I don't see how one could combine a perl script with an embedded assembler program, except perhaps over a network, or by using exec() two cases in which the FSF would consider that they are not even derivatives anymore, they are "at arm's length", and it would be legal to do something like this if one bit was released under the GPL while the other was not. At the same time, it's possible to engrave (part of) an essay into a statue, for example. Or make a technical apparatus which looks like a statue but which will play music if you come near it. Both would be valid cases of some artist combining two separate and (at first sight) distinct things into one work.
Sure, you could crowdsource sculpture, but I doubt it will have much practical application. (And certainly difficult over the Internet, but I digress...)
It's called RepRap, and it's very much possible over the Internet. Joe moves on, with 'Unintended consequences of forkable prose'.
Take the GNU Manifesto, for example. If you could "fork" the manifesto, I could easily see variant versions modified to justify open source instead of free software.
This argument had been brought forward during the discussions which Debian had on the GFDL vote, and has been seriously debunked there. In short: the ability and right to fork someone's prose does not, in any way, give you the right to misrepresent them. While I believe it should be okay for someone to base a theoretical Open Source Manifesto on the GNU Manifesto, I do not believe it should be okay for them to still keep calling it the GNU Manifesto. It's not the same thing anymore, so that should be clear. This is true within Free Software as well: if you fork, you give it a new name, so it becomes clear that you're not the original anymore. Moreover, I doubt the kind of prose found in the GNU Manifesto is the same kind of prose that Nina would like to see in a free culture movement. Not because the GNU Manifesto is a bad piece of writing it isn't but because Free Culture likely focuses on other things than manifestos. Next, we get "Solving a problem that doesn't exist"
The spark that fired off the GNU revolution was a legitimate problem because of non-free code, a user was unable to make use of hardware they (or their university) legitimately owned.
(...)
While Paley and others may want the same freedoms to go with cultural works, it is not a real problem in the same sense.
Pardon ye me? I am a musician. Not a very brilliant one, mind you, but I play the flute with much enthusiasm, and have done so in a number of places. If you've been to any debconf since 2007, surely you must've heard me play. Also, I've been singing in choirs since I was sixteen which, I just realized, means I've now been singing for more than half my lifetime. Time flies, it must be said. One of the orchestra that I've been playing in was an extended flute choir with a rather unusual instrumentarium: apart from 30-something flautists, the orchestra also had a piano and drummer, a number of guitar players, and a bass guitar. Such an unusual set of music means you have three options: you play original music, written specifically for your group; you play something written for a different set of instruments, without changing anything, and hope nobody notices that it doesn't actually sound very well; or you make modifications. You make a derivative work of the music, so you can actually play it. There, I've said the word. This isn't just true for music; I've yet to see the first theatre performance that is true, word-for-word, to the original script. Also, movies use music all the time. When they do, there are usually two options: either they have original pieces of music written specifically for their movie (as is the case for most Hollywood blockbusters), or they take a piece of existing music, and adapt it so it fits the events in the movie better. By, say, shortening it, so it doesn't run into the next scene. Or by repeating the refrain an extra time, so the words line up nicely with the action. Or by having a group of musicians record a version of that piece specifically for your movie, which is the same thing in everything except the speed so that the feet of the actor walking on the big screen hit the ground in sync with the music. Or... well. You get the idea. By claiming that artists have no need to modify and derive from others, the only thing Joe accomplishes is to show that he really, really, really doesn't know what the fuck he's talking about. Artists have a need to modify other artists' works all the time. The only difference with free software is that they either need to invoke the fair use clause, or need to pay big bucks to the original rightsholder. Sound familiar? Joe finishes up with stating that despite all of that, Free Culture is still possible. And:
That doesn't mean that free culture is impossible. It simply means that different standards apply.
Exactly: different standards apply. And why? There is no good reason for that. Because people make money from cultural works? Well, money is made from software, too, that has never been a good enough reason. Because Culture is not functional? How many in the Free Software movement would even be willing to only be doing software all the time, and not ever do anything remotely related to Culture? None, I would say. Culture is just as functional as Software, it just serves different functions. In the comments, Joe clarifies that he isn't against people preferring not to use the ND clause in the CC licenses they select, he's just against demanding that others do the same. That argument would make sense if not for the fact that the FSF demands, mostly, that Software Should Be Free!!1!. By not complaining about the GNU Manifesto, but complaining about Paley's "Rantifesto", which basically complains about the lack of such an environment in the free culture movement, Joe just makes a fool of himself. Anyway, 'nuff said.

8 July 2011

Julien Blache: Farewell, Space Shuttle Program

Well, today was the day and I ve just watched the last ever Space Shuttle launch. Let s just say there s something very, very wrong with this world and leave it at that. Hats off to all the men and women who dedicated their lives or part thereof to these magnificent birds over the past 30+ years, from the original design studies back in the days to the transition & retirement operations that will last for a year still. We ll most probably never get to see any other machine pushing boundaries like the Shuttle has done, and this is a gigantic loss for us all. One day people will realize If you aren t familiar with the challenges faced by the Shuttle, with the advances in science and countless engineering disciplines brought by Shuttle and more generally with Shuttle and facts about the Shuttle, go read Wings in Orbit; a book by the people who built and operated the Space Shuttle, available freely in PDF format from NASA. Did you know that the Space Shuttle Main Engines (SSME, RS-25) are the most powerful, most efficient engines ever built? They were designed 40 years ago. I won t make a list, but this is true of a number of other components. What the hell have we been doing all these years?

7 July 2011

Matthew Palmer: Honest Achmed is my CA of Choice

It s not often that you get clarity and transparency from a Certificate Authority, so it s good to see that Honest Achmed has requested his CA root certificate be included in Firefox. Really, how can you not trust a guy whose name is Honest Achmed , and who has such a strong and clear-cut verification statement:
Honest Achmed promises to studiously verify that payment from anyone requesting a certificate clears before issuing it (except for his uncles, who are good for credit). Achmed guarantees that no certificate will be issued without payment having been received, as per the old latin proverb nil certificati sine lucre .
His response to Mozilla s CA Recommended Practices is forthright and trustworthy, too:
Honest Achmed promises to abide by these practices. If he s found not to abide by them, he ll claim it was a one-off slip-up in procedures and that policies have been changed to ensure that it doesn t happen again. If it does happen again, he ll blame it on one of his uncles or maybe his cousin, who still owes him some money for getting the car fixed.
I m getting all my SSL certificates from Honest Achmed. He can t be any worse than Comodo

5 July 2011

Rudy Godoy: Compute Clusters Integration for Debian Development and Testing Report 3

Hello this is the third report regarding my project. As usual, quick
summary first. From my last report I have progressed much little than I
would like but, as stated previously also, I m finishing semester
and between exams and projects it takes almost all my time. However, I m
free of academic duties next week and I ll double the speed on the project. What was done so far

- Started working on the modifications to the Eucalyptus code, as
expected.
I ve almost complete the code for the Libvirt s XML Domain
definition. I ve abstracted the wrapper in order to support a broader
number of arches. Previously it was tailored for supporting x86/amd64
based machines. The idea is to have a list of supported arches and
generate the XML file according that. For ARM some parameters and
elements are different than for the others. This would allow for
people to support another architectures, given they are well supported
by KVM-Qemu.
Started working on changes to util/data.c to make sure the correct
parameters are being passed to the XML generator and to libvirt kvm
calls.
- Define and start to setup the testing / demo environment. Basically
involves having one machine to run KVM as hypervisor so the Eucalyptus
handler uses it to run ARM kvm-qemu images.
- Over this weekend I ll be publising the code changes on my
website. Steffen suggested to create some docs regarding the progress
too. I ll do that once I feel I m on track again for my current
pending work, that s coding. What I ll be working over the next two weeks

- My primary goal is to catch up on the work pace and finish the neccesary
code for Eucalyptus to support ARM images. This means to finish
modifications to:
node/handlers_kvm.c handling nc creation. Little changes are need
here from what I ve indentified already.
util/data. c,h handling instance creation. This is the part where
most of the work shall be done. I ve started to work here already.
- Test the code I ve complete already and send the patches to the teams
involved. To date I m still working on a local environment, because no
usable patches have been generated already, just little pieces here
and there.
- Since this is a sort of gluing project, that is code here and there
and make it work together. I m in the process of finding a machine to
be able to test parts of it once they are complete. I m still
contacting people to get such machine, in anycase I gues I can manage
to get one myself. I can t use my current machine since it involves
reinstalling and working at the OS level.
- For the project s demo I ll use both machines, one as the front-end
and the kvm one for NCs (nodes). That s the goal. What is required after that

- I d like to release a package that includes my changes, so it can be
installed either from official packages or my own packages, this
involves:
Updating the packaging bits to the pkg-Eucalyptus repo.
Talking to upstream about it.
- Write the reference Steffen suggested. I believe that I can finish the first part in one week, because of the
work I ve done in the past weeks to understand the Eucalyptus code. I m
also will be talking more to the Eucalyptus team and also to the
maintainers about such changes. I guess that s all for now.

John Goerzen: The Lives of Others

It s not very often that I watch a movie anymore. It s been a few years since I ve actually purchased one (normally I see them from Netflix). But yesterday I saw one that may change that. The Lives of Others is an incredible film set in the former East Germany (GDR/DDR) mostly in 1984. The authenticity of it is incredible and so is the story. It s subtitled, but if you re an American wary of subtitled European films, don t be wary of this one. It is easy to watch and worth every minute. The story revolves around the Stasi, the GDR Ministry for State Security ( secret police ). It is an incredible picture of what living in a police state was like, and how many of the informants were victims of the regime too. My breath caught near the beginning of the film, showing the inside of a Stasi building. A prisoner was being interrogated for helping someone attempt to escape to the west. But the reason my breath caught was this incredible feeling of I was there . Last year, Terah and I were in Leipzig and visited the Stasi museum there, Museum in der Runden Ecke . I always have an incredible sense of history when being in a preserved place, and this building was literally the Stasi headquarters for Leipzig. Much of it was preserved intact, and seeing it in the film brought home even more vividly the terrible things that happened in that building, and others like it, not so very long ago. IMG_2717 We watched the special features on the Blu-Ray disc, and one of them was an interview with director Florian Henckel von Donnersmarck. He described how he spent a lot of time interviewing both victims of the Stasi, as well as ex-Stasi officers. One of the most disturbing things to me was his almost offhand comment that most of the former Stasi officers still had some pride in performing their jobs well. Even now, freed of the state s ideology, they were proud of the work they did which could be put most charitably as ruining people s lives. What leads a person to view life that way? How can we try to make sure it doesn t happen again elsewhere? I am happy to say that most of us have never experienced anything like the Stasi. And yet, small reflections of that mindset can be seen almost everywhere. Societies at wartime or feeling under threat, even Western democracies, can drum up those feelings. In the USA, for instance, the McCarthyism era saw people s careers ruined for alleged anti-state behavior. Contemporary examples include the indefinite detention (I hate that word; shouldn t we say imprisonment ?) of terrorism suspects at Guantanamo Bay, and the terrible treatment of Bradley Manning, who revealed some true but embarrassing things about the US military which really needed to be revealed. Even tobacco farmers and companies are selling a product they know ruins lives, but somehow keep doing it. And there are still members of the public that try to make life difficult for people that don t think like they do. From organizing campaigns of telephone harassment of colleges that don t perform the American national anthem before sporting events, to tossing about the term un-American (a loaded McCarthyist one, which many may not even be aware) at an inflated rate, we are not immune from attempts at forcing conformity or silence in others, and blind loyalty to state. I am never in a particularly celebratory mood on July 4, the biggest day for American boasting, faux patriotism, militarism, and general flag-waving. We do have a lot to be proud of and thankful for, but it seems that we celebrate all the wrong things on July 4, and see it as an occasion to proclaim American exceptionalism rather than as one to see how far we ve come and bolster hope for how far we can, and should, yet go. No, I don t think that the land of the free ought to have operated secret prisons in Europe (nor the Europeans to have been complicit in it), or that the American military was defending our freedom 100% of the time they were deployed, or that it is right for governments to mandate daily recitation of an untrue document (the pledge of allegiance) in schools. And yet, I am mindful that I have a lot to be thankful for stability, lack of much internal violent conflict, etc. And this particular day I am happy that a post like this is not something that gets the attention of some government agency and mostly that I will have a handful of angry emails to delete.

Next.

Previous.