should work now. (testing)
to do: add code for daemonization
--- a/Makefile Sat Feb 28 18:52:48 2009 +0100
+++ b/Makefile Sat Feb 28 22:04:02 2009 +0100
@@ -12,10 +12,16 @@
install -m 0644 tele-watch.8.gz ${DESTDIR}/${man8dir}
clean:
- -rm -f tele-watch.8.gz
+ -rm -f tele-watch.8.gz tele-watch
+
+%: %.pl
+ perl -c $<
+ cp -f $< $@
+ chmod a+x-w $@
%.gz: %
gzip -f $<
%.8: %
pod2man --section 8 $< >$@
+
--- a/tele-watch Sat Feb 28 18:52:48 2009 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,134 +0,0 @@
-#! /bin/bash -e
-# (c) Heiko Schlittermann <hs@schlittermann.de>
-
-ME=$(basename $0)
-WATCHPOINTS=
-
-function abs_path() {
- local a="$1"
- case "$a" in
- /*) echo "$a";;
- *) t="$(cd $t && pwd)";;
- esac
-}
-
-if test "$#" = 0; then
- echo "need at least one directory to watch"
- exit 1
-fi
-
-for watch; do
- w=${watch%:*}
- t=${watch#*:}
-
- test "$w:$t" = "$watch" || {
- echo "to many ':' in \"$watch\"" >&2
- exit 1
- }
-
- w=$(abs_path "$w")
- t=$(abs_path "$t")
-
- if test -f "$w/.target"; then
- read pid target <"$w/.target"
-
- kill -0 $pid 2>/dev/null && {
- echo "already watched by $pid for $target" >&2
- exit 1
- }
- fi
-
- echo "$$ $t" > "$w/.target"
- WATCHPOINTS="${WATCHPOINTS:+$WATCHPOINTS }$w"
-done
-
-inotifywait -q -m --format "%e %w %f" -e create,move,delete $WATCHPOINTS | while read EVENT WATCHER NAME
-do
- echo "$EVENT -- $WATCHER $NAME"
-
- case "$EVENT" in
- *ISDIR*) ;;
- *) continue;;
- esac
-
- read dummy TARGET <$WATCHER/.target
- DIRS=$(cd $TARGET && echo *)
-
- case "$EVENT" in
- *CREATE*)
- for dir in $DIRS; do
- mkdir "/.m/$dir/$NAME"
- echo "$NAME" >"$WATCHER/$NAME/.name"
- ln -s "/.m/$dir/$NAME" "$WATCHER/$NAME/$dir"
- done
- ;;
- *MOVED_TO*)
- name=$(tail -1 "$WATCHER/$NAME/.name")
- for link in "$WATCHER/$NAME/"*; do
- test -L "$link" || continue
- dst=$(readlink $link)
- if test "$(basename $dst)" = "$name"; then
- new="$(dirname $dst)/$NAME"
- mv -v "$dst" "$new"
- ln -vsf "$new" "$link"
- fi
- done
- echo "$NAME" >>"$WATCHER/$NAME/.name"
- ;;
- *DELETE*)
- for dir in $DIRS; do
- mkdir -p "/.m/$dir/,old"
- mv -v "/.m/$dir/$NAME" "/.m/$dir/,old/$NAME-$(date +%Y%m%d-%H%M%S)"
- done
- esac
-done
-exit
-
-=head1 NAME
-
-tele-watch - guard the dtele directory policy
-
-=head1 SYNOPSIS
-
- tele-watch "<dir:target>"...
-
-=head1 DESCRIPTION
-
-B<tele-watch> should run as a daemon.
-
-B<tele-watch> watches the list of directories I<dir>... (absolute path names)
-via "inotify" and performs some actions on:
-
-=over
-
-=item CREATION of new directory
-
-It checks F</.m/*> and assumes, that all directories there should
-reflect in the newly created directory:
-
- <NEW1>/_tmp -> /.m/_tmp/NEW1/
- <NEW1>/homepage -> /.m/homepage/NEW1/
- ...
-
-After done this it writes the name of the newly created directory into
-the file F<< <NEW1>/.name >>
-
-=item RENAMING of a directory
-
-If the directory gets renamed, the above links needs to be updated.
-
-=item DELETION of a directory
-
-If the root directory is removed, the targets of the former links should
-be removed, we do our best, to do this. (Actually not removing the
-targets, but moving them into an F</.m/_tmp/,old> folder.)
-
-=back
-
-=head1 AUTHOR
-
-Heiko Schlittermann <hs@schlittermann.de>
-
-=cut
-
-# vim:tw=72 sts=4 ts=4 sw=4 aw ai sm:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tele-watch.pl Sat Feb 28 22:04:02 2009 +0100
@@ -0,0 +1,249 @@
+#! /usr/bin/perl
+# (c) Heiko Schlittermann <hs@schlittermann.de>
+
+use strict;
+use warnings;
+use Pod::Usage;
+use File::Basename;
+use Getopt::Long;
+use Linux::Inotify2;
+use Unix::Syslog qw(:macros :subs);
+use Cwd qw(abs_path);
+
+my $ME = basename $0;
+
+my $opt_block;
+
+sub writef($@);
+sub readf($;$);
+sub notice($;@);
+sub timestamp();
+
+openlog($ME, LOG_PID|LOG_PERROR, LOG_DAEMON);
+
+MAIN: {
+ my %TARGET;
+ my %COOKIE;
+ END {
+ foreach (keys %TARGET) {
+ if (readf("$_/.watched") == $$) {
+ unlink "$_/.watched";
+ system("chattr", "+i" => "$_") if $opt_block;
+ syslog(LOG_NOTICE, "cleaned $_/.watched");
+ }
+ }
+ }
+
+ $SIG{INT} = sub { syslog(LOG_NOTICE, "got signal @_"); exit 0 };
+ $SIG{__DIE__} = sub { die @_ if not defined $^S;
+ syslog(LOG_ERR, "%s", "@_"); exit $? };
+ $SIG{__WARN__} = sub { warn @_ if not defined $^S;
+ syslog(LOG_WARNING, "%s", "@_");
+ };
+
+ GetOptions(
+ "h|help" => sub { pod2usage(-exitval => 0, -verbose => 1) },
+ "m|man" => sub { pod2usage(-exitval => 0, -verbose => 3) },
+ "block!" => \$opt_block,
+ ) and @ARGV or pod2usage();
+
+ foreach (@ARGV) {
+ my ($w, $t, $r) = split /:/;
+ die "too many \":\" in \"$_\"\n" if defined $r;
+ $w = abs_path($w);
+ $t = abs_path($t);
+ $TARGET{$w} = $t;
+ }
+
+ # 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";
+ }
+ }
+ system("chattr", "-i" => $_);
+ writef(">$_/.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";
+ }
+
+ while () {
+ my @events = $inotify->read;
+ die "read error on notify: $!\n" if !@events;
+ EVENT: foreach my $e (@events) {
+ next unless $e->IN_ISDIR;
+
+ my $target = $TARGET{$e->{w}{name}};
+ my $fullname = $e->fullname;
+
+ if ($e->IN_CREATE) {
+ notice "new dir $fullname";
+
+ foreach my $t (map { basename($_) } grep {-d} glob "$target/*") {
+ my $dir = "$target/$t/$e->{name}";
+ my $link = "$fullname/$t";
+
+ if (!-e $dir) {
+ notice "mkdir $dir";
+ mkdir $dir => 0755;
+ }
+
+ notice "symlink $dir <= $link";
+ unlink $link;
+ symlink $dir => $link;
+ }
+ next EVENT;
+ }
+
+ if ($e->IN_MOVED_FROM) {
+ notice "$fullname moved from, set cookie";
+ $COOKIE{$e->{cookie}} = $e->{name};
+ next EVENT;
+ }
+
+ if ($e->IN_MOVED_TO) {
+
+ if (!exists ($COOKIE{$e->{cookie}})) {
+ warn "no known source for $fullname\n";
+ next EVENT;
+ }
+
+ my $from = $COOKIE{$e->{cookie}};
+ my $from_base = basename $from;
+ notice "$fullname moved here from $from";
+
+ # change the link targets
+
+ # find the links pointing to the $target/
+ foreach my $link (grep {-l && readlink =~ /^$target\// } glob "$fullname/*") {
+ my $x = readlink($link);
+ my ($t) = ($x =~ /^$target\/(.*)\/$from_base$/);
+
+ my $y = "$target/$t/$e->{name}";
+
+ notice "rename $x => $y";
+ rename(readlink($link), "$target/$t/$e->{name}") or die "Can't rename: $!\n";
+
+ notice "symlink $y <= $fullname/$t";
+ unlink $link;
+ symlink $y => "$fullname/$t" or die "Can't symlink $y => $fullname/$t: $!\n";
+ }
+
+ delete $COOKIE{$e->{cookie}};
+ next EVENT;
+ }
+
+ if ($e->IN_DELETE) {
+ foreach my $dir (grep {-d} glob "$target/*") {
+
+ -d "$dir/,old"
+ or mkdir "$dir/,old" => 0755
+ or die "Can't mkdir $dir/,old: $!\n";
+
+ my $x = "$dir/$e->{name}";
+ if (-d $x) {
+ my $y = "$dir/,old/$e->{name}-" . timestamp();
+ notice "move $x => $y";
+ rename $x => $y or die "Can't rename $x => $y: $!\n";
+ }
+ }
+ next EVENT;
+ }
+ }
+ }
+}
+
+sub timestamp() {
+ my @now = localtime;
+ return sprintf "%4d%02d%02d-%02d%02d%02d",
+ $now[5]+1900, $now[4] + 1, $now[3],
+ @now[2,1,0];
+}
+
+sub notice($;@) {
+ syslog(LOG_NOTICE, $_[0], @_[1..$#_]);
+}
+
+sub readf($;$) {
+ my $fn = shift;
+ my $rs = @_ ? shift : undef;
+ open(my $fh, $fn) or die "Can't open $fn: $!\n";
+ return <$fh>;
+}
+
+sub writef($@) {
+ my $fn = shift;
+ open(my $fh, $fn) or die "Can't open $fn: $!\n";
+ print {$fh} @_;
+}
+
+__END__
+
+=head1 NAME
+
+tele-watch - guard the dtele directory policy
+
+=head1 SYNOPSIS
+
+ tele-watch [options] "<dir:target>"...
+
+=head1 DESCRIPTION
+
+B<tele-watch> should run as a daemon.
+
+B<tele-watch> watches the list of directories I<dir>... (absolute path names)
+via "inotify" and performs some actions on:
+
+=over
+
+=item CREATION of new directory
+
+It checks F</.m/*> and assumes, that all directories there should
+reflect in the newly created directory:
+
+ <NEW1>/_tmp -> /.m/_tmp/NEW1/
+ <NEW1>/homepage -> /.m/homepage/NEW1/
+ ...
+
+After done this it writes the name of the newly created directory into
+the file F<< <NEW1>/.name >>
+
+=item RENAMING of a directory
+
+If the directory gets renamed, the above links needs to be updated.
+
+=item DELETION of a directory
+
+If the root directory is removed, the targets of the former links should
+be removed, we do our best, to do this. (Actually not removing the
+targets, but moving them into an F</.m/_tmp/,old> folder.)
+
+=back
+
+=head1 OPTIONS
+
+=over
+
+=item B<--[no]block>
+
+If set, on exit the watched directories are blocked by C<chattr +i>.
+(default: off)
+
+=back
+
+=head1 AUTHOR
+
+Heiko Schlittermann <hs@schlittermann.de>
+
+=cut
+
+# vim:tw=72 sts=4 ts=4 sw=4 aw ai sm:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tele-watch.sh Sat Feb 28 22:04:02 2009 +0100
@@ -0,0 +1,134 @@
+#! /bin/bash -e
+# (c) Heiko Schlittermann <hs@schlittermann.de>
+
+ME=$(basename $0)
+WATCHPOINTS=
+
+function abs_path() {
+ local a="$1"
+ case "$a" in
+ /*) echo "$a";;
+ *) t="$(cd $t && pwd)";;
+ esac
+}
+
+if test "$#" = 0; then
+ echo "need at least one directory to watch"
+ exit 1
+fi
+
+for watch; do
+ w=${watch%:*}
+ t=${watch#*:}
+
+ test "$w:$t" = "$watch" || {
+ echo "to many ':' in \"$watch\"" >&2
+ exit 1
+ }
+
+ w=$(abs_path "$w")
+ t=$(abs_path "$t")
+
+ if test -f "$w/.target"; then
+ read pid target <"$w/.target"
+
+ kill -0 $pid 2>/dev/null && {
+ echo "already watched by $pid for $target" >&2
+ exit 1
+ }
+ fi
+
+ echo "$$ $t" > "$w/.target"
+ WATCHPOINTS="${WATCHPOINTS:+$WATCHPOINTS }$w"
+done
+
+inotifywait -q -m --format "%e %w %f" -e create,move,delete $WATCHPOINTS | while read EVENT WATCHER NAME
+do
+ echo "$EVENT -- $WATCHER $NAME"
+
+ case "$EVENT" in
+ *ISDIR*) ;;
+ *) continue;;
+ esac
+
+ read dummy TARGET <$WATCHER/.target
+ DIRS=$(cd $TARGET && echo *)
+
+ case "$EVENT" in
+ *CREATE*)
+ for dir in $DIRS; do
+ mkdir "/.m/$dir/$NAME"
+ echo "$NAME" >"$WATCHER/$NAME/.name"
+ ln -s "/.m/$dir/$NAME" "$WATCHER/$NAME/$dir"
+ done
+ ;;
+ *MOVED_TO*)
+ name=$(tail -1 "$WATCHER/$NAME/.name")
+ for link in "$WATCHER/$NAME/"*; do
+ test -L "$link" || continue
+ dst=$(readlink $link)
+ if test "$(basename $dst)" = "$name"; then
+ new="$(dirname $dst)/$NAME"
+ mv -v "$dst" "$new"
+ ln -vsf "$new" "$link"
+ fi
+ done
+ echo "$NAME" >>"$WATCHER/$NAME/.name"
+ ;;
+ *DELETE*)
+ for dir in $DIRS; do
+ mkdir -p "/.m/$dir/,old"
+ mv -v "/.m/$dir/$NAME" "/.m/$dir/,old/$NAME-$(date +%Y%m%d-%H%M%S)"
+ done
+ esac
+done
+exit
+
+=head1 NAME
+
+tele-watch - guard the dtele directory policy
+
+=head1 SYNOPSIS
+
+ tele-watch "<dir:target>"...
+
+=head1 DESCRIPTION
+
+B<tele-watch> should run as a daemon.
+
+B<tele-watch> watches the list of directories I<dir>... (absolute path names)
+via "inotify" and performs some actions on:
+
+=over
+
+=item CREATION of new directory
+
+It checks F</.m/*> and assumes, that all directories there should
+reflect in the newly created directory:
+
+ <NEW1>/_tmp -> /.m/_tmp/NEW1/
+ <NEW1>/homepage -> /.m/homepage/NEW1/
+ ...
+
+After done this it writes the name of the newly created directory into
+the file F<< <NEW1>/.name >>
+
+=item RENAMING of a directory
+
+If the directory gets renamed, the above links needs to be updated.
+
+=item DELETION of a directory
+
+If the root directory is removed, the targets of the former links should
+be removed, we do our best, to do this. (Actually not removing the
+targets, but moving them into an F</.m/_tmp/,old> folder.)
+
+=back
+
+=head1 AUTHOR
+
+Heiko Schlittermann <hs@schlittermann.de>
+
+=cut
+
+# vim:tw=72 sts=4 ts=4 sw=4 aw ai sm: