New URL for NEMO forge!   http://forge.nemo-ocean.eu

Since March 2022 along with NEMO 4.2 release, the code development moved to a self-hosted GitLab.
This present forge is now archived and remained online for history.
SSH.pm in vendors/lib/FCM/Util/Locator – NEMO

source: vendors/lib/FCM/Util/Locator/SSH.pm @ 10669

Last change on this file since 10669 was 10669, checked in by nicolasmartin, 5 years ago

Import latest FCM release from Github into the repository for testing

File size: 6.6 KB
Line 
1# ------------------------------------------------------------------------------
2# (C) British Crown Copyright 2006-17 Met Office.
3#
4# This file is part of FCM, tools for managing and building source code.
5#
6# FCM is free software: you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation, either version 3 of the License, or
9# (at your option) any later version.
10#
11# FCM is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with FCM. If not, see <http://www.gnu.org/licenses/>.
18# ------------------------------------------------------------------------------
19use strict;
20use warnings;
21
22# ------------------------------------------------------------------------------
23package FCM::Util::Locator::SSH;
24use base qw{FCM::Class::CODE};
25
26use FCM::Util::Exception;
27use File::Temp;
28use Text::ParseWords qw{shellwords};
29
30our %ACTION_OF = (
31    can_work_with     => \&_can_work_with,
32    can_work_with_rev => sub {},
33    cat               => \&_cat,
34    dir               => \&_dir,
35    export            => \&_export,
36    export_ok         => sub {1},
37    find              => \&_find,
38    origin            => \&_parse,
39    parse             => \&_parse,
40    reader            => \&_reader,
41    read_property     => sub {},
42    test_exists       => \&_test_exists,
43    trunk_at_head     => sub {},
44);
45# Alias to the exception class
46my $E = 'FCM::Util::Exception';
47
48# Creates the class.
49__PACKAGE__->class(
50    {type_util_of => '%', util => '&'},
51    {action_of => \%ACTION_OF},
52);
53
54# Returns true if $value looks like a legitimate HOST:PATH.
55sub _can_work_with {
56    my ($attrib_ref, $value) = @_;
57    if (!$value) {
58        return;
59    }
60    if (index($value, ':') < 0) {
61        return;
62    }
63    my ($auth) = split(':', $value, 2);
64    my $host = index($auth, '@') >= 0 ? (split('@', $auth, 2))[1] : $auth;
65    $host ? gethostbyname($host) : undef;
66}
67
68# Joins @paths to the end of $value.
69sub _cat {
70    my ($attrib_ref, $value, @paths) = @_;
71    my ($auth, $path) = split(':', $value, 2);
72    $auth . ':' . $attrib_ref->{type_util_of}{fs}->cat($path, @paths);
73}
74
75# Returns the directory name of $value.
76sub _dir {
77    my ($attrib_ref, $value) = @_;
78    my ($auth, $path) = split(':', $value, 2);
79    $auth . ':' . $attrib_ref->{type_util_of}{fs}->dir($path);
80}
81
82# Rsync $value to $dest.
83sub _export {
84    my ($attrib_ref, $value, $dest) = @_;
85    my ($auth, $path) = _dir($attrib_ref, $value);
86    my $value_hash_ref = $attrib_ref->{util}->shell_simple([
87        _shell_cmd_list($attrib_ref, 'rsync'),
88        $value . '/',
89        $dest,
90    ]);
91    if ($value_hash_ref->{rc}) {
92        die($value_hash_ref);
93    }
94}
95
96# Searches directory tree.
97sub _find {
98    my ($attrib_ref, $value, $callback) = @_;
99    my ($auth, $path) = split(':', $value, 2);
100    my $value_hash_ref = $attrib_ref->{util}->shell_simple([
101        _shell_cmd_list($attrib_ref, 'ssh'),
102        $auth,
103        "find $path -type f -not -path \"*/.*\" -printf \"%T@ %p\\\\n\"",
104    ]);
105    if ($value_hash_ref->{rc}) {
106        die($value_hash_ref);
107    }
108    my $found;
109    LINE:
110    for my $line (grep {$_} split("\n", $value_hash_ref->{o})) {
111        $found ||= 1;
112        my ($mtime, $name) = split(q{ }, $line, 2);
113        my $ns = substr($name, length($path) + 1);
114        $callback->(
115            $auth . ':' . $name,
116            {   is_dir        => undef,
117                last_mod_rev  => undef,
118                last_mod_time => $mtime,
119                ns            => $ns,
120            },
121        );
122    }
123    $found;
124}
125
126# Returns a reader (file handle) for a given file system value.
127sub _reader {
128    my ($attrib_ref, $value) = @_;
129    my ($auth, $path) = split(':', $value, 2);
130    my $handle = File::Temp->new();
131    my $e;
132    my $rc = $attrib_ref->{util}->shell(
133        [_shell_cmd_list($attrib_ref, 'ssh'), $auth, 'cat', $path],
134        {'e' => \$e, 'o' => sub {print($handle $_[0])}},
135    );
136    if ($rc) {
137        die($e);
138    }
139    seek($handle, 0, 0);
140    return $handle;
141}
142
143# Returns $value in scalar context, or ($value,undef) in list context.
144sub _parse {
145    my ($attrib_ref, $value) = @_;
146    my ($auth, $path) = split(':', $value, 2);
147    $value = $auth . ':' . $attrib_ref->{type_util_of}{fs}->parse($path);
148    return (wantarray() ? ($value, undef) : $value);
149}
150
151# Return a true value if the location $value exists.
152sub _test_exists {
153    my ($attrib_ref, $value) = @_;
154    my ($auth, $path) = split(':', $value, 2);
155    my $value_hash_ref = $attrib_ref->{util}->shell_simple([
156        _shell_cmd_list($attrib_ref, 'ssh'), $auth, "test -e '$path'",
157    ]);
158    return !$value_hash_ref->{rc};
159}
160
161# Get a named command and its flags, return a list.
162sub _shell_cmd_list {
163    my ($attrib_ref, $key) = @_;
164    map {shellwords($_)} (
165        $attrib_ref->{util}->external_cfg_get($key),
166        $attrib_ref->{util}->external_cfg_get($key . '.flags'),
167    );
168}
169
170# ------------------------------------------------------------------------------
1711;
172__END__
173
174=head1 NAME
175
176FCM::Util::Locator::SSH
177
178=head1 SYNOPSIS
179
180    use FCM::Util::Locator::SSH;
181    $util = FCM::Util::Locator::SSH->new(\%option);
182    $handle = $util->reader($value);
183
184=head1 DESCRIPTION
185
186Provides utilities to manipulate the values of locators on file systems on
187remote hosts accessible via SSH and RSYNC.
188
189=head1 METHODS
190
191=over 4
192
193=item $util->can_work_with($value)
194
195Returns true if $value is in the form AUTH:PATH and AUTH is a valid user@host.
196
197=item $util->can_work_with_rev($revision)
198
199Dummy. Always returns false.
200
201=item $util->cat($value,@paths)
202
203Joins @paths to the end of $value.
204
205=item $util->dir($value)
206
207Returns the auth:parent-directory of $value.
208
209=item $util->export($value,$dest)
210
211Rsync a clean directory tree of $value to $dest.
212
213=item $util->export_ok($value)
214
215Returns true if $util->can_work_with($value).
216
217=item $util->find($value,$callback)
218
219Searches directory tree of $value.
220
221=item $util->origin($value)
222
223Alias of $util->parse($value).
224
225=item $util->parse($value)
226
227In scalar context, returns $value. In list context, returns ($value,undef).
228
229=item $util->reader($value)
230
231Returns a file handle for $value, if it is a readable regular file.
232
233=item $util->read_property($value,$property_name)
234
235Dummy. Always returns undef.
236
237=item $util->test_exists($value)
238
239Return a true value if the location $value exists.
240
241=item $util->trunk_at_head($value)
242
243Dummy. Always returns undef.
244
245=back
246
247=head1 COPYRIGHT
248
249(C) Crown copyright Met Office. All rights reserved.
250
251=cut
Note: See TracBrowser for help on using the repository browser.