# HG changeset patch # User root@sonne.dd.dtele.de # Date 1235860962 -3600 # Node ID 3959408aa03d248112cabe8f9e441b8a01aa7f14 # Parent a54c42c041e618146365a7b1d7f1800c14da78ee Works! But... missing is putting permissions from parent directory to child. diff -r a54c42c041e6 -r 3959408aa03d tele-watch.pl --- a/tele-watch.pl Sat Feb 28 22:04:02 2009 +0100 +++ b/tele-watch.pl Sat Feb 28 23:42:42 2009 +0100 @@ -9,21 +9,28 @@ use Linux::Inotify2; use Unix::Syslog qw(:macros :subs); use Cwd qw(abs_path); +use File::Temp qw(tempfile); +use POSIX qw(setsid); my $ME = basename $0; -my $opt_block; +my $opt_block = 1; +my $opt_daemon = 1; +my $opt_pidfile = "/var/run/$ME.pid"; sub writef($@); +sub updatef($@); sub readf($;$); sub notice($;@); sub timestamp(); +sub dir($); openlog($ME, LOG_PID|LOG_PERROR, LOG_DAEMON); MAIN: { + my @_ARGV = @ARGV; my %TARGET; - my %COOKIE; + END { foreach (keys %TARGET) { if (readf("$_/.watched") == $$) { @@ -32,9 +39,11 @@ syslog(LOG_NOTICE, "cleaned $_/.watched"); } } + unlink $opt_pidfile if $opt_pidfile and readf($opt_pidfile) == $$; } $SIG{INT} = sub { syslog(LOG_NOTICE, "got signal @_"); exit 0 }; + $SIG{TERM} = $SIG{INT}; $SIG{__DIE__} = sub { die @_ if not defined $^S; syslog(LOG_ERR, "%s", "@_"); exit $? }; $SIG{__WARN__} = sub { warn @_ if not defined $^S; @@ -45,37 +54,68 @@ "h|help" => sub { pod2usage(-exitval => 0, -verbose => 1) }, "m|man" => sub { pod2usage(-exitval => 0, -verbose => 3) }, "block!" => \$opt_block, + "daemon!" => \$opt_daemon, + "pidfile=s" => \$opt_pidfile, ) and @ARGV or pod2usage(); foreach (@ARGV) { my ($w, $t, $r) = split /:/; die "too many \":\" in \"$_\"\n" if defined $r; + pod2usage() if not defined $w or not defined $t; $w = abs_path($w); $t = abs_path($t); $TARGET{$w} = $t; } + writef($opt_pidfile, $$) if $opt_pidfile; + # mark the directories as watched foreach (keys %TARGET) { - if (-f "$_/.watched") { - my $pid = readf("$_/.watched"); - if (kill 0 => $pid) { - die "$_ is watched by (running) process $pid\n"; - } + my $watcher = readf("$_/.watched"); + if (defined $watcher and kill 0 => $watcher) { + die "$_ is watched by (running) process $watcher\n"; + } + else { + unlink "$_/.watched"; } system("chattr", "-i" => $_); - writef(">$_/.watched", $$); + notice("watching $_"); + writef("$_/.watched", $$); + } + + $0 = "$ME @_ARGV"; + chdir("/") or die "Can't chdir to /: $!\n"; + + if ($opt_daemon) { + open(STDIN, "/dev/null") or die "Can't redir STDOUT: $!\n"; + defined(my $pid = fork()) or die "Can't fork: $!\n"; + if ($pid) { + %TARGET = (); + notice "child is $pid"; + $opt_pidfile = ""; + exit 0; + } + setsid(); + open(STDERR, ">&STDOUT") or die "Can't dup stdout: $!\n"; + + updatef($opt_pidfile, $$) if $opt_pidfile; + foreach (keys %TARGET) { + updatef("$_/.watched", $$); + } } # now start the real watching my $inotify = new Linux::Inotify2 or die "Can't get inotify object: $!\n"; + foreach (keys %TARGET) { $inotify->watch($_, IN_CREATE | IN_MOVED_TO | IN_MOVED_FROM | IN_DELETE) or die "Can't create watcher for \"$_\": $!\n"; } + my %COOKIE; while () { my @events = $inotify->read; die "read error on notify: $!\n" if !@events; @@ -88,7 +128,7 @@ if ($e->IN_CREATE) { notice "new dir $fullname"; - foreach my $t (map { basename($_) } grep {-d} glob "$target/*") { + foreach my $t (map { basename($_) } grep {-d} dir "$target/") { my $dir = "$target/$t/$e->{name}"; my $link = "$fullname/$t"; @@ -105,7 +145,7 @@ } if ($e->IN_MOVED_FROM) { - notice "$fullname moved from, set cookie"; + notice "$fullname moves away, set cookie"; $COOKIE{$e->{cookie}} = $e->{name}; next EVENT; } @@ -124,7 +164,7 @@ # change the link targets # find the links pointing to the $target/ - foreach my $link (grep {-l && readlink =~ /^$target\// } glob "$fullname/*") { + foreach my $link (grep {-l && readlink =~ /^$target\// } dir "$fullname/") { my $x = readlink($link); my ($t) = ($x =~ /^$target\/(.*)\/$from_base$/); @@ -143,7 +183,7 @@ } if ($e->IN_DELETE) { - foreach my $dir (grep {-d} glob "$target/*") { + foreach my $dir (grep {-d} dir "$target/") { -d "$dir/,old" or mkdir "$dir/,old" => 0755 @@ -162,6 +202,12 @@ } } +sub dir($) { + my $base = shift; + opendir(my $dir, $base) or die "Can't open $base: $!\n"; + return map { "$base/$_" } grep !/^(?:\.\.?)/, sort readdir $dir; +} + sub timestamp() { my @now = localtime; return sprintf "%4d%02d%02d-%02d%02d%02d", @@ -176,13 +222,25 @@ sub readf($;$) { my $fn = shift; my $rs = @_ ? shift : undef; - open(my $fh, $fn) or die "Can't open $fn: $!\n"; + open(my $fh, $fn) or return undef; return <$fh>; } sub writef($@) { my $fn = shift; - open(my $fh, $fn) or die "Can't open $fn: $!\n"; + my ($fh, $tmpfn) = tempfile(DIR => dirname($fn), UNLINK => 1) + or die "Can't get temp file name in dir " + . dirname($fn) + . ": $!\n"; + print {$fh} @_; + close $fh; + link($tmpfn, $fn) or do die "Can't rename $tmpfn => $fn: $!\n"; + unlink($tmpfn); +} + +sub updatef($@) { + my $fn = shift; + open(my $fh, "+>$fn") or die "Can't open +>$fn: $!\n"; print {$fh} @_; } @@ -236,7 +294,17 @@ =item B<--[no]block> If set, on exit the watched directories are blocked by C. -(default: off) +(default: on) + +=item B<--[no]daemon> + +If set, the scripts daemonizes itself short after start. The pid gets +written to the F (see pidfile option). (default: on) + +=item B<--pidfile>=I + +The name of the file holding the pid of the running process. (default: +/var/run/tele-watch.pid) =back