Category Archives: Linux

Systemdatenträger wechseln ohne “Augen zu und durch”-Mentalität

Einleitung

In diesem Artikel beschreibe ich wie man Windows in wenigen Schritten von einem Speichermedium zum Anderen überträgt — mit professionellen Werkzeugen. Das ist ein kritischer Prozess. Aber ein simpler Prozess, den man verstehen kann und sollte, bevor man ihn einleitet. Wie immer gibt es zahlreiche schändliche Anleitungen da draußen, bei denen dieser Prozess nicht hinreichend erklärt und irgendwelchen bunten Klick-Tools überlassen wird. Das Problem dabei: der Vorgang wird dem Nutzer übersimplifiziert präsentiert. Wichtige Entscheidungen trifft die angepriesene Software selbst, essentielle Details werden vom Nutzer ferngehalten. Aber im Bereich von Datenträgerstrukturen gibt es zu viele potentielle Komplikationen, deren Behandlung man keiner Pseudointelligenz oder auch der “Holzhammermethode” überlassen darf:

Holzhammermethode, zweckentfremdet von http://www.datamartist.com.

Holzhammermethode, zweckentfremdet von datamartist.com.

In professionellen Umgebungen wird ein solcher Migrationsprozess normalerweise akribisch geplant und jeder Schritt mit dem einen dafür etablierten Werkzeug ausgeführt. Ergebnis: die Migration gelingt schnell und zuverlässig.

In privaten Umgebungen herrscht Unsicherheit, man kennt die technischen Details nicht und folgt gerne einer simplen Abstraktion des Problems. Es herrscht Augen zu und durch-Mentalität. Grafisch hübsch aufgemachte Software-Assistenten suggerieren schließlich eine gewisse Sicherheit. Doch Hoffnung ist keine Strategie. Das etwas andere Ergebnis: mit einiger Wahrscheinlichkeit geht etwas schief und man verbringt Stunden oder Tage mit einer chaotischen Phase der Problemlösung, bei der vielleicht Dritte mit einbezogen werden müssen.

Eine grundsätzliche Ursache dieses Gefälles zwischen professioneller und privater Umgebung ist sicherlich der Mangel an Detailwissen in Letzterer. Damit einher geht aber — und das ist das eigentliche Übel — fehlende Anerkennung, dass ein solcher Prozess akribisch geplant werden muss. Ich schreibe das hier auf Deutsch und mit dieser kleinen Einleitung, um in meinem direkten Umfeld ein besseres Bewusstsein für diese Thematik und Problematik zu schaffen.

Es wird unten stehend demonstriert, wie ein solcher Prozess zuverlässig umgesetzt werden kann. Als Beispiel dient ein Spezialfall, den die meisten Windows-Nutzer zu Hause haben. Die Grundidee — Detailplanung und die verwendeten Ansätze und Werkzeuge — sind jedoch auch auf andere Situationen und Betriebssysteme anwendbar. Ihr werdet merken, wenn Eure Situation von der hier beschriebenen abweicht. Dann könnt Ihr mich gerne fragen. Dementsprechend erhebt die folgende Anleitung keinen Anspruch auf Vollständigkeit.

Migrationsprozess

1) Live-Linux präparieren (5 Minuten)

Ihr braucht einen USB-Stick mit einem modernen Live-Linux-System, welches die notwendigen Werkzeuge bereithält, um zuverlässig Daten auf Datenträgern zu analysieren und zu manipulieren. Ich empfehle die Linux-Distribution SystemRescueCD. Gibt es hier zum Herunterladen. Wie bekommt Ihr das auf einen USB-Stick, von dem der Rechner starten kann? Mit dem quelloffenen Rufus. Dort wählt Ihr “create a bootable disk” und bei “select ISO file” wird das SystemRescueCD-Abbild ausgewählt, was gerade heruntergeladen wurde.

2) Transfersystem vorbereiten (5 Minuten)

Der Quell-Datenträger (mit der bisherigen Windows-Installation) wird aus dem Produktivsystem herausgenommen. Der Ziel-Datenträger wird bereitgelegt.

Es wird ein Rechner gebraucht, in dem diese beiden Datenträger gleichzeitig angeschlossen werden. Dieser Rechner wird vom zuvor präparierten USB-Stick gestartet. Beim Start von SystemRescueCD wird der default Start-Modus gewählt, das Tastaturlayout (z. B. DE) definiert und die Desktop-Umgebung mittels startx gestartet.

In diesem Zustand ist der Zugriff auf alle Datenträger möglich. SystemRescueCD wagt jedoch keine autonomen Zugriffe. Ohne Anweisung wird kein einziges Dateisystem eingehängt. Insbesondere wird ohne konkreten Befehl auf keinen Datenträger schreibend zugegriffen. Das ist ein konzeptioneller Schutz vor ungeplanten Ereignissen.

3) Quelle und Ziel eindeutig identifizieren (2 Minuten)

Jedes Linux-Betriebssystem nummeriert die Datenträger intern durch. Jeder Datenträger und jede Partition ist per /dev/sd* lesend und schreibend erreichbar. Jetzt muss die Bezeichnung des bisherigen Windows-Datenträgers ausfindig gemacht werden. Es hilft eine Auflistung der verfügbaren Datenträger samt Partitionen. Ein praktischer Befehl ist fsarchiver probe simple, welcher in ein Terminal eingegeben wird. Das ist die Ausgabe in meinem Fall:

[======DISK======] [=============NAME==============] [====SIZE====] [MAJ] [MIN]
[sda             ] [KINGSTON SVP200S               ] [   111.79 GB] [  8] [  0]
[sdb             ] [WDC WD10EAVS-00D               ] [   931.51 GB] [  8] [ 16]
[sdc             ] [Samsung SSD 850                ] [   476.94 GB] [  8] [ 32]
[sdd             ] [RALLY2                         ] [     3.74 GB] [  8] [ 48]
[sde             ] [USB DISK                       ] [     7.55 GB] [  8] [ 64]
 
[=====DEVICE=====] [==FILESYS==] [======LABEL======] [====SIZE====] [MAJ] [MIN] 
[loop0           ] [squashfs   ] [<unknown>        ] [   278.66 MB] [  7] [  0] 
[sda1            ] [ntfs       ] [System Reserved  ] [   100.00 MB] [  8] [  1] 
[sda2            ] [ntfs       ] [<unknown>        ] [   111.69 GB] [  8] [  2] 
[sdb1            ] [ntfs       ] [Software         ] [   196.16 GB] [  8] [ 17] 
[sdb2            ] [ntfs       ] [Daten            ] [   735.35 GB] [  8] [ 18] 
[sdd1            ] [vfat       ] [SYSRCD-4_4_      ] [     3.74 GB] [  8] [ 49] 
[sde1            ] [vfat       ] [<unknown>        ] [     7.55 GB] [  8] [ 65]

Beim Lesen der Ausgabe braucht man ein bisschen Kenntnis über die eigene Hardware: in der oberen Liste sollten Gerätebezeichnung und Datenträgerkapazität genügend Aufschluss geben. Der bisherige Windows-Datenträger von Kingston heißt in diesem Fall sda. Er ist per Dateisystempfad /dev/sda erreichbar.

In der unteren Liste sind die automatisch erkannten Partitionen aufgeführt. Die Partition sda1 ist eine 100 MB große Partition mit dem Namen “System Reserved …”. Diese wird für gewöhnlich von Windows 7 & 8 bei der Installation angelegt und übernimmt essentielle Aufgaben (sie wird im Betrieb von Windows nicht als normaler Datenträger angezeigt aber muss bei der Migration mitgenommen werden). Die Partition sda2 ist das, was man aus Windows-Sicht als Systemlaufwerk bezeichnen würde (meist Laufwerk “C:”). Bei beiden Partitionen hat fsarchiver wie erwartet erkannt, dass sie ein NTFS-Dateisystem enthalten. Es ist keine weitere Partition auf /dev/sda vorhanden. Die Bestandsaufnahme der Quelle ist somit abgeschlossen.

/dev/sdc ist der Datenträger, der zukünftig Windows tragen soll. Bisher ist keine Partition auf diesem Datenträger eingerichtet.

4) Die Transferstrategie festlegen

Die Strategie im Groben: Die Inhalte beider Quell-Partitionen (/dev/sda1 und /dev/sda2) sollen auf Block-Ebene auf den neuen Datenträger kopiert werden. Vorher bekommt der neue Datenträger eine frische Partitionstabelle. Der Bootloader wird vom alten Datenträger auf den Neuen kopiert.

Die Strategie im Detail:

  • Per GNU fdisk wird eine neue Partitionstabelle vom Typ msdos auf /dev/sdc erstellt, sodass die Situation vom alten Datenträger in den wesentlichen Punkten repliziert wird:
    • /dev/sdc1 erhält die selbe Startposition und Größe wie /dev/sda1.
    • In diesem Fall ist der neue Datenträger größer als der alte: /dev/sdc2 soll den Rest des Datenträgers füllen.
    • Beide Partitionen erhalten eine Markierung vom Typ 7 (HPFS/NTFS/exFAT).
    • /dev/sdc1 wird als aktiv markiert.
  • Der Master Boot Record wird per dd nur teilweise vom alten Datenträger auf den neuen kopiert (nur der Bootloader und die Datenträgersignatur, nicht jedoch die Partitionstabelle).
  • /dev/sda1 wird blockweise per dd nach /dev/sdc1 kopiert.
  • /dev/sda2 wird blockweise per dd nach /dev/sdc2 kopiert.
  • Das NTFS-Dateisystem welches auf /dev/sda2 befindet hat nun mehr Platz auf /dev/sdc2. Es wird per ntfsresize entsprechend darauf aufmerksam gemacht, sodass es den verbleibenden Platz innerhalb der Partition nutzen kann. Zur Vereinfachung dieses Schritts wird als Wrapper GParted benutzt. Beide Tools sind quelloffen und Industriestandard.

Alle hier genannten Werkzeuge sind Teil der SystemRescueCD-Distribution.

5) Realisierung (5 Minuten Arbeit, X Minuten Warten)

  • Details der Partitionstabelle vom bisherigen Windows-Datenträger per fdisk -l auslesen:
    Device    Boot     Start       End    Blocks  Id System
    /dev/sda1 *         2048    206847    102400   7 HPFS/NTFS/exFAT
    /dev/sda2         206848 234438655 117115904   7 HPFS/NTFS/exFAT

    Beide Partitionen sind von Typ 7. /dev/sda1 ist Boot-markiert (“active”). Start und Ende sind präzise angegeben. Diese Parameter wollen wir exakt replizieren. Die einzige Änderung der neuen Tabelle gegenüber der Alten wird die Endmarkierung der Partition /dev/sda2 sein, da der neue Datenträger größer ist als der Alte.

  • Neue Partitionstabelle auf neuem Datenträger per fdisk /dev/sdc erstellen. fdisk wird schrittweise per Tastatur bedient:
    • o create a new empty DOS partition table
    • n add a new partition
    • p primary (0 primary, 0 extended, 4 free)
    • Partition number 1, first sector: 2048 (default), last sector: 206847
    • n add a new partition
    • p primary (0 primary, 0 extended, 4 free)
    • Partition number 2, first sector: default, last sector: default (bis zum Ende des Datenträgers)
    • mit Befehl t change a partition type wird Typ 7 für beide Partitionen gewählt.
    • mit Befehl a toggle a bootable flag wird Partition 1 aktiv markiert.
    • mit Befehl w write table to disk and exit werden die Änderungen geschrieben.
  • Bootloader und Datenträgersignatur blockweise kopieren (die ersten 446 Bytes des Datenträgers):
    # dd if=/dev/sda of=/dev/sdc bs=446 count=1
    1+0 records in
    1+0 records out
    446 bytes (446 B) copied, 0.00403888 s, 110 kB/s
  • kleine “System Reserved”-Partition blockweise kopieren:
    # dd if=/dev/sda1 of=/dev/sdc1
    204800+0 records in
    204800+0 records out
    104857600 bytes (105 MB) copied, 2.02602 s, 51.8 MB/s
  • Windows-Partition (“C:”) blockweise kopieren:
    # dd if=/dev/sda2 of=/dev/sdc2
    234231808+0 records in
    234231808+0 records out
    119926685696 bytes (120 GB) copied, 2271.83 s, 52.8 MB/s
  • NTFS-Dateisystem auf /dev/sdc2 vergrößern:
    • GParted starten.
    • /dev/sdc2 rechtsklicken und Check wählen.
    • In der Ausgabe kann man verfolgen, dass ntfsresize --force --force /dev/sdc2 aufgerufen wird. Es wird der Größenunterschied zwischen Dateisystem und Partition aufgeführt: Current volume size: 119926682112 bytes (119927 MB) vs. Current device size: 512004284416 bytes (512005 MB). Am Ende steht ein Successfully resized NTFS on device '/dev/sdc2'. Da keine Daten verschoben werden müssen, dauert dieser Vorgang nur wenige Sekunden.

Bei den obig angegebenen Schritten ist zu beachten, dass ein Vertauschen/Verschreiben bei den Gerätepfaden (/dev/sd*) unverzeihlich ist. Bitte drei mal denken und kontrollieren, bevor Ihr auf Enter drückt. Ansonsten habt Ihr ja sicherlich eine Sicherung Eurer persönlichen Daten.

6) Rückbau des Datenträgers in das Produktivsystem (5 Minuten)

Der neue Datenträger mit der Windows-Installation wird in das Produktivsystem zurückgebaut. Per BIOS-Einstellung wird das Produktivsystem vom neuen Datenträger gestartet. Es sind keine weiteren Schritte erforderlich.

Fazit

Etwa 25 Minuten Arbeit für eine Betriebssystemmigration (+ Zeit für Datentransfer). Das ist schnell. Warum gewährleistet obige Strategie Zuverlässigkeit? Der durchgeführte Prozess stellt sicher, dass sich zwischen der alten und der neuen Situation nur zwei Kleinigkeiten geändert haben: die Bezeichnung des Datenträgers und die Größe des NTFS-Dateisystems in dem Windows (mehrheitlich) installiert ist. Aus Sicht des BIOS und aus Sicht des Betriebssystems ist der Rest des Gesamtsystems äquivalent zum vorherigen Zustand. Mit dieser Betrachtungsweise ist es leicht zu verstehen, dass die Migration mit hoher Wahrscheinlichkeit gelingt. Die präzise Kontrolle der Geschehnisse erleichtert auch eine etwaige Fehlersuche: gibt es nach Schritt 6 ein Problem, so muss es an einem der beiden geänderten Parameter liegen (in der Tat: UEFI Systeme mögen bedingt durch den Wechsel des Geräte-Namens bzw. der Geräte-ID einen weiteren kleinen Schritt erfordern).

Bei Eurem nächsten Wechsel des Systemdatenträgers solltet Ihr die Migration systematisch durchführen. Wenn Ihr den Prozess unter Kontrolle habt, dann gelingt er mit hoher Wahrscheinlichkeit. Traut lieber den quelloffenen Standard-Werkzeugen welche in professionellen Umgebungen eingesetzt werden als den großen Versprechungen von (kommerziell erhältlichen) Software-Assistenten für Privantanwender.

Hello SPF record.

Introduction

Say Google’s or Yahoo’s mail transfer agent (MTA) retrieves an email sent from an MTA running on the host with the (fictional) IP address 1.1.1.1. Say the sending MTA identifies itself as sending on behalf of important.com (within the EHLO/HELO handshake, or with a From address with the domain important.com). Then the receiving MTA (Google or Yahoo, or something else) usually queries the important.com domain via DNS for the so-called SPF (Sender Policy Framework) record. Using this SPF record the retrieving MTA can verify whether the owner of the domain important.com intended to send mail from 1.1.1.1 or not.

The SPF record might say that important.com does not send mails. Then the receiving MTA knows for sure that the incoming mail is invalid/fake/spam. Or this record says that important.com actually sends mail, but only from the (fictional) IP address 2.2.2.2. Then the retrieving MTA also knows for sure that something is wrong with the mail. Another possibility is that the record states that important.com actually sends mail from 1.1.1.1, in which case the incoming mail likely is intended by the owner of the domain important.com. This does not mean that this mail is not spam, but at least one can be quite sure that nobody crafted that mail on behalf of important.com.

That was a simplified description for why it is important to have a proper SPF record set for a certain domain in case one intends to send mail from this domain. In the next paragraphs I explain how I have set the SPF record for gehrcke.de and why.

SPF record for gehrcke.de

Domainfactory is the tech-c for gehrcke.de. However, the DNS entries for gehrcke.de are managed by Cloudflare (free, and quite comfortable). Today, I added an SPF record for gehrcke.de via Cloudflare’s web interface. The first important thing to take note of is that the SPF record actually is a TXT record. There once was a distinct SPF record proposed, but in RFC 7208 (which specifies SPF) we clearly read:

The SPF record is expressed as a single string of text found in the RDATA of a single DNS TXT resource record

and

SPF records MUST be published as a DNS TXT (type 16) Resource Record (RR) [RFC1035] only. Use of alternative DNS RR types was supported in SPF’s experimental phase but has been discontinued.

By now it should be clear that I had to add a new TXT record for gehrcke.de, with a special syntax, the SPF syntax. This syntax is specified here. What I need to express in the record is that mail sent on behalf of gehrcke.de is only sent by the machine with the IPv4 address 5.45.109.1 / or from the IPv6 subnet fe80::5054:8dff:fe9b:358d/64. This is the machine that I know should send mail. No other people / machines should send mail on behalf of my domain. The corresponding record is:

v=spf1 ip4:5.45.109.1 ip6:fe80::5054:8dff:fe9b:358d/64 -all

The prefix v=spf1 declares the SPF version, the ip4 and ip6 markers declare that mail from a certain address or subnet are valid. -all expresses that mail from all other hosts is disallowed. The fact that I do not “allow” others to send mail on behalf of gehrcke.de is just my declaration of intent. A retrieving MTA is not required to respect my SPF record. However, larger mail providers should respect it and at least classify a mail that does not pass the SPF test as spam, if not reject it right away.

Whenever I switch machines or IP addresses, I need to remember to update this record. This is a minor disadvantage. To circumvent this, in many cases a short

v=spf1 a -all

would be enough. It expresses that mail sent from the IP address corresponding to the DNS A record of the domain is allowed. In my case, however, this does not work since the A record points to Cloudflare’s cache rather than directly to my machine.

Testing the record

There are many web-based tools for checking whether a certain domain has an SPF record set and whether its syntax is valid. Particularly useful is, for instance, mxtoolbox.com/SuperTool.aspx. However, a real full-system validation obviously requires you to send a mail to an external service that resembles a serious mail provider and debugs the communication for you, meaning that it informs you about all externally visible details of your mail setup, including the SPF record. A great verification tool that does exactly this is provided by Port25. On the command line I used my local MTA (Exim4) to send mail to their service, instructing them to return the corresponding report to may Googlemail address:

$ echo "This is a test." | mail -s Testing check-auth-jgehrcke=googlemail.com@verifier.port25.com

The following is an excerpt of the test result, indicating that the actual mail sender matches the data provided in the SPF record:

HELO hostname:  gurke2.gehrcke.de
Source IP:      5.45.109.1
mail-from:      user@gehrcke.de
 
----------------------------------------------------------
SPF check details:
----------------------------------------------------------
Result:         pass 
ID(s) verified: smtp.mailfrom=user@gehrcke.de
DNS record(s):
    gehrcke.de. SPF (no records)
    gehrcke.de. 300 IN TXT "v=spf1 ip4:5.45.109.1 ip6:fe80::5054:8dff:fe9b:358d/64 -all"

Great.

Good to know checkrestart from debian-goodies

The security audits triggered by Heartbleed lead to an increasing discovery of security issues in libraries such as GnuTLS and OpenSSL within the past weeks. These issues were dealt with responsibly, i.e. they were usually published together with the corresponding binary updates by major Linux distributions such as Debian (subscribing to debian-security-announce or comparable sources is highly recommended if you want to keep track of these developments).

After downloading an updated binary of a shared library, it is important to restart all services (processes) that are linked against this library in order to make the update take effect. Usually, processes load a shared library code segment to random access memory once during startup (actually, the program loader / runtime linker does this), and do not reload that code afterwards throughout their life time, which may be weeks or months in case of server processes. Prime examples are Nginx/Apache/Exim being linked against OpenSSL or GnuTLS: if you update the latter but do not restart the former, you have changed your disk contents but not updated your service. In the worst case the system is still vulnerable. It should therefore be the habit of a good system administrator to question which services are using a certain shared library and to restart them after a shared library update, if required.

There is a neat helper utility in Debian, called checkrestart. It comes with the debian-goodies package. First of all, what is debian-goodies? Let’s see (quote from Wheezy’s package description):

Small toolbox-style utilities for Debian systems
 
These programs are designed to integrate with standard shell tools, extending them to operate on the Debian packaging system.
 
 dgrep  - Search all files in specified packages for a regex
 dglob  - Generate a list of package names which match a pattern
 
These are also included, because they are useful and don't justify their own packages:
 
 debget          - Fetch a .deb for a package in APT's database
 dpigs           - Show which installed packages occupy the most space
 debman          - Easily view man pages from a binary .deb without extracting
 debmany         - Select manpages of installed or uninstalled packages
 checkrestart    - Help to find and restart processes which are using old
                   versions of upgraded files (such as libraries)
 popbugs         - Display a customized release-critical bug list based on
                   packages you use (using popularity-contest data)
 which-pkg-broke - find which package might have broken another

Checkrestart is a Python application wrapping lsof (“list open files”). It tries to identify files used by processes that are not in the file system anymore. How so?

Note that during an update a certain binary file becomes replaced: the new version is first downloaded to disk and then rename()ed in order to overwrite the original. During POSIX rename() the old file becomes deleted. But the old file is still in use! The standard says that if any process still has a file open during its deletion, that file will remain “in existence” until the last file descriptor referring to it is closed. While these files that are still held “in existence” for running processes by the operating system, they are not listed in the file system anymore. They can however easily be identified via the lsof tool. And this is exactly what checkrestart does.

Hence, checkrestart “compares” the open files used by running processes to the corresponding files in the file system. If the file system contains other (e.g. newer) data than the process is currently using, then checkrestart proposes to restart that process. In a tidy server environment, this usually is the case only for updated shared library files. Below you can find example output after updating Java, Python, OpenSSL, and GnuTLS:

# checkrestart
Found 12 processes using old versions of upgraded files
(5 distinct programs)
(5 distinct packages)
 
Of these, 3 seem to contain init scripts which can be used to restart them:
The following packages seem to have init scripts that could be used
to restart them:
nginx-extras:
    20534   /usr/sbin/nginx
    20533   /usr/sbin/nginx
    20532   /usr/sbin/nginx
    19113   /usr/sbin/nginx
openssh-server:
    3124    /usr/sbin/sshd
    22964   /usr/sbin/sshd
    25724   /usr/sbin/sshd
    22953   /usr/sbin/sshd
    25719   /usr/sbin/sshd
exim4-daemon-light:
    3538    /usr/sbin/exim4
 
These are the init scripts:
service nginx restart
service ssh restart
service exim4 restart
 
These processes do not seem to have an associated init script to restart them:
python2.7-minimal:
    2548    /usr/bin/python2.7
openjdk-7-jre-headless:amd64:
    4348    /usr/lib/jvm/java-7-openjdk-amd64/jre/bin/java

Nginx (web server) and OpenSSH (SSH server) are linked against OpenSSL, and Exim (mail transfer agent) is linked against GnuTLS. So that output makes sense. Obviously, after an update of Python and Java processes using this interpreter or VM (respectively) also need to be restarted in order to use the new code. Checkrestart is extremely helpful, thanks for this nice tool.

Update 2014-09-03:

With the upcoming Debian 8 (Jessie, currently unstable), a new package has been introduced — needrestart. It is a more modern version of checkrestart, and tightly integrates with the systemd service infrastructure. Notably, needrestart comes in its own package and with the goal to become more popular than checkrestart (which was hidden in debian-goodies, as discussed above). Currently, a discussion is going on on the debian-security mailing list about installing and running needrestart in a default Debian installation.

exim4: add hostname for local routing

These days, I was confused by exim4 behavior on debian:

# hostname
gurke2
# cat /etc/hostname
gurke2
# exim -bt herbert@gurke2
R: nonlocal for herbert@gurke2
herbert@gurke2 is undeliverable: Mailing to remote domains not supported

So, although the recipient herbert@gurke2 clearly should be treated as a recipient in the local domain (the local host is named gurke2), exim thinks that gurke2 is a remote domain.

After digging into the config files, I came across the options named local_domain and MAIN_LOCAL_DOMAINS, searched the web for these and found the solution in this thread. The dc_other_hostnames is the right place to add host names (or fully qualified domain names) that should be treated by exim’s local_user router. I set this to gurke2, reloaded the exim configuration, and tested again. It works:

# vi /etc/exim4/update-exim4.conf.conf
# cat /etc/exim4/update-exim4.conf.conf | grep dc_other_hostnames
dc_other_hostnames='gurke2'
# service exim4 reload
[ ok ] Reloading exim4 configuration files: exim4.
# exim -bt herbert@gurke2
R: system_aliases for herbert@gurke2
R: userforward for herbert@gurke2
R: procmail for herbert@gurke2
R: maildrop for herbert@gurke2
R: lowuid_aliases for herbert@gurke2 (UID 1000)
R: local_user for herbert@gurke2
herbert@gurke2
  router = local_user, transport = mail_spool

numpy built with recent Intel compilers: MKL FATAL ERROR

I just spent hours trying to build numpy 1.8.0 with the most recent Intel MKL, Intel C++ Composer, and Intel Fortran Composer (Version 2013 SP1, from January 2014). numpy built fine, imported fine, and basic math worked fine. But one of the earlier tests (when running numpy.test()) triggered this error:

MKL FATAL ERROR: Cannot load libmkl_mc3.so or libmkl_def.so.

This error message is obviously not generated by the system, but written out by some MKL code. I double-checked that LD_LIBRARY_PATH was set correctly, and that these libraries were actually available. MKL itself seems to be confused about how to access these libraries.

There is not too much to find about this error — the two most important resources being

  • An Intel forum thread, where even the Intel engineers are struggling in identifying the problem. The user there solved his issue by accident, there was no logical solution.
  • and a StackOverflow answer saying “Intel people claim this was MKL library bug.

More people have this kind of problem:

The Intel guys have a general “solution” to this class of problem: http://software.intel.com/en-us/articles/mkl-fatal-error-cannot-load-neither-xxxx-with-intel-mkl-10x — this article provides insights into how things should work, but did not really help in my scenario.

Some of the workarounds that magically solve this issue for some people involve explicit declaration of a list of libraries to link to (via mkl_libs in site.cfg in case of numpy), but that did not help in my case. According to http://software.intel.com/en-us/articles/numpy-scipy-with-mkl and other Intel resources, it should be the best idea to rely on mkl_rt for dynamic dependency resolution, so this is what I wanted to do. And most importantly the mkl_rt method should, according to Intel, prevent this error from happening (reference). This was also propagated in a forum thread in 2010 (here):

“To who’m concerns,

The mkl dynamic library issue will be fixed thoroughly in MKL 10.3 and future version. A new librar mkl_rt was introduced. Now to build the custom MKL library based on dynamic MKL, one can easilyuse the library libmkl_rt.so instead of -lmkl_intel_lp64 -lmkl_intel_thread -lmkl_core

The new mkl version will be integrated to next intel Compiler version too, which targeted to be release in Nov.”

Yeah, that’s ironic. I do not want to say that it is impossible to get things running using recent Intel compiler suites, this statement would be wrong. But it is definitely tedious! See https://github.com/GalSim-developers/GalSim/issues/261. One quote from that thread:

Actually, this might be harder than I thought. I googled the error you were having and found a thread that ends with this comment:

“I remember now that I had the same problem recently – it is a
fundamental incompatibility between MKL and Python way of loading shared
libraries through dlopen. AFAIK, there is no solution to this problem,
except for using the static libraries.”

I’m not sure what they mean about using static libraries though, since python usually can’t handle static libraries.

For sure, the conclusion after this bit of research is that this error message is the result of a serious issue, and the direct solution to it is not well documented. There is a simpler way to tackle this:

I went a few steps back and built with the 12.1.3 suite (MKL, icc, ifort). It just works (and suggests that there are actual issues with newer releases of MKL/icc/ifort).

I describe the build process in another blog post here.