<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>divisionbyzero &#187; perl5</title>
	<atom:link href="http://divisionbyzero.net/blog/tag/perl5/feed/" rel="self" type="application/rss+xml" />
	<link>http://divisionbyzero.net/blog</link>
	<description>question . authority</description>
	<lastBuildDate>Tue, 06 Jul 2010 16:43:27 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>Vacation and PHP</title>
		<link>http://divisionbyzero.net/blog/2009/07/13/vacation-and-php/</link>
		<comments>http://divisionbyzero.net/blog/2009/07/13/vacation-and-php/#comments</comments>
		<pubDate>Tue, 14 Jul 2009 02:53:40 +0000</pubDate>
		<dc:creator>brad</dc:creator>
				<category><![CDATA[Rant]]></category>
		<category><![CDATA[perl]]></category>
		<category><![CDATA[perl5]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://divisionbyzero.net/blog/?p=153</guid>
		<description><![CDATA[I took a much needed vacation the latter part of last week. Prior to that, I was helping a few coworkers with getting PHP Web Applications developed on Fedora Core 5 to run on CentOS 5 with upgraded PHP, Apache, and libraries. Every time I work with PHP, it gives me serious perspective as to [...]]]></description>
			<content:encoded><![CDATA[<p>I took a much needed vacation the latter part of last week.  Prior to that, I was helping a few coworkers with getting PHP Web Applications developed on Fedora Core 5 to run on CentOS 5 with upgraded PHP, Apache, and libraries.  Every time I work with PHP, it gives me serious perspective as to why the <a href="http://www.modernperlbooks.com" alt="Modern Perl">Modern Perl</a> / <a href="http://www.enlightenedperl.org">Enlightened Perl</a> / <a href="http://search.cpan.org/~mschwern/perl5i-20090424/lib/perl5i.pm">perl5i</a> Projects are incredibly important.  The Matt&#8217;s Scripts Perl era needs to die.  This stagnant snapshot has poisoned Perl&#8217;s reputation for too long.</p>
<p>The main difference between Perl and PHP, is writing maintainable, intelligent Perl is only slightly more work <em>at first</em> than writing horrible Matt&#8217;s Scripts style Perl.  With PHP, writing decent PHP is possible, but it&#8217;s incredibly difficult.  The majority of the PHP I&#8217;ve come across is code written by a web developer with no programming experience and the language design and direction accommodate that demographic.  PHP&#8217;s language design gets in the way of writing sane, maintainable code.  It&#8217;s not impossible, but you have to really, really want it.</p>
<p>When you write good Perl, the programming experience becomes easier, and more fun.</p>
<p>I&#8217;m trying to get back to my programming projects, and thus back to writing more on Perl.  For now, understand that if you think Perl and PHP are the same beast, you&#8217;re wrong.  I&#8217;ve been paid to develop both for periods of years.  Perl is much more eloquent, evolutionary, and intelligent.</p>
]]></content:encoded>
			<wfw:commentRss>http://divisionbyzero.net/blog/2009/07/13/vacation-and-php/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Using POE to hook syslog-ng</title>
		<link>http://divisionbyzero.net/blog/2009/07/02/using-poe-to-hook-syslog-ng/</link>
		<comments>http://divisionbyzero.net/blog/2009/07/02/using-poe-to-hook-syslog-ng/#comments</comments>
		<pubDate>Thu, 02 Jul 2009 23:21:13 +0000</pubDate>
		<dc:creator>brad</dc:creator>
				<category><![CDATA[Article]]></category>
		<category><![CDATA[perl]]></category>
		<category><![CDATA[perl5]]></category>
		<category><![CDATA[poe]]></category>
		<category><![CDATA[security]]></category>
		<category><![CDATA[syslog]]></category>
		<category><![CDATA[syslog-ng]]></category>

		<guid isPermaLink="false">http://divisionbyzero.net/blog/?p=121</guid>
		<description><![CDATA[A massively code heavy post on integrating syslog into in house applications using syslog-ng and Perl's amazing POE Event Framework to do really crazy things!]]></description>
			<content:encoded><![CDATA[<p>Being able to do analysis, sorting, or database storage of syslog messages is incredibly useful.  There are tons of solutions on the market to do just that.  If you&#8217;re working on a system developed in house that you&#8217;d like to incorporate syslog messages into, then it may be easier to hook directly into the syslog stream than to introduce another piece of software into the environment which needs to be glued.</p>
<p>Syslog-ng facilitates easy integration with Perl binaries as the Perl program is spawned once during the daemon start up and a handle to that program&#8217;s STDIN is maintained for dispatching of messages.  Using POE, we can turn this into an event driven model, making additional complexity simple.</p>
<p>In this example, we&#8217;ll create a POE Master session that receives all of the syslog-ng input from STDIN.  Using off the shelf components, we&#8217;ll run a TCP Server on port 9514 that will allow clients to connect and subscribe to feeds based on the &#8220;program&#8221; name of the message being dispatched.<br />
<span id="more-121"></span><br />
Anytime I&#8217;m using Regular Expressions over and over, I like to &#8220;precook&#8221; them.  This compiles the regular expression, and lets the engine skip that step each time they&#8217;re used.  Doing so is simply a matter of declaring the regex with the <code>qr//</code> operator:</p>
<pre class="brush: perl;">
my %cooked = (
	program =&gt; qr/\s+\d+:\d+:\d+\s+\S+\s+([^:\s]+)(:|\s)/,
);
</pre>
<h2>Initialization</h2>
<p>Next we&#8217;ll create the administrative session in charge of dispatching the messages to the proper channels:</p>
<pre class="brush: perl;">
# Dispatcher Master Session
POE::Session-&gt;create(
	inline_states =&gt; {
		_start					=&gt; \&amp;dispatcher_start,
		_stop					=&gt; sub { print &quot;SESSION &quot;, $_[SESSION]-&gt;ID, &quot; stopped.\n&quot;; },
		register_client			=&gt; \&amp;register_client,
		subscribe_client		=&gt; \&amp;subscribe_client,
		hangup_client			=&gt; \&amp;hangup_client,

		dispatch_message		=&gt; \&amp;dispatch_message,
	},
);
</pre>
<p>We&#8217;ll define those subroutines shortly, but we need to setup the rest of our sessions.  Next, we&#8217;ll need a TCP Server to handle the client connections, we can get that using <code>POE::Component::Server::TCP</code>:</p>
<pre class="brush: perl;">
# TCP Session Master
POE::Component::Server::TCP-&gt;new(
		Alias		=&gt; 'server',
		Address		=&gt; '127.0.0.1',
		Port		=&gt; 9514,

		ClientConnected		=&gt; \&amp;client_connect,
		ClientInput			=&gt; \&amp;client_input,

		ClientDisconnected	=&gt; \&amp;client_term,
		ClientError			=&gt; \&amp;client_term,

		InlineStates		=&gt; {
			client_print		=&gt; \&amp;client_print,
		},
);
</pre>
<p>The final session will handle the Input on STDIN from syslog-ng:</p>
<pre class="brush: perl;">
# Syslog-ng Stream Master
POE::Session-&gt;create(
		inline_states =&gt; {
			_start		=&gt; \&amp;stream_start,
			_stop		=&gt; sub { print &quot;SESSION &quot;, $_[SESSION]-&gt;ID, &quot; stopped.\n&quot;; },
			stream_line		=&gt; \&amp;stream_line,
		},
);
</pre>
<p>Now we have to define the subroutines that we&#8217;ll be dispatching events to.  The heavy lifting is done by POE, and we&#8217;re left to handle simple things.</p>
<h1>Session Routines: <code>dispatcher</code></h1>
<p>This session is going to managing which clients receive which messages.  The actual input is handled by the <code>stream</code> session, and the sending of the messages to the client by the <code>server</code> session.  As we have a raw <code>POE::Session</code>, our first subroutine <code>dispatcher_start</code> is just going to do some basic preparation:</p>
<pre class="brush: perl;">
sub dispatcher_start {
	my ($kernel, $heap) = @_[KERNEL, HEAP];

	$kernel-&gt;alias_set( 'dispatcher' );  # allow named dispatch to this session.

	$heap-&gt;{subscribers} = {};
        $heap-&gt;{clients} = {};

}
</pre>
<p>Next event to be handled is the <i>register_client</i> event which is fired anytime a connection is established to the <code>server</code> session.  All the dispatcher does is register it&#8217;s session_id into an internal heap.  Nothing happens with it, but if we needed to send a message to all clients, we could loop over this hash and broadcast message.</p>
<pre class="brush: perl;">
sub register_client {
    # ARG0 =&gt; TCP Client Session ID
    my ($kernel,$heap,$sid) = @_[KERNEL,HEAP,ARG0];

    $heap-&gt;{clients}{$sid} = 1;
}
</pre>
<p>Clients can subscribe to a program name, which they do by entering &#8220;<code>sub dhcpd, dnsmasq</code>&#8221; into the TCP Server.  It&#8217;s not fancy, but man is it convenient for debugging and development purposes.  The <code>server</code> session determines that the subscription is occurring and passes it&#8217;s argument string to the <code>dispatcher</code> session via the <i>subscribe_client</i> event.  This subroutine is called:</p>
<pre class="brush: perl;">
sub subscribe_client {
    # ARG0 =&gt; SID of Client
    # ARG1 =&gt; Argument String of the subscribe
	my ($kernel,$heap,$sid,$argstr) = @_[KERNEL,HEAP,ARG0,ARG1];

    # Split the input at commas or spaces into words:
	my @progs = map { lc } split /[\s,]+/, $argstr;
    # Add the SID to the list of Subscribed Clients for that program
	foreach my $prog (@progs) {
		$heap-&gt;{subscribers}{$prog}{$sid} = 1;
	}

    # Inform the client they've subscribed via client_print
	$kernel-&gt;post( $sid =&gt; 'client_print' =&gt; 'Subscribed to : ' . join(', ', @progs ) );
}
</pre>
<p>If a client disconnects, we remove it from the message dispatching hash:</p>
<pre class="brush: perl;">
sub hangup_client {
    # ARG0 =&gt; SID of Client Disconnecting
	my ($kernel,$heap,$sid) = @_[KERNEL,HEAP,ARG0];

	delete $heap-&gt;{clients}{$sid};

	foreach my $p ( keys %{ $heap-&gt;{subscribers} } ) {
		delete $heap-&gt;{subscribers}{$p}{$sid}
			if exists $heap-&gt;{subscribers}{$p}{$sid};
	}
}
</pre>
<p>Now comes the most important event the dispatcher handles, <i>dispatch_message</i>.  In this event, we have a message from syslog-ng that needs to go to interested parties.  This event determines the &#8220;program&#8221; and it&#8217;s subscribers and sends that message along appropriately:</p>
<pre class="brush: perl; highlight: [14];">
sub dispatch_message {
    # ARG0 =&gt; The raw message from syslog-ng
	my ($kernel,$heap,$msg) = @_[KERNEL,HEAP,ARG0];

    # Determine the program name
	if( my ($program) = map { lc } ($msg =~ /$cooked{program}/) ) {
		# remove the sub process and PID from the program
		$program =~ s/\(.*//g;
		$program =~ s/\[.*//g;

        # If we have subscribers, send them the message.
		if( exists $heap-&gt;{subscribers}{$program} ) {
			foreach my $sid (keys %{ $heap-&gt;{subscribers}{$program} }) {
				$kernel-&gt;post( $sid =&gt; 'client_print' =&gt; $msg );
			}
		}
}
</pre>
<p>You'll notice on line 14 above, the <code>post( $sid => client_print => $msg )</code> sends the event to the appropriate client and calls the <i>client_print</i> event on itself.  This is all the dispatcher needs to do.  The rest is handled by other other sessions.</p>
<h1>Session Routines: <code>server</code></h1>
<p>This session accepts new tcp clients and handles writing to the sockets.  We'll take a look at a few subroutines here.  Fist we'll look at the <i>ClientConnect</i> event.</p>
<pre class="brush: perl;">
sub client_connect {
    # SESSION is the client's session object
	my ($kernel,$heap,$ses) = @_[KERNEL,HEAP,SESSION];

	my $SID = $ses-&gt;ID;

    # Register the Client with the Dispatcher
	$kernel-&gt;post( 'dispatcher' =&gt; 'register_client' =&gt; $SID );

    # Store the current entry for 'client' in the heap so we can communicate later
	$heap-&gt;{clients}{ $SID } = $heap-&gt;{client};

	# Say hello to the client.
	$heap-&gt;{client}-&gt;put( &quot;Hello Client: $SID&quot; );
}
</pre>
<p>We also need a <i>disconnect</i> event:</p>
<pre class="brush: perl;">
sub client_term {
	my ($kernel,$heap,$ses) = @_[KERNEL,HEAP,SESSION];
	my $sid = $ses-&gt;ID;

    # Delete the Client's Dispatch Table
	delete $heap-&gt;{dispatch}{$sid};
    # Tell the dispatcher session we're through
	$kernel-&gt;post( 'dispatcher' =&gt; 'hangup_client' =&gt;  $sid );
}
</pre>
<p>Next we&#8217;ll handle sending message to the client, which is incredibly easy:</p>
<pre class="brush: perl;">
sub client_print {
    # ARG0 =&gt; Message to Send to the Client
	my ($kernel,$heap,$ses,$mesg) = @_[KERNEL,HEAP,SESSION,ARG0];

	$heap-&gt;{clients}{$ses-&gt;ID}-&gt;put($mesg);
}
</pre>
<p>Now we a routine to handle the <i>ClientInput</i> event.  This event will take commands from the clients and do something with them.  We&#8217;ll use an internal dispatch table in the form of a hash to handle translating commands.  This will allow us to expand our API if we need to.</p>
<pre class="brush: perl; highlight: [12,13,14,15,34,36];">
sub client_input {
    # SESSION is the Client Session Object with input
    # ARG0 =&gt; Input waiting from that client
	my ($kernel,$heap,$ses,$msg) = @_[KERNEL,HEAP,SESSION,ARG0];
	my $sid = $ses-&gt;ID;

    # Build a Dispatch Table if one does not exists in the heap for this entry.
	if( !exists $heap-&gt;{dispatch}{$sid} ) {
		$heap-&gt;{dispatch}{$sid} = {

			subscribe		=&gt; {
				re			=&gt; qr/^sub(?:scribe)? (.*)/,
				callback	=&gt; sub {
					$kernel-&gt;post( 'dispatcher' =&gt; 'subscribe_client' =&gt; $sid, shift );
				},
			},
            # FUTURE API for Clients receiving every message!
			#fullfeed		=&gt; {
			#	re			=&gt; qr/^(fullfeed)/,
			#	callback	=&gt; sub {
			#		$kernel-&gt;post( 'dispatcher' =&gt; 'fullfeed_client' =&gt; $sid );
			#	},
			#},
		};
	}

	#
	# Check for messages:
	my $handled = 0;
    # Get Our Dispatch Table
	my $dispatch = $heap-&gt;{dispatch}{$sid};
    # Look up and take action according to our dispatch table
	foreach my $evt ( keys %{ $dispatch } ) {
		if( my($args) = ($msg =~ /$dispatch-&gt;{$evt}{re}/)) {
			$handled = 1;
			$dispatch-&gt;{$evt}{callback}-&gt;($args);
			last;
		}
	}

    # Inform the client that their command was not understood.
	if( !$handled ) {
		$kernel-&gt;post( $sid =&gt; 'client_print' =&gt; 'UNKNOWN COMMAND, Ignored.' );
	}
}
</pre>
<p>That&#8217;s the most complicated routine in the program, but it does allow us to morph the dispatch tables for individual clients.  Lines 12-15 build a dispatch table entry with the regular expression to match the command, followed by a callback subroutine reference which handles the command.  Lines 34 and 36 are where these rules are applied to the input from the client.</p>
<h1>Session Routines: <code>stream</code></h1>
<p>The last session is very simple.  This session maintains the connection to STDIN from syslog-ng and dispatches those lines as events to the <code>dispatcher</code> session.  There is a startup routine:</p>
<pre class="brush: perl;">
sub stream_start {
	my ($kernel, $heap) = @_[KERNEL, HEAP];

	$kernel-&gt;alias_set( 'stream' );

	#
	# Initialize the connection to STDIN as a POE::Wheel
	my $stdin = IO::Handle-&gt;new_from_fd( \*STDIN, 'r' );
	my $stderr = IO::Handle-&gt;new_from_fd( \*STDERR, 'w' );

	$heap-&gt;{stream} = POE::Wheel::ReadWrite-&gt;new(
		InputHandle		=&gt; $stdin,
		OutputHandle	=&gt; $stderr,
		InputEvent		=&gt; 'stream_line',
	);
}
</pre>
<p>And the <i>stream_line</i> event which sends the incoming syslog messages to the <code>dispatcher</code> session for processing:</p>
<pre class="brush: perl;">
#--------------------------------------------------------------------------#
sub stream_line {
    # ARG0 =&gt; Line from STDIN, New line delimited.
	my ($kernel,$msg) = @_[KERNEL,ARG0];

	return unless length $msg;

	$kernel-&gt;post( 'dispatcher' =&gt; 'dispatch_message' =&gt; $msg );

}
</pre>
<h1>Setting it up with syslog-ng</h1>
<p>If we store our POE program in <code>/usr/local/bin/poe-syslog-ng.pl</code>, in the syslog-ng.conf we need to specify it as a program:</p>
<pre class="brush: plain;">
#
# Subscriber Feeds
destination d_subscribers {
	program(&quot;/usr/local/bin/poe-syslog-ng.pl&quot;);
};
</pre>
<p>Then you can feed it based on filters, just like the rest of the <code>destination</code> macros in syslog-ng:</p>
<pre class="brush: plain;">
#
# SUBSCRIPTION SERVICE:
log { source(s_ext); source(s_udp); filter(f_database); destination(d_subscribers); };
</pre>
<h1>The whole #! </h1>
<p>For those interested, I&#8217;ve written a program that expands this example with enhanced functionality.  The full source is available here:</p>
<pre class="brush: perl; collapse: true; light: false; toolbar: true;">
#!/usr/bin/perl
#
# This is the POE Master Server.
#  1) Take all the syslog input
#  2) Listen for parsers
#  3) Filter streams to parsers
#  TODO: 4) Maintain Parser State, restarting on crash

use strict;
use warnings;

use Socket;
use Regexp::Common qw(net);

sub POE::Kernel::ASSERT_DEFAULT (){ 1 }
#sub POE::Kernel::TRACE_DEFAULT (){ 1 }
use POE qw(
	Wheel::ReadWrite
	Component::Server::TCP
);

my %cooked = (
	program =&gt; qr/\s+\d+:\d+:\d+\s+\S+\s+([^:\s]+)(:|\s)/,
);

#--------------------------------------------------------------------------#
# POE Session Initialization

# Dispatcher Master Session
POE::Session-&gt;create(
	inline_states =&gt; {
		_start					=&gt; \&amp;dispatcher_start,
		_stop					=&gt; sub { print &quot;SESSION &quot;, $_[SESSION]-&gt;ID, &quot; stopped.\n&quot;; },
		register_client			=&gt; \&amp;register_client,
		subscribe_client		=&gt; \&amp;subscribe_client,
		unsubscribe_client		=&gt; \&amp;unsubscribe_client,
		fullfeed_client			=&gt; \&amp;fullfeed_client,
		dispatch_message		=&gt; \&amp;dispatch_message,
		broadcast				=&gt; \&amp;broadcast,
		hangup_client			=&gt; \&amp;hangup_client,
		server_shutdown			=&gt; \&amp;server_shutdown,
		debug_client			=&gt; \&amp;debug_client,
		nobug_client			=&gt; \&amp;nobug_client,
		debug_message			=&gt; \&amp;debug_message,
	},
);

# TCP Session Master
POE::Component::Server::TCP-&gt;new(
		Alias		=&gt; 'server',
		Address		=&gt; '127.0.0.1',
		Port		=&gt; 9514,

		ClientConnected		=&gt; \&amp;client_connect,
		ClientInput			=&gt; \&amp;client_input,

		ClientDisconnected	=&gt; \&amp;client_term,
		ClientError			=&gt; \&amp;client_term,

		InlineStates		=&gt; {
			client_print		=&gt; \&amp;client_print,
		},
);

# Syslog-ng Stream Master
POE::Session-&gt;create(
		inline_states =&gt; {
			_start		=&gt; \&amp;stream_start,
			_stop		=&gt; sub { print &quot;SESSION &quot;, $_[SESSION]-&gt;ID, &quot; stopped.\n&quot;; },

			stream_line		=&gt; \&amp;stream_line,
			stream_error	=&gt; \&amp;stream_error,
		},
);

#--------------------------------------------------------------------------#

#--------------------------------------------------------------------------#
# POE Main Loop
POE::Kernel-&gt;run();
exit 0;
#--------------------------------------------------------------------------#

#--------------------------------------------------------------------------#
# POE Event Functions
#--------------------------------------------------------------------------#

#--------------------------------------------------------------------------#
sub debug {
	my $msg = shift;
	chomp($msg);
	$poe_kernel-&gt;post( 'dispatcher' =&gt; 'debug_message' =&gt; $msg );
	print &quot;[debug] $msg\n&quot;;
}
#--------------------------------------------------------------------------#
sub dispatcher_start {
	my ($kernel, $heap) = @_[KERNEL, HEAP];

	$kernel-&gt;alias_set( 'dispatcher' );

	$heap-&gt;{subscribers} = { };
	$heap-&gt;{full} = { };
	$heap-&gt;{debug} = { };
}

#--------------------------------------------------------------------------#
sub register_client {
	my ($kernel,$heap,$sid) = @_[KERNEL,HEAP,ARG0];

	$heap-&gt;{clients}{$sid} = 1;
}

#--------------------------------------------------------------------------#
sub debug_client {
	my ($kernel,$heap,$sid) = @_[KERNEL,HEAP,ARG0];

	if( exists $heap-&gt;{full}{$sid} ) {  return;  }

	$heap-&gt;{debug}{$sid} = 1;
	$kernel-&gt;post( $sid =&gt; 'client_print' =&gt; 'Debugging enabled.' );
}

#--------------------------------------------------------------------------#
sub nobug_client {
	my ($kernel,$heap,$sid) = @_[KERNEL,HEAP,ARG0];

	delete $heap-&gt;{debug}{$sid}
		if exists $heap-&gt;{debug}{$sid};
	$kernel-&gt;post( $sid =&gt; 'client_print' =&gt; 'Debugging disabled.' );
}

#--------------------------------------------------------------------------#
sub fullfeed_client {
	my ($kernel,$heap,$sid) = @_[KERNEL,HEAP,ARG0];

	#
	# Remove from normal subscribers.
	foreach my $prog (keys %{ $heap-&gt;{subscribers} }) {
		delete $heap-&gt;{subscribers}{$prog}{$sid}
			if exists $heap-&gt;{subscribers}{$prog}{$sid};
	}

	#
	# Turn off DEBUG
	if( exists $heap-&gt;{debug}{$sid} ) {
		delete $heap-&gt;{debug}{$sid};
	}

	#
	# Add to fullfeed:
	$heap-&gt;{full}{$sid} = 1;

	$kernel-&gt;post( $sid =&gt; 'client_print' =&gt; 'Full feed enabled, all other functions disabled.');
}

#--------------------------------------------------------------------------#
sub subscribe_client {
	my ($kernel,$heap,$sid,$argstr) = @_[KERNEL,HEAP,ARG0,ARG1];

	if( exists $heap-&gt;{full}{$sid} ) {  return;  }

	my @progs = map { lc } split /[\s,]+/, $argstr;
	foreach my $prog (@progs) {
		$heap-&gt;{subscribers}{$prog}{$sid} = 1;
	}

	$kernel-&gt;post( $sid =&gt; 'client_print' =&gt; 'Subscribed to : ' . join(', ', @progs ) );
}
#--------------------------------------------------------------------------#
sub unsubscribe_client {
	my ($kernel,$heap,$sid,$argstr) = @_[KERNEL,HEAP,ARG0,ARG1];

	my @progs = map { lc } split /[\s,]+/, $argstr;
	foreach my $prog (@progs) {
		delete $heap-&gt;{subscribers}{$prog}{$sid};
	}

	$kernel-&gt;post( $sid =&gt; 'client_print' =&gt; 'Subscription removed for : ' . join(', ', @progs ) );
}

#--------------------------------------------------------------------------#
sub hangup_client {
	my ($kernel,$heap,$sid) = @_[KERNEL,HEAP,ARG0];

	delete $heap-&gt;{clients}{$sid};

	foreach my $p ( keys %{ $heap-&gt;{subscribers} } ) {
		delete $heap-&gt;{subscribers}{$p}{$sid}
			if exists $heap-&gt;{subscribers}{$p}{$sid};
	}

	if( exists $heap-&gt;{debug}{$sid} ) {
		delete $heap-&gt;{debug}{$sid};
	}

	if( exists $heap-&gt;{full}{$sid} ) {
		delete $heap-&gt;{full}{$sid};
	}

	debug(&quot;Client Termination Posted: $sid\n&quot;);

}

#--------------------------------------------------------------------------#
sub stream_start {
	my ($kernel, $heap) = @_[KERNEL, HEAP];

	$kernel-&gt;alias_set( 'stream' );

	#
	# Initialize the connection to STDIN as a POE::Wheel
	my $stdin = IO::Handle-&gt;new_from_fd( \*STDIN, 'r' );
	my $stderr = IO::Handle-&gt;new_from_fd( \*STDERR, 'w' );

	$heap-&gt;{stream} = POE::Wheel::ReadWrite-&gt;new(
		InputHandle		=&gt; $stdin,
		OutputHandle	=&gt; $stderr,
		InputEvent		=&gt; 'stream_line',
		ErrorEvent		=&gt; 'stream_error',
	);
}

#--------------------------------------------------------------------------#
sub stream_line {
	my ($kernel,$msg) = @_[KERNEL,ARG0];

	return unless length $msg;

	$kernel-&gt;post( 'dispatcher' =&gt; 'dispatch_message' =&gt; $msg );

}

#--------------------------------------------------------------------------#
sub stream_error {
	my ($kernel) = $_[KERNEL];

	debug(&quot;STREAM ERROR!!!!!!!!!!\n&quot;);
	$kernel-&gt;call( 'dispatcher' =&gt; 'server_shutdown' =&gt; 'Stream lost' );
}

#--------------------------------------------------------------------------#
sub server_shutdown {
	my ($kernel,$heap,$msg) = @_[KERNEL,HEAP,ARG0];

	$kernel-&gt;call( dispatcher =&gt; 'broadcast' =&gt; 'SERVER DISCONNECTING: ' . $msg );
	$kernel-&gt;call( 'server' =&gt; 'shutdown' );
	exit;
}

#--------------------------------------------------------------------------#
sub client_connect {
	my ($kernel,$heap,$ses) = @_[KERNEL,HEAP,SESSION];

	my $KID = $kernel-&gt;ID();
	my $CID = $heap-&gt;{client}-&gt;ID;
	my $SID = $ses-&gt;ID;

	$kernel-&gt;post( 'dispatcher' =&gt; 'register_client' =&gt; $SID );

	$heap-&gt;{clients}{ $SID } = $heap-&gt;{client};
	#
	# Say hello to the client.
	$heap-&gt;{client}-&gt;put( &quot;EHLO Streamer (KERNEL: $KID:$SID)&quot; );
}

#--------------------------------------------------------------------------#
sub client_print {
	my ($kernel,$heap,$ses,$mesg) = @_[KERNEL,HEAP,SESSION,ARG0];

	$heap-&gt;{clients}{$ses-&gt;ID}-&gt;put($mesg);
}

#--------------------------------------------------------------------------#
sub broadcast {
	my ($kernel,$heap,$msg) = @_[KERNEL,HEAP,ARG0];

	foreach my $sid (keys %{ $heap-&gt;{clients} }) {
		$kernel-&gt;post( $sid =&gt; 'client_print' =&gt; $msg );
	}
}
#--------------------------------------------------------------------------#
sub dispatch_message {
	my ($kernel,$heap,$msg) = @_[KERNEL,HEAP,ARG0];

	foreach my $sid ( keys %{ $heap-&gt;{full} } ) {
		$kernel-&gt;post( $sid =&gt; 'client_print' =&gt; $msg );
	}

	if( my ($program) = map { lc } ($msg =~ /$cooked{program}/) ) {
		# remove the sub process and PID from the program
		$program =~ s/\(.*//g;
		$program =~ s/\[.*//g;

		debug(&quot;DISPATCHING MESSAGE [$program]&quot;);

		if( exists $heap-&gt;{subscribers}{$program} ) {
			foreach my $sid (keys %{ $heap-&gt;{subscribers}{$program} }) {
				$kernel-&gt;post( $sid =&gt; 'client_print' =&gt; $msg );
			}
		}
		else {
			debug(&quot;Message discarded, no listeners.&quot;);
		}
	}
	else {
			debug(&quot;Message discarded, format not understood.&quot;);
	}
}

#--------------------------------------------------------------------------#
sub debug_message {
	my ($kernel,$heap,$msg) = @_[KERNEL,HEAP,ARG0];

	foreach my $sid (keys %{ $heap-&gt;{debug} }) {
		$kernel-&gt;post( $sid =&gt; 'client_print' =&gt; '[debug] ' . $msg );
	}
}

#--------------------------------------------------------------------------#
sub client_input {
	my ($kernel,$heap,$ses,$msg) = @_[KERNEL,HEAP,SESSION,ARG0];
	my $sid = $ses-&gt;ID;

	if( !exists $heap-&gt;{dispatch}{$sid} ) {
		$heap-&gt;{dispatch}{$sid} = {
			fullfeed		=&gt; {
				re			=&gt; qr/^(fullfeed)/,
				callback	=&gt; sub {
					$kernel-&gt;post( 'dispatcher' =&gt; 'fullfeed_client' =&gt; $sid );
				},
			},
			subscribe		=&gt; {
				re			=&gt; qr/^sub(?:scribe)? (.*)/,
				callback	=&gt; sub {
					$kernel-&gt;post( 'dispatcher' =&gt; 'subscribe_client' =&gt; $sid, shift );
				},
			},
			unsubscribe 	=&gt; {
				re			=&gt; qr/^unsub(?:scribe)? (.*)/,
				callback	=&gt; sub {
					$kernel-&gt;post( 'dispatcher' =&gt; 'unsubscribe_client' =&gt; $sid, shift );
				},
			},
			debug 	=&gt; {
				re			=&gt; qr/^(debug)/i,
				callback	=&gt; sub {
					$kernel-&gt;post( 'dispatcher' =&gt; 'debug_client' =&gt; $sid, shift );
				},
			},
			nobug 	=&gt; {
				re			=&gt; qr/^(no(de)?bug)/i,
				callback	=&gt; sub {
					$kernel-&gt;post( 'dispatcher' =&gt; 'nobug_client' =&gt; $sid, shift );
				},
			},
			#quit			=&gt; {
			#	re			=&gt; qr/(exit)|q(uit)?/,
			#	callback	=&gt; sub {
			#			$kernel-&gt;post( $sid =&gt; 'client_print' =&gt; 'Terminating connection on your request.');
			#			$kernel-&gt;post( $sid =&gt; 'shutdown' );
			#	},
			#},
			#status			=&gt; {
			#	re			=&gt; qr/^status/,
			#	callback	=&gt; sub {
			#		my $cnt = scalar( keys %{ $heap-&gt;{clients} } );
			#		my $subcnt = scalar( keys %{ $heap-&gt;{subscribers} });
			#		my $msg = &quot;Currently $cnt connections, $subcnt subscribed.&quot;;
			#		$kernel-&gt;post( $sid, 'client_print', $msg );
			#	},
			#},
		};
	}

	#
	# Check for messages:
	my $handled = 0;
	my $dispatch = $heap-&gt;{dispatch}{$sid};
	foreach my $evt ( keys %{ $dispatch } ) {
		if( my($args) = ($msg =~ /$dispatch-&gt;{$evt}{re}/)) {
			$handled = 1;
			$dispatch-&gt;{$evt}{callback}-&gt;($args);
			last;
		}
	}

	if( !$handled ) {
		$kernel-&gt;post( $sid =&gt; 'client_print' =&gt; 'UNKNOWN COMMAND, Ignored.' );
	}
}

#--------------------------------------------------------------------------#
sub client_term {
	my ($kernel,$heap,$ses) = @_[KERNEL,HEAP,SESSION];
	my $sid = $ses-&gt;ID;

	delete $heap-&gt;{dispatch}{$sid};
	$kernel-&gt;post( 'dispatcher' =&gt; 'hangup_client' =&gt;  $sid );

	debug(&quot;SERVER, client $sid disconnected.\n&quot;);
}

#--------------------------------------------------------------------------#
</pre>
]]></content:encoded>
			<wfw:commentRss>http://divisionbyzero.net/blog/2009/07/02/using-poe-to-hook-syslog-ng/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
