# HG changeset patch # User root@sonne.dd.dtele.de # Date 1235855042 -3600 # Node ID a54c42c041e618146365a7b1d7f1800c14da78ee # Parent b4b46fe7bf9fd6dbd9ad5c564dbd8236b9f54af0 should work now. (testing) to do: add code for daemonization diff -r b4b46fe7bf9f -r a54c42c041e6 Makefile --- 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 $< >$@ + diff -r b4b46fe7bf9f -r a54c42c041e6 tele-watch --- 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 - -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 ""... - -=head1 DESCRIPTION - -B should run as a daemon. - -B watches the list of directories I... (absolute path names) -via "inotify" and performs some actions on: - -=over - -=item CREATION of new directory - -It checks F and assumes, that all directories there should -reflect in the newly created directory: - - /_tmp -> /.m/_tmp/NEW1/ - /homepage -> /.m/homepage/NEW1/ - ... - -After done this it writes the name of the newly created directory into -the file F<< /.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 folder.) - -=back - -=head1 AUTHOR - -Heiko Schlittermann - -=cut - -# vim:tw=72 sts=4 ts=4 sw=4 aw ai sm: diff -r b4b46fe7bf9f -r a54c42c041e6 tele-watch.pl --- /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 + +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] ""... + +=head1 DESCRIPTION + +B should run as a daemon. + +B watches the list of directories I... (absolute path names) +via "inotify" and performs some actions on: + +=over + +=item CREATION of new directory + +It checks F and assumes, that all directories there should +reflect in the newly created directory: + + /_tmp -> /.m/_tmp/NEW1/ + /homepage -> /.m/homepage/NEW1/ + ... + +After done this it writes the name of the newly created directory into +the file F<< /.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 folder.) + +=back + +=head1 OPTIONS + +=over + +=item B<--[no]block> + +If set, on exit the watched directories are blocked by C. +(default: off) + +=back + +=head1 AUTHOR + +Heiko Schlittermann + +=cut + +# vim:tw=72 sts=4 ts=4 sw=4 aw ai sm: diff -r b4b46fe7bf9f -r a54c42c041e6 tele-watch.sh --- /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 + +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 ""... + +=head1 DESCRIPTION + +B should run as a daemon. + +B watches the list of directories I... (absolute path names) +via "inotify" and performs some actions on: + +=over + +=item CREATION of new directory + +It checks F and assumes, that all directories there should +reflect in the newly created directory: + + /_tmp -> /.m/_tmp/NEW1/ + /homepage -> /.m/homepage/NEW1/ + ... + +After done this it writes the name of the newly created directory into +the file F<< /.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 folder.) + +=back + +=head1 AUTHOR + +Heiko Schlittermann + +=cut + +# vim:tw=72 sts=4 ts=4 sw=4 aw ai sm: