Debiteringssystem för CUPS-server

Hej, jag heter är Christian och jag praktiserar här på Örestad Linux.
Mina uppgifter utgörs för tillfället av att titta närmare på nätverksinfrastrukturen för hela Malmö Arena-kontorsförening – vilken vi tillhör – och pilla på olika lösningar för saker och ting som hör kontorslandskapet till.

Vidare till vad dagens bloggpost handlar om:
Jag fick under mina första dagar här uppgiften att implementera något slags debiteringssystem på skrivarservern. Anledningen är att kontoret ska koppla in en nyare färglaserskrivare där kostnaden för varje utskriven sida ökar markant jämfört med tidigare – och då skrivarservern delas av ett flertal företag i byggnaden är utskriftskontrollen en kommande prioritet.

Sagt och gjort – gröngölingen går loss på nätverket.

Syfte
Sätta upp en simpel billing/debiterings-lösning för en CUPS-server.
Tillvägagångssätt
Vid första anblicken utgörs problemet av två moment:
-Logga utskriftstillfällen
-Kalkylera antalet sidor för utskrivet dokument

Att logga utskriftstillfällen kan göras på ett flertal sätt, men för att på ett smidigt sätt hantera loggning och sidokalkylen i ett och samma moment valde jag att använda mig av programmet tea4cups.
Tea4cups är en fiffig open-source backend till CUPS med vilken man enkelt kan filtrera utskrifter och specifiera åtgärder som skall göras både före och efter en utskriftssession.
För sidkalkyleringen använder jag mig av pkpgcounter – ett smidigt program som kan räkna ut sidantal och även bläck/färgmängden(!) per angivet dokument.

Programvara
Installationen av tea4cups är enkel – hämta hem, extrahera, kopiera filer dit de ska – det exekverbara pythonskriptet placeras i /usr/lib/cups/backend/ och konfigurationsfilen i /etc/cups/.
När sedan CUPS startas om känner den av den och man kan lägga till skrivare med tea4cups-funktion – jag valde dock att konfigurera de skrivare jag ville skulle beröras av tea4cups genom att manuellt editera skrivarkonfigurationsfilen /etc/cups/printers.conf – och helt enkelt lägga till tea4cups:// före skrivarens DeviceURI:
DeviceURI tea4cups://hp:/usb/hp_LaserJet…
Starta om CUPS. Klart.

Programmet pkpgcounter hittar man antingen i aptitudes repositories
#apt-get install pkpgcounter
eller som tarball

Konfiguration
Om jag nu vill att något speciellt ska hända vid en utskrift lägger jag helt enkelt till en såkallad ”hook” i /etc/cups/tea4cups.conf. ”prehook_” kommer att köras innan utskriftsjobbet skickas vidare till CUPS och ”posthook_” körs när utskriften är klar.
Vad tea4cups även tillhandahåller är en uppsättning miljövariabler som kan komma till nytta vid ‘hook’arna – hela listan ges i dokumentationen – jag kommer att använda mig av
$TEACLIENTHOST -IPadress på klienten som skickat utskriften
$TEAJOBID -Jobid på utskriften, relativt mot filerna i /var/spool/cups/
$TEAPRINTERNAME -Namnet på den skrivare utskriften skickades till
$TEADATAFILE -Sökvägen till utskriftsjobbets temporära fil
$TEAUSERNAME -Användarnamn på klienten som skickade utskriften

Dessa variabler kommer jag att använda som parametrar till ett skript som kommer att köras från en posthook. Först kallar jag på skriptet med en hook i /etc/cups/tea4cups.conf

posthook_billing : /bin/bash /etc/cups/billing.sh $TEACLIENTHOST $TEAJOBID \ $TEAPRINTERNAME $TEADATAFILE `echo $TEAUSERNAME | sed ‘s/ /-/g’`
(sedbehandlingen av användarnamnet ersätter eventuella mellanrum i användarnamnet med streck)

Sedan ett skript som kan hantera dessa parametrar och mata ut en logg.

———–

#!/bin/bash
declare IP="$1"
declare JOBID="$2"
declare PRINTER="$3"
declare PAGES=`pkpgcounter $4`
declare USER="$5"
declare DATE=`date`
declare PLIST="/billing/prints"
if [ ! -f $PLIST ]; then
echo "$DATE" > $PLIST
chmod 644 $PLIST
fi
echo "$IP::$USER::$PAGES::$JOBID::$PRINTER::$DATE" \
>> /billing/prints
exit 0

———–
Lite omdöpande av variabler – men kontentan är att en rad med de inkommande parametrarna matas ut i en loggfil.

Dock vill jag även att MAC-adressen hos klienten som skickade utskriften anges i loggen – IPadresser är ju dynamiska och det är kanske inte en bra idé att införa ett debiteringssystem där man helt plötsligt kan ärva någon annans skuld..
Först löste jag detta med att använda IP-variabeln för att pinga klienten och sedan kolla upp MACadressen i arp-cachen.. men detta var opålitligt(blockera ping – skriva ut gratis. hmm.) så jag löste det med att göra ett skript som samarbetar med DHCP-servern där IP och MAC kan matchas i en lease.
Jag lägger till MAC-ändringarna i skriptet:
declare MAC=`ssh user@dhcpserver ./macfinder.sh $1`
echo ”$IP::$MAC::$USER::$PAGES::$JOBID::$PRINTER::$DATE” >> /billing/prints
Jag kallar på ett skript på dhcpservern via ssh(lösenordslös login!) – parametern som skickas är IPadressen och svaret är MACadressen den tillhör för nuläget.

Skriptet på DHCPservern ser ut som så här:
——–
#!/bin/bash
declare MAC
declare -i X=0
while read line; do
if [ $X -eq 1 ]; then
if [ "${line:0:8}" = "hardware" ]; then
MAC=`echo "$line" | sed 's/;//' | \
awk '{print $3}'`
break
fi
fi
if [ "$line" = "lease $1 {" ]; then
let X=1
fi
done </var/lib/dhcp3/dhcpd.leases
echo "$MAC"
exit 0

——-
Vad denna gör är att stycka upp dhcpd.leases och leta efter IPadressen som angavs och mata ut nästkommande MACadress.

Såja, vid det här laget bör billing/debiteringssystemet fungera – en loggrad bör se ut som
192.168.1.2::00:00:00:00:00:00::user::2::1680::HP_LaserJet_3015::Mon-Mar–1-14:40:01-CET-2010
Här syns en IP- och en MAC-adress från klienten, med anv.namn: user, som skickat ett skrivarjobb på två sidor till skrivaren HP_Laserjet_3015 den 1:a mars kl 14:40. Lagom detaljerad logg.

Nu – ett litet skript som kokar ihop loggen och får ut hur mycket klienterna har skrivit ut för. Just nu nöjer jag mig med att lista pris för varje loggad MACadress.
——-

#!/bin/bash
declare HPRICE="0.50"
declare LPRICE="0.10"
declare DATE=`date +"%B%d%Y"`
declare PAGES DEBT
declare -i RESULT
declare COLPAGES=0 NORMPAGES=0
declare LIST="/billing/prints"
declare MAC=(`awk -F "::" '{print $2}' $LIST | sort -u`)
declare OUTPUTFILE="/billing/RAPPORT$DATE"
declare COLPRINTER="EXPENSIVECOLORLASERPRINTER"
echo `date` > $OUTPUTFILE
for X in ${MAC[*]}; do
DEBT=0
COLPAGES=0
NORMPAGES=0
declare -a PRINTER=(`grep $X $LIST | awk -F "::" \
'{print $6}'`)
for Y in ${PRINTER[*]}; do
RESULT=0
PAGES=`grep $X $LIST | grep $Y | awk -F "::" \
'{print $4}'`
for Z in $PAGES; do
let RESULT=RESULT+Z
done
if [ "$Y" = "$COLPRINTER" ]; then
COST=`echo "$RESULT*$HPRICE" | bc`
COLPAGES="$RESULT"
else
COST=`echo "$RESULT*$LPRICE" | bc`
NORMPAGES="$RESULT"
fi
done
CDEBT=`echo "$COLPAGES*$HPRICE" | bc`
NDEBT=`echo "$NORMPAGES*$LPRICE" | bc`
DEBT=`echo "$NDEBT+$CDEBT" | bc`
echo "$X has current debt: $DEBT Kr -- \
color:$COLPAGES( $HPRICE ea.=total $CDEBT) - \
b/w:$NORMPAGES($LPRICE ea.=total $NDEBT)" \
>> $OUTPUTFILE
done
exit 0

——-
Detta skript går igenom skrivarloggen, sorterar ut MACadresser, sorterar ut vilka skrivare som använts av de olika MACadresserna och räknar ihop antalet utskrivna sidor per skrivare – jag har prissatt en skrivare lite högre, detta ska vara namnet på den dyrare färglaserskrivaren i framtiden, och alla andra skrivare har fått ett lägre pris. Sist matas en rad ut per MACadress där all samlad information om dess utskrifter räknats ihop.

Så – en lösning för ett simpelt billingsystem, löst med ett par enkla program och lite grundläggande skalskripting.
Tack för mig!

Tarball med skripts