File Coverage

lib/Sorauta/Device/USB/Synchronizer.pm
Criterion Covered Total %
statement 3 3 100.0
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 4 4 100.0


line stmt bran cond sub pod time code
1             #============================================
2             # USB監視 & ディレクトリ同期モジュール
3             #  USBが接続されると特定のディレクトリを同期する
4             #  親はUSB側なので、PC側にUSB側で削除されているファイルがある場合削除される
5             # -------------------------------------------
6             # アクセサ
7             # target_dir_path String USBと同期するディレクトリのフルパス
8             # ex)/Library/WebServer/Documents/resource/projects
9             # synchronized_dir_list ArrayRef 同期したいディレクトリ一覧
10             # ex) [pic, flv, init_pic]
11             # os String OSの種類
12             # Win ... windows
13             # Mac ... mac os
14             # interval_time Integer バッチの実行周期
15             # 0の場合は一度のみ実行
16             # allow_override_file Integer 同一ファイルがUSBに入っている場合更新するか?
17             # 0 ... 更新しない(デフォルト)
18             # 1 ... 更新する
19             # debug Integer デバッグモード
20             # 0 ... ログ表示しない(デフォルト)
21             # 1 ... ログ表示する
22             #
23             # connected_event_ref FunctionRef 監視対象USB接続時のイベント
24             # USBへのファイルコピーなど行う
25             # updated_event_ref FunctionRef 監視対象ディレクトリ更新時のイベント
26             # 何か色々やるかなーと
27             # driver_check_list Hash 接続中のドライバを保持するHash
28             # デフォルトは{}
29             # update_flag Integer バッチの実行結果、pattern.xmlを更新するかフラグ
30             # 0 ... 更新しない
31             # 1 ... 更新する
32             #============================================
33             package Sorauta::Device::USB::Synchronizer;
34 1     1   21422 use base qw/Class::Accessor::Fast/;
  1         3  
  1         641  
35              
36             use 5.012003;
37             use strict;
38             use warnings;
39             use utf8;
40             use CGI::Carp qw/fatalsToBrowser/;
41             use Data::Dumper;
42             use LWP::UserAgent;
43             use File::Copy;
44             use Sorauta::Utility;
45              
46             our $VERSION = '0.01';
47              
48             my $MAC_ROOT_DIR = "/Volumes";
49             my $WIN_DRIVER_LIST = ['A'..'Z'];
50              
51             __PACKAGE__->mk_accessors(qw/
52             target_dir_path synchronized_dir_list os interval_time allow_override_file debug
53             connected_event_ref updated_event_ref driver_check_list update_flag/);
54              
55             #==========================================
56             # USBの監視を実行する
57             # req:
58             # res:
59             # result: 成功時は1、失敗時はそれ以外
60             #==========================================
61             sub execute {
62             my $self = shift;
63              
64             # debug
65             if ($self->debug == 1) {
66             #print Dumper($self);
67             }
68              
69             # must be defined accessors
70             if (!$self->target_dir_path
71             || !$self->synchronized_dir_list
72             || !$self->os
73             || !length($self->interval_time)
74             || !length($self->allow_override_file)
75             ) {
76             die 'must be define accessor '.
77             'target_dir_path(/Library/WebServer/Documents/resource/hogehoge), synchronized_dir_list([hoge, fuga]), '.
78             'os(Win|Mac), interval_time(10), allow_override_file(0|1)';
79             }
80              
81             # インスタンス変数を初期化
82             $self->driver_check_list({});
83             $self->update_flag(0);
84              
85             # 監視対象のディレクトリが存在しない場合作成
86             if (!-e $self->target_dir_path) {
87             print 'mkdir: ', $self->target_dir_path, $/;
88             mkdir($self->target_dir_path, 0755);
89             }
90             for (@{$self->synchronized_dir_list}) {
91             my $npath = cat($self->target_dir_path, $_);
92             if (!-e $npath) {
93             print 'mkdir: ', $npath, $/;
94             mkdir($npath, 0755);
95             }
96             }
97              
98             print '=================================', $/;
99             print ' Batch Start', $/;
100             print '=================================', $/;
101              
102             while (1) {
103             $self->execute_batch;
104             if ($self->interval_time) {
105             sleep $self->interval_time;
106             }
107             else {
108             last;
109             }
110             }
111              
112             return 1;
113             }
114              
115             #==========================================
116             # 定期バッチ実行
117             # req:
118             # res:
119             #==========================================
120             sub execute_batch {
121             my $self = shift;
122              
123             # 監視対象フォルダ更新フラグを初期化
124             $self->update_flag(0);
125              
126             # 接続されたドライブから、目的のUSBが無いか検索、更新
127             $self->check_drivers;
128              
129             # アップデート時に実行するイベント
130             if ($self->update_flag == 1 && $self->updated_event_ref) {
131             $self->updated_event_ref->($self);
132             }
133              
134             print "[", get_timestamp(time), "]execute ok, wait ", $self->interval_time, "sec", $/;
135             }
136              
137             #------------------------------------------
138             # 各ドライブが接続されたか監視する
139             # req:
140             # res:
141             #------------------------------------------
142             sub check_drivers {
143             my $self = shift;
144              
145             if ($self->os eq 'Mac') {
146             opendir(my $DIR, $MAC_ROOT_DIR) or die 'Can\'t open('.$MAC_ROOT_DIR.'): '.$!;
147             while (my $driver_name = readdir($DIR)) {
148             next if ($driver_name eq '.' || $driver_name eq '..');
149             my $driver_path = cat($MAC_ROOT_DIR, $driver_name);
150             $self->check_connect_usb($driver_path, $driver_name);
151             }
152             close $DIR;
153             }
154             elsif ($self->os eq 'Win') {
155             foreach my $driver_name(@$WIN_DRIVER_LIST) {
156             my $driver_path = $driver_name . ':/';
157             $self->check_connect_usb($driver_path, $driver_name);
158             }
159             }
160             }
161              
162             #------------------------------------------
163             # USBが接続されたか判断、接続時は同期していく
164             # req:
165             # driver_path: ドライバ(USB)までのフルパス
166             # ex)C:/ or /Volumes/usb-name-hoge
167             # driver_name: ドライバ名(USB)
168             # ex)C or usb-name-hoge
169             # res:
170             #------------------------------------------
171             sub check_connect_usb {
172             my($self, $driver_path, $driver_name) = @_;
173              
174             # 新たに接続された場合
175             if (-e $driver_path) {
176             print '# ', $driver_path, "\t connected. ";
177              
178             # 監視対象のUSBが接続されたら,内容をコピーする.
179             if ($self->is_signage_usb($driver_path)) {
180             # 接続時のイベント発行
181             if ($self->connected_event_ref) {
182             $self->connected_event_ref->($self, $driver_path);
183             }
184             # 内容コピー
185             $self->recrusive_copy($driver_path, $self->target_dir_path);
186             }
187              
188             # update check list
189             $self->driver_check_list->{$driver_name} = 1;
190             }
191              
192             # 取り外された場合
193             if ($self->driver_check_list->{$driver_name} && !(-e $driver_path)) {
194             print '# ', $driver_path, "\t disconnected", $/;
195              
196             # update check list
197             $self->driver_check_list->{$driver_name} = 0;
198             }
199             }
200              
201             #------------------------------------------
202             # 監視対象のUSBか判断
203             # req:
204             # driver_path: ドライバ(USB)までのフルパス
205             # ex)C:/ or /Volumes/usb-name-hoge
206             # res:
207             # result: 監視対象USBの場合は1、それ以外は0
208             #------------------------------------------
209             sub is_signage_usb {
210             my($self, $driver_path) = @_;
211              
212             # 監視対象のディレクトリが含まれているか確認
213             my $cnt = 0;
214             for my $search_dir_name(@{$self->synchronized_dir_list}) {
215             if (-e cat($driver_path, $search_dir_name)) {
216             $cnt++;
217             }
218             }
219              
220             # 全て含まれている場合は同期対象と判断
221             if ($cnt == scalar(@{$self->synchronized_dir_list})) {
222             print "And, this drive is signage usb", $/;
223             return 1;
224             }
225             else {
226             print "But, this drive is not signage usb", $/;
227             return 0;
228             }
229             }
230              
231             #------------------------------------------
232             # 再帰的なファイルコピー
233             # req:
234             # usb_dir_path: USB側のフルパス
235             # target_dir_path: PC側のフルパス
236             # res:
237             #------------------------------------------
238             sub recrusive_copy {
239             my($self, $usb_dir_path, $target_dir_path) = @_;
240              
241             opendir(my $D, $usb_dir_path) or die 'Can\'t open('.$usb_dir_path.'): '.$!;
242             while (my $file_name = readdir($D)) {
243             # コピーするファイル名に制約をつける場合(Sorauta::Utilityにて定義)
244             if (is_unnecessary_copying_file($file_name)) {
245             next;
246             }
247              
248             # コピー元とコピー先のディレクトリのフルパス取得
249             my($from_path, $destination_path) = (
250             cat($usb_dir_path, $file_name), cat($usb_dir_path, $file_name));
251             if ($self->os eq 'Mac') {
252             $destination_path =~ s/^$MAC_ROOT_DIR\/(?:.*?)\/([\w]+)/$target_dir_path\/$1/g;
253             }
254             elsif ($self->os eq 'Win') {
255             $destination_path =~ s/^[A-Z]{1,2}:[\/\\]([\w]+)/$target_dir_path\/$1/g;
256             }
257              
258             # ディレクトリの場合,再帰的に探索
259             if (-d $from_path) {
260             # debug
261             if ($self->debug == 1) {
262             #print 'recrusive copy this dir: ', $from_path, $/;
263             }
264              
265             my $fromdir_updated = (stat $from_path)[9];
266             my $destdir_updated = (stat $destination_path)[9];
267              
268             # コピー先のディレクトリがコピ^元より古い場合、ディレクトリ削除
269             if ($destdir_updated && abs($fromdir_updated - $destdir_updated) > 20) {
270             print 'rmdir: ', $destination_path, $/;
271             if ($self->os eq 'Win') {
272             system("del", "/q", $destination_path);
273             }
274             elsif ($self->os eq 'Mac') {
275             my $cmd = "rm -rf $destination_path";
276             `$cmd`;
277             }
278             utime(time, (stat $destination_path)[9], $from_path);
279             }
280              
281             # コピー元のディレクトリがコピー先に存在しない場合は, ディレクトリ生成
282             if (!-e $destination_path) {
283             print 'mkdir: ', $destination_path, $/;
284             mkdir($destination_path);
285              
286             utime(time, (stat $destination_path)[9], $from_path);
287             }
288              
289             # 再帰的にコピーを繰り返す
290             $self->recrusive_copy($from_path, $self->target_dir_path);
291             }
292             # ファイルの場合普通にコピー
293             else {
294             print "[[", $from_path, "]] ";
295              
296             # TODO: pattern.xmlを出力する場合,エレメント追加する
297              
298             # 既に存在する場合
299             if (-e $destination_path) {
300             #print "\talready exist", $/;
301              
302             # コピー元とコピー先のファイル更新日時を取得,
303             my $fromfile_updated = (stat $from_path)[9];
304             my $destfile_updated = (stat $destination_path)[9];
305              
306             # 上書き禁止な場合スキップ
307             if (!$self->allow_override_file) {
308             print "\tyou don't have override permission. skip.", $/, $/;
309             next;
310             }
311              
312             # 更新日時が同じ場合スキップ
313             if (abs($destfile_updated - $fromfile_updated) < 20) {
314             print "\tskip file.", $/;
315             next;
316             }
317              
318             # コピーするよメッセージ
319             print "\toverride old file", $/;
320             }
321              
322             # ディレクトリ更新したよフラグ
323             $self->update_flag(1);
324              
325             # コピー実行
326             {
327             print "\tcopy dest: ", $destination_path, $/;
328             copy($from_path, $destination_path) or
329             print "[copy failed: $!]$/",
330             "\tfrom_path: $from_path$/",
331             "\tdest_path: $destination_path$/";
332              
333             utime(time, (stat $destination_path)[9], $from_path);
334             }
335             }
336             }
337             close($D);
338             }
339              
340             1;
341             __END__