aerotools-ng

Wie bereits angekündigt hatte ich vor einiger Zeit die ersten Hürden vor Aquaero5-Support für aerotools genommen.

Nun bin ich endlich dazu gekommen, den derzeitigen Stand der Dinge für jedermann zugänglich zu machen – in Form des neuen Projekts aerotools-ng.

Code und Readme gibts auf GitHub: https://github.com/lynix/aerotools-ng

xfce4-notifyd über dbus ansprechen

Kürzlich bin ich von der awesome-eigenen Notificaion-Implementierung naughty auf xfce4-notifyd gewechselt – hauptsächlich wegen des (einfacheren) Themings.

Leider enden bei mir aus noch ungeklärten Gründen notify-send-Aufrufe sehr oft in Traps:

Nov 15 13:37:15 thor kernel: traps: notify-send[1857] trap int3 ip:7f92f516fa27 sp:7fff35a3a870 error:0

Also habe ich nach einem Weg gesucht, den xfce4-notifyd direkt per dbus anzusprechen.
Hierfür müsste eigentlich ein dbus-send-Aufruf à la

dbus-send --session --dest=org.freedesktop.Notifications \
    --type=method_call /org/freedesktop/Notifications \
    org.freedesktop.Notifications.Notify \
    string:'application' uint32:0 \
    string: string:'headline' \
    string:'message body' \
    array:string: dict:string: int32:3000

genügen, allerdings passt da irgendwas mit der Signatur der aufzurufenden Methode nicht und nicht mal dbus-explorer kann sie aufrufen.

Als Workaround, bis ich die Ursache gefunden habe, nutze ich diesen kleinen Fetzen Python, den ich aus einem Forum entnommen (und angepasst) habe:

#!/usr/bin/env python2
 
import dbus
import sys
 
item = ('org.freedesktop.Notifications')
path = ('/org/freedesktop/Notifications')
interface = ('org.freedesktop.Notifications')
 
if len(sys.argv) > 3:
	icon = sys.argv[3]
else:
	icon = 'dialog-information'
 
array = ''
hint = ''
time = 10000
app_name = ('')
title = (sys.argv[1])
body = (sys.argv[2])
 
bus = dbus.SessionBus()
notif = bus.get_object(item, path)
notify = dbus.Interface(notif, interface)
notify.Notify(app_name, 0, icon, title, body, array, hint, time)

(Keine Beschwerden bitte, das sind die ersten Zeilen Python, die ich je angefasst habe ;) )

Aquaero5 Payload geknackt

An alle Nutzer meiner aerotools: der erste Schritt in Richtung Support für das aquaero5 ist getan, ich konnte heute die ersten Bytes des Payloads entschlüsseln zuordnen!

Damit lassen sich schon mal die Temperatursensoren auslesen:

[alex@thor aerotools-ng]$ ./main /dev/hidraw2
temp0: 29.97°C
temp1: 27.27°C
temp2: 33.96°C
temp3: not connected
temp4: not connected
temp5: not connected
temp6: not connected
temp7: not connected

Hatte nach Lektüre unzähliger Specs, Dokus und Codesamples zu USB, libhid & Co. schon fast aufgegeben, dabei war des Rätsels Lösung direkt vor meiner Nase :)

Details folgen, sobald es der Prüfungsstress zulässt.

The Travel Theme langsam wegen fsockopen()

Wer ein Blog mit dem WordPress-Theme The Travel Theme betreibt, wird sich vielleicht über die sagenhaft schlechte Performance (unabhängig von der verwendeten Server-Hardware) ärgern.

Schuld ist ein fsockopen()-Call in Zeile 128 der Datei footer.php, der ein JSON-Objekt von einem externen Server lädt.

Der betreffende Codeblock grenzt meiner Meinung nach schon fast an Spyware, außerdem kommt der betreffende Server anscheinend nicht mit dem hohen Load durch die weite Verbreitung des Themes klar.

Ich versuche das jetzt mal so neutral wie möglich zu formulieren (keine Ahnung, ob die Lizenz des Themes eine Modifikation zulässt): ohne diesen Codeblock würde das Theme völlig normal (=schnell) funktionieren… ;)

 

Munin Plugin für postfix-policyd-spf-python

Nachdem ich nach DKIM auch SPF auf meinem Mailserver aufgesetzt hatte (Paket postfix-policyd-spf-python) wollte ich natürlich auch sehen, welchen Effekt dieser zusätzliche Filter auf den Mailverkehr hat.

Wieder ein Grund zur Bash-Scripterei! :)  Folgendes kleine Plugin habe ich heute Morgen runtergehackt:

#!/bin/bash
#
#   Munin plugin to count postfix-policyd-spf-python results
#           2012 Alexander Koch <lynix47@gmail.com> 
# 
# Parameters understood:
#	config		(required)
#	autoconf 	(optional - used by munin-config)
# 
# Config variables:
#       logfile      - Where to find the postfix log (mail.log)
#
# Add the following line to a file in /etc/munin/plugin-conf.d:
# 	env.logfile /var/log/your/mail.log
#
# Magic markers (optional - used by munin-config and installation scripts):
#
#%# family=auto
#%# capabilities=autoconf
 
 
#
# Configuration
#
 
STAT_FILE=${STAT_FILE:-/var/lib/munin/plugin-state/plugin-plcyd-spf-python.state}
LOGFILE=${logfile:-/var/log/mail.log}
 
if [ "$1" = "autoconf" ]; then
	echo yes
	exit 0
fi
 
if [ "$1" = "config" ]; then
	echo 'graph_title SPF Check Results'
	echo 'graph_category postfix'
	echo 'graph_args --base 1000 -l 0'
	echo 'graph_vlabel Count/s'
 
	echo 'count_pass.label Pass'
	echo 'count_pass.type DERIVE'
	echo 'count_pass.min 0'
	echo 'count_pass.colour 00cc00'
	echo 'count_fail.label Fail'
	echo 'count_fail.type DERIVE'
	echo 'count_fail.min 0'
	echo 'count_fail.colour cc0000'
	echo 'count_none.label None'
	echo 'count_none.type DERIVE'
	echo 'count_none.min 0'
	echo 'count_none.colour 0066b3'
	echo 'count_temperror.label Temperror'
	echo 'count_temperror.type DERIVE'
	echo 'count_temperror.min 0'
	echo 'count_temperror.colour ff8000'
	echo 'count_neutral.label Neutral'
	echo 'count_neutral.type DERIVE'
	echo 'count_neutral.min 0'
	echo 'count_neutral.colour ffcc00'
 
	exit 0
fi
 
 
#
# Log parsing
#
 
function get_log_count() {
	egrep "policyd-spf\[[0-9]+\]: $1;" "$LOGFILE" | grep "$(date '+%b %e')" | wc -l
}
 
PASS=$(get_log_count "Pass")
FAIL=$(get_log_count "Fail")
NONE=$(get_log_count "None")
TEMPERR=$(get_log_count "Temperror")
NEUTRAL=$(get_log_count "Neutral")
 
echo "count_pass.value $PASS"
echo "count_fail.value $FAIL"
echo "count_none.value $NONE"
echo "count_temperror.value $TEMPERR"
echo "count_neutral.value $NEUTRAL"
 
 
exit 0

Wie immer gibts den Code auch in meinem GitHub-Repo.

Ein Screenshot des produzierten Graphen folgt, sobald ich genug Daten für eine vollständige Periode (24h) gesammelt habe. Edit: hier der Screeny, anscheinend habe ich zu wenig Traffic auf dem Mailserver ;)

hdd-spindown.sh

Mal wieder Zeit gefunden, einen schon leicht angestaubten Punkt auf meiner 2code-Liste abzuhaken:

Ein Bash-Script, das Platten bei Inaktivität nach definierbarem Timeout per hdparm -y in Standby schickt. Für mich sehr nützlich bei SATA-Platten, die sich nicht per hdparm -S konfigurieren lassen.

Gehostet wie immer auf github, oder direkt hier:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#!/bin/bash
 
#
# Automatic disk standby using kernel diskstats and hdparm
#
# Version 1.1, 2011 by Alexander Koch <lynix47@gmail.com>
#
 
 
# DEVICES syntax: DEVICE:TIMEOUT_SEC
DEVICES=( "sda:120" "sdb:120" )
 
function device_worker() {
	test -e "/dev/$1" || return 1
	logger -t hdd-spindown.sh "spawned monitor thread for $1"
	COUNT_R=0
	COUNT_W=0
	while true; do
		NEW_R=$(awk '{print $1}' /sys/block/$1/stat)
		NEW_W=$(awk '{print $5}' /sys/block/$1/stat)
		if [ $COUNT_R -eq $NEW_R ] && [ $COUNT_W -eq $NEW_W ]; then
			if hdparm -C /dev/$1 | grep active &>/dev/null; then
				logger -t hdd-spindown.sh "suspending $1"
				hdparm -qy /dev/$1
				if [ $? -gt 0 ]; then
					logger "failed to suspend $1"
					return 1
				fi
			fi
		else
			COUNT_R=$NEW_R
			COUNT_W=$NEW_W
		fi
		sleep $2
	done
}
 
for D in ${DEVICES[@]}; do
	device_worker $(echo "$D" | cut -d ':' -f 1) $(echo "$D" | cut -d ':' -f 2) &
done
 
exit 0

Auf Performance-Optimierungen habe ich bewusst verzichtet und alles möglichst simpel gehalten.

Funktionsbedingt ist der einstellbare Wert für den Timeout (T) übrigens nicht hart; die reale Zeit an Inaktivität ti genügt der Bedingung T ≤ ti < 2T.

aerotools Update

Dank der Semesterferien fand ich nun endlich Zeit, mein aerotools-Projekt auf Vordermann zu bringen. Neben einigen Bugfixes und Vereinfachungen am Daemon habe ich die Library auf libusb-1.0 portiert, was laut Homepage die empfohlene, aktuelle Version ist. Unter Arch coexistiert sie als libusb1 neben der libusb, die standardmäßig installiert wird – etwas verwirrend, wenn man den Hinweistext auf der Homepage nicht gelesen hat ;)

Alle aquaero®-Besitzer seien also herzlich eingeladen, die neue Version zu testen! Bugreports sind immer willkommen, am Besten über die “Issues”-Funktion auf Github.

logcheck-rules prüfen

Beim Ändern von ignore-Regelsätzen für logcheck unterlaufen mir ganz gern Tippfehler in komplexeren Ausdrücken. Dumm nur, wenn ich das erst beim nächsten Cron-Run merke, in Form einer solchen Meldung:

egrep: Unmatched ) or \)

Toll: weder Regelsatz noch Zeilennummer werden genannt, also manuell suchen. Da es sich aber meist um umfangreiche Regelsätze handelt und ich (selbst bei gefundenem Satz) keine Möglichkeit gefunden habe, egrep die Zeilennummer des ungültigen Ausdrucks zu entlocken, entstand auf die Schnelle folgendes simples Script:

#!/bin/bash
 
if [ $# -ne 1 ]; then
    echo "usage: $0 RULES_FILE"
    exit 1
fi
 
if ! [ -r "$1" ]; then
    echo "unable to read $1"
    exit 1
fi
 
LINES=$(wc -l "$1" | cut -d ' ' -f 1)
 
for I in $(seq $LINES); do
    echo "line $I..."
    echo "foo" | egrep "$(cat "$1" | head -n $I | tail -n 1)"
done
 
exit 0

Es gibt bestimmt auch irgendeine elegantere Lösung, aber vielleicht erspart es ja jemandem die nervige Sucherei :)

Arch ramdisk-script 1.4

In meinem Arch Ramdisk-Script <1.4 hatte sich ein dämlicher Bug eingeschlichen: bei sämtlichen rsync-Aufrufen fehlte das --delete.

Das bewirkt z.B. bei Verwendung mit Firefox, dass der Cache nie geleert wird und (streng) monoton wächst – unschön.

Hier die gefixte Version :)

#!/bin/sh
 
#
# Manages outsourcing of specified directories into memory on bootup and
# takes care of synchronization/backup on system shutdown.
#
# Version 1.4, 2010-04-26, by Alexander Koch
#
 
# includes
 
. /etc/rc.conf
. /etc/rc.d/functions
 
 
# configuration (syntax is: [persist. storage]:[mountpoint]:[mount options])
 
DISKS=('/home/alex/.ramdisks/_mozilla:/home/alex/.mozilla:size=100M,uid=1000,gid=100' \
	   'empty:/home/alex/.adobe:size=10M,uid=1000,gid=100' \
	   'empty:/home/alex/.macromedia:size=10M,uid=1000,gid=100')
 
 
# helper functions
 
function activate_rd() {
	[ -d "$1" ] || [ "$1" = "empty" ] || return 1
	[ -d "$2" ] || return 1
	mount | grep "$2" &>/dev/null && return 1
	MNT="mount -t tmpfs"
	[ -z "$3" ] || MNT="$MNT -o $3"
	$MNT none "$2"
	[ $? -gt 0 ] && return 1
	if [ "$1" != "empty" ]; then
		for D in $1/.* $1/*; do
			[ "$(basename "$D")" == "." ] && continue
			[ "$(basename "$D")" == ".." ] && continue
			rsync -axq "$D" "$2" &>/dev/null
			if [ $? -gt 0 ]; then
				umount "$2"
				return 1
			fi
		done
	fi
	return 0
}
 
function backup_rd() {
	mount | grep "$1" &>/dev/null || return 0
	if [ "$2" != "empty" ]; then
		for D in $1/.* $1/*; do
			[ "$(basename "$D")" == "." ] && continue
			[ "$(basename "$D")" == ".." ] && continue
			rsync -axq --delete "$D" "$2" &>/dev/null
			if [ $? -gt 0 ]; then
				tar -cf "/root/$(basename "$2")-failed.tar" "$1"
				return 1
			fi
		done
	fi
	umount "$1" || return 1
	return 0
}
 
 
# main logic
 
case $1 in
	start)
		stat_busy "Mounting ramdisks"
		error=0
		for M in ${DISKS[@]}; do
			FROM="$(echo "$M" | cut -d ':' -f 1)"
			TO="$(echo "$M" | cut -d ':' -f 2)"
			OPTS="$(echo "$M" | cut -d ':' -f 3)"
			activate_rd "$FROM" "$TO" "$OPTS" || error=1
		done
		if [ $error -eq 0 ]; then
			add_daemon ramdisks
			stat_done
		else
			stat_fail
			exit 1
		fi
		;;
	stop)
		stat_busy "Saving ramdisks"
		error=0
		for M in ${DISKS[@]}; do
			FROM="$(echo "$M" | cut -d ':' -f 2)"
			TO="$(echo "$M" | cut -d ':' -f 1)"
			backup_rd "$FROM" "$TO" || error=1
		done
		if [ $error -eq 0 ]; then
			rm_daemon ramdisks
			stat_done
		else
			stat_fail
			echo -n "WARNING: failed to save ramdisk(s), tried to make "
			echo "backup(s) under /root."
			echo "Hit enter to proceed shutdown."
			read DUMMY
			exit 1
		fi
		;;
	restart)
		if ! ck_daemon ramdisks; then
			"$0" stop && sleep 3
		fi
		"$0" start
		;;
	*)
		echo "usage: $0 {start|stop|restart}"
		;;
esac
 
exit 0