Viele Ubuntu-User wissen gar nicht, dass ihnen der Zeitgeist-Dämon heimlich über die Schulter schaut und ihre Desktop-Aktionen mitprotokolliert. Einige Perl-Skripts bereiten die Lauscherberichte auf und bringen interessante Tatsachen über die Usergewohnheiten ans Tageslicht.
Woher weiß eigentlich der Nautilus-Browser, welche Dateien ein Ubuntu-User
in letzter Zeit bearbeitet hat, um sie in der Sparte "Recent" anzuzeigen?
In Abbildung 1 listet er zum Beispiel zwei Screenshots auf, die ich kurz
vorher zu
Kontrollzwecken mit dem Foto-Viewer Eye Of Gnome eog
angesehen habe.
Da Nautilus und Eye of Gnome zwei völlig getrennte Applikationen sind,
die untereinander keine Daten austauschen, hilft auf dem Ubuntu-Desktop
ein inoffizieller Mitarbeiter namens zeitgeist
mit, Spitzeldaten zu
übermitteln. Interessierte können ihn mit
ps aux | grep zeitgeist
in der Prozessliste als unter ihrer Userid laufend enttarnen. Gnome-Applikationen wie zum Beispiel der Foto-Viewer "eog" stehen über den DBus mit dem Zeitgeist-Dämon in Verbindung, der relevante Vorgänge neugierig in eine Datenbank protokolliert und dort interessierten Anwendungen, wie zum Beispiel dem Nautlius-Browser, zur Verfügung stellt. Als Format für die mitgeschnittenen Desktop-Events nutzt Zeitgeist eine SQLite-Datenbank, die neugierige Perl-Schnüffler ebenfalls auslesen und auswerten können. Wer dem Dämon dabei zuschauen möchte, wie er vom Desktop eintrudelnde Ereignisse aufschnappt, kann selbigen mit
$ zeitgeist-daemon -r --log-level=DEBUG
im Debug-Modus starten, wobei obiger Aufruf wegen der Option -r
auch gleich den bereits laufenden Dämon stoppt und ihn durch den im
Vordergrund gestarteten ersetzt.
Das Zeitgeist-Projekt auf Launchpad [2] scheint allerdings seit 2013 im Dornröschenschlaf zu schlummern. Heute bringen Suchanfragen zum Thema Zeitgeist hauptsächlich kritische Stimmen von um Ihren Datenschutz besorgten Usern auf Internetforen zutage, die verzweifelt versuchen, dem Dämon den Garaus zu bereiten, ohne dabei ihren Ubuntu-Desktop lahmzulegen. Auch wenn dem Projekt möglicherweise keine rosige Zukunft bevorsteht, lohnt es sich nichtsdestotrotz, hinter die Kulissen zu schauen und die gesammelten Daten einem kritischen Blick zu unterziehen.
Abbildung 1: Kürzlich angesehene Dateien im Nautilus-Filebrowser. |
Weil das ungefragte Mitprotokollieren aller Aktionen auf dem Desktop die
Privatsphäre des Users verletzen könnte, erlaubt es der Unity-Desktop dem User,
die Sammelwut einzuschränken, ganz abzustellen oder gar alle bislang
gesammelten Daten zu verwerfen. In den "System
Settings" unter dem Eintrag "Security und Privacy" kann der User den Dämon
durch das Deaktivieren einiger Checkboxen in seine Schranken weisen. Dann
bekommt Zeitgeist eine Blacklist zugespielt und ignoriert eintrudelnde Events
geblockter Applikationen. Abbildung 2 zeigt, dass Zeitgeist sich dazu
überreden lässt, wahlweise keine Aktionen betreffs Musik, Videos, Fotos, Chat
logs, oder Dokumenten mitzuschneiden. Durch Drücken des Buttons
"Clear Data" fordert der User den Dämon gar auf, bislang gesammelte Daten
permanent zu löschen.
Was der Zeitgeist-Dämon so alles sammelt, zeigt auch der
zeitgeist-explorer
aus dem gleichnamigen Ubuntu-Paket grafisch an
(Abbildung 3).
Abbildung 2: Dieser Dialog in den System Settings unter "Security and Privacy" bestimmt, welche Vorgänge der Zeitgeist-Dämon archiviert. |
Auf einer frischen Ubuntu-Installation protokolliert Zeitgeist aber
alles Erhältliche mit. Doch nicht alle Applikationen geben sich so
geschwätzig wie Eye of Gnome aus dem einleitenden Beispiel.
Wünscht sich der User nämlich, dass mittels
Rhythmbox abgespielte Musikstücke in die Fänge des Zeitgeists gelangen, muss er
das Ubuntu-Paket rhythmbox-plugin-zeitgeist
installieren. Für Chrome oder
Firefox existieren weitere Plugins, für User, die ihre Webaktivitäten
mitverfolgen wollen.
Abbildung 3: Die Utility zeitgeist-explorer zeigt eintrudelnde Ereignisse in Echtzeit. |
Die nachfolgend vorgestellten Perlskripts wühlen nach Herzenslust in den
gesammelten Daten herum.
Um zum Beispiel festzustellen, welche neuen Fotos der User zuletzt angesehen
hat, liest Listing 1 die Tabelle uri
der SQLite-Datenbank
activity.sqlite
im Pfad .local/share/zeitgeist
unter dem
Home-Verzeichnis des Users aus. Die SQL-Abfrage SELECT * from uri
listet
einfach alle Datenreihen aus der Tabelle uri
auf, in denen Zeitgeist
neu gefundenen Pfaden oder URLs Indexnummern zuweist. Das CPAN-Modul
DBD::SQLite enthält nicht nur den Perl-Wrapper zum Ansteuern von
SQLite-Datenbanken, sondern auch den C-Code des SQLite-Projekts selbst.
Wer das Ubuntu-Paket sqlite3
installiert hat, kann mit dem
Kommando
$ sqlite3 activity.sqlite .schema
im Verzeichnis der Zeitgeistdatenbank erst einmal nachsehen, welche Tabellen in der Datenbank liegen und später mit interaktiv abgesetzten SQL-Kommandos deren Inhalt erforschen.
Abbildung 4: Der Zeitgeist-Dämon speichert alle angesehenen Abbildungen in einer SQLite-Datenbank. |
01 #!/usr/local/bin/perl -w 02 use strict; 03 use DBD::SQLite; 04 05 my( $home ) = glob "~"; 06 my $dbfile = "$home/.local/share/" . 07 "zeitgeist/activity.sqlite"; 08 my $dbh = DBI->connect( 09 "dbi:SQLite:dbname=$dbfile", "", "" ); 10 11 my $sth = $dbh->prepare( 12 "SELECT * from uri" ); 13 $sth->execute(); 14 15 while( my( $id, $uri ) = 16 $sth->fetchrow_array() ) { 17 print "$uri\n"; 18 }
Listing 1 nutzt die Methode prepare()
des DBI-Moduls mit dem geladenen
SQLite-Driver, um das SQL-Kommando vorzubereiten, und execute()
auf das
zurückkommende Statement-Handle, um es dem SQLite-Engine zu übermitteln.
Wie in DBI üblich, schnappen sich wiederholte Aufrufe von fetchrow_array()
die vom Datenbank-Engine zurückkommenden Ergebnisreihen, deren jede
(im Fall der Tabelle uri
)
zwei Spaltenwerte, nämlich id
und value
enthalten, die das
Skript zwei gleichnamigen
Perl-Variablen zuweist. Abbildung 4 zeigt, dass von Listing 1 nicht nur
Foto-Pfade zurückkommen, sondern auch seltsam anmutende URLs wie
application://firefox.desktop
, die von Events zeugen, bei denen der User
bestimmte Applikationen, wie zum Beispiel hier den Firefox-Browser,
aufgerufen hat.
Abbildung 5: Die Tabelle event enthält protokollierte Aktionen mit Zeitstempel und Referenzen auf Daten in anderen Tabellen. |
Die Datenbanktabelle event
hingegen speichert die zeitliche Abfolge
der vom Zeitgeist-Dämon aufgeschnappten Ereignisse.
Der SQL-Query in Abbildung 5 zeigt einige Beispiel-Records von
Events mit zugeordneten Zeitstempeln, die mit numerischen Schlüsseln
auf andere Tabellen verweisen wie zum Beispiel uri
, in denen dann
der Schlüssel einem Texteintrag zugeordnet ist.
Das Zeitstempelformat
der Spalte timestamp
ist allerdings nicht ein direkt von SQLite
unterstütztes, sondern die Unixzeit seit 1970 in Sekunden gefolgt von
drei weiteren Ziffern, die tausenstel Sekunden anzeigen. Listing 2
extrahiert aus der Tabelle event
die Desktop-Aktionen der letzten
12 Stunden und muss das Zeitgeist-Datum im abgesetzten SQL mit
date(substr(timestamp,1,10),'unixepoch')
in ein SQLite-Datum umwandeln, bevor es den Zeitstempel mit dem im
SQLite-Dialekt praktischerweise mittels date('now', '-12 hours')
verfügbaren
Zeitfenster vergleichen kann.
01 #!/usr/local/bin/perl -w 02 use strict; 03 use DBD::SQLite; 04 05 my( $home ) = glob "~"; 06 my $dbfile = "$home/.local/share/" . 07 "zeitgeist/activity.sqlite"; 08 my $dbh = DBI->connect( 09 "dbi:SQLite:dbname=$dbfile", "", "" ); 10 11 my $sth = $dbh->prepare( 12 "select uri.value from event,uri where 13 subj_id = uri.id and interpretation = 1 14 AND 15 date(substr(timestamp,1,10),'unixepoch') 16 >= date('now', '-12 hours')" ); 17 $sth->execute(); 18 19 my %apps = (); 20 21 while( my( $uri ) = 22 $sth->fetchrow_array() ) { 23 if( $uri =~ /^application:/ ) { 24 $apps{ $uri }++; 25 } 26 } 27 28 for my $app ( sort 29 { $apps{ $b } <=> $apps{ $a } } 30 keys %apps ) { 31 print "$app: $apps{ $app }\n"; 32 }
Abbildung 6: Wie oft hat der User welche Applikation in den letzten 6 Stunden aufgerufen? |
Die while
-Schleife ab Zeile 21 arbeitet
alle gefundenen Ergeignis-Records ab, und kumuliert Applikations-URLs im
Hash %apps
auf. Die for
-Schleife ab Zeile 28 gruppiert die
Einträge dann absteigend nach Zählerstand und gibt das Ergebnis aus.
Abbildung 6 zeigt die Ausgabe von Listing 2, und
es stellt sich heraus, dass ich in der untersuchten Zeitspanne 18 mal ein
XTerm-Fenster geöffnet und insgesamt sechs Screenshots gezogen habe.
Wie fleißig war der User in den letzten 24 Stunden, hat er auch ordentlich
auf dem Desktop herumgeklickt? Das findet Listing 3 heraus, in dem es
die Activity-Tabelle event
nach Einträgen des vergangenen Tages durchforstet,
und pro laufender Stunde jedes Mal einen Zähler hochzählt, falls ein
Desktop-Ereignis in diesen Zeitrahmen fällt.
01 #!/usr/local/bin/perl -w 02 use strict; 03 use DBD::SQLite; 04 use DateTime::Format::SQLite; 05 06 my( $home ) = glob "~"; 07 my $dbfile = "$home/.local/share/" . 08 "zeitgeist/activity.sqlite"; 09 my $dbh = DBI->connect( 10 "dbi:SQLite:dbname=$dbfile", "", "" ); 11 12 my $sth = $dbh->prepare( 13 "SELECT 14 datetime(substr(timestamp,1,10), 15 'unixepoch') 16 AS time FROM event WHERE 17 time >= datetime('now', '-24 hours')" ); 18 $sth->execute(); 19 20 my %activity = (); 21 22 while( my( $time ) = 23 $sth->fetchrow_array() ) { 24 25 my $dt = DateTime::Format::SQLite-> 26 parse_datetime( $time ); 27 $dt->set_time_zone( "local" ); 28 $activity{ $dt->hour() }++; 29 } 30 31 my $now = DateTime->now( 32 time_zone => "local" ); 33 my $dt = 34 $now->clone->subtract( days => 1 ); 35 36 while( $dt < $now ) { 37 my $hour = $dt->hour(); 38 printf "%02d:00 %d\n", 39 $hour, $activity{ $hour } || 0; 40 $dt = $dt->add( hours => 1 ); 41 }
Abbildung 7: Ereignisse auf dem Desktop über die letzten 24 Stunden. |
Die SQL-Abfrage ab Zeile 13 holt Ereignisse mit Zeitstempeln der letzten 24 Stunden aus der Datenbank, und das CPAN-Modul DateTime::Format::SQLite wandelt das herauskommende Datumsformat in das von Datums-Tausendsassa DateTime benutzte um.
Damit die aus
dem Zeitstempel extrahierten Stunden nicht die der UTC-Zeitzone sondern
der lokalen entsprechen, setzt Zeile 27 dieselbe mit dem Kennwort "local".
Der Hash %activity
zählt in Zeile 28 jeweils um eins hoch,
falls ein Ereignis in eine bestimmte Stunde fällt und abschließend
braucht die while
-Schleife ab Zeile 36 nur noch vom Datum des
gestrigen Tages bis heute stundenweise hochzuzählen und jeweils die
Anzahl der Ereignisse mit printf
auszudrucken.
Die Ausgabe in Abbildung 7 bestätigt, dass der User nach dem Motto "Am Abend wird der Faule fleißig" nur nach 19 Uhr bis etwa 23 Uhr aktiv war, während unter Tags absolut nichts geschah. Oder war es etwa so, dass der User tagsüber im Büro arbeitete und sich abends und nachts an seinen Heimcomputer setzte, um Artikel fürs Linux-Magazin zu schreiben? Dank Zeitgeist besteht nun Erklärungsbedarf.
Listings zu diesem Artikel: ftp://www.linux-magazin.de/pub/listings/magazin/2015/05/Perl
Zeitgeist-Projekt auf Launchpad: https://launchpad.net/zeitgeist