should work now. (testing)
authorroot@sonne.dd.dtele.de
Sat, 28 Feb 2009 22:04:02 +0100
changeset 1 a54c42c041e6
parent 0 b4b46fe7bf9f
child 2 3959408aa03d
should work now. (testing) to do: add code for daemonization
Makefile
tele-watch
tele-watch.pl
tele-watch.sh
--- 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: