Archiv der Kategorie: Linux

String in der Shell in Groß- oder Kleinbuchstaben wandeln

Mitunter will man in einem Shell-Script einen String ist Groß- oder Kleinbuchstaben wandeln. Das lässt sich recht einfach mit dem Kommando tr bewerkstelligen, welches auf vielen Systemen verfügbar ist.

So werden Kleinbuchstaben in Großbuchstaben gewandelt:

VAR=öäüß
echo $VAR
VAR=`echo $VAR|tr "[:lower:]" "[:upper:]"`
echo $VAR

Durch die Verwendung von [:lower:] und [:upper:] werden auch Umlaute berücksichtigt, was bei [a-z] bzw. [A-Z] nicht der Fall wäre.

Die umgekehrte Richtung funktioniert logischer Weise so:

VAR=GROSSÄÜÖ
echo $VAR
VAR=`echo $VAR|tr "[:upper:]" "[:lower:]"`
echo $VAR

Dateien finden, die nicht von allen gelesen werden können

Manchmal muss man die Dateien finden, deren Rechte so gesetzt sind, dass sie nicht von allen gelesen werden können. Mit

find . -type f -not -perm -0004 -ls

findet man alle Dateien im aktuellen Verzeichnis und darunter, deren Worldreadable-Bit nicht gesetzt ist.

Und so macht man sie für alle lesbar:

find . -type f -not -perm -0004 -exec chmod -c a+r {} ;

Das gleiche für Verzeichnisse:

find . -type d -not -perm -0004 -exec chmod -c a+rx {} ;

Linux-Citrix-Client installieren

Neuerdings braucht der Citrix-Client auf Linux die libXm.so.3 – aber wo ist die?

Die libXm gehört zum Paket openMotif, welches sich einfach über Yast installieren lässt. Bei 64-Bit-Systemen muss man leider die 32-Bit-Version von openMotif installieren, da Citrix die Software noch nicht auf 64-Bit portiert hat. Dazu im Yast zusätzlich das Paket openmotif-libs-32bit auswählen.

Allerdings wird hier bei SuSE 10.2 die neuere Version 2.3 installiert, die die libXm.so.4 mitbringt. Das ist aber nicht weiter schlimm, denn man kann als root einen Link anlegen:

ln -s /usr/lib/libXm.so.4.0.0 /usr/lib/libXm.so.3

Danach klappt’s auch mit Citrix 😉

Update 06.12.2013

Heute wollte ich die 64bit-Version des Citrix-Receivers, wie der Client nun heißt, installieren. Klar, dass das nicht „mal eben“ funktioniert:

# rpm -Uhv ICAClient-13.0.0.256735-0.x86_64.rpm
error: Failed dependencies:
        libcurl.so.4 >= 7.19.1 is needed by ICAClient-13.0.0.256735-0.x86_64
        libxerces-c-3.1.so is needed by ICAClient-13.0.0.256735-0.x86_64
        libwebkitgtk-1.0.so.0 is needed by ICAClient-13.0.0.256735-0.x86_64

Ein Blick in die Paketverwaltung zeigte aber, dass alle Bibliotheken installiert sind – in der 64bit-Version, logischerweise. Sollte etwa der ICA-Client, obwohl als 64bit deklariert, wieder die 32bit-Versionen der Bibliotheken haben wollen? Ja, genauso war’s. Und bei libwebkitgtk musste es auch die 1er Version sein, die 3er half nicht.

Die libxerces gibt es bei SuSE nicht in der Paketverwaltung, aber in einem Post im OpenSuSE-Forum fand sich ein passender Link.

Speed und Duplex eines Interfaces beim Booten einstellen

Manchmal muss ein Netzwerk-Interface fest auf eine Geschwindigkeit und/oder Duplex-Verfahren eingestellt werden, weil die Autonegoation nicht funktioniert.

Grundsätzlich ist die Autonegoation, also die selbstständige, optimale Einstellung zweier verbundener Netzwerkports eine gute Sache – wenn sie funktioniert und sie auch bei beiden Ports eingeschaltet ist (siehe auch „Duplex Mismatch und Autonegotiation im Ethernet„). Wenn nicht, müssen die Ports manuell konfiguriert werden. Man kann diese Einstellungen mit dem Werkzeug ethtool vornehmen, aber beim Booten des Rechners wird das Interface wieder auf Auto stehen.

Leider gibt es kein einfachen Konfigurationsparameter (z.B. in der Interfacekonfiguration unter /etc/sysconfig/network), der das bewerkstelligt. Damit diese Einstellungen auch beim Booten wirksam werden muss bei SuSE 10.x ein Script angelegt werden, welches aufgerufen wird, wenn das Interface gestartet wurde. Dies ist ein sehr universelles Konzept, welches auch viele andere Möglichkeiten bietet. Grundsätzlich gibt es zwei Möglichkeiten, ein Script beim Starten ausführen zu lassen:

  • Ein Script in /etc/sysconfig/network/if-up.d
  • Ein Script mit der Variablen POST_UP_SCRIPT in der Interfacekonfigurationsdatei /etc/sysconfig/network/ifcfg-* angeben

Bei der ersten Möglichkeit wird das Script bei jedem Interface ausgeführt, man muss daher die übergebenen Parameter analysieren und ein Fehler im Script kann die gesamte Netzkonfiguration des Rechners durcheinander bringen.

Ich verwende daher die zweite Möglichkeit, bei dem ein spezielles Script für das gewünschte Interface erstellt wird. Man kann aber auch ein universelles Script erstellen, welches die übergebenen Parameter analysieren. Da es aber nur für die Interface ausgeführt wird, die dieses Script auch in POST_UP_SCRIPT stehen haben, ist das sicherer als die erste Methode. Wer mehr zu den beiden Verfahren wissen möchte, dem seinen die Man-Pages zu ifup und ifcfg empfohlen.

Ich habe nun also in der Konfigurtationsdatei für die betreffende Netzwerkkarte die folgende Zeile hinzugefügt:

POST_UP_SCRIPT='post_up_eth2'

Das Script sieht dann so aus:

#!/bin/sh
# Stellt das Interface auf 100 Full ein
#echo $0 $1 $2 $3 $4 $5
CONF_NAME=$1
HW_NAME=$2

ethtool -s $HW_NAME autoneg off speed 100 duplex full

ethtool $HW_NAME

Wird in der Konfigdatei kein Pfad mit angegeben, so wird das Script in /etc/sysconfig/network/scripts erwartet.

Dateinamen mit Leerzeichen auf der Kommandozeile verarbeiten

Dateinamen, die Leerzeichen enthalten, machen auf der Kommandozeile oder in Scripten häufig Ärger, da das Leerzeichen meist als Trennzeichen zwischen den Parametern dient. hier nun ein paar Tricks, wie man die Probleme umschiffen kann.

Will man beispielsweise untersuchen, wieviel Platz die Dateien und Unterverzeichnisse des aktuellen Verzeichnisses jeweils einnehmen, kann man das mit

ls -A -1 | du -sh

machen. Aber das funktioniert nur für Dateinamen und Verzeichnisse, die keine Leerzeichen enthalten. Um auch solche zu verarbeiten muss man die folgende Kommandokette verwenden:

find . -maxdepth 1 -print0 | xargs -0 du -sh

Wir benutzen hier find, weil dieses die gefunden Namen durch das Argument -print0 als sog. nullterminierten String ausggeben kann. Dies teilen wir xargs mit dem Schalter -0 mit, wodurch xargs automatisch alle Leerzeichen etc. escaped an das eigentliche Kommando „du -sh“ weitergibt. Damit find nicht auf die Idee kommt, auch die Unterverzeichnisse zu durchsuchen, begrenzen wir die Suche mit „-maxdepth 1“ auf das angegebene Verzeichnis.

Unter Linux (bzw. mit GNU-ls) kann man auch die Option -Q des ls Kommandos nutzen, welches die Namen in Anführungszeichen setzt. Xargs ist aber trotzdem erforderlich:

/bin/ls -A1Q | xargs du -sh

[Wird fortgesetzt…]

Interface-Name bei SuSE festlegen

Hat man in einem Rechner mehrere Netzwerkkarten möchte man eigentlich, das die Karte, die heute eth1 ist das auch nach dem nächsten booten ist. Durch die automatische Hardware-Erkennung ist das aber leider nicht immer der Fall, was gerade bei einer Firewall recht störend sein kann.

Liesen sich diese Einstellungen früher in der Netzwerkkonfiguration unter /etc/sysconfig/network eintragen, so muss man diese bei SuSE 10.0 ganz wo anders einstellen.

Um die Namen für die Interfaces festzulegen müssen diese in der Datei
/etc/udev/rules.d/30-net_persistent_names.rules
eingetragen werden.
Normalerweise trägt Yast bei der Konfiguration alle Karten dort ein. Ein solcher Eintrag sieht beispielsweise so aus:

SUBSYSTEM=="net", ACTION=="add", SYSFS{address}=="00:50:ba:5e:17:4b", IMPORT="/sbin/rename_netiface %k eth0"

Hier kann dann einfach ein anderer Bezeichner gewählt werden oder die Reihenfolge angepasst werden.

Weitere Infos finden sich auch in der Datei /usr/share/doc/packages/sysconfig/README.Persistent_Interface_Names.

Bei SuSE 11.1 heißt die Datei 70-persistent-net.rules und die Einträge sehen so aus:

SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="00:1c:23:88:3f:63", ATTR{type}=="1", KERNEL=="eth*", NAME="eth1"

Die o.g. README gibt es leider nicht mehr, zu udev (und auch zu den Netzwerk-Interfacen) gibt es /usr/share/doc/packages/udev/writing_udev_rules/index.html.

Es gibt wohl auch ein Tool namens nameif, aber das funktioniert bei mir nicht (keine /etc/mactab).

Meldungen auf dem Linux-Desktop ausgeben

Wenn man aus einem Script heraus eine Meldung auf dem Linux-Desktop ausgeben will, hat man dazu verschiedene Möglichkeiten. Einige stelle ich hier vor.

Ich gehe hier mal davon aus, das KDE eingesetzt wird, da ich mit Gnome keine Erfahrung habe. Die ein oder andere Lösung mag aber u.U. auch mit Gnome funktionieren.

Ausgabeumleitung in ein Fenster

Mit KDE kann man einfach ein

echo huhu > /dev/pts/0

machen. Der KWrited Daemon lauscht auf diesem Device und stellt den Text in einem Fenster dar. Das funktioniert auch, wenn das Script nicht unter dem Benutzer läuft, der das Display besitzt. Weitere Ausgaben, auch von anderen Scripten werden an die Ausgabe angehängt, es geht also immer nur ein Fenster auf. (Wie man den Inhalt löscht o.ä. habe ich noch nicht herausgefunden).

Es ist aber nicht immer /dev/pts/0, es kann auch /dev/pts/3 o.ä. sein. Auf welchem Device der Daemon lauscht kann mit

lsof | grep kded4 | grep '/dev/pts/'

ermittelt werden.

Xmessage

Mit Xmessage lassen sich einzelne Meldungen anzeigen, die erst nach dem Klick auf den OK-Button verschwinden. Man kann aber auch mit -timeout eine Zeit in Sekunden angeben, nach der die Meldung automatisch verschwinden soll. Mit -center wird die Nachricht in der Bildschirmmitte ausgegeben, die Größe lässt sich mit -geometry BreitexHöhe (z.B. -geometry 500×100) angeben. Auch die Buttons können beeinflusst werden (siehe man 1 xmessage)

KDialog

Die schönsten Meldungen und sogar richtige Dialoge mit Eingabefeldern lassen sich mit kdialog erstellen.

Eine einfache Meldung erscheint mit

kdialog --msgbox "Danke, Sie haben eine einfache Dialogbox sehr glücklich gemacht"

Um die Meldung auf allen Arbeitsflächen des Desktop erscheinen zu lassen muss man kstart zu Hilfe nehmen:

kstart --alldesktops --window Information \nkdialog --msgbox "Danke, Sie haben eine einfache Dialogbox sehr glücklich gemacht"

Leider gibt es anscheinend zu kstart außer dem was mit kstart --help angezeigt wird keine weitere Dokumentation. Zu kdialog gibt es immerhin ein deutschsprachiges Tutorial unter techbase.kde.org/Development/Tutorials/Shell_Scripting_with_KDE_Dialogs_%28de%29.

Und sonst

Es gibt noch weitere Tool für die X-Ausgabe, hier sind einige kurz vorgestellt:

xmore
Zeigt den Inhalt einer Datei an. Kann anscheinden nichts mit STDIN anfangen.
gmessage
AFAIK ein xmessage-Clone für Gnome.
perl-tk
Für Perl gibt es eine Tk-Erweiterung, mit der man „richtige“ Programme bauen kann.

Links

Zwei verschiedene MySQL-Server auf einem Rechner

Wie bringt man einen 4.1-Server und einen 5.0-Server auf einem Linux-Rechner gleichzeitig ans laufen? Diese Frage stellte sich mir kürzlich, da ich verschiedene Intra- und Extra-Websites betreue und hier gibts bei einer nun auch Mysql 5.0. Die anderen laufen aber noch mit der ‚alten‘ Version 4.1

Nach einiger Recherche fand ich zwar eine Anleitung von Kristian Köhntopp, wie man viele verschiedenen MySQL Instanzen auf einem Server recht komfortabel zum Laufen bringt, aber ich wollte nur eine zusätzliche Instanz und hatte auch keine Lust, den Server selbst zu kompilieren (was von MySQL AB auch nicht wirklich empfohlen wird). So suchte ich nach einer Lösung, das Ganze mit den von MySQL bereitgestellten RPMs zu machen.

Zzt. ist auf meinem Linux-Rechner die Version 4.1 installiert (von den SuSE-10.0 CDs) und läuft.

Zwei Dinge mussten bewerkstelligt werden:

  1. Die Installation des zweiten Servers muss in andere Verzeichnisse erfolgen, als im RPM-Paket vorgesehen.
  2. Der 5.0er Server muss einen anderen TCP-Port und einen anderen Socket benutzen. Dazu muss eine eigene my.cnf benutzt werden.

Punkt 1 kann mit der relocate-Funktion von RPM bewerkstelligt werden. Zusätzlich mussten noch ein paar Dateien gepatcht werden, damit der Server auch die neuen Verzeichnisse findet. Doch dazu später mehr.

Die Sachen mit den Config-Dateien ist relativ einfach, der Server (und auch der Client) sucht sie entweder in /etc, im sog. Datadir (normalerweise /var/lib/mysql) oder als .my.cnf im Home-Verzeichnis des Users. Wenn also in /etc nichts liegt aber im jeweiligen Datadir (was ja unbedingt unterschiedlich sein muss), findet jeder Server automatisch die richtige Config-Datei. Na ja, so ganz automatisch nicht, dazu muss ein wenig an den Start-Scripten geschraubt werden. Doch zunächst mal zur

Installation.

Man besorge sich die aktuellen RPMs bei www.mysql.com, bei mir waren es

  • MySQL-server-5.0.21-0.glibc23.i386.rpm
  • MySQL-Max-5.0.21-0.glibc23.i386.rpm
  • MySQL-client-5.0.21-0.glibc23.i386.rpm

Wer die speziellen Funktionalitäten des Max-Servers nicht braucht lässt ihn weg. Ebenso kann auch auf das Client-Paket verzichtet werden, aber wo man gerade schon mal dabei ist …

Als Nächstes sollte man die bestehenden Datenbanken sichern, man weiß ja nie 😎

Dazu wird erstmal der laufenden Server gestoppt:

rcmysql stop

Danach die Datenbanken sichern:

cd /var/lib
cp -prv mysql mysql41

Die Config-Datei und das Start-Script sollten auch vorsichtshalber in Sicherheit gebracht werden:

cp -pv /etc/my.cnf /etc/my41.cnf
cp -pv /etc/init.d/mysql /etc/init.d/mysql41

Nun die Verzeichnisse anlegen:

mkdir -p /usr/local/mysql50 /var/lib/mysql50

Wir sehen, das Installationsverzeichnis soll /usr/local/mysql50 sein und das Datadir, in dem später u.a. die Datenbank-Dateien liegen, soll /var/lib/mysql50 sein.

Entsprechend werden die Installationspfade per –relocate=<alt>=<neu> verbogen (siehe man rpm).
Mit –test macht man erstmal ein Testlauf:

rpm -vv --test
  --relocate=/usr/share/mysql/=/usr/local/mysql50/share/mysql/
  --relocate=/usr/share/man/=/usr/local/mysql50/man/
  --relocate=/usr/share/info/=/usr/local/mysql50/info/
  --relocate=/usr/sbin/=/usr/local/mysql50/sbin/
  --relocate=/usr/lib/mysql/=/usr/local/mysql50/lib/
  --relocate=/usr/bin/=/usr/local/mysql50/bin/
  --relocate=/etc/=/usr/local/mysql50/etc/
    -ih MySQL-server-5.0.21-0.glibc23.i386.rpm

Wenn das soweit gut aussieht kann man den Test-Schalter weglassen und installieren.
Nach dem Server kann man nun auf die gleiche Art das Max- und das Client-Paket installieren, so man will.

Konfiguration und Einstellung

Nun sollte man eine Konfigdatei erstellen und nach /var/lib/mysql50 legen. Wichtig ist, dass sich die Angaben für Port und Socket von denen der 4.1er Konfigdatei unterscheiden.
Desweiteren ganz wichtig: in der my.cnf für den 5.0er Server muss die folgenden Zeile beim Abschnitt [mysqld] rein:

language = /usr/local/mysql50/share/mysql/english

Sonst sucht der Server diese Datei in /usr/share/mysql/english/errmsg.sys und findet logischerweise die der 4.1er-Version, was nicht passt (im error-log steht dann: „Error message file ‚/usr/share/mysql/english/errmsg.sys‘ had only 304 error messages, but it should contain at least 463 error messages.“)
Meine Konfig-Datei sieht so aus: my.cnf

Die alte my.cnf aus /etc wird nach /var/lib/mysql verschoben, damit jeder Server seine eigene Datei benutzt. In /etc darf keine mehr sein!

Nun das Startscript ins richtige Verzeichnis kopieren und umbenennen:

cp /usr/local/mysql50/etc/init.d/mysql /etc/init.d/mysql50

In dieser Datei müssen basedir und datadir entspr. gesetzt werden:
basedir=/usr/local/mysql50
datadir=/var/lib/mysql50

Dann muss ein wenig gebastelt werden, denn das Hilfs-Startscript mysqld-safe in /usr/local/mysql50/bin interessiert sich nicht richtig für die vom eigentlichen Startscript übergebenen Werte sondern versucht selbst basedir und datadir zu ermitteln und findet, da dort die falschen Vorgaben drin stehen, dabei die falschen Pfade. Dadurch wird schlussendlich die falsche my.cnf gefunden und somit die falschen Vorgaben verwendet (bei SuSEs mysqld-safe sind andere defaults eingebaut). Ich habe das korrigiert:

116c116
< if test -f ./share/mysql/english/errmsg.sys -a -x ./bin/mysqld
---
> if test -f ./share/mysql/english/errmsg.sys -a -x ./sbin/mysqld
119c119
<   ledir=$MY_BASEDIR_VERSION/bin               # Where mysqld is
---
>   ledir=$MY_BASEDIR_VERSION/sbin      # Where mysqld is
150c150
<   DATADIR=/var/lib/mysql
---
>   DATADIR=/var/lib/mysql50

Zum Einspielen des Patches benennt man mysqld_safe in mysqld_safe-orig um und verwendet das Programm patch zum editieren:

patch -o mysqld_safe mysqld_safe-orig mysqld_safe.patch

… 3 – 2 – 1 – Start

Nun sollte sich der Server mit

/etc/init.d/mysql50 start

zum Leben erwecken lassen. Mit netstat -ltn kann man prüfen, ob der Server auf dem neuen Port lauscht.

Dem Client muss man nun natürlich sagen, mit welchem Server er in Verbindung treten soll:

mysql -S /var/lib/mysql50/mysql.sock -u root -p

Dabei wird der alte Client der SuSE-Installation verwendet. Um den neuen Client zu verwenden, muss man ihn mit vollen Pfad ansprechen:

/usr/local/mysql50/bin/mysql -S /var/lib/mysql50/mysql.sock -u root -p

Klappt natürlich nur, wenn man ihn wie oben angegeben installiert hat.

Man kann das Ganze aber mit Hilfe der entspr. cnf-Datei machen:

/usr/local/mysql50/bin/mysql --defaults-file=/var/lib/mysql50/my.cnf -u root -p mysql

Bei PHP muss man entweder in der php.ini den entsprechenden Socket angeben oder die PHP-Variable mysql.default_socket bzw. mysql.default_port in der Apache-Konfig oder per htaccess definieren. Natürlich kann man auch den Port oder Socket beim Aufruf von mysql_connect() an dem Hostnamen anhängen. Man sollte bei der Verwendung von Port aber beachten, das man „127.0.0.1“ statt „localhost“ benutzt, mysql_connect zieht sonst u.U. den (falschen) Socket vor. Ich empfehle die Lektüre der entsprechenden PHP-Doku.

So, und nun wünsche ich viel Spaß beim nachbasteln…

Verzeichnisse auf einen anderen Rechner übertragen

Im vorigen Artikel habe ich gezeigt, wie man mit TAR ein komplettes Verzeichnis so verschieben bzw. kopieren kann, dass alle Rechte erhalten bleiben. Hier kommt nun die erweiterte Fassung zum kopieren von Verzeichnissen auf einen anderen Rechner mittels SSH und TAR. Und als Bonbon gibt’s ein Script zum Erstellen von Backups von anderen Rechnern.

Um das Verzeichnis foo vom eigenen Rechner auf dem Rechner kermit als Unterverzeichnis von /bar zu speichern benutzt man den folgenden Befehl:

tar cpSf - foo | ssh kermit "cd /bar && tar xpSvf -"

Wie man sieht, ähnelt der Aufruf sehr dem Befehl zum kopieren auf dem lokalen Rechner, lediglich das „ssh kermit“ ist neu (und die Klammern brauchen wir hier nicht). Ich gehe hier davon aus, dass der User des lokalen Rechners auch auf dem Rechner kermit bekannt ist. Soll auf kermit ein anderer Account, z.B. klaus, verwendet werden, schreibt man einfach „ssh klaus@kermit“. Der User muss auf jeden Fall ausreichende Rechte im Zielverzeichnis haben. Da ein Ziel ist, die Rechte beizubehalten, sollte das root sein.

Man kann tar die Daten auch komprimieren lassen und bei geringer Bandbreite das Ganze somit beschleunigen:

tar zcpSf - foo | ssh kermit "cd /bar && tar zxpSvf -"

Man kann das Ganze auch umkehren, also Verzeichnisse des entfernten Rechners auf den lokalen kopieren:

ssh kermit tar cSpf - /foo | (cd /bar && tar xpSvf -)

kopiert das Verzeichnis /foo von kermit auf dem lokalen Rechner ins Verzeichnis /bar.

Man kann dem ersten tar-Befehl übrigens auch mehrere Verzeichnisse übergeben. Das eignet sich hervoragend für ein einfaches Backup, insbesondere zusammen mit gzip:

ssh kermit tar cSpf - /foo /bar | gzip -f9 > kermit_backup.tgz

Das sichert die Verzeichnisse /foo und /bar des Rechners kermit als komprimiertes Tar-Archiv in der Datei kermit_backup.tgz auf dem Lokalen Rechner.

Ich mache so täglich Sicherungen von wichtigen Verzeichnissen meiner Server.

Auch hier kann man tar die Komprimierung übernehmen lassen:

ssh kermit tar zcSpf - /foo /bar > kermit_backup.tgz

Diese Variante ist schneller, die andere komprimiert dafür geringfügig besser.