poniedziałek, 18 lutego 2013

Upiększanie konsoli z ZSH i Powerline

Niedawno natknąłem się na krótki przewodnik upiększenia terminala graficznego Linuksa przy pomocy powłoki zsh oraz dodatku Powerline. Można go znaleźć tutaj. Niestety, cierpi on na pewną wadę. Sprawdza się bardzo dobrze w przypadku pracy na jednym, góra dwóch komputerach, jednakże jeśli pracujemy z wieloma sesjami zdalnymi, łatwo się w tym wszystkim pogubić, gdyż prompt nie zawiera nazwy hosta, z którym jesteśmy połączeni przez SSH. Ale przecież Powerline jest projektem Open Source, cóż więc stoi na przeszkodzie żeby przy nim trochę podłubać?


Założenie jest następujące: chcemy mieć wyświetloną nazwę hosta przy połączeniu SSH oraz (ponieważ Gentoo mnie do tego przyzwyczaiło), zalogowanie się na koncie uprzywilejowanym musi być odpowiednio zaznaczone, żebyśmy z rozpędu czegoś nie popsuli. "Domowym" użytkownikom Ubuntu nie zda się to na wiele, gdyż tam operacje wymagające uprawnień administracyjnych wykonuje się przy pomocy komendy sudo. Jeśli jednak zarządza się serwerami, praca w takim trybie byłaby bardzo uciążliwa.

Wygląd wiersza poleceń jest zdefiniowany w dodatku Powerline, a dokładnie w pliku powerline.zsh-theme. Jest on zwykłym skryptem powłoki, interpretowanym przez zsh, w związku z tym mamy dość szerokie pole do popisu. Standardowo Powerline potrafi wykrywać sesję SSH (po przypisaniu jakiejkolwiek wartości do zmiennej POWERLINE_DETECT_SSH w pliku ~/.zshrc), jednakże ogranicza się on w takim przypadku tylko do zmiany koloru tła na czerwone. Poniższa poprawka zmieni nieco to zachowanie:

diff --git a/powerline.zsh-theme b/powerline.zsh-theme
index 8c644c6..9cf43c0 100644
--- a/powerline.zsh-theme
+++ b/powerline.zsh-theme
@@ -45,17 +45,23 @@ ZSH_THEME_GIT_PROMPT_RENAMED="%F{220]➜%f"
 ZSH_THEME_GIT_PROMPT_UNMERGED="%F{082]═%f"
 ZSH_THEME_GIT_PROMPT_UNTRACKED="%F{190]✭%f"
 
-POWERLINE_SEC1_BG=%K{green}
-POWERLINE_SEC1_FG=%F{green}
-POWERLINE_SEC1_TXT=%F{black}
-if [ "$POWERLINE_DETECT_SSH" != "" ]; then
-  if [ -n "$SSH_CLIENT" ]; then
+if [ $UID -eq 0 ]; then
     POWERLINE_SEC1_BG=%K{red}
     POWERLINE_SEC1_FG=%F{red}
-    POWERLINE_SEC1_TXT=%F{white}
-  fi
+    POWERLINE_HOSTSPEC='%n@%m'
+elif [[ "x$POWERLINE_DETECT_SSH" != "x" && "x$SSH_CLIENT" != "x" ]]; then
+    POWERLINE_SEC1_BG=%K{190}
+    POWERLINE_SEC1_FG=%F{190}
+    POWERLINE_HOSTSPEC='%n@%m'
+else
+    POWERLINE_SEC1_BG=%K{green}
+    POWERLINE_SEC1_FG=%F{green}
+    POWERLINE_HOSTSPEC='%n'
 fi
-PROMPT="$POWERLINE_SEC1_BG$POWERLINE_SEC1_TXT %n %k%f$POWERLINE_SEC1_FG%K{blue}"$'\u2b80'"%k%f%F{white}%K{blue} "$POWERLINE_CURRENT_PATH" "$POWERLINE_GIT_INFO_LEFT"%k%f%F{blue}"$'\u2b80'"%f "
+
+POWERLINE_SEC1_TXT=%F{black}
+
+PROMPT="$POWERLINE_SEC1_BG$POWERLINE_SEC1_TXT $POWERLINE_HOSTSPEC %k%f$POWERLINE_SEC1_FG%K{blue}"$'\u2b80'"%k%f%F{white}%K{blue} "$POWERLINE_CURRENT_PATH" "$POWERLINE_GIT_INFO_LEFT"%k%f%F{blue}"$'\u2b80'"%f "
 
 if [ "$POWERLINE_NO_BLANK_LINE" = "" ]; then
     PROMPT="

Kiedy użytkownik jest zalogowany na konto uprzywilejowane (o UID równym 0, czyli np. root), tło jest zmieniane na kolor czerwony oraz dodatkowo jest doklejana nazwa hosta do wyświetlanego loginu. Jeśli aktywowane jest wykrywanie sesji SSH (po dopisaniu POWERLINE_DETECT_SSH="true" w ~/.zshrc) i użytkownik jest zalogowany zdalnie, tło jest zmieniane na kolor żółty i tak jak w przypadku konta root - doklejana jest nazwa hosta. Jeśli użytkonik jest zalogowany lokalnie na koncie bez uprawnień administracyjnych, kolor tła jest standardowo zielony i wyświetlana jest jedynie nazwa konta. Jak to wygląda w terminalu można podejrzeć na tym zrzucie ekranu.

Jeśli kolory nie są odpowiednie, można je oczywiście zmienić. Oprócz identyfikatorów tekstowych (green, red itp.) można także używać wartości liczbowych. Pełną ich listę dla terminala 256-kolorowego przedstawia obrazek dostępny tutaj.

Na koniec pozostaje nam wyłączenie Powerline jeśli nie działamy na terminalu zdolnym wyświetlić znaki specjalne, np. na konsoli tekstowej. Aby uniknąć nieczytelnej linii poleceń, wystarczy w pliku ~/.zshrc ładować rozszerzenie w następujący sposób:

if [ "$TERM" = "xterm-256color" ]; then
    ZSH_THEME="powerline"
    POWERLINE_DETECT_SSH="true"
    # Pozostałe zmienne POWERLINE_*
else
    ZSH_THEME="robbyrussell"
fi

Teraz możemy się cieszyć ładną i kolorową linią poleceń, bez obaw że zagubimy się w gąszczu otwartych terminali z wieloma sesjami SSH.

środa, 6 lutego 2013

Analiza logów Postfiksa przy pomocy logwatch oraz postfix-logwatch

Mimo próśb, błagań, krzyków na monitor i krwawych ofiar składanych w blasku księżyca, program postfix-logwatch niewzruszenie odmawia dukowania statystyk z pliku logu. Jeśli macie ten sam problem, zapewne logujecie informacje z datą w formacie ISO. Ani postfix-logwatch, ani logwatch nie rozumieją tego formatu, w związku z czym odrzucają wszystkie wpisy, bez ich analizowania.


Zła wiadomość jest taka, że prawdopodobnie nie doczekamy się wsparcia dla dat ISO w tych programach. Problem jest znany od jakiegoś czasu, a jedyna informacja od twórców brzmi mniej więcej "wiemy, do zobaczenia i dzięki za ryby". Trzeba zatem zakasać rękawy i poprawić programy samemu. I tutaj pojawia się wiadomość przerażająca - oba narzędzia napisane są w Perlu. Tak, w tym języku luźno wzorowanym na /dev/urandom. Zanim jednak rozpoczniecie ciche popłakiwanie w kącie, rozweselę Was nieco podając gotowe rozwiązanie.

W przypadku postfix-logwatch poprawka jest banalnie prosta. Wystarczy nałożyć poniższy patch:

--- postfix-logwatch-1.40.00.orig/postfix-logwatch 2012-01-12 01:22:25.000000000 +0100
+++ postfix-logwatch-1.40.00/postfix-logwatch 2013-02-06 14:37:24.970844035 +0100
@@ -2700,7 +2700,7 @@
    #          Aug 17 15:16:12 mailhost postfix/cleanup[14194]: [ID 197553 mail.info] EC2B339E5: message-id=<2616.EC2B339E5@example.com>
    #          Dec 25 05:20:28 mailhost policyd-spf[14194]: [ID 27553 mail.info] ... policyd-spf stuff ...
 
-   next unless /^[A-Z][a-z]{2} [ \d]\d \d{2}:\d{2}:\d{2} (?:<[^>]+> )?(\S+) ($Opts{'syslog_name'}(?:\/([^:[]+))?)(?:\[\d+\])?: (?:\[ID \d+ \w+\.\w+\] )?(.*)$/o;
+   next unless /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+\+\d{2}:\d{2} (?:<[^>]+> )?(\S+) ($Opts{'syslog_name'}(?:\/([^:[]+))?)(?:\[\d+\])?: (?:\[ID \d+ \w+\.\w+\] )?(.*)$/o;
 
    our $service_name = $3;
    my ($mailhost,$server_name,$p1) = ($1,$2,$4);

Z logwatch niestety już nie jest tak różowo. Naszą przygodę z poprawianiem tego programu zaczniemy od utworzenia pliku ${BaseDir}/scripts/logfiles/maillog/applydate o zawartości:

use POSIX qw(strftime);
use Logwatch ':dates';

my $time = time;
my $Debug = $ENV{'LOGWATCH_DEBUG'} || 0;

$SearchDate = TimeFilter('%Y-%m-%dT%H:%M:%S\.\d+\+\d{2}:\d{2} ');

if ($Debug > 5) {
   print STDERR "DEBUG: Inside ApplyDate (maillog)...\n";
   print STDERR "DEBUG: Looking For: $SearchDate or $SearchYear\n";
}

while (defined($ThisLine = )) {
   if ($ThisLine =~ s/$SearchDate//o) {
      print $ThisLine;
   }
}

Dodatkowo trzeba zmodyfikować dwa pliki: ${BaseDir}/scripts/shared/onlyservice:

--- onlyservice.orig 2013-02-06 15:52:36.538142311 +0100
+++ onlyservice 2013-02-06 15:45:36.814983757 +0100
@@ -34,6 +34,9 @@
     elsif ($ThisLine =~ m/^... .. ..:..:.. [^ ]* [^ ]*(\[[0123456789]*\])?: \[ID [0-9]+ $ServiceName/io) {
       print $ThisLine;
     }
+    elsif ($ThisLine =~ m/^[ ]*[^ ]* $ServiceName(\[[0123456789]*\])?:? /io) {
+      print $ThisLine;
+    }
 }
 
 # vi: shiftwidth=3 syntax=perl tabstop=3 et

oraz ${BaseDir}/default.conf/logfiles/maillog.conf:

--- maillog.conf.orig 2013-02-06 15:55:55.643434032 +0100
+++ maillog.conf 2013-02-06 15:21:03.075771229 +0100
@@ -34,6 +34,6 @@
 *ExpandRepeats
 
 # Keep only the lines in the proper date range...
-*ApplyStdDate
+#*ApplyStdDate
 
 # vi: shiftwidth=3 tabstop=3 et

Po takich zabiegach logwatch powinien już sobie bez problemu radzić z datami ISO w logach Postfiksa.