# HG changeset patch # User Heiko Schlittermann # Date 1233205268 -3600 # Node ID a725449912253647f62ee9d3c6dcff3e9495bf29 # Parent 044bd52da73571c500c7b38f17f7ba3038d1b5de Added PID, ACCESS, ERROR files and logrotation. diff -r 044bd52da735 -r a72544991225 hlog --- a/hlog Thu Jan 29 04:32:12 2009 +0100 +++ b/hlog Thu Jan 29 06:01:08 2009 +0100 @@ -23,6 +23,7 @@ use Getopt::Long; use IO::Socket::INET; use Pod::Usage; +use File::Basename; use POSIX qw(:sys_wait_h setsid); use Cwd; @@ -30,13 +31,31 @@ my $opt_port = 8080; my $opt_lines = 10; my $opt_daemon = 1; -my $logfile = "hlog.log"; -my %FILE; +my $opt_kill = 0; + +my $ME = basename $0; +my $rundir = [ "/var/run/$ME", "$ENV{HOME}/.$ME" ]; +my $logdir = [ "/var/log/$ME", "$ENV{HOME}/.$ME" ]; +my $access = \"%s/access.log"; +my $errors = \"%s/error.log"; +my $pidfile = \"%s/%s.%s.pid"; # %dir/%ip.%port +my $maxlogsize = 1000_000_000; # ca 1 MByte + +END { + unlink $pidfile + if defined $pidfile and not ref $pidfile; +} + sub handle_request($); sub date1123(;$); sub http($@); sub bad_request(); +sub log_open($); +sub log_write($); +sub find_writable_dir(@); + +my %FILE; MAIN: { @@ -44,13 +63,19 @@ "addr=s" => \$opt_addr, "port=i" => \$opt_port, "lines=i" => \$opt_lines, + "daemon!" => \$opt_daemon, + "kill" => \$opt_kill, "help" => sub { pod2usage(-verbose => 1, -exitval => 0) }, "man" => sub { pod2usage(-verbose => 2, -exitval => 0) }, - "daemon!" => \$opt_daemon, ) or pod2usage(); - open(LOG, ">>$logfile"); - print LOG localtime() . " started\n"; + if (defined($logdir = find_writable_dir(@$logdir))) { + $access = sprintf $$access, $logdir; + $errors = sprintf $$errors, $logdir; + log_open($access); + } + + $rundir = find_writable_dir(@$rundir); pod2usage() if not @ARGV; @@ -71,28 +96,56 @@ ReuseAddr => 1, ) or die "Can't create listener socket: $!\n"; + if ($rundir) { + $pidfile = sprintf $$pidfile, $rundir, + $listener->sockhost, + $listener->sockport; + } + else { $pidfile = undef } + # go daemon chdir("/") or die "Can't chdir to /: $!\n"; if ($opt_daemon) { - open(STDIN, "/dev/null") or die "Can't read /dev/null: $!\n"; - open(STDOUT, ">/dev/null") or die "Can't write to /dev/null: $!\n"; + defined(my $pid = fork()) or die "Can't fork: $!\n"; + # parent if ($pid) { - warn "listener $pid $opt_addr:$opt_port\n"; + print "listener $pid " + . $listener->sockhost . ":" + . $listener->sockport . "\n"; + undef $pidfile; exit 0; } # child + setsid() or die "Can't start a new session: $!\n"; + open(STDIN, "/dev/null") or die "Can't read /dev/null: $!\n"; + open(STDOUT, ">/dev/null") or die "Can't write to /dev/null: $!\n"; - setsid() or die "Can't start a new session: $!\n"; - open(STDERR, ">&STDOUT") or die "Can't dup stdout: $!\n"; + if (defined $logdir) { + open(STDERR, $_ = ">>$errors") or warn "Can't open $_: $!\n"; + } + else { + open(STDERR, ">&STDOUT") or die "Can't dup stdout: $!\n"; + } + + } + + $SIG{INT} = $SIG{TERM} = sub { warn "Got signal $_[0]\n"; exit 0 }; + $SIG{__DIE__} = sub { print STDERR @_; exit $? }; + + if (defined $pidfile) { + open(PID, ">$pidfile") + or die "Can't open $pidfile: $!\n"; + + print PID "$$\n"; + close PID; } $SIG{CHLD} = sub { - while ((my $pid = waitpid(-1, WNOHANG)) > 0) { - print LOG localtime() . " child $pid died\n"; + while (waitpid(-1, WNOHANG) > 0) { } }; @@ -100,12 +153,6 @@ my $client = $listener->accept; next if not defined $client; # may be because of signal - print LOG $_ = - localtime() - . " access from " - . $client->peerhost . ":" - . $client->peerport . "\n"; - my $pid = fork(); die "Can't fork: $!\n" if not defined $pid; if ($pid == 0) { @@ -115,6 +162,38 @@ exit 0; } $client->close; + + # maintenance of logfiles + if (-s $access > $maxlogsize) { + rename $access, "$access.1"; + log_open($access); + } + + if (-s $errors > $maxlogsize) { + rename $errors, "$errors.1"; + open(STDERR, ">>$errors"); + } + } + +} + +sub find_writable_dir(@) { + foreach (@_) { + return $_ if -d and -w _; + return $_ if mkdir $_, 0755; + } + return undef; +} + +{ + my $fh; + + sub log_open($) { + open($fh, $_ = ">>$_[0]") or die "Can't open $_: $!\n"; + } + + sub log_write($) { + $fh->print(localtime() . " $_[0]\n"); } } @@ -144,17 +223,18 @@ if (not exists $FILE{$tag}) { $client->print(http "500 unknown file tag", "Sorry, unknown file tag \"$tag\""); - print LOG " unknown tag $tag"; + log_write("unknown tag $tag"); return; } my %file = analyze($FILE{$tag}); if (!%file) { $client->print(http "500 internal error", "internal error"); - print LOG $@; return; } + log_write($client->peerhost . ":" . $client->peerport . " $tag ($lines)"); + seek($file{fh}, -($lines + 1) * $file{avglen}, 2); $file{fh}->getline; @@ -259,6 +339,18 @@ This script should run as a server providing access to the last lines of a logfile. It should understand basic HTTP/1.x. +B looks for a writable "log" directory (or tries to create on) as +F and F<$HOME/.hlog> and opens there an F and +an F. It is not fatal if it doesn't find a writable +directory. Regularly (currently at each incoming request) the size of +these logs is checked and the files are renamed to "*.1" whenever they +are bigger than about 1MB. + +B looks for a writable "run" directory (or tries to create on) +as F and F<$HOME/.hlog> and writes there the PID-file +as I.I.F. It's not fatal if it can't write the +PID-file. + =head1 OPTIONS =over