Cls' Public Notes

dd Notes

To check if disk can be read without errors run

dd if=/dev/sda of=/dev/null

If read errors in source disks should be ignored

dd if=/dev/sda of=/media/backupdisk/image.img conv=noerror,sync

Compressing on the fly

dd if=/dev/sda | gzip > disk.img.gz (fast)
dd if=/dev/sda | bzip2 > disk.img.bz2 (slow)
dd if=/dev/sda | pigz > disk.img.gz (faster)

Uncompressing on the fly

gunzip -c /mnt/sda1/hda.img.gz | dd of=/dev/sda conv=sync,noerror

To get a better compression ratio, first fill the unused sectors of the disk with zeros like this

nice cat /dev/zero > zero.fill
sync
sleep 1
sync
rm -f zero.fill

It is a good idea to backup the disk partition info as well:

fdisk -l /dev/hda > /media/backupdisk/sda_fdisk.info

Locate Defective Sectors On Hard Disk And Fix Them

I encountered the following strange behaviour on my computer: Some programs didn’t respond for a long time whilst the hard disk activity LED was lit all the time but I couldn’t hear any seeking noise from the disk. I took a look at the kernel log and discovered the following:

Apr 16 12:06:45 deepblue kernel: [29731.290118] sd 0:0:0:0: [sda] Unhandled sense code
Apr 16 12:06:45 deepblue kernel: [29731.290123] sd 0:0:0:0: [sda] Result: hostbyte=DID_OK driverbyte=DRIVER_SENSE
Apr 16 12:06:45 deepblue kernel: [29731.290130] sd 0:0:0:0: [sda] Sense Key : Medium Error [current] [descriptor]
Apr 16 12:06:45 deepblue kernel: [29731.290140] Descriptor sense data with sense descriptors (in hex):
Apr 16 12:06:45 deepblue kernel: [29731.290145]         72 03 11 04 00 00 00 0c 00 0a 80 00 00 00 00 00 
Apr 16 12:06:45 deepblue kernel: [29731.290168]         06 8d 51 1e 
Apr 16 12:06:45 deepblue kernel: [29731.290177] sd 0:0:0:0: [sda] Add. Sense: Unrecovered read error - auto reallocate failed
Apr 16 12:06:45 deepblue kernel: [29731.290210] ata1: EH complete

This looked to me like a hard disk failure so I inspected the disk with the smartmontools

sudo smartctl -a /dev/sda

This revealed the following:

Error 2428 occurred at disk power-on lifetime: 1109 hours (46 days + 5 hours)
  When the command that caused the error occurred, the device was in an unknown state.

  After command completion occurred, registers were:
  ER ST SC SN CL CH DH
  -- -- -- -- -- -- --
  40 51 08 17 51 8d e6  Error: UNC 8 sectors at LBA = 0x068d5117 = 109924631

  Commands leading to the command that caused the error were:
  CR FR SC SN CL CH DH DC   Powered_Up_Time  Command/Feature_Name
  -- -- -- -- -- -- -- --  ----------------  --------------------
  c8 00 08 17 51 8d e6 00      02:07:21.312  READ DMA
  ec 00 00 00 00 00 a0 00      02:07:18.125  IDENTIFY DEVICE
  ef 03 46 00 00 00 a0 00      02:07:18.125  SET FEATURES [Set transfer mode]
  ec 00 00 00 00 00 a0 00      02:07:18.125  IDENTIFY DEVICE

So it seems that I have 8 defective sectors sitting at 0x068d5117 (109924631). Of course I was curious, which file would sit at this position and how I probably could mark this sector as defective. I found this very informative site: [http://smartmontools.sourceforge.net/badblockhowto.html].

I went ahead by checking on which partition this sector sits:

uli@deepblue:~$ sudo fdisk -lu /dev/sda
Disk /dev/sda: 160.0 GB, 160041885696 bytes
255 heads, 63 sectors/track, 19457 cylinders, total 312581808 sectors
Units = sectors of 1 * 512 = 512 bytes
Disk identifier: 0x0009bd2c

Device Boot      Start         End      Blocks   Id  System
/dev/sda1   *          63   299805029   149902483+  83  Linux
/dev/sda2       299805030   312576704     6385837+   5  Extended
/dev/sda5       299805093   312576704     6385806   82  Linux swap / Solaris

Seems that the defective sector is located in partition /dev/sda1. We need now the block size of this partition:

uli@deepblue:~$ sudo tune2fs -l /dev/sda1 | grep Block
Block count:              37475620
Block size:               4096
Blocks per group:         32768

The following formula finds the corresponding file system block number:

b = (int)((L-S)*512/B)

where:
b = File System block number
B = File system block size in bytes
L = LBA of bad sector
S = Starting sector of partition as shown by fdisk -lu
and (int) denotes the integer part.

Resulting in

b = (int)((109924631-63)*512/4096) = 13740571

With this information we are now able to determine the corresponding file:

uli@deepblue:~$ sudo debugfs 
debugfs 1.41.9 (22-Aug-2009)
debugfs:  open /dev/sda1
debugfs:  testb 13740571
Block 13740571 marked in use
debugfs:  icheck 13740571
Block	Inode number
13740571	159700
debugfs:  ncheck 159700
Inode	Pathname
159700	/home/uli/.mozilla/firefox/n87cja9y.default/places.sqlite

We can now check if this file really uses the defective sector by trying to access it

uli@deepblue:~$ cp /home/uli/.mozilla/firefox/n87cja9y.default/places.sqlite delme.bin
cp: reading `/home/uli/.mozilla/firefox/n87cja9y.default/places.sqlite': Input/output error

Ouch. Ok. Found. We can now fix it with the following commands

uli@deepblue:~$ sudo dd if=/dev/zero of=/dev/sda1 bs=4096 count=1 seek=13740571
1+0 records in
1+0 records out
4096 bytes (4.1 kB) copied, 7.4032e-05 s, 55.3 MB/s
uli@deepblue:~$ sudo sync
uli@deepblue:~$ rm /home/uli/.mozilla/firefox/n87cja9y.default/places.sqlite

The file is now lost though - in this case I was lucky though since it was just a dynamically generated data base of firefox. But since ff accessed it very often I was exposed to this defective sector in a heavy way.

You should run a fsck afterwards.

NOTE: THIS IS PURELY INFORMATIVE. EVERYTHING YOU DO YOU DO AT YOUR OWN RISK.

This is available as script from [http://www.pro-linux.de/NB3/artikel/2/140/2,fehlerhafte-festplattensektoren-neu-zuweisen.html] in German.

M-Audio Audiophile 2496 w/ Pulse Audio (Ubuntu 9.10)

Comment out the following lines in /etc/pulse/default.pa

load-module module-udev-detect
load-module module-detect

and add the following lines below

load-module module-alsa-sink sink_name=M2496_out device=hw:M2496 format=s32le channels=10 channel_map=left,right,aux0,aux1,aux2,aux3,aux4,aux5,aux6,aux7
load-module module-alsa-source source_name=M2496_in device=hw:M2496 format=s32le channels=10 channel_map=left,right,aux0,aux1,aux2,aux3,aux4,aux5,aux6,aux7
set-default-sink M2496_out
set-default-source M2496_in

Thinkpad T60 fan control for Ubuntu 9.10

Get source distribution, compile and install:

wget http://phuk.ath.cx:3080/thinkfan/thinkfan-latest.tar.gz
tar xzf thinkfan-latest.tar.gz
cd thinkfan
make
sudo cp thinkfan /usr/bin/thinkfan
sudo cp thinkfan.conf.thinkpad /etc/thinkfan.conf
cd ..

Thinkpad acpi driver must be loaded with fan control enabled. For that we set module loading parameters as required for ubuntu:

sudo echo "options thinkpad_acpi fan_control=1" > /etc/modprobe.d/thinkpad_acpi.conf
modprobe -r thinkpad_acpi && modprobe thinkpad_acpi

The thinkfan daemon should be launched at startup, so we need a startup script:

#!/bin/bash
### BEGIN INIT INFO
# Provides:       thinkfan
# Required-Start: $syslog
# Required-Stop:
# Default-Start:  2 3 4 5
# Default-Stop:   0 1 6
# Short-Description: 
### END INIT INFO

case "$1" in
start)
    echo -n "Starting thinkpad fan control..."
    /usr/bin/thinkfan -q -p 1.0
    echo "done."
    ;;
stop)
    echo -n "Stopping thinkpad fan control..."
    kill `cat /var/run/thinkfan.pid`
    echo "done."
    ;;
*)
    echo "Usage: $0 {start|stop}"
    exit 1
esac

Save this to a file called thinkfan.sh and:

chmod uga+x thinkfan.sh
sudo cp thinkfan.sh /etc/init.d/
sudo sudo update-rc.d thinkfan.sh defaults

HW Info

for VirtualBox’ USB forwarding the vendor ID and device name must be known.

hwinfo | grep usb

process files recursively

dos2unix `find ./ -name "*.sh"

whereas dos2unix can be any command accepting multiple arguments.

Extracting Portions of a File

Extract everything between

//START

and

//END

from source.c and print it into target.c

cat source.c | sed -n '/\/\/START/,/\/\/END/ s/.*/&/p' > target.c

Firefox memory muching

Sometimes I really hate ff, because it eats up my memory like mad. One solution which forces ff to free up memory on minimizing it can be set in ff’s preferences. Add boolean

config.trim_on_minimize

and set it to true.

mingw gdb and eclipse

More recent gdb releases on windows stop on every library event. After the first halt enter

set stop-on-solib-events 0

in the gdb command line. This should disable this behavior. Unfortunately this command takes effect not before the first halt. Adding this command to the .gdbinit has no effect.

Run windows installer from command line

msiexec.exe /i installer-of-your-program.msi

Combine multiple pdf files w/ ghostscript

gs -dBATCH -dNOPAUSE -q -sDEVICE=pdfwrite \
        -sOutputFile=all.pdf file1.pdf file2.pdf

lrzsz on cygwin

I found out that this is only required when the drive is mounted in text mode. When mounted in binary mode no changes are required

Get the lrzsz package from http://www.ohse.de/uwe/software/lrzsz.html and apply the following patch to src/lsz.c:

$ diff ../src/lsz.c.bak ../src/lsz.c
847c847
<               fd=open(tmp,O_WRONLY|O_CREAT|O_EXCL,0700);
---
>               fd=open(tmp,O_WRONLY|O_CREAT|O_EXCL|O_BINARY,0700);
1044c1044
<       } else if ((input_f=fopen(oname, "r"))==NULL) {
---
>       } else if ((input_f=fopen(oname, "rb"))==NULL) {

This fixes the problem that binary sequences in the file like 0x0D 0x0A get corrupted to 0x0A due to windoze/micro$ofts fucked up “textmode”. I should bill them for the time I had problems w/ that.

Note that I have tested lsz only. The correctness of lrz is unknown (and I even read on some lists that there were some problems - perhaps related to this?).

Here some useful commands to use lsz in conjunction w/ redboot:

# set baudrate
stty -F /dev/com1 115200
printf "load -r -m ymodem -b 0x30000\n" > /dev/ttyS0
lsz --zmodem ../bin/myOperatingSystem.bin <> /dev/ttyS0 >&0

...

Copy partition table from one HD to another

sfdisk -d /dev/sda | sfdisk /dev/sdb

List all partitions of all drives

fdisk -l

Runlevels:

  • 0 to halt computer (turn off)
  • 1 for single user,
  • 2 for text mode
  • 3 for text mode with network
  • 5 for graphic system
  • 6 to reboot computer

...

When I really dislike something:

#pragma once
 
#include "stdafx.h"
 
#include <vector>
#include <string>
using namespace std;

This is compiler anti-portability paired with unused headers (the guy was to lazy to check if fsking “stdafx.h” is required at all) and name space spoiling.

Run Process not as Child of a Terminal

$ nohup abcd &
$ exit
But the screen application is better.

abcde4ever.pl

#! /usr/bin/perl -w
 
# this script encodes your CD library to whatever format using
# "a better CD encoder" (abcde) with as few user intervention
# as possible (CD changing by hand is required though).
 
use strict;
package main;
 
$| = 1;
 
print <<HEADER ;
$0 version 1.0 (uli franke <cls\@nebadje.org>)
inspired by autorip.pl from jonathan
 
usage guide:
	1. configure /etc/abcde.conf
	2. run $0
	3. drop audio CD into the CD-ROM tray.
	4. do something else until autorip ejects your CD.
	5. repeat steps 3-4 until CD supply is exhausted.
 
HEADER
 
while (1) {
	print "Waiting for next disk...\n";
	{
		while (1) {
			my $error = `cdparanoia -qQ 2>&1`;
			last if (($? >> 8) == 0);
			sleep(2);
		}
	}
	# verbosed, non interactive, eject CD when done
	system ("abcde -N -V -x");
}
 
exit(0);

ZyXel G-302 v3 on Debian Etch 2.6.18-5-686

Get the latest rtl-wifi release from http://rtl-wifi.sourceforge.net/wiki/Installing. I used http://www.hauke-m.de/fileadmin/rtl-wifi/rtl-wifi-20070729.tar.bz2 (rev. 57). Untar it then build it

cd rtl-wifi
make

Remove the default ieee80211 modules (you can backup them somewhere outside the /lib/modules directory):

find /lib/modules/`uname -r` -name ieee80211
rm -r /lib/modules/`uname -r`/kernel/net/ieee80211

test your freshly built modules with

insmod ieee80211/ieee80211_crypt-rtl.ko
insmod ieee80211/ieee80211_crypt_wep-rtl.ko
insmod ieee80211/ieee80211_crypt_tkip-rtl.ko
insmod ieee80211/ieee80211_crypt_ccmp-rtl.ko
insmod ieee80211/ieee80211-rtl.ko
insmod rtl818x-newstack/r8180.ko

Perhaps you setup your network before doing this by adding your wireless configuration similar to the following to your /etc/network/interfaces file

# Wireless
auto wlan0
allow-hotplug wlan0
iface wlan0 inet dhcp
      wireless-mode managed
      wireless-essid XXXXXXXXXXXXXXX
      wireless-key XXXXXXXXXXXXXXXXXXXXXXXX
      wireless-keymode restricted

If you can connect to your network the modules have to be installed such that they get loaded at boot time. The make install mentioned in the rtl-wifi wiki did not work, therefore you have to install them manually (from within your rtl-wifi dir):

cp iee80211/*.ko /lib/modules/$(uname -r)/kernel/net/ieee80211/
cp rtl818x-newstack/r8180.ko /lib/modules/$(uname -r)/kernel/drivers/net/wireless/

Then rebuild the modules dependency database (found in /lib/modules/$(uname -r)/modules.dep) with

depmod

And finally add your modules as follows to your /etc/modules file (the modules listed there get loaded at boot time):

ieee80211_crypt-rtl
ieee80211_crypt_wep-rtl
ieee80211_crypt_tkip-rtl
ieee80211_crypt_ccmp-rtl
ieee80211-rtl
r8180

That’s it. Any questions? Email me: cls{A_T}nebadje{D_O_T}org

Cygwin X-Server CH Keyboard

To get a swiss keyboard running w/ the default Cygwin installation Roller found out that you have to install a symbolic link

ln -s de de_CH

in

/etc/X11/xkb/symbols/pc

The umlauts and accents won’t work but at least all important keys are mapped correctly.

FM TX

Eagle

  • Move a certain part to a certain position MOVE part (x y)

Un-tar gz and bz2

tar xzf expat-2.0.0.tar.gz
tar xjf whatever.tar.bz2

Links

Latex Source Listings

% first this
\usepackage{listings}

% then this (listings settings, can be redefined for each code inclusion ...)
\lstset{% general command to set parameter(s)
	basicstyle=\small\ttfamily, % print whole listing small and typewriter
	keywordstyle=\color{blue}\bfseries, % bold blue keywords
	identifierstyle=, % nothing happens
	commentstyle=\color{red}, % white comments
	stringstyle=\ttfamily\emph, % typewriter type for strings
	showstringspaces=false, %
	frame=tRBl, %
	language=[ANSI]C, %
	tabsize=4,
	numbers=left,
	numberstyle=\tiny,
	stepnumber=2,
	numbersep=5pt}

% finally this here...
\lstinputlisting[]{../../firmware/globals.h}

Dark Star

I love John Carpenter’s Dark Star

4.10.2006 21:00

cimg1280r.jpgcimg1277r.jpgcimg1278r.jpgcimg1281r.jpgcimg1282r.jpgcimg1285r.jpgcimg1286r.jpgcimg1287r.jpgcimg1288r.jpg

Letters in Latex

\documentclass[a4paper,twoside,10pt]{letter}
\usepackage[ansinew]{inputenc} % äü...
\pagestyle{plain}
\usepackage{a4}
% uncomment if envelope address labels should be printed
% \makelabels
\address{
	Va Fanculo\\
	Via Stronza 2\\
	666 Zurigo\\
	Switzerland}
\telephone{0041 44 XXX XX XX}
\name{Uli Franke}
\signature{Uli}
\begin{document}
\begin{letter}{
	Rolf Anderegg\\
	Hell's Avenue 667\\
	8055 Zürich\\
	Switzerland}
\opening{Liebster Roller,}
Hei, so geil. Ab jetzt schreibe ich meine Briefe nur noch in \LaTeX2e \ldots
\closing{Alles Gute, Dein}
\end{letter}
\end{document}

Tgif on Cygwin

  1. Download the current source code distribution from the tgif homepage: http://bourbon.usc.edu/tgif/download.html, select the Open Source tar.gz (NOT the binaries of course)
  2. You’ll need the X11 headers and the X11 base system. Make sure they are installed (use the cygwin setup)
  3. unzip the code into any directory in your cygwin home dir
  4. type the commands listed below
  5. run the x-server by typing $ startx
  6. run tgif from the xterm by typing $ tgif&
rm Tgif.tmpl
cp Tgif.tmpl-cygwin Tgif.tmpl
xmkmf
make tgif.exe
make install

Zidane

Including shared libraries in the bundle (MacOS X)

uli-in-20-years.jpg The dynamic libraries on MacOS X have to reside in /usr/local/lib. The linker includes the paths to the dylibs in the executable what makes it difficult to have dylibs on different locations. If an application has to be “self-contained” and must be installable without copying to any system directories (by leaving the libraries in the bundle) but it relies on dynamic libraries (.dylib) it can be chosen between two methods:

  1. Setting a temporary dylib search path by altering the environment variable DYLD_LIBRARY_PATH (adding the path of the dylib location). This can be useful for debugging but not for distribution.
  2. Patching the binaries (executables and dynamic libraries) depending on certain dynamic libraries to their new location.

Method 2. is described up to a certain degree here. This information has to be extended by mentioning that when linking the binaries (libraries and executables) the -headerpad_max_install_names should be turned on. Otherwise complications may arise due to the fact that there is no room for a change of the pathnames of the dynamic libraries to the new locations. Furthermore when using a library consisting of several dynamic libraries (e.g. wxWidgets) all library-internal cross references have to be removed (replaced) too. In the case of wxWidgets the library has been linked internally to the major.minor.micro named versions of the dynamic libraries (e.g. libwx_mac_qa-2.6.0.dylib).

To accomplish this task I’ve written a bash script which is listed here:

WXLIBPOSTFIX=*wx*2.6.0.dylib
APP=WorldApp
 
WXLIBDIR=../../../wxwidgets/build-dynamic/lib
BINDIR=./build/Release/$APP.app/Contents/MacOS
LIBDEFDIR=/usr/local/lib
 
echo "Copying dynamic libraries to " $BINDIR " ..."
cp $WXLIBDIR/*.dylib $BINDIR
 
echo "Changing directory to " $BINDIR " ..."
export TMP=$PWD
cd $BINDIR
 
# patch all wx dynlibs and Saracon executable
for file in `ls $WXLIBPOSTFIX`
do
      # patch all library internal cross references
      echo "Patching " $file "..."
      for fileother in `ls $WXLIBPOSTFIX `
      do
            # library
            echo "  Patching " $fileother " with " $file "..."
            install_name_tool  -change $LIBDEFDIR/$file @executable_path/$file  $fileother
      done
      # patch current library itself
      install_name_tool  -id  @executable_path/$file  $file
      # patch executable
      install_name_tool  -change $LIBDEFDIR/$file @executable_path/$file $APP
done
cd $TMP

This script can be added for instance to the build phases of XCode (after linking) or called from within of a makefile. Make sure that it’s a bash shell, others might cause errors.

The following variables are required and must be set by the user:

  • WXLIBPOSTFIX: The wx dylib pattern which matches the desired version and wx-internal cross references.
  • APP: The executable name.
  • WXLIBDIR: Directory of the wxWidgets dylibs (can also be a system directory when installed)
  • BINDIR: Directory of the executable AND dylibs (it’s a limitation of this script but it can be adapted easily)
  • LIBDEFDIR: Default dylib directory when linking (ususally it’s /usr/local/lib). It can be found out when inspecting the crash report...

Pantani Memorial Velotour

Fotos der Pantani Memorial Velotour 2006 können hier heruntergeladen werden: pantani-2006-uli.zip (6MB)

curva-prima-berguen.jpgpeloton.jpggruppo-berguen.jpgrhb-ponte.jpg bici-small.jpg

Gimp Scripts

Some Script-Fu scripts to efficiently handle repetitive image processing jobs.

Deep Copies in C++

If a class which allocates memory dynamically provides a copy constructor and overloads the assignment operator, then another class which uses this class as a member will do deep copies of those objects automatically.

Remember

A good team player

  • Does not imply that each team member has to be nice to each other
  • Demands also a good handling of conflict situations
  • Does not reject ideas as a matter of principle
  • Can tolerate other’s deficiencies

Audio Signal Detection

Detection of audio signals can become handy for remote amplifiers which switch on autonomously on audio input activity and turn of after a certain delay.

The circuit presented here is an excerpt of an upcoming project here at nebadje and it has been designed and simulated last weekend (18.02.2006) by jns and cls. The simulation makes use of a very nice feature of LT-Spice (see next section): The ability of using wav-files as voltage source.

The circuit is a precision full-wave rectifier combined with an integrator (at the second operational amplifier U2). It rectifies the audio input signal and integrates the result with a timeconstant formed by R4 and C1. U2 is single supply but that s not mandatory, it’s simply useful when this part of the circuit is operated on a digital board.

The green graph (top) shows the input, the blue (bottom) the output. Click the pictures for larger view. The project file for LT-Spice is available here.

Monitoring Negative Voltages


How to monitor negative voltages with a microcontroller which is powered by a positive voltage (e.g. 5V) using only the 5V supply and as few components as possible? Make use of the virtual ground of an single supplied opamp in inverting amplifier operation. The common node of R1 and R2 will be at 0V and the current through R2 will be copied into R1, because of the very high input impedance of the opamp.

U_{Out}/U_{In} = - R_1/R_2


The upper graph is the output the lower the input. Rail-to-rail opamps are more suitable than the opamp used in this example (LT1001). The LM324 (LM124 = single) for example swings down to ground even when powered from a single positive supply. Citing its datasheet:

In the linear mode the input common-mode voltage range includes ground and the output voltage can also swing to ground, even though operated from only a single power supply voltage

The SPICE analysis shown here was done with LT-Spice.

"Register" Button for DokuWiki

[cls] 2006/02/11 00:04
We had many confused users who did not know how to get to the registration page. This button (rendered beneath the “login” button of our current DokuWiki template) should solve this issue.

<?php
 
/** Generates buttons with $caption calling the link
 * specified in $link.
 * $ author: cls$
 */
function html_link_button($caption,$link){
    $ret  = '';
    $ret  = '<a href="'.htmlspecialchars($link).'"><input type="button" class="button" value="'.htmlspecialchars($caption).'" /></a>';
    print $ret;
}
 
/** Generates a button which opens the "Register" page within
 * DokuWiki, when ACL is enabled and the current user is not
 * logged in.
 * $ author: cls$
 */
function html_register_button(){
	global $conf;
	if($conf['useacl']){
		if($_SERVER['REMOTE_USER']){
		}else{
		    html_link_button('Register', '?id=start&do=register');
		}
	}
}
?>

Logging Downloads in DokuWiki

Patch for the ./lib/exe/fetch.php module of the DokuWiki. Logs all (currently scanning for .mp3 files) downloads passing through fetch.php and writes the details to a log file located in ./data/medialog.log.

To patch your current DokuWiki distribution, perform the following steps:

Step 1

Add the following line to the “fetch” code (to the end, before the functions):

addMediaLogEntry($FILE);

Step 2

Add the following line to the ./conf/dokuwiki.php main configuration file:

$conf['medialog']    = 'medialog.log';    //must be placed in data dir

And create the empty text file ./data/medialog.log.

Step 3

Finally append the following function to the end of the fetch.php file.

/**
 * Add's an entry to the media log
 * HACK! currently logs only mp3
 * Stolen and adapted from the generic
 * addLogEntry function.
 * @author Uli Franke <xxx@nebadje.org>
 */
function addMediaLogEntry($mediafile){
  global $conf;
  
  //log only mp3
  if(!(substr($mediafile,-3,3) == 'mp3')){
    return;
  }
  
  $file = DOKU_INC.$conf['savedir'].'/'.$conf['medialog'];
 
  if(!@is_writable($file)){
    msg($file.' is not writable!',-1);
    return;
  }
 
  //$date = time();
  //somehow human readable
  $timestamp = time();
  $date = date("Y.m.d - H:i",$timestamp);
  $remote = $_SERVER['REMOTE_ADDR'];
  $user   = $_SERVER['REMOTE_USER'];
  $size   = filesize($mediafile);
  $logline = join("\t",array($date,$remote,$user,$size,$mediafile))."\n";
  io_saveFile($file,$logline,true);
}

Notes

The entries in the generated logfile look like:

Time IP User Size [Bytes] File
1139663347 80.254.166.165 chaib 9109944 /.../tracks/klangforschung/waermeregefilde.mp3
2006.02.11 - 14:09 80.254.166.165 chaib 9109944 /.../tracks/klangforschung/waermeregefilde.mp3

If you like to log all downloads (attention: this includes every stupid picture every user gets displayed), modify the following lines of the above function according to your needs.

  //log only mp3
  if(!(substr($mediafile,-3,3) == 'mp3')){
    return;
  }

Generating Statistics

Logging functionality without analysis doesn’t make sense somehow. Therefor here some code which generates a nice table listing all users sorted by their download behaviour.

The function has to be located in a function.php (or similar) file located in the template directory and the function itself has to be added to the main.php just below the wiki generator (or somewhere else if you like)1). The download logging patch is running of course. To get the statistics displayed

  1. You must have admin rights and
  2. call http://www.mycoolwiki.net?stat=dwn (This is just an example, it works on every wiki page...)

Implementation

if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/');
require_once (DOKU_INC.'inc/auth.php');
 
/** Print out statistics. global arguments:
 *
 *	name: 'stat'
 *		value: 'dwn':	download statistics
 *
 */
function html_print_statistics(){
 
	global $conf;
	$STATS = $_REQUEST['stat'];
	
	//print $STATS;
	
	if ( auth_quickaclcheck('') < AUTH_ADMIN ) return;
	
	if ( $STATS == 'dwn' ){
		
		$filename = DOKU_INC.$conf['savedir'].'/'.$conf['medialog'];
	
		$handle = @fopen($filename, "r");
		
		if ($handle) {
		
			$entries = array();
			while (!feof($handle)) {
			
				// get a line from log file
				$buffer = fgets($handle, 4096);
				
				// parse line into words
				$words = array();
				$numwords = 0;
				$lastwordpos = 0;
				for( $i = 0; $i < strlen($buffer) ; $i++) {
					
					// space or tab found
					if ( $buffer{$i} == "\t" || $buffer{$i} == " " ){
						$words[$numwords] = substr($buffer, $lastwordpos, $i - $lastwordpos);
						trim( $words[$numwords] );
						$numwords++;
						$lastwordpos = $i;
					}
				}
				// one entry per user gets accumulated
				$entries[$words[2]] = $entries[$words[2]] + $words[3];
			}
	
			// sort (highest at top (idx=0))
			arsort ($entries);
			$users = array_keys($entries);
			$amount = array_values($entries);
			$i = 0;
			print	'
				<br><br>
				<a name="download_statistics"></a><h1>Download Statistics</h1>
				<br>
				<table class="inline">
				<tr>
					<th class="rightalign">User:</th>
					<th class="rightalign">Data [Bytes]</th>
				</tr>';
			foreach( $users as $a ){
				print '<tr>';
				print '<td class="rightalign">'.$users[$i].'</td>';
				print '<td class="rightalign">'.$amount[$i].'</td>';
				print '</tr>';
				$i++;
			}
			print	'</table><br><br>';
			
		}
		fclose($handle);
	}
}
1) Add @require_once(dirname(FILE).’/functions.php’); to the head of the file
 
forum/blogcls.txt · Last modified: 2010/04/20 13:51 by cls
 
Nebadje Controls You