Live Transcoding for Video and Audio Streaming

With slowly increasing upload bandwidths (even in Germany), video and audio streaming for home servers become feasible. In Germany, currently the highest upload bandwidth you can get as a non-corporate is 10 Mbit/s with VDSL, enough to provide actual viewable video streaming in near DVD quality to a small number of concurrent viewers. However it was kind of hard to get a setup working that suits my requirements.

In my case the starting point are video and audio files, the video files are mostly XviD (MPEG4) encoded (with one or multiple MP3 or AC3 audio streams) in AVI containers. The audio files are mostly MP3 files with a constant or variable bitrate.

Because I want to stream (more or less) directly to the browser, possible player technologies are: Browser Plugins (WMP/ActiveX, Mplayer/VLC Mozilla Plugins, Quicktime), Flash or HTML5 Video and Audio Tags.

All of them support different codecs, containers and streaming protocols. I use HTTP as a streaming protocol for a numerous of reasons, but lets just say the decisive factors are that HTTP is supported by all mentioned players and is easy to integrate in existing web applications.

Browser Plugins

The different player plugins are a mess, there is no standard what so ever and it is hard to make any cross platform assumption on support of this. It has the theoretical capability to stream my video and audio file formats but in my experience it is most likely to fail out of the box so I would not count on that.

Flash

The popular flash plugin supports the mediocre Sorenson Spark and VP6 video codecs and most recently H.264 video. The audio codecs are MP3 and most recently AAC. Flash uses its own Container Format FLV, in more recent versions other containers are also supported.

There are two notable non-free open source flash players. The jwplayer (custom commercial/open source license) with a phone home logo/branding and flowplayer with a (In my mind) violating GPLv3 dual license and branding.

Maybe I should mention that Flash supports their own proprietary streaming protocol RTMP, since its DRM infected (RTMPe) and just evil, I ignore it, even through there is an open source server implementation.

The major problem of flash video is (besides of its proprietary status) the exceedingly modest video decoder that requires high end hardware and obscene resources to play even medium quality video.

HTML5

Most recent and upcoming browsers implement the Video and Audio functionality of HTML5. Unfortunately and due to the nature of HTML5, w3c does not specify video and audio codec requirements. As a result every browser and even different versions have a different subset of supported codecs. The formats are (Video Codec/Audio Codec/Container): Theora/Vorbis/OGG, H.264/AAC/MP4 and VP8/Vorbis/WebM. For more information visit the excellent site diveintohtml5.org.

Transcoding

Form to create streams with advanced settings.

I want support for HTML5 and Flash for my home server streaming setup, this should support streaming for all modern browsers and operating systems (even mobile devices). As you may have noticed, none of them can handle my commonly used file format and the variety of formats used for HTML5 makes transcoding unavoidable.

Transcoding means the encoding from one digital video/audio file to another. So the solution would be to transcode all files to the required formats (at least Sorenson Spark, H264 and OGV) beforehand. And now comes the tricky part: Because for some reasons (too many files, no disk space to waste) this is not an option in my case. Now normally that would be the end of this article, but I came up with a solution although it has its own problems and limitations.

Live Transcoding

The basic idea is to start the transcoding process the same time the user requests streaming for a audio/video file. It works kind of like a live feed, but instead of using the video and audio stream from a camera and microphone, it reads video and audio files. Because it starts a resource hungry encoding process for each stream, the maximum possible concurrent viewers are very limited. In my case, because my upload bandwidth is limited anyway, this is not really a problem, but in other, larger setups, it sure is.

Another big limitation is seeking, although theoretically possible with pseudo HTTP streaming, it is very difficult to implement in this kind of live streaming. I have some vague ideas how this could work, but I’ve not implemented any of it. As a kind of workaround, the user can specify a timecode, FFmpeg should seek to, before the transcoding starts.

One last thing: I talked about the different formats supported by HTML5 capable browsers. Some of them only support H.264/AAC within MP4 container. The problem is that, the MP4 container is not capable of streaming this way because it needs to seek inside the output file. FFmpeg will just quit with the message muxer does not support non seekable output. Theres a alternative for Apple stuff but I do not plan to implement that anytime soon.

Proof of Concept

I’ve created a proof of concept implementation using PHP, FFmpeg for transcoding and MPlayer to identify encodings and bitrates. The user can select player technology, audio/video bitrate (to transcode for lower bandwidths) and adjust the transcoding settings in detail. My solution is pointed to technical experienced users, depending on your audience you probably would use browser sniffing to hide technical details completely. The scripts need Linux, PHP5 and the Smarty Template Language installed. If you want to give it a try I’ve created a github repository: php-live-transcode. Make sure you met the requirements and change the config.inc.php file to match your setup.

Conclusion

I’m well aware about the different problems of this peace of software, although some could be fixed, there are some fundamental problems that are just unavoidable, so it is important that you are aware of the limitations before you use any of this. Notice the configuration value FFMPEG_MAX_INSTANCES, this defines how many ffmpeg instances should be allowed to run simultaneously, this is very important because this setup just calls out for a Denial of Service attack, so be careful.

I guess thats it for now, please leave a comment if you have tried it. Please use the issue tracker at github if you encounter a problem.

Memory Debugging or a “Universal Game Trainer” with Python and ptrace

Reading and writing other process memory can have many different purposes: besides debugging, this technique is probably most frequently known to be used by game trainers, to alter different values of a running game (like the value of health or money).

Most (if not all) of the game trainers available, are developed for Windows and only for a specific game, but there are some universal trainers, that could be used to locate and change values in memory of any game or application and with kcheat and Tursiops there are some solutions for Linux.
I’ve developed a proof of concept to locate and change any byte value using python and the ptrace bindings. ptrace is a Unix system call for controlling other processes, it is for instance used by the gdb debugger.

The following code creates a debugging instance and attaches the other process by PID:

from ptrace.debugger.debugger import PtraceDebugger
 
dbg = PtraceDebugger()
process = dbg.addProcess(1234, False)

Now the process instance can be used to inspect and manipulate the process. To manipulate the memory it is important to know the virtual memory mappings (‘areas’) of the process:

from ptrace.debugger.memory_mapping import readProcessMappings
 
memory_mapping = readProcessMappings(process)

Now the list contains MemoryMapping instances, describing the virtual memory areas of the process. We are interested in their location and permission (Quickref: mapping.start, mapping.end, mapping.permissions). The trainer should only be searching within memory mappings with “rw” permissions.

Last but not least, the reading and writing from/to memory methods:

value = process.readBytes(0x11223344, 1) # this reads 1 byte
process.writeBytes(0x11223344, value) # writes len(value) bytes

After that you can close the debugger instance by:

dbg.quit()

I encountered some difficulties with closing/detaching the debugging process, it seems that the process is only really detached if the script is terminated, as a workaround I forked the memory search process, to circumvent that in my proof of concept trainer.

That’s it! If you’re interested, you can checkout my proof of concept for a universal game trainer: pycheat.py (GPLv3) It can locate and change byte values (char) in other processes memory:

% python pycheat.py 8169
pycheat: universal game trainer v0.1
process id: 8169
highest address: 0xffffff
 
searching for address by byte value: 23
search memory area[0x00DB8000-0x00DC0000] address[0x00DBD989] value[0x17 (023)]    
 
found 5926 occurrences, change value to: 33
search memory area[0x00DB8000-0x00DC0000] address[0x00DBFD88] value[0x21 (033)]    
 
found 1 occurrence! correct address for this value is 0x00C4335A
change value in memory at 0x00C4335A to: 99
done.

First you enter the known value, the trainer is searching it inside the memory, chances are high, that there are many occurrences of it, so you change the value inside the game and repeat the search with the new one. After ~2-4 iterations the correct location should be found and you can manipulate it.

edit: smaller changes

Dynamic Loading of SDL

If you want to use the SDL library (or any other library for that matter) in your programs, you normally would just link with the library, for instance by -lSDL (GCC). Sometimes however, it would be more flexible to dynamically load the library at runtime. With dynamic loading, if your application supports alternative libraries (e.g.: GLUT SFML as an replacement for SDL), it can discover and use available libraries at runtime. The system your program is running on, just needs one of those libraries installed and can switch them without the need to recompile your program. This technique is probably most commonly used to load plugins into programs at runtime.

Theres a helpful Wikipedia article about Dynamic loading, they even describe how to load the SDL library in their examples. Besides that, theres an old discussion about that at lists.libsdl.org and an article at eaten by a grue (when good libraries go bad). However I didn’t find a complete example implementation for this, so I’m trying to create one with this article, although I’m not a very experienced C++ programmer.

The first thing to do is to load the library file, on POSIX/UNIX systems we do this with dlopen(), on windows we use LoadLibrary() and for closing dlclose() and FreeLibrary() respectively. The following code snippet for open/closing the library should work on both Linux and Windows systems:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#ifdef _WIN32
    #include <windows.h>
#else
    #include <dlfcn.h>
#endif
 
// [...]
 
#ifdef _WIN32
    HMODULE libhandle;
    libhandle = LoadLibrary("SDL.dll");
#else
    void *libhandle;
    libhandle = dlopen("libSDL.so", RTLD_LAZY);
#endif
 
// ... dlsym() and GetProcAddress() calls ...
 
#ifdef _WIN32
    FreeLibrary(libhandle);
#else
    dlclose(libhandle);
#endif

You need to link your programs to the dl library for this to work (such as -ldl in gcc).

The returned handle can be used to retrieve symbol addresses and then to convert them to function pointers. Theres dlsym() on POSIX and GetProcAddress() on Windows machines. The following code does that cross-platform for SDL_SetVideoMode():

1
2
3
4
5
6
7
8
SDL_Surface * (*p_SDL_SetVideoMode)(int, int, int, Uint32);
#ifdef _WIN32
    p_SDL_SetVideoMode = 
        (SDL_Surface * (*)(int, int, int, Uint32)) GetProcAddress(libhandle, "SDL_SetVideoMode");
#else
    p_SDL_SetVideoMode = 
        (SDL_Surface * (*)(int, int, int, Uint32)) dlsym(libhandle, "SDL_SetVideoMode");
#endif

You need to look at the header files (/usr/include/SDL/SDL_video.h) for the exact declaration (return and parameter types).

Theres a problem here, when casting from an void pointer that is returned by dlsym() to a function pointer which is prohibited by the C/C++ ISO standards. I’m using a simple explicit (C-style) cast here and in practice, I not even encountered a warning with modern compilers.

Johan Petersson has written an article (When standards collide: the problem with dlsym) about that problem in Scatter/Gather thoughts, I want to quote:

You probably noticed that I omitted the C-style cast from my earlier example. Alas, most C and C++ compilers will allow the conversion when you use a C-style cast. You may not even get a warning, even though it’s prohibited in ISO C as well as ISO C++. This kind of conversion is a common compiler extension. So common, in fact, that many people don’t realize it’s not in the standards.

The article is from 2004, I don’t know if the current C/C++ standards had changed any of that.

In the last code snippet theres an redundancy that can be resolved by using a typedef instead:

1
2
3
4
5
6
7
typedef SDL_Surface * (*Type_SDL_SetVideoMode)(int, int, int, Uint32);
Type_SDL_SetVideoMode p_SDL_SetVideoMode;
#ifdef _WIN32
    p_SDL_SetVideoMode = (Type_SDL_SetVideoMode) GetProcAddress(libhandle, "SDL_SetVideoMode");
#else
    p_SDL_SetVideoMode = (Type_SDL_SetVideoMode) dlsym(libhandle, "SDL_SetVideoMode");
#endif

Thats everything you need to call that function: (p_SDL_SetVideoMode)(...); The major problem here is that SDL has over 200 functions that “must” be declared and addressed this way. I’ve written a ruby script for Linux that does that for the SDL headers within /usr/include/SDL, at first it reads the symbol names directly from the libSDL.so shared library utilizing nm, then it parses the header files for the function declarations found by nm and generates the code for the pointer declaration and explicit casts (/dlsym calls) automatically.

I’ve created some example code that defines an abstract class DLLoader and an implementation DLLoaderSDL that includes an struct with the automatically generated function pointers etc. The test program loads the library and shows a SDL window for 3 seconds.
I’ve tested the program with GCC 4.5.0 (Linux), GCC 3.4.5 (Windows MinGW) and CL 14.0 (Windows VC++). I also added Makefiles for each of those compiler suites.

Update: I’m currently working on a new project that involves this dynamic loading and I’ve created a much more improved and flexible ruby script for parsing header files and built dynamic loader scripts:

Usage: src/dlib/dlib_generator.rb [OPTIONS]
Parse shared library binary and headers to generate code for a dynamic loader.

You need at least specify a name, a header file (or directory) and a prototype
pattern. The binary options are optional, but can help to improve the result.

Generator Options:
    -n, --name NAME                  The name used to identify the library
    -t, --template FILE              Use FILE as generator template 
                                     (Default: dlib_generator.tpl)
    -c, --config FILE                Use FILE for configuration, overwrites other options
    -w, --write [FILE]               Write generated code to FILE, in addition to stdout
                                     (Default file: CLibraryLoader#{options.name}.hpp)
                                     Need to be specified after name!
    -d, --define                     Create a C macro for each procedure
    -i, --include a.hpp,b.hpp        List include header files.

Library Header Options:
    -f, --header_file FILE           Use FILE or PATH for header declarations
    -p, --header_path PATH
    -r, --file_pattern PATTERN       Parse only files in header PATH that matches
                                     PATTERN (Default: \.h$)
    -R, --prototype_pattern PATTERN  Use PATTERN to parse procedures, assumes three
                                     groups: Return, procedure name and arguments

Library Binary Options:
    -l, --library FILE               Shared library FILE parsed with nm for symbols
    -s, --symbol_prefix PREFIX       Use symbol PREFIX to extract procedure names
                                     from shared library

Miscellaneous:
    -v, --verbose                    Activates console messages
    -h, --help                       Show this message

Zensursula: Teil 1 – DNS

“Sie werden sich noch wünschen wir wären Politikverdrossen” (@343max)

zensursula-300x292Ich hatte in den letzten Wochen den Eindruck, als ob die gesamte Internet-Gemeinschaft in Deutschland Sturm lief, überall gab und gibt es nur ein Thema: Die Internetzensur Bestrebungen der großen Koalition. Dabei hatte man am Ende den Eindruck gegen eine Mauer von Unkenntnis, Ignoranz aber vielleicht auch einfach blanker böswilliger Berechnung einfach nichts ausrichten zu können.
Besonders Enttäuschend empfand ich die Medien, überraschend war es freilich nicht dass sie praktisch ausschließlich Parteipropaganda nachplapperten und die Mainstream Meinung nach dem Willen der Regierung formten. Von investigativem Journalismus jedenfalls war in den Holzmedien nichts zu sehen, das überlässt man offenbar anderen.

Ein wenig Ironisch ist es schon, ist doch gerade das Internet die einzige breit zugängliche alternative Informationsquelle(wie wir eindrucksvoll sehen konnten), die mit der jetzt beschlossenen Infrastruktur möglicherweise schon bald stark eingeschränkt wird. Dabei geht es auch um die Frage ob wir der Regierung soweit vertrauen, nach der Installation einer bis dato einmaligen Infrastruktur für Zensur, bei der Sperrung von Kinderpornographischen Inhalten zu bleiben. In den letzten Jahren hat uns die große Koalition keinen Grund geliefert das zu glauben.

Die eigentliche Kluft wie häufig Thematisiert, liegt nicht zwischen den Internet-Natives und den “Internet-Ausdruckern” sondern vielmehr in dem Teil der Bevölkerung der sich unabhängig Informiert und dem Teil, der den großen Medien und Agenturen(auch im Internet) blind vertaut und als einzige Informationsquelle zur eigenen Meinungsbildung heranzieht, also den überwiegenden Teil der Gesellschaft.

Gestern nun hat die EU die Zensur in China gerügt(so Merkwürdig wie das auch immer ist), demnach vergessen wir einfach alles was die EU-Länder sonst so treiben und beschäftigen uns nun, offensichtlich im Wohlwollen der EU, um die aktiven technischen Möglichkeiten der Umgehung jedweder Zensur-Infrastruktur, außerdem kann es nicht schaden die von den Zensoren eingesetzte Technologie mal etwas genauer zu beleuchten.

In diesem ersten Teil soll es um die Zensur durch DNS Manipulationen gehen.

Es ist noch die leichteste Form der Zensur, die Einstiegsdroge der Staaten sozusagen, denn DNS Manipulationen lassen sich Kinderleicht aushebeln was wohl schnell den Wunsch nach härteren Mitteln wecken dürfte. Bei dieser Form der Zensur installieren die ISP’s die Sperrlisten in ihren DNS-Servern, ist doch zumindest anzunehmen, das die Mehrheit der Internet Nutzer die Server ihres Providers verwenden, wobei das bei leibe nicht immer der Fall ist. So war und ist es schon immer möglich seinen eigenen DNS-Server zu betreiben. Jetzt wo man allen Grund hat den DNS-Servern der Provider zu misstrauen, ist das eine sehr praktikable Möglichkeit, zumindest für diejenigen die sich mit dem Internet auskennen.
Doch der Reihe nach, im Prinzip reicht es statt eines Domain-Namens in der Adresszeile die Ip-Adresse einzugeben um diese Zensur zu umgehen, besser ist es da schon einen alternativen DNS-Server zu verwenden, vorzugsweise im Ausland, aber auch der CCC und der Foebud stellen unzensierte DNS-Server (noch) frei zur Verfügung. Listen mit freien Servern gibt es z.B. bei Wikileaks, dem CCC oder die Server des Open NIC Projektes.

DNS Caching Server

Nützlich ist auch das Einrichten eines eigenen Caching DNS-Servers der zufällig aus einer Liste, öffentliche Servern abfragt. Ich verwende dafür den pdnsd, nicht zu verwechseln mit dem vollwertigen DNS-Server, PowerDNS. Je nach Distribution sollte sich die Installation einfach gestalten, z.B. reicht ein pacman -S pdnsd um mit Arch Linux den Server zu installieren. Jetzt sollte man sich die Beispiel Konfiguration kopieren und Anpassungen vornehmen. Hier eine mögliche Beispielkonfiguration, es muss nur die server_ip angepasst werden:

global {
        perm_cache=512000; # cache groesse in KB (hier "leicht" uebertriebene 500 MB)
        cache_dir="/var/cache/pdnsd";
        run_as="nobody";
        server_ip=10.0.0.4; # ANPASSEN
        status_ctl=on;
        paranoid=on; # prevents cache poisoning
        query_method=udp_tcp;
        min_ttl=15m; # min/max TTL
        max_ttl=4w;
        timeout=15; # global timeout 15 seconds
        neg_rrs_pol=auth;
        par_queries=2; # maximale parallele abfrage von servern
}
 
server { # ein paar oeffentliche server (alle getestet)
        label = "random";
        randomize_servers = on;
        ip = 85.214.73.63, # foebud
        204.152.184.76, # ISC (USA)
        213.73.91.35, # CCC
        194.95.202.198, # DFN
        58.6.115.43, # Westnet (Australien)
        82.229.244.191, # Frankreich
        88.191.77.10, # Frankreich
	216.87.84.209, # OpenNIC
	88.191.77.10; # OpenNIC
        timeout=10; # 10 sekunden maximal
        uptest=ping;
        ping_timeout=400; #ms
        interval=30m; # uptest der server per ping
}
 
# Unveraendert aus Sample Conf:
# This section is meant for resolving from root servers.
server {
        label = "root-servers";
        root_server = on;
        randomize_servers = on; # Give every root server an equal chance
                                # of being queried.
        ip =    198.41.0.4
        ,       192.228.79.201
        ,       192.33.4.12
        ,       128.8.10.90
        ,       192.203.230.10
        ,       192.5.5.241
        ,       192.112.36.4
        ,       128.63.2.53
        ,       192.36.148.17
        ,       192.58.128.30
        ,       193.0.14.129
        ,       198.32.64.12
        ,       202.12.27.33
        ;
        timeout = 5;
        uptest = query;         # Test availability using empty DNS queries.
        interval = 30m;         # Test every half hour.
        ping_timeout = 300;     # Test should time out after 30 seconds.
        purge_cache = off;
        exclude = .localdomain;
        policy = included;
        preset = off;
}
 
# ganz praktisch, damit braucht man nur eine einzige hosts Datei im ganzen LAN zu pflegen:
source { 
        owner=localhost;
        serve_aliases=yes;
        file="/etc/hosts";
}

Ich habe alle DNS Server getestet. Eventuell die Konfiguration noch auf die eigenen Anforderungen anpassen, eine Dokumentation findet ihr hier. Die /etc/resolv.conf noch auf die lokale IP-Adresse umstellen dann den Server (neu)starten. Jetzt sollte er funktionieren, testen kann man das z.B. mit nslookup oder: dig 4poc.org [IP des DNS], dig zeigt auch wie lange er zum resolven brauchte.

Um festzustellen welche DNS-Server verwendet werden habe ich ein kleines Skript geschrieben, eigentlich passt die Thematik(siehe Anmerkung im Script) nicht direkt zum Thema aber den folgenden Artikeln sei schon mal etwas vorgegriffen: nstest.4poc.org (DNS Tester, falls jemanden einen besseren Namen dafür einfällt nur her damit.)

Ivacy VPN unter Linux: PPTP/OpenVPN und Socks5-Gateway

Die VPN Provider sprießen, dank der “Sicherheits-Politik” in Europa und den USA, wie Pilze aus dem Boden. Seit Juli 2008 gibt es Ivacy, einem durchaus interessanten VPN-Anbieter über den es möglich ist sich der Vorratsdatenspeicherung Europas zu entziehen. Hier geht es mir darum zu zeigen wie man Ivacy unter Linux nutzen kann, genauer gesagt wie man OpenVPN und PPTP unter Linux einrichtet, die Country-Selection Funktion von Ivacy nutzt und wie man einen Socks5 Proxy einrichtet der Verbindungen über Ivacy weiterleitet, dazu später mehr. Diese Anleitung sollte nicht nur mit Ivacy funktionieren, alle VPN-Anbieter bieten OpenVPN oder PPTP(das proprietäre VPN Protokoll von Microsoft).
Update: Habe einen OpenVPN-Abschnitt eingefügt.
Update 4.2.09@20:50: OpenVPN-Abschnitt überarbeitet.
Update 8.2.09@22:15: Artikel nochmals überarbeitet.
Update 21.4.09@22:00: Privoxy-Abschnitt eingefügt.

Bevor es losgeht sollte ich vielleicht noch erwähnen das es sich hierbei um keine Anleitung für Linux-Anfänger oder Ubuntu/Suse-”Profis” handelt. *g* Continue reading

xinetd: info script

Ich wollte von unterwegs aus den Status meines Heimservers abrufen können. Dabei ging es mir vorallem um die Temperatur von CPU, Mainboard und den Festplatten. Der auf sixserv.org laufende rbot(im Freenode idled der in #sixserv) soll auf Kommando den Status anzeigen. Soweit so gut. Ein kleines Ruby-Skript das auf dem Server zuhause läuft erfasst die Temperaturen per “sensors” und “hddtemp”. Der xinetd-Daemon konfigurierte ich daraufhin so das auf einen Port das Skript gebunden wird. Es erwartet bevor es die Daten übermittelt ein Passwort, einfach zum zusätzlichen Schutz auch wenn das vielleicht gar nicht nötig ist. Jemand der einen Portscan durchführt könnte eben so informationen zum System gelangen, die Passwortabfrage verhindert dies.

Zunächst zu dem Ruby-Script(z.B. /opt/botinfo.rb):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/usr/bin/ruby
 
i = Kernel.gets
if i.chomp != 'DASGEHEIMEPASSWORT' then
	# puts 'Wrong Password'
	exit
end
 
puts `uptime`.lstrip
 
# HDD Temps:
matches = `cat /proc/partitions`.scan /([s|h]d[a-z])/
matches.uniq!
matches.each do |disk|
	print "#{disk}: #{`hddtemp -n /dev/#{disk}`.chomp}.0*C (#{$1}GB) "
end
puts 
 
systemp = `sensors`
 
temp1 = systemp.scan /CPU Temp:    \+([0-9]+)\.0.C/
temp2 = systemp.scan /M\/B Temp:    \+([0-9]+)\.0.C/
 
puts "System: #{temp2[0]}.0*C #{temp2[1]}.0*C | CPUs: #{temp1[0]}.0*C #{temp1[1]}.0*C"

Hier muss natürlich sensors und hddtemp installiert sein, aber dieses Script kann praktisch alles mögliche an Informationen sammeln und ausgeben.
Die Konfiguration von xinetd gestaltet sich sehr einfach, in dem Verzeichnis /etc/xinetd.d einfach eine neue Datei für das Script erstellen(z.B. “botinfo”):

1
2
3
4
5
6
7
8
9
10
11
service botinfo
{
    disable         = no
    port            = 8888
    socket_type     = stream
    protocol        = tcp
    wait            = no
    user            = apoc
    server          = /opt/botinfo.rb
    type            = unlisted
}

Den Port, User und den Skript Pfad entsprechend anpassen und xinetd neu starten. Mit netcat kann es man danach testen:

1
2
3
4
5
$ nc localhost 8888
DASGEHEIMEPASSWORT
18:40:35 up 3 days, 41 min,  4 users,  load average: 0.00, 0.02, 0.20
sda: 30.0*C (10GB) sdb: 29.0*C (10GB)
System: 39.0*C 38.0*C | CPUs: 37.0*C 36.0*C

Der Port muss ggf. vom Router geforwarded werden damit ein Entfernter Server darauf zugreifen kann. Auch ein dyndns ist hilfreich, sofern man über keine statische IP verfügt. Ein einfaches rbot-Plugin um diese Daten vom irc aus abzufragen sieht z.B. so aus:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
require 'socket'
 
class BotinfoPlugin < Plugin
  def help(plugin, topic="")
    'info => return system information'
  end
 
  def info(m, params)
    sock = TCPSocket.new('heimserver.dyndns.org', 8888)
    sock.puts('DASGEHEIMEPASSWORT')
    m.reply sock.recv(1024)
    sock.close
  end
end
plugin = BotinfoPlugin.new
plugin.map 'info'

Die Daten können ebenfalls von einem PHP-Script aus abgefragt werden. Keines Beispiel:

1
2
3
4
5
6
7
8
9
<?php
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_connect($socket, "heimserver.dyndns.org", "8888");
 
$pass = "DASGEHEIMEPASSWORT\n\n";
socket_write($socket, $pass, strlen($pass));
 
echo socket_read($socket, 2048);
?>

Vielleicht findet das ja irgendjemand interessant ;)