Added "-k|--kill" option.
--- /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
--- 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)
--- 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<hlog> looks for a writable "log" directory (or tries to create on) as
-F</var/log/hlog> and F<$HOME/.hlog> and opens there an F<access.log> and
-an F<error.log>. 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<hlog> looks for a writable "run" directory (or tries to create on)
-as F</var/run/hlog> and F<$HOME/.hlog> and writes there the PID-file
-as I<addr>.I<port>.F<pid>. It's not fatal if it can't write the
-PID-file.
+See the L<FILES> 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<port>
The port to listen on. (default: 8080)
@@ -387,5 +417,25 @@
http://<server>:8080/error
http://<server>:8080/access?10
+=head1 FILES
+
+The B<hlog> tool tries to create several files
+
+=head2 F<access.log> and F<error.log>
+
+These files will be written to F</var/log/hlog/> or F<$HOME/.hlog/> if
+possible. The mentioned directories (the leave part) will be created, if
+possible. It is no fatal error if B<hlog> fails on this.
+
+=head2 PID file
+
+B<hlog> tries to create a pid file in F</var/run/hlog/> 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