# HG changeset patch # User Heiko Schlittermann # Date 1233237978 -3600 # Node ID 4fb7b2a136d34d7a7c5f5ad6b642b6783bed7692 # Parent 1523d46da1d21c9ee56f9ea129b40cf4c2b47b85 Added "-k|--kill" option. diff -r 1523d46da1d2 -r 4fb7b2a136d3 .hgignore --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.hgignore Thu Jan 29 15:06:18 2009 +0100 @@ -0,0 +1,1 @@ +hlog.1.gz diff -r 1523d46da1d2 -r 4fb7b2a136d3 Makefile --- a/Makefile Thu Jan 29 06:01:16 2009 +0100 +++ b/Makefile Thu Jan 29 15:06:18 2009 +0100 @@ -1,8 +1,10 @@ DESTDIR = prefix = /usr/local -bindir = ${prefix}/bin +bindir = ${prefix}/bin man1dir = ${prefix}/share/man/man1 +logdir = /var/log +rundir = /var/run .PHONY: all clean install @@ -14,11 +16,16 @@ all: $(MAN1) install: all - # bin + # mandatory directories install -m 0755 -d ${DESTDIR}${bindir} + install -m 0755 -d ${DESTDIR}${man1dir} + + # optional directories + -install -m 0755 -d ${DESTDIR}${logdir} + -install -m 0755 -d ${DESTDIR}${rundir} + + # script and manpage install -m 0755 $(SCRIPT) ${DESTDIR}${bindir}/ - # man - install -m 0755 -d ${DESTDIR}${man1dir} install -m 0644 $(MAN1) ${DESTDIR}${man1dir}/ clean: ; -rm -f $(CLEANFILES) diff -r 1523d46da1d2 -r 4fb7b2a136d3 hlog --- a/hlog Thu Jan 29 06:01:16 2009 +0100 +++ b/hlog Thu Jan 29 15:06:18 2009 +0100 @@ -33,27 +33,35 @@ my $opt_daemon = 1; 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 +my $ME = basename $0; + +# these vars will be filled with the real dirs later +my $rundir = [ "/var/run/$ME", "$ENV{HOME}/.$ME" ]; +my $logdir = [ "/var/log/$ME", "$ENV{HOME}/.$ME" ]; + +my $maxlogsize = 1000_000_000; # ca 1 MByte +my $killtimeout = 3; + +# these are refs to detect if they're converted already +my $access = \"%s/access.log"; +my $errors = \"%s/error.log"; +my $pidfile = \"%s/%s.%s.pid"; # %dir/%ip.%port END { - unlink $pidfile - if defined $pidfile and not ref $pidfile; + unlink $pidfile + if defined $pidfile and not ref $pidfile; } +sub find_writable_dir(@); + +sub log_open($); +sub log_write($); sub handle_request($); -sub date1123(;$); sub http($@); + sub bad_request(); -sub log_open($); -sub log_write($); -sub find_writable_dir(@); +sub date1123(;$); my %FILE; @@ -64,7 +72,7 @@ "port=i" => \$opt_port, "lines=i" => \$opt_lines, "daemon!" => \$opt_daemon, - "kill" => \$opt_kill, + "kill" => \$opt_kill, "help" => sub { pod2usage(-verbose => 1, -exitval => 0) }, "man" => sub { pod2usage(-verbose => 2, -exitval => 0) }, ) or pod2usage(); @@ -75,10 +83,40 @@ log_open($access); } - $rundir = find_writable_dir(@$rundir); + if (defined($rundir = find_writable_dir(@$rundir))) { + + # santize hostname + (my $host = $opt_addr) =~ s/([^\w.-])/sprintf "%%%02X", ord($1)/gie; + $pidfile = sprintf $$pidfile, $rundir, $host, $opt_port,; + } + else { $pidfile = undef } + + if ($opt_kill) { + warn "Killing process on $opt_addr:$opt_port\n"; + open(my $p, $pidfile) or die "Can't open $pidfile: $!\n"; + defined($_ = <$p>) or die "no pid found in $pidfile\n"; + chomp; + kill -15 => $_ or die "Can't kill pid $_: $!\n"; + + # we can't wait, it's not our process group, so we've to poll + eval { + $SIG{ALRM} = sub { die "TIMEOUT\n" }; + alarm($killtimeout); + for (my $sleep = 1 ; kill -0 => $_ ; $sleep++) { + sleep($sleep > 10 ? 10 : $sleep); + } + alarm(0); + }; + if ($@ eq "TIMEOUT\n") { + warn "Child $_ didn't respond. Using violence.\n"; + kill -9 => $_; + } + exit 0; + } pod2usage() if not @ARGV; + # resolve tags and filenames foreach (@ARGV) { $_ = "default=$_" if not /=/ or /^\//; my ($tag, $file) = split /=/, $_, 2; @@ -88,6 +126,7 @@ $FILE{$tag} = $file; } + # start the listener my $listener = new IO::Socket::INET( LocalAddr => $opt_addr, LocalPort => $opt_port, @@ -96,13 +135,6 @@ 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"; @@ -115,7 +147,7 @@ print "listener $pid " . $listener->sockhost . ":" . $listener->sockport . "\n"; - undef $pidfile; + undef $pidfile; exit 0; } @@ -134,6 +166,7 @@ } $SIG{INT} = $SIG{TERM} = sub { warn "Got signal $_[0]\n"; exit 0 }; + $SIG{__WARN__} = sub { print STDERR localtime() . " ", @_ }; $SIG{__DIE__} = sub { print STDERR @_; exit $? }; if (defined $pidfile) { @@ -193,7 +226,8 @@ } sub log_write($) { - $fh->print(localtime() . " $_[0]\n"); + $fh->print(localtime() . " $_[0]\n") + if defined $fh; } } @@ -328,6 +362,7 @@ =head1 SYNOPSIS hlog [--[no]daemon] + [-k|--kill] [-a|--address address] [-p|--port port] [--lines n] {file|tag=file ...} @@ -339,17 +374,7 @@ 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. +See the L section for more information on files. =head1 OPTIONS @@ -367,6 +392,11 @@ The number of lines to show. (default: 10) +=item B<-k>|B<--kill> + +With this option the corresponding (address/port) process gets killed. +(default: off) + =item B<-p>|B<--port> I The port to listen on. (default: 8080) @@ -387,5 +417,25 @@ http://:8080/error http://:8080/access?10 +=head1 FILES + +The B tool tries to create several files + +=head2 F and F + +These files will be written to F or F<$HOME/.hlog/> if +possible. The mentioned directories (the leave part) will be created, if +possible. It is no fatal error if B fails on this. + +=head2 PID file + +B tries to create a pid file in F or +F<$HOME/.hlog>. It even tries to create the leave part the directory. +Failing on this it not fatal, but then the B<--kill> option will not +work! + +The pid file will be named according to the hostname (see B<--address>) +beeing used. For safety the hostname will be sanitized to avoid +dangerous filenames. =cut