#!/usr/bin/perl -s #BEGIN{ use FileCache; package Flog; local($PREFIX, $VERSION, %opts, %FILES); chomp($PREFIX = ); chomp($VERSION = ); use sigtrap 'handler' => \&Flog::handler, "normal-signals"; $SIG{__DIE__} = \&Flog::handler; #} if( $main::t || $main::V ){ require $PREFIX."lib/flog.lib"; exit 0; } #MAIN{ *croak = sub { debug(2, @_); }; #import switches... and undef them in main foreach (qw(c d g o u)){ $a = "main::$_"; $opts{$_} = ${$a}; undef ${$a}; } #reset() after copy options into namespace? #this way no need to undef? kill A-Z even (despite path etc.?) #OR #Let be known (in advanced?) that they can call flog with own switches #for internal config options (like apache's -C) open(DEBUG, ($opts{d} != 1 ? $opts{d} : ">/dev/tty8")) if $opts{d}; _init(); while(){ print DEBUG if $opts{d}; &main::flog(); } #} #Fun - ctions! sub become { my($ret, $user, $group) = (0, @_); unless( $user =~ /^\d*$/ ){ $user = (getpwnam($user))[2]; } unless( $group =~ /^\d*$/ ){ $group = (getgrnam($group))[2]; } $ret += $user && ($> = $user) == $user ? 1 : 0; $ret += $group && ($) = $group) == $group ? 2 : 0; return $ret; } sub debug { return unless $Flog::opts{d}; printf Flog::DEBUG "[%s] [%s] $_[1]", scalar localtime(time), ["notice", "error", "crit."]->[$_[0]]; } sub fh { #sure hope this is in package Flog my($fh, $keepOpen) = @_; unless( fileno($_[0]) ){ if( $keepOpen == 2 ){ $fh = $FILES{$fh}->[0]; unless( $FileCacheFILES{$fh} ){ #FileCache supplies it's own />>?/ $fh =~ s/^[ +]*>>?[ +]*//; $FileCacheFILES{$_[0]} = $fh; } cacheout($fh); } else{ open($fh, $FILES{$fh}->[0]) || $opts{d} && debug(1, "Unable to open $FILES{$fh}->[0]: $!\n"); } flush($fh, 1) if $FILES{$fh}->[1]; } return $fh; } sub flush { select($_[0]); return $|= $_[1]; } #Some are not real filehandles if printFlog( ... , 2) was used #Playing with fire! sub handler { my $SIG = shift(); if( $SIG eq 'HUP' ){ debug(0, "SIGHUP received. Attempting to restart($$)\n"); map(close($_), keys %Flog::FILES); map(close($_), values %Flog::FileCacheFILES); undef %Flog::FileCacheFILES; _init(); } if($SIG =~ /(?:TERM)|(?:DIE)|(?:INT)/ ){ Flog::debug(0, "caught SIG$SIG, shutting down\n"); map(close($_), keys %Flog::FILES); map(close($_), keys %Flog::FileCacheFILES); exit 0; } if($SIG eq 'USR1' ){ Flog::debug(0, "caught SIGUSR1, flushing filehandles\n"); map(flush($_, 1)&&flush($_, 0), keys %Flog::FILES); map(flush($_, 1)&&flush($_, 0), values %Flog::FileCacheFILES); } } sub _init { if( $opts{u} || $opts{g} ){ my $guess = exists($opts{u}) + 2 * ( exists($opts{g}) ); my $got = &become($opts{u}||undef, $opts{g}||undef); debug(($guess==$got?0:2), "Becoming $opts{u}:$opts{g}, should get $guess got $got\n"); } package main; do $Flog::opts{c}; if( $@ ){ Flog::debug(2, "flog $VERSION($$) configuration compilation failed -- exiting\n"); exit 0;} else{ Flog::debug(0, "flog $VERSION($$) configured -- resuming normal operations\n"); } $FileCache::cacheout_maxopen = $MAXOPEN || 16; } sub init { my $pkg = (caller())[0]; #Make flog aware of the files foreach my $key ( keys %{"${pkg}::FILES"} ){ my $aryref = $FILES{"{$pkg}::{$key}"} = ${"${pkg}::FILES"}{$key}; if( $aryref->[0] =~ /^[ +]*>>[ +]*(.*)/ ){ $FileCache::saw{$1}++; } } unless( $opts{o} ){ debug(0, "flog $VERSION($$) Pre-opening %%FILES for $pkg\n"); map(&fh($_), keys %FILES); select(STDOUT); } else{ debug(0, "flog $VERSION($$) Not pre-opening %%FILES for $pkg\n"); } *{"${pkg}::padIP"} = \&Flog::padIP; *{"${pkg}::printFlog"} = \&Flog::printFlog; } sub padIP { my $pad = 15; if( $_[0] ){ s/^([\S]*)/$1 . ' 'x($pad-length($1))/eg; } else{ s/((?:\d{1,3}\.){3}\d{1,3})/$1 . ' 'x($pad-length($1))/eg; } } sub printFlog { my $pkg = (caller())[0]; my $fh = &Flog::fh("{$pkg}::{$_[0]}", $_[1]); print { $fh } $_; close($fh) if $_[1] == 1; } #-u & -g options to setuid and setgid !! :-) better security... #ESP. if multi vhosts with multi flog processes __DATA__