1 #!/usr/bin/perl |
|
2 |
|
3 # Copyright (C) 2012-2017 Matthias Förste |
|
4 # |
|
5 # This program is free software: you can redistribute it and/or modify |
|
6 # it under the terms of the GNU General Public License as published by |
|
7 # the Free Software Foundation, either version 3 of the License, or |
|
8 # (at your option) any later version. |
|
9 # |
|
10 # This program is distributed in the hope that it will be useful, |
|
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
13 # GNU General Public License for more details. |
|
14 # |
|
15 # You should have received a copy of the GNU General Public License |
|
16 # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
17 # |
|
18 # Matthias Förste <foerste@schlittermann.de> |
|
19 |
|
20 =encoding utf8 |
|
21 =cut |
|
22 |
|
23 use strict; |
|
24 use warnings; |
|
25 |
|
26 my $VERSION = '0.1'; |
|
27 my $ME = $0; |
|
28 |
|
29 use File::Basename; |
|
30 |
|
31 # File::Rsync in squeeze does not support --xattrs yet |
|
32 #use File::Rsync; |
|
33 use Getopt::Long; |
|
34 use Pod::Usage; |
|
35 use Linux::Inotify2; |
|
36 use Sys::Syslog; |
|
37 |
|
38 my $master; |
|
39 |
|
40 BEGIN { |
|
41 |
|
42 openlog($ME, 'ndelay,nowait,pid', 'LOG_USER'); |
|
43 $SIG{__WARN__} = sub { |
|
44 warn @_ if not defined $^S; |
|
45 print STDERR "@_"; |
|
46 syslog('warning', "@_"); |
|
47 }; |
|
48 $SIG{__DIE__} = sub { |
|
49 die @_ if not defined $^S; |
|
50 print STDERR "@_"; |
|
51 syslog('err', "@_"); |
|
52 exit $?; |
|
53 }; |
|
54 sub dolog { print "@_"; syslog('info', "@_"); } |
|
55 sub trace { print @_ if $ENV{DEBUG}; } |
|
56 |
|
57 use sigtrap qw(die normal-signals); |
|
58 |
|
59 } |
|
60 |
|
61 my %opts = ( |
|
62 daemon => 1, |
|
63 map => 'wgnd-watch.map.pl', |
|
64 pidfile => '/var/run/wgnd-watch.pid', |
|
65 'rsync-args' => [ qw(-ihv -aAX --no-times) ], |
|
66 'rsync-cmd' => '/usr/bin/rsync' |
|
67 ); |
|
68 |
|
69 GetOptions( |
|
70 "map=s" => \$opts{map}, |
|
71 "daemon!" => \$opts{daemon}, |
|
72 "pidfile=s" => \$opts{pidfile}, |
|
73 "rsync-args=s@" => \$opts{'rsync-args'}, |
|
74 "rsync-cmd=s" => \$opts{'rsync-cmd'}, |
|
75 "h|help!" => sub { pod2usage(-verbose => 0, -exitval => 0) }, |
|
76 "m|man!" => sub { |
|
77 pod2usage( |
|
78 -verbose => 2, |
|
79 -exitval => 0, |
|
80 -noperldoc => (`perldoc -V 2>/dev/null`, $? != 0)[-1] |
|
81 ); |
|
82 }, |
|
83 ) or pod2usage(); |
|
84 |
|
85 our $source; |
|
86 use lib grep { defined } ('.', $ENV{HOME}, '/etc'); |
|
87 require $opts{map}; |
|
88 |
|
89 $0 = "$ME @ARGV"; |
|
90 |
|
91 chdir("/") or die "Can't chdir to /: $!\n"; |
|
92 |
|
93 if ($opts{daemon}) { |
|
94 |
|
95 open STDIN, "</dev/null" or die "Can't redir STDIN: $!\n"; |
|
96 open STDOUT, ">/dev/null" or die "Can't redir STDOUT: $!\n"; |
|
97 |
|
98 defined(my $pid = fork) or die "Can't fork: $!\n"; |
|
99 if ($pid) { |
|
100 dolog "Child is [$pid]"; |
|
101 exit 0; |
|
102 } |
|
103 POSIX::setsid() |
|
104 or die "Can't setsid: $!\n"; |
|
105 open(STDERR, ">&STDOUT") or die "Can't dup stdout: $!\n"; |
|
106 |
|
107 open(P, '>', $opts{pidfile}) |
|
108 or die "Can't open '>', '$opts{pidfile}': $!\n"; |
|
109 print P $$; |
|
110 close P; |
|
111 $master = $$; |
|
112 |
|
113 } |
|
114 |
|
115 my $inotify = new Linux::Inotify2 |
|
116 or die "Can't create new inotify object: $!\n"; |
|
117 my @rsync = ($opts{'rsync-cmd'}, @{$opts{'rsync-args'}}); |
|
118 |
|
119 for (keys %{$source}) { |
|
120 |
|
121 # add watchers |
|
122 $inotify->watch( |
|
123 "$_", |
|
124 IN_CREATE, |
|
125 sub { |
|
126 |
|
127 my $pid = fork; |
|
128 |
|
129 if (not defined $pid) { |
|
130 warn "Can't fork: $!\n"; |
|
131 } elsif ($pid == 0) { |
|
132 my $e = shift; |
|
133 my $name = $e->fullname; |
|
134 dolog "$name was created\n" if $e->IN_CREATE; |
|
135 dolog "$name is no longer mounted\n" if $e->IN_UNMOUNT; |
|
136 dolog "$name is gone\n" if $e->IN_IGNORED; |
|
137 dolog "events for $name have been lost\n" if $e->IN_Q_OVERFLOW; |
|
138 exec @rsync, $source->{ $e->{w}->{name} }, $name; |
|
139 warn "Can't exec: $!\n"; |
|
140 } |
|
141 |
|
142 } |
|
143 ) or die "Can't add watch: $!\n"; |
|
144 } |
|
145 |
|
146 while (1) { |
|
147 |
|
148 $inotify->poll; |
|
149 while (-1 != (my $pid = wait)) { |
|
150 my $e = $? >> 8; |
|
151 dolog "Child ${ME}[$pid]: exit $e\n"; |
|
152 } |
|
153 |
|
154 } |
|
155 |
|
156 END { |
|
157 |
|
158 unlink $opts{pidfile} |
|
159 if $opts{daemon} |
|
160 and defined $master |
|
161 and $master eq $$; |
|
162 dolog "exit"; |
|
163 closelog; |
|
164 |
|
165 } |
|
166 |
|
167 __END__ |
|
168 |
|
169 =pod |
|
170 |
|
171 =head1 NAME |
|
172 |
|
173 wgnd-watch - inotify actions for wiegand |
|
174 |
|
175 =head1 SYNOPSIS |
|
176 |
|
177 wgnd-watch [--map filename] |
|
178 |
|
179 wgnd-watch -m|--man |
|
180 -h|--help |
|
181 |
|
182 =head1 DESCRIPTION |
|
183 |
|
184 wgnd-watch currently watches some directories for newly created subdirectories |
|
185 and synchronises these from a given template directory. |
|
186 |
|
187 =head1 OPTIONS |
|
188 |
|
189 =over |
|
190 |
|
191 =item B<--[no]daemon> |
|
192 |
|
193 [Don't] run as a daemon. Default: do. |
|
194 |
|
195 =item B<--map> I<filename> |
|
196 |
|
197 Name of a file containing mappings between templates and directories. Defaults |
|
198 to F<wgnd-watch.map.pl>. |
|
199 |
|
200 =item B<--pidfile> I<filename> |
|
201 |
|
202 Location of the pid file. This is ignored unless we are running as a daemon. |
|
203 Defaults to F</var/run/wgnd-watch.pid>. |
|
204 |
|
205 =item B<--pidfile> I<filename> |
|
206 |
|
207 Location of the pid file. This is ignored unless we are running as a daemon. |
|
208 Defaults to F</var/run/wgnd-watch.pid>. |
|
209 |
|
210 =item B<--rsync-args> I<args> |
|
211 |
|
212 Arguments to pass to the rsync command. May be given multiple times. Defaults to B<-ihv -aAX --no-times>. |
|
213 |
|
214 =item B<--rsync-cmd> I<cmd> |
|
215 |
|
216 Rsync Command. Defaults to F</usr/bin/rsync>. |
|
217 |
|
218 =back |
|
219 |
|
220 =head1 FILES |
|
221 |
|
222 =over |
|
223 |
|
224 =item F<wgnd-watch.map.pl> |
|
225 |
|
226 default for B<--map> |
|
227 |
|
228 =item F</var/run/wgnd-watch.pid> |
|
229 |
|
230 default for B<--pidfile> |
|
231 |
|
232 =back |
|
233 |
|
234 =head1 AUTHOR |
|
235 |
|
236 Matthias Förste <foerste@schlittermann.de> |
|
237 |
|
238 =cut |
|