tuxad blog
Syndicate
Site (RSS, Atom)
Contact
Weblog status
Total entries: 25
Last entry: 2012-07-17 23:05:08
Last updated: 2012-07-17 23:36:02
powered by vim, bash, cat, grep, sed, and nb 3.4.2

2012-07-17 23:05:08

Oracle Instant Client RPM failure

Oracle provides its own RPM packages for e.g. Red Hat Enterprise Linux. But some packages are faulty because they don't "provide" libs they contain and therefore cannot fullfill requirements other packages have. Here's an example:

$ yum install php-5.3.13-1m.el6.x86_64.rpm
Error: Package: php-5.3.13-1m.el6.x86_64
           Requires: libclntsh.so.10.1()(64bit)
 You could try using --skip-broken to work around the problem
 You could try running: rpm -Va --nofiles --nodigest
$ ls /usr/lib/oracle/10.2.0.4/client64/lib/*clntsh*
/usr/lib/oracle/10.2.0.4/client64/lib/libclntsh.so
/usr/lib/oracle/10.2.0.4/client64/lib/libclntsh.so.10.1
$ rpm -qf /usr/lib/oracle/10.*/client64/lib/libclntsh.so.*
oracle-instantclient-basic-10.2.0.4-1.x86_64

Apparently the Oracle RPM package builders switched the "AutoProv" tag in the spec file off. This is easy to fix if you know how a spec file should look like and how packages are built. But let me show you a nice tool: rpmrebuild. With this tool you can rebuild RPM packages from binary packages, reconstruct spec file from binary packages and much more. You can rebuild the oracle packages with i.e.

rpmrebuild --package --notest-install -e \
  oracle-instantclient-basic-10.2.0.4-1.x86_64.rpm

With turning AutoProv and AutoReq on you can build the oracle package without the "requires" error shown above.

But stop! There are some more enhancements which could be done. You can provide an entry in /etc/ld.so.conf.d for the dynamic loader. And then there is a small bug left - at least in some oracle instant client packages version 10: The libs and binaries have the flag for executable stack set which can cause trouble when using SELinux.

I took the spec file extracted with rpmrebuild and made many changes. The libs and binaries of the oracle package did I store in a tgz archive:

# tar tzf oracle-instantclient-basic-10.2.0.4.tgz 
oracle-instantclient-basic-10.2.0.4/
oracle-instantclient-basic-10.2.0.4/bin/
oracle-instantclient-basic-10.2.0.4/bin/genezi
oracle-instantclient-basic-10.2.0.4/lib/
oracle-instantclient-basic-10.2.0.4/lib/libocijdbc10.so
oracle-instantclient-basic-10.2.0.4/lib/libociei.so
oracle-instantclient-basic-10.2.0.4/lib/ojdbc14.jar
oracle-instantclient-basic-10.2.0.4/lib/libocci.so.10.1
oracle-instantclient-basic-10.2.0.4/lib/libclntsh.so.10.1
oracle-instantclient-basic-10.2.0.4/lib/libnnz10.so

The spec file looks like this:

%define __spec_install_post %{nil}
%define __os_install_post %{nil}

BuildArch:     x86_64
Name:          oracle-instantclient-basic
Version:       10.2.0.4
Release:       1m
License:       Oracle 
Group:         Applications/File
Summary:       Instant Client for Oracle Database 10g
Source0:       %{name}-%{version}.tgz
Conflicts:     oracle-instantclient-basiclite  
Provides:      %{name} = %{version}-1
BuildRequires: prelink >= 0.3.3

%description
Instant Client allows you to run your applications without
installing the standard Oracle client or having and
ORACLE_HOME.  OCI, OCCI, ODBC, and JDBC applications work
without modification, while using significantly less disk
space than before.  Even SQL*Plus can be used with Instant
Client.  No recompile, no hassle.
This is the Basic package, supporting OCI, OCCI, and JDBC-OCI
with all
languages.

%prep
%setup -q

%install
install -m 755 -d \
$RPM_BUILD_ROOT/usr/lib/oracle/%{version}/client64/{bin,lib}
install -m 755 -d $RPM_BUILD_ROOT/etc/ld.so.conf.d

cp -af bin/* \
$RPM_BUILD_ROOT/usr/lib/oracle/%{version}/client64/bin/
cp -af lib/* \
$RPM_BUILD_ROOT/usr/lib/oracle/%{version}/client64/lib/
for f in bin/genezi lib/libocijdbc10.so lib/libociei.so \
 lib/libocci.so.10.1 lib/libclntsh.so.10.1 lib/libnnz10.so
do
  execstack -c \
$RPM_BUILD_ROOT/usr/lib/oracle/%{version}/client64/$f
done
echo /usr/lib/oracle/%{version}/client64/lib \
>$RPM_BUILD_ROOT/etc/ld.so.conf.d/%{name}.conf

%post -p /sbin/ldconfig
%postun -p /sbin/ldconfig

%files
%attr(0644 root root) "/etc/ld.so.conf.d/%{name}.conf"
%attr(0755 root root) \
"/usr/lib/oracle/%{version}/client64/bin/genezi"
%attr(0644 root root) \
"/usr/lib/oracle/%{version}/client64/lib/libclntsh.so.10.1"
%attr(0644 root root) \
"/usr/lib/oracle/%{version}/client64/lib/libnnz10.so"
%attr(0644 root root) \
"/usr/lib/oracle/%{version}/client64/lib/libocci.so.10.1"
%attr(0644 root root) \
"/usr/lib/oracle/%{version}/client64/lib/libociei.so"
%attr(0644 root root) \
"/usr/lib/oracle/%{version}/client64/lib/libocijdbc10.so"
%attr(0644 root root) \
"/usr/lib/oracle/%{version}/client64/lib/ojdbc14.jar"

Here's the installation of the newly created package:

$ yum install oracle-instantclient-basic-10.2.0.4-1m.*.rpm 
...
Dependency Installed:
  compat-libstdc++-33.x86_64 0:3.2.3-69.el6                                                                                                                                                                                    

Updated:
  oracle-instantclient-basic.x86_64 0:10.2.0.4-1mm1                                                                                                                                                                            

Complete!

As you can see there was libstdc++-33 installed as requirement for the oracle libs - this was also broken in the original oracle package. The custom php package which required libclntsh.so could also be installed without error. As a last test I did compare an original package installation and the new package installation for "SELinux compatibility". Old package install:

$ execstack -q /usr/lib/oracle/10.2.0.4/client64/lib/lib*so*
X /usr/lib/oracle/10.2.0.4/client64/lib/libclntsh.so
X /usr/lib/oracle/10.2.0.4/client64/lib/libclntsh.so.10.1
X /usr/lib/oracle/10.2.0.4/client64/lib/libnnz10.so
X /usr/lib/oracle/10.2.0.4/client64/lib/libocci.so
X /usr/lib/oracle/10.2.0.4/client64/lib/libocci.so.10.1
X /usr/lib/oracle/10.2.0.4/client64/lib/libociei.so
X /usr/lib/oracle/10.2.0.4/client64/lib/libocijdbc10.so

Install of the new rebuilded package:

$ execstack -q /usr/lib/oracle/10.2.0.4/client64/lib/lib*so*
- /usr/lib/oracle/10.2.0.4/client64/lib/libclntsh.so
- /usr/lib/oracle/10.2.0.4/client64/lib/libclntsh.so.10.1
- /usr/lib/oracle/10.2.0.4/client64/lib/libnnz10.so
- /usr/lib/oracle/10.2.0.4/client64/lib/libocci.so
- /usr/lib/oracle/10.2.0.4/client64/lib/libocci.so.10.1
- /usr/lib/oracle/10.2.0.4/client64/lib/libociei.so
- /usr/lib/oracle/10.2.0.4/client64/lib/libocijdbc10.so

All problems were solved with this rebuilded oracle rpm package.


Posted by Frank W. Bergmann | Permanent link | File under: shell

2011-12-31 18:37:32

DJB daemontools with upstart or systemd

The daemontools softwaresuite are widely used for supervising processes. Unlike the common way of launching daemons or other processes in background, writing their PID to a file and watch for a process (actually any process) with this PID by crappy tools like monit the daemontools offer a direct supervision of processes with an immediate restart on a crash.

Using the traditional SysV-init it is most easy to launch the daemontools processes:

$ grep -B1 SV /etc/inittab
si::sysinit:/etc/rc.d/rc.sysinit
SV:12345:respawn:/command/svscanboot3

Note: svscanboot3 is a binary replacement of svscanboot. It is part of ngtx.

SysV-init process ID 1 is very small, approx. 700 KB RSS. With many manual optimizations it is possible to start a system / server as fast as with upstart or systemd.

Upstart offers event-based booting. A time ago it was dealed as a successor to SysV-init. To start daemontools as a upstart-service you must create a config file:

$ cat /etc/init/daemontools.conf 
description     "DJB daemontools"
start on filesystem
stop on runlevel [06]
respawn
exec /command/svscanboot3

Some latest Linux (actual "linux-only") distros switch to systemd. Systemd also offers event-based and parallel booting with linux-only features like cgroups or fanotify. Its features are very nice but its PID 1 RSS is 13 MB which is very big compared to SysV-init. For systemd you must also create a config file (and a symlink):

$ ls -l /etc/systemd/system/multi-user.target.wants/ \
  daemontools.service
lrwxrwxrwx. 1 root root 39 21. Dez 08:34
  /etc/systemd/system/multi-user.target.wants/ \
  daemontools.service -> /lib/systemd/system/ \
  daemontools.service

$ ls -l /lib/systemd/system/daemontools.service
-rw-r--r--. 1 root root 151 21. Dez 08:33
  /lib/systemd/system/daemontools.service

$ cat /lib/systemd/system/daemontools.service
[Unit]
Description=DJB daemontools
After=sysinit.target

[Service]
ExecStart=/command/svscanboot3
Restart=always

[Install]
WantedBy=multi-user.target

Posted by Frank W. Bergmann | Permanent link | File under: shell

2011-12-03 20:53:11

MySQL Slow Query Log Filter

Filtering all queries of one user out of a MySQL Slow Query Log is a common task. Maatkit is a toolkit for analyzing log files and more. Here is an example run to filter all queries of user123 of a 707 MB big slow-query-log:

$ time mk-query-digest --filter \
 '($event->{user} || "") =~ m/user123/' \
 --print --no-report log-slow-queries.log >1
log-slow-queries.log:  15% 02:49 remain
log-slow-queries.log:  29% 02:26 remain
log-slow-queries.log:  44% 01:51 remain
log-slow-queries.log:  59% 01:20 remain
log-slow-queries.log:  76% 00:45 remain
log-slow-queries.log:  90% 00:18 remain

real    3m20.097s
user    0m16.616s
sys     3m2.592s

Well, 707 MB in more than 3 minutes? Simple filtering can also be done with awk, try it:

$ time awk \
'/^# Time: /{t=1;s=$0;p=0}'\
'!/^# /{t=0}/^# User@Host: /{p=0}'\
'/^# User@Host: user123/{p=1};'\
'{if(p){if(t){print s;t=0;}print$0}}' \
 <log-slow-queries.log >2

real    0m58.844s
user    0m2.501s
sys     0m3.268s

Nice speed, but awk is a symlink to mawk, try gawk:

$ time gawk \
'/^# Time: /{t=1;s=$0;p=0}'\
'!/^# /{t=0}/^# User@Host: /{p=0}'\
'/^# User@Host: user123/{p=1};'\
'{if(p){if(t){print s;t=0;}print$0}}' \
 <log-slow-queries.log >2

real    3m30.287s
user    0m29.934s
sys     2m58.800s

Ooops, but didn't we already know, how slow GNU-software can be? Try a different server:

$ time gawk \
'/^# Time: /{t=1;s=$0;p=0}'\
'!/^# /{t=0}/^# User@Host: /{p=0}'\
'/^# User@Host: user123/{p=1};'\
'{if(p){if(t){print s;t=0;}print$0}}' \
 <log-slow-queries.log >2

real    2m4.899s
user    2m2.240s
sys     0m1.890s

This server runs more fast, but it lacks mawk, is virtual and it runs like the first server only 32-bit-linux. Try gawk and mawk on 64-bit-linux (same physical host):

$ time gawk \
'/^# Time: /{t=1;s=$0;p=0}'\
'!/^# /{t=0}/^# User@Host: /{p=0}'\
'/^# User@Host: user123/{p=1};'\
'{if(p){if(t){print s;t=0;}print$0}}' \
 <log-slow-queries.log >2

real    1m49.348s
user    1m40.242s
sys     0m1.528s

$ time mawk \
'/^# Time: /{t=1;s=$0;p=0}'\
'!/^# /{t=0}/^# User@Host: /{p=0}'\
'/^# User@Host: user123/{p=1};'\
'{if(p){if(t){print s;t=0;}print$0}}' \
 <log-slow-queries.log >3

real    0m13.115s
user    0m1.000s
sys     0m2.392s

Conclusions:

  • simple filter can be done faster with awk than with maatkit
  • mawk does this filtering faster than gawk
  • mawk and gawk run faster on 64-bit-linux

Posted by Frank W. Bergmann | Permanent link | File under: mysql, shell

2011-11-30 22:15:57

Indirekte Variablenzuweisung mit "eval"

Das Shell-Builtin "eval" bewertet Ausdrücke bzw. führt Befehle aus. Einfaches Beispiel:

$ unset A
$ echo $A

$ eval A=1
$ echo $A
1

Da die Shell vor Ausführen von eval wie auch bei jeder anderen Kommandozeile erst die üblichen Ersetzungen (Variablen, Commands etc.) macht, kann man auch folgendes machen:

$ B=A
$ eval $B=2
$ echo $A
2

Und wenn man ganz verrückte Sachen machen will, wie beispielsweise in einem Script mehrere Variablen in einer Schleife auf die gleiche Weise zu verändern, dann sieht das so aus:

$ U=a
$ for i in U;do eval $i=\"\$$i X\";done;echo $U
a X
$ for i in U;do eval $i=\"\$$i X\";done;echo $U
a X X
$ for i in U;do eval $i=\"\$$i X\";done;echo $U
a X X X

Posted by Frank W. Bergmann | Permanent link | File under: shell

2011-09-19 22:07:58

Call to undefined function gzopen()

Auf 32-Bit-Systemen und aktiviertem Largefilesupport in der Zlib kann es beispielsweise bei Verwendung von PHP zu "Fehlern" kommen. Dieser Fehler erschien bei dem Versuch, Magento auf einem 32-Bit-System mit LFS-Support in Zlib und PHP zu installieren:

Fatal error: Call to undefined function gzopen() in \
  /html/install/downloader/lib/Mage/Archive/Gz.php on line 60

Das ist aber anscheinend ein allgemeines Problem und durchaus aktuell, wie dieser Thread zeigt: https://bugs.launchpad.net/ubuntu/+source/php5/+bug/432291

Der Fehler tauchte bei dieser Distro auf, nachdem der Debian-Patch "019-z_off_t_as_long.patch" deaktiviert wurde. Dabei trat dann der Nebeneffekt auf, dass nur noch gzopen64() definiert war. Der Ansatz war ja löblich, den Patch nicht ungeprüft von dieser Distribution zu übernehmen (ya remember "OpenSSL-Debakel" und "16 Bit Entropie"?), doch leider wurde dieser Patch dann doch wieder aktiviert, anscheinend ohne sich über die Hintergründe im Klaren zu sein. Zu dem Patch findet man folgende Infos:

* Add 019-z_off_t_as_long.patch, including local headers for
zlib, forcing off_t = long for gzip file functions, however
disable it for now, as we'll only need it if we reenable LFS
(closes: #208608)

Der Patch bewirkt die Installation von zwei Zlib-Header-Dateien. Dabei wird der Zlib-interne off_t-Typ als "long" definiert. Die Header-Dateien selbst stammen von der Zlib 1.2.1.1 und überschreiben ihre Brüder, die systemweit installiert sind und vermutlich von einer neueren Zlib stammen. So eine Vorgehensweise "unsauber" zu nennen, wäre ein Kompliment, aber zum Thema QM kann nur noch einmal auf OpenSSL und die 16 Bit breite Entropie verwiesen werden.

Doch wie löst man das Problem? Als Maintainer kann man bugs.php.net bemühen oder es über einen Kontakt klären. Für die Homebrew-Variante schauen wir uns mal einen Teil der aktuellen Zlib-Header-Dateien an:

$ grep -B1 -A21 "define gzopen" /usr/include/zlib.h 
#if !defined(ZLIB_INTERNAL) && _FILE_OFFSET_BITS-0 == 64 && \
_LFS64_LARGEFILE-0
#  define gzopen gzopen64
#  define gzseek gzseek64
#  define gztell gztell64
#  define gzoffset gzoffset64
#  define adler32_combine adler32_combine64
#  define crc32_combine crc32_combine64
#  ifdef _LARGEFILE64_SOURCE
     ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const \
char *));
     ZEXTERN z_off_t ZEXPORT gzseek64 OF((gzFile, z_off_t, \
int));
     ZEXTERN z_off_t ZEXPORT gztell64 OF((gzFile));
     ZEXTERN z_off_t ZEXPORT gzoffset64 OF((gzFile));
     ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong,
\
z_off_t));
     ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, \
z_off_t));
#  endif
#else
   ZEXTERN gzFile ZEXPORT gzopen OF((const char *, const char \
*));
   ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile, z_off_t, int));
   ZEXTERN z_off_t ZEXPORT gztell OF((gzFile));
   ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile));
   ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, \
z_off_t));
   ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, \
z_off_t));
#endif

Da wir die Zlib intern in PHP nutzen wollen und dabei auf keinen Fall gzopen64(), sondern statt dessen gzopen() haben wollen, definieren wir zum Übersetzen von PHP ganz einfach ZLIB_INTERNAL, beispielsweise mit der gcc-Option "-DZLIB_INTERNAL=1". Wenn man das an die CFLAGS anhängt, bevor man ./configure aufruft, dann wird PHP damit übersetzt und beim Include der Zlib-Header-Dateien werden die "internen" Namen gzopen() etc. deklariert.


Posted by Frank W. Bergmann | Permanent link | File under: c, php, developer

2011-05-29 12:02:39

Prozessoren, Cores und Hyperthreading

Wie erkennt man auf einem System, wie viele Prozessoren und Cores man hat, und ob Hyperthreading zur Verfügung steht? Fangen wir direkt mit einem Beispiel an:

s1# cat /proc/cpuinfo
processor       : 0
vendor_id       : GenuineIntel
cpu family      : 6
model           : 15
model name      : Intel(R) Xeon(R) CPU            5110  @
1.60GHz
stepping        : 6
cpu MHz         : 1595.984
cache size      : 4096 KB
physical id     : 0
siblings        : 2
core id         : 0
cpu cores       : 2
apicid          : 0
initial apicid  : 0
fdiv_bug        : no
hlt_bug         : no
f00f_bug        : no
coma_bug        : no
fpu             : yes
fpu_exception   : yes
cpuid level     : 10
wp              : yes
flags           : fpu vme de pse tsc msr pae mce cx8 apic sep
mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse
sse2 ss ht tm pbe nx lm constant_tsc arch_perfmon pebs bts
pni monitor ds_cpl vmx tm2 ssse3 cx16 xtpr dca lahf_lm
bogomips        : 3194.84
clflush size    : 64
power management:

processor       : 1
vendor_id       : GenuineIntel
cpu family      : 6
model           : 15
model name      : Intel(R) Xeon(R) CPU            5110  @
1.60GHz
stepping        : 6
cpu MHz         : 1595.984
cache size      : 4096 KB
physical id     : 0
siblings        : 2
core id         : 1
cpu cores       : 2
apicid          : 1
initial apicid  : 1
fdiv_bug        : no
hlt_bug         : no
f00f_bug        : no
coma_bug        : no
fpu             : yes
fpu_exception   : yes
cpuid level     : 10
wp              : yes
flags           : fpu vme de pse tsc msr pae mce cx8 apic sep
mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse
sse2 ss ht tm pbe nx lm constant_tsc arch_perfmon pebs bts
pni monitor ds_cpl vmx tm2 ssse3 cx16 xtpr dca lahf_lm
bogomips        : 3192.06
clflush size    : 64
power management:

Hier sieht man das "ht"-Flag, aber der Xeon 5110 hat kein Hyperthreading. Zum Vergleich mal ein anderes System:

s2# cat /proc/cpuinfo
processor       : 0
vendor_id       : GenuineIntel
cpu family      : 15
model           : 4
model name      : Intel(R) Pentium(R) 4 CPU 3.00GHz
stepping        : 1
cpu MHz         : 2995.049
cache size      : 1024 KB
physical id     : 0
siblings        : 2
core id         : 0
cpu cores       : 1
fdiv_bug        : no
hlt_bug         : no
f00f_bug        : no
coma_bug        : no
fpu             : yes
fpu_exception   : yes
cpuid level     : 5
wp              : yes
flags           : fpu vme de pse tsc msr pae mce cx8 apic sep
mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse
sse2 ss ht tm pbe constant_tsc pebs bts sync_rdtsc pni
monitor ds_cpl cid xtpr
bogomips        : 5994.31
clflush size    : 64

processor       : 1
vendor_id       : GenuineIntel
cpu family      : 15
model           : 4
model name      : Intel(R) Pentium(R) 4 CPU 3.00GHz
stepping        : 1
cpu MHz         : 2995.049
cache size      : 1024 KB
physical id     : 0
siblings        : 2
core id         : 0
cpu cores       : 1
fdiv_bug        : no
hlt_bug         : no
f00f_bug        : no
coma_bug        : no
fpu             : yes
fpu_exception   : yes
cpuid level     : 5
wp              : yes
flags           : fpu vme de pse tsc msr pae mce cx8 apic sep
mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse
sse2 ss ht tm pbe constant_tsc pebs bts sync_rdtsc pni
monitor ds_cpl cid xtpr
bogomips        : 5990.26
clflush size    : 64

Hier steht auch "ht", diesmal hat die CPU aber Hyperthreading. Das Flag wurde wegen einer gewissen Abwärtskompatibilität gesetzt. Grund waren unter anderem Betriebssysteme, die pro Sockel lizenziert wurden. Hätte man das mit dem Pentium4 eingeführte ht-Flag weggelassen, hätten diese "OS" den virtuellen zweiten Kern nicht genutzt / nutzen können.

Wichtig sind hierbei die Angaben zu den Siblings (so etwas wie gleichartige Kinder einer "Vater-CPU"), der Physical ID, der Anzahl der Cores und die Nummerierung der Core IDs. Mit grep wird das dann deutlicher:

s1# egrep \
'processor|physical id|siblings|core id|cpu cores|^$' \
/proc/cpuinfo
processor       : 0
physical id     : 0
siblings        : 2
core id         : 0
cpu cores       : 2

processor       : 1
physical id     : 0
siblings        : 2
core id         : 1
cpu cores       : 2

Wir haben 2 CPU cores mit ID 0 und 1 auf 1 physischen Prozessor bei 2 Siblings. die 2 Siblings beziehen sich hier auf die 2 Cores pro physischer CPU.

s2# egrep \
'processor|physical id|siblings|core id|cpu cores|^$' \
/proc/cpuinfo
processor       : 0
physical id     : 0
siblings        : 2
core id         : 0
cpu cores       : 1

processor       : 1
physical id     : 0
siblings        : 2
core id         : 0
cpu cores       : 1

Hier haben wir 2 (virtuelle) Prozessoren mit 1 physical ID, 1 Core ID und 1 CPU core, aber mit 2 Siblings. Es handelt sich somit um eine einzelne Singlecore-CPU. Die zwei virtuellen Prozessoren kommen durch die 2 Siblings zustande: Diese CPU hat also Hyperthreading. Pro Core hat man hier also "cpu cores" geteilt durch "siblings" = 2 / 1 = 2 virtuelle HT-Prozessoren.

Schauen wir uns einen dritten Server an:

s3# egrep processor /proc/cpuinfo
processor       : 0
processor       : 1
processor       : 2
processor       : 3
processor       : 4
processor       : 5
processor       : 6
processor       : 7
processor       : 8
processor       : 9
processor       : 10
processor       : 11
processor       : 12
processor       : 13
processor       : 14
processor       : 15
root@coabmaster:~ > egrep 'core id' /proc/cpuinfo|sort -u
core id         : 0
core id         : 1
core id         : 2
core id         : 3

Wir haben 16 virtuelle/nicht-virtuelle Prozessoren und 4 Kerne pro physischem Prozessor.

s3# egrep \
'siblings|cpu cores|physical id' /proc/cpuinfo \
|sort -u
cpu cores       : 4
physical id     : 0
physical id     : 1
siblings        : 8

Hier sieht man noch mal die Anzahl der Kerne, die IDs der physischen Prozessoren und 8 Siblings (8 Siblings durch 4 Cores = 2 virtuelle HTT-CPUs). Das macht also 2 Quadcore-CPUs mit Hyperthreading = 16 virtuelle Prozessoren.

Mit einem kleinen Script lässt sich das bequem auswerten:

s4# cat cpucores.sh 
#!/bin/bash

for f in \
  'model name' processor 'physical id' 'cpu cores' siblings
do
  grep "$f" /proc/cpuinfo|tail -n 1
done \
|sed 's,.*: ,,' \
|{
  read M
  read P;let P=P+1
  read I;[ -z "$I" ] && I=0;let I=I+1
  read C;[ -z "$C" ] && C=1
  read S;[ -z "$S" ] && S=1
  echo $M: $I CPUs with $C cores and $S siblings\
    $([ $S -gt $C ] && echo " (hyperthreading)") \
    '("'$P virtual processors'")'
}
s4# ./cpucores.sh 
Intel(R) Xeon(R) CPU E5520 @ 2.27GHz: 2 CPUs with 4 cores and
8 siblings (hyperthreading) ("16 virtual processors")

Da das Script so kurz ist, kann man es auch als Einzeiler verwenden. Zum Schluss noch ein paar Beispielausgaben des Scripts:

Intel(R) Xeon(R) CPU E5520 @ 2.27GHz: 2 CPUs with 4 cores and
8 siblings (hyperthreading) ("16 virtual processors")
Intel(R) Xeon(R) CPU X5650 @ 2.67GHz: 2 CPUs with 6 cores and
12 siblings (hyperthreading) ("24 virtual processors")
Intel(R) Pentium(R) 4 CPU 3.00GHz: 1 CPUs with 1 cores and 2
siblings (hyperthreading) ("2 virtual processors")
Intel(R) Core(TM)2 CPU 4400 @ 2.00GHz: 1 CPUs with 2 cores
and 2 siblings ("2 virtual processors")
Intel(R) Xeon(R) CPU 3060 @ 2.40GHz: 1 CPUs with 2 cores and
2 siblings ("2 virtual processors")
Intel(R) Xeon(R) CPU 5110 @ 1.60GHz: 4 CPUs with 2 cores and
2 siblings ("4 virtual processors")
Intel(R) Xeon(R) CPU E5310 @ 1.60GHz: 1 CPUs with 4 cores and
4 siblings ("4 virtual processors")
Intel(R) Xeon(R) CPU E5520 @ 2.27GHz: 2 CPUs with 4 cores and
8 siblings (hyperthreading) ("8 virtual processors")
Intel(R) Xeon(R) CPU E5620 @ 2.40GHz: 2 CPUs with 4 cores and
8 siblings (hyperthreading) ("8 virtual processors")

Posted by Frank W. Bergmann | Permanent link | File under: shell

2011-05-26 23:15:58

base64 für SMTP-Auth und HTTP Basic authentication

Sowohl bei SMTP-Auth als auch bei HTTP Basic access authentication kommt base64-Kodierung zur Anwendung. Dieser Blogpost zeigt dazu Beispiele und die Verwendung von Python zur base64-Kodierung.

SMTP-Auth

Hier der Ausschnitt eines SMTP-Dialogs mit PLAIN-Authentication:

$ telnet mail.domain.de 25
Trying 128.64.32.16...
Connected to mail.domain.de.
Escape character is '^]'.
220 mail.domain.de ESMTP Postfix
EHLO franksbox.localdomain
250-mail.domain.de
250-PIPELINING
250-SIZE 35840000
250-ETRN
250-AUTH LOGIN PLAIN
250-AUTH=LOGIN PLAIN
250-ENHANCEDSTATUSCODES
250-8BITMIME
250 DSN
AUTH PLAIN bWFpbDEAbWFpbDEAc2FzbHB3
235 2.0.0 Authentication successful

Nach RFC4616 besteht die Zeichenkette für den "PLAIN SASL Mechanism" aus der "authorization identity", einem NUL-Zeichen, der "authentication identity", einem weiteren NUL-Zeichen und dem Passwort. Normalerweise sind "authorization identity" und "authentication identity" gleich (die Postfachkennung). Mit Python kann man die Zeichenkette mit base64 kodieren und auch zurück dekodieren:

$ python -c "import base64;print \
> base64.encodestring('mail1\0mail1\0saslpw')"
bWFpbDEAbWFpbDEAc2FzbHB3

$ python -c "import base64;print \
> base64.decodestring('bWFpbDEAbWFpbDEAc2FzbHB3')" \
> |tr \\000 /
mail1/mail1/saslpw

HTTP basic access authentication

Auch hier kommt base64-Kodierung zum Einsatz. Die Zeichenkette zur Authorisierung besteht nach RFC1945 aus Name, einem Doppelpunkt und dem Passwort. Ein HTTP-Request sieht damit beispielsweise so aus:

GET /index.html HTTP/1.1
Host: www.domain.de
Authorization: Basic dzE6aHRwdw==

Auch hier nutzen wir wieder Python, um manuell zu en- und dekodieren.

$ python -c \
> "import base64;print base64.encodestring('w1:htpw')"
dzE6aHRwdw==

$ python -c \
> "import base64;print base64.decodestring('dzE6aHRwdw==')"
w1:htpw


Posted by Frank W. Bergmann | Permanent link | File under: http, smtp, python

2011-05-25 00:35:23

Apache hat keine freie Semaphore

Das ist auch ein kleiner Klassiker, darum sollen dieses Problem und seine Lösung auch nicht unerwähnt bleiben. Es kann vorkommen, dass in einem Apache Error-Log die folgende Zeile auftaucht:

No space left on device (errno: 28)

Eine Prüfung ergibt dann oft, dass auf den (HD-) Devices sehr wohl noch "Space" vorhanden ist. In den Logzeilen findet sich dann auch ein Eintrag wie:

could not create private SSLMutex semaphore

Das liefert dann auch den entscheidenden Hinweis darauf, dass Apache keinen neuen Semaphor (ein klassisches Werkzeug für IPC) erzeugen kann. Die man-page zu semget() führt auch diese Fehlermöglichkeit auf:

# man semget|grep -A4 ENOSPC
   ENOSPC A semaphore set has to be  created  but  the
          system limit for the maximum number of sema-
          phore sets (SEMMNI), or the system wide max-
          imum number of semaphores (SEMMNS), would be
          exceeded.

Das sind bei vielen Hosts oft noch die zu niedrigen Standardwerte:

# cat /proc/sys/kernel/sem 
250     32000   32      128

Die man-page proc(5) liefert uns folgende Infos dazu:

   /proc/sys/kernel/sem (since Linux 2.4)
          This file contains 4 numbers defining limits for
          System  V  IPC semaphores.  These fields are, in
          order:

          SEMMSL  The  maximum  semaphores  per semaphore
                  set.

          SEMMNS  A  system-wide  limit  on  the number of
                  semaphores in all semaphore sets.

          SEMOPM  The maximum number  of  operations that
                  may be specified in a semop(2) call.

          SEMMNI  A  system-wide limit on the maximum num-
                  ber of semaphore identifiers.

Eine Zählung der Semaphore des Users "www" liefert das:

# ipcs -s|grep -c www
128

Jetzt stoppen wir Apache, löschen alle "vergessenen" Semaphore, setzen die Werte höher und starten Apache wieder:

# ipcs -s|grep www|cut -d\  -f2|xargs -r -n 1 ipcrm -s
# echo '500 64000 32 1024' >/proc/sys/kernel/sem 

Posted by Frank W. Bergmann | Permanent link | File under: apache

2011-05-21 09:37:23

Logging in der Konsole

Dieser Post zeigt ein paar Möglichkeiten, wie man Befehle oder Ausgaben in einer Shell-Session mitschneiden kann.

Der Befehl script erzeugt eine neue Shell, bei der alle Ein- und Ausgaben in eine Datei typescript geschrieben werden. Nach Verlassen der durch script gestarteten Shell kann man sich dieses Logfile anschauen. Allerdings ist dort jedes einzelne Zeichen aufgezeichnet, einschliesslich aller Steuerzeichen für Farben, für Zeilen Editierung (Backspace, Tab, Return etc). Wie man mittels vi dieses Log von Steuerzeichen befreit, würde diesen Beitrag sprengen.

Als Alternative kann man sich auch nur die Textausgabe von Programmen in ein Logfile packen. Hier ein Beispiel:

$ avscan -a . 2>&1 |tee -a log.avscan

Wenn man ein Logging mit Timestamp der einzelnen Zeilen braucht, kann man so etwas machen:

$ /opt/drweb/drweb -al . 2>&1 |tai64n |tee -a log.drweb
@400000004c5bbe8b1d775b9c Dr.Web (R) Scanner for Linux
@400000004c5bbe8b1e3010c4 Copyright (c) Igor Daniloff,

Das Tool tai64n gehört zu der Softwaresuite daemontools von Dan Bernstein. Hier fügt tai64n am Beginn jeder Zeile einen Timestamp @40000... ein. Lesen/konvertieren kann man das mit tai64nlocal, was den tai64n-Timestamp in lokale und human readable Zeit konvertiert:

$ tai64nlocal <log.drweb |tail -1
2010-08-06 09:43:03.225062500 2009_02_12-ftp.log.gz - Ok
$ 

Man kann sich den "Live-Stream" auch direkt bei der Ausgabe wieder konvertiert anzeigen lassen:

$ /opt/drweb/drweb -al . 2>&1 |tai64n \
  |tee -a log.drweb |tai64nlocal
2010-08-06 09:47:52.433362500 Dr.Web (R) Scanner for Linux
2010-08-06 09:47:52.433364500 Copyright (c) Igor Daniloff,

Wenn man mit sehr viel Daten bei der Ausgabe rechnet und vielleicht nur den letzten Teil haben möchte, dann kann man so eine Zeile nutzen:

$ /opt/drweb/drweb -al . 2>&1 |tee /dev/stderr \
  |multilog t s10000 n20 ./logdir

Diese Zeile zeigt die Programmausgabe kontinuierlich an und schreibt bis 20 maximal 10 KB große Logdateien in das Verzeichnis logdir. Das Tool multilog aus den daemontools hat ein eingebautes "logrotate". Auf diese Weise kann man die Menge der Logdaten begrenzen.


Posted by Frank W. Bergmann | Permanent link | File under: shell

2011-05-20 08:00:18

access_log-Statistiken mit awk

Heute gibt's nur kleine Einzeiler. Ein Klassiker zum Anzeigen der IP-Adressen eines Apache access_log, letzte 1000 Zugriffe und sortiert nach Häufigkeit der Zugriffe:

tail -n 1000 access_log | awk \
'{f[$1]++}END{for(g in f)print f[g]" "g}' \
| sort -n | tail -n 5

Möchte man nach der angefragten Datei sortieren, muss man '$1' durch '$7' ersetzen, für Sortierung nach Return Code '$9'.

tail -n 1000 access_log | awk \
'$9=="404"{f[$7" "$11]++}END{for(g in f)print f[g]" "g}' \
| sort -n | tail -n 5

Das sortiert die Zeilen mit 404 Return Code ("File Not Found") und gibt dabei die angeforderte Datei und den Referer an.

tail -n 1000 access_log | awk \
'$9~/30./{f[$7" "$11]++}END{for(g in f)print f[g]" "g}' \
| sort -n | tail -n 5

Statt 404 werden hier alle 30x Redirects sortiert.


Posted by Frank W. Bergmann | Permanent link | File under: shell, apache