line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package CASCM::Wrapper; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
####################### |
4
|
|
|
|
|
|
|
# LOAD MODULES |
5
|
|
|
|
|
|
|
####################### |
6
|
5
|
|
|
5
|
|
5661
|
use 5.006001; |
|
5
|
|
|
|
|
18
|
|
|
5
|
|
|
|
|
194
|
|
7
|
|
|
|
|
|
|
|
8
|
5
|
|
|
5
|
|
22
|
use strict; |
|
5
|
|
|
|
|
7
|
|
|
5
|
|
|
|
|
187
|
|
9
|
5
|
|
|
5
|
|
31
|
use warnings FATAL => 'all'; |
|
5
|
|
|
|
|
10
|
|
|
5
|
|
|
|
|
253
|
|
10
|
|
|
|
|
|
|
|
11
|
5
|
|
|
5
|
|
4016
|
use File::Temp qw(); |
|
5
|
|
|
|
|
88898
|
|
|
5
|
|
|
|
|
163
|
|
12
|
5
|
|
|
5
|
|
37
|
use Carp qw(croak carp); |
|
5
|
|
|
|
|
7
|
|
|
5
|
|
|
|
|
19677
|
|
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
####################### |
15
|
|
|
|
|
|
|
# VERSION |
16
|
|
|
|
|
|
|
####################### |
17
|
|
|
|
|
|
|
our $VERSION = '0.12'; |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
####################### |
20
|
|
|
|
|
|
|
# MODULE METHODS |
21
|
|
|
|
|
|
|
####################### |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
# Constructor |
24
|
|
|
|
|
|
|
sub new { |
25
|
5
|
|
|
5
|
0
|
3258
|
my $class = shift; |
26
|
5
|
|
100
|
|
|
32
|
my $options_ref = shift || {}; |
27
|
|
|
|
|
|
|
|
28
|
5
|
|
|
|
|
10
|
my $self = {}; |
29
|
5
|
|
|
|
|
17
|
bless $self, $class; |
30
|
5
|
|
|
|
|
18
|
return $self->_init($options_ref); |
31
|
|
|
|
|
|
|
} ## end sub new |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
# Set Context |
34
|
|
|
|
|
|
|
sub set_context { |
35
|
4
|
|
|
4
|
1
|
858
|
my $self = shift; |
36
|
4
|
|
50
|
|
|
16
|
my $context = shift || {}; |
37
|
|
|
|
|
|
|
|
38
|
4
|
50
|
|
|
|
16
|
if ( ref $context ne 'HASH' ) { |
39
|
0
|
|
|
|
|
0
|
$self->_err("Context must be a hash reference"); |
40
|
0
|
|
|
|
|
0
|
return; |
41
|
|
|
|
|
|
|
} ## end if ( ref $context ne 'HASH') |
42
|
|
|
|
|
|
|
|
43
|
4
|
|
|
|
|
8
|
$self->{_context} = $context; |
44
|
4
|
|
|
|
|
26
|
return 1; |
45
|
|
|
|
|
|
|
} ## end sub set_context |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
# load context |
48
|
|
|
|
|
|
|
sub load_context { |
49
|
1
|
|
|
1
|
1
|
2
|
my $self = shift; |
50
|
1
|
|
33
|
|
|
5
|
my $file |
51
|
|
|
|
|
|
|
= shift || ( $self->_err("File required but missing") and return ); |
52
|
|
|
|
|
|
|
|
53
|
1
|
50
|
|
|
|
72
|
if ( not -f $file ) { $self->_err("File $file does not exist"); return; } |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
eval { |
56
|
1
|
|
|
|
|
9
|
require Config::Tiny; |
57
|
1
|
|
|
|
|
8
|
Config::Tiny->import(); |
58
|
1
|
|
|
|
|
4
|
return 1; |
59
|
1
|
50
|
|
|
|
2
|
} or do { |
60
|
0
|
|
|
|
|
0
|
$self->_err( |
61
|
|
|
|
|
|
|
"Please install Config::Tiny if you'd like to load context files" |
62
|
|
|
|
|
|
|
); |
63
|
0
|
|
|
|
|
0
|
return; |
64
|
|
|
|
|
|
|
}; |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
my $config = Config::Tiny->read($file) |
67
|
1
|
0
|
|
|
|
6
|
or do { $self->_err("Error reading $file") and return; }; |
|
0
|
50
|
|
|
|
0
|
|
68
|
|
|
|
|
|
|
|
69
|
1
|
|
|
|
|
175
|
my $context = {}; |
70
|
1
|
|
|
|
|
2
|
foreach ( keys %{$config} ) { |
|
1
|
|
|
|
|
4
|
|
71
|
3
|
100
|
|
|
|
6
|
if ( $_ eq '_' ) { $context->{global} = $config->{$_}; } |
|
1
|
|
|
|
|
3
|
|
72
|
2
|
|
|
|
|
4
|
else { $context->{$_} = $config->{$_}; } |
73
|
|
|
|
|
|
|
} ## end foreach ( keys %{$config} ) |
74
|
|
|
|
|
|
|
|
75
|
1
|
|
|
|
|
4
|
return $self->set_context($context); |
76
|
|
|
|
|
|
|
} ## end sub load_context |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
# Update Context |
79
|
|
|
|
|
|
|
sub update_context { |
80
|
1
|
|
|
1
|
1
|
2
|
my $self = shift; |
81
|
1
|
|
50
|
|
|
4
|
my $new = shift || {}; |
82
|
|
|
|
|
|
|
|
83
|
1
|
50
|
|
|
|
3
|
if ( ref $new ne 'HASH' ) { |
84
|
0
|
|
|
|
|
0
|
$self->_err("Context must be a hash reference"); |
85
|
0
|
|
|
|
|
0
|
return; |
86
|
|
|
|
|
|
|
} ## end if ( ref $new ne 'HASH') |
87
|
|
|
|
|
|
|
|
88
|
1
|
|
|
|
|
3
|
my $context = $self->get_context(); |
89
|
|
|
|
|
|
|
|
90
|
1
|
|
|
|
|
1
|
foreach my $type ( keys %{$new} ) { |
|
1
|
|
|
|
|
4
|
|
91
|
2
|
|
|
|
|
19
|
foreach my $key ( keys %{ $new->{$type} } ) { |
|
2
|
|
|
|
|
6
|
|
92
|
2
|
|
|
|
|
5
|
$context->{$type}->{$key} = $new->{$type}->{$key}; |
93
|
|
|
|
|
|
|
} |
94
|
|
|
|
|
|
|
} ## end foreach my $type ( keys %{$new...}) |
95
|
|
|
|
|
|
|
|
96
|
1
|
|
|
|
|
2
|
return $self->set_context($context); |
97
|
|
|
|
|
|
|
} ## end sub update_context |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
# Parse logs |
100
|
|
|
|
|
|
|
sub parse_logs { |
101
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
102
|
0
|
0
|
|
|
|
0
|
if (@_) { |
103
|
0
|
|
|
|
|
0
|
$self->{_options}->{parse_logs} = shift; |
104
|
0
|
0
|
|
|
|
0
|
if ( $self->{_options}->{parse_logs} ) { |
105
|
0
|
0
|
|
|
|
0
|
eval { |
106
|
0
|
|
|
|
|
0
|
require Log::Any; |
107
|
0
|
|
|
|
|
0
|
return 1; |
108
|
|
|
|
|
|
|
} |
109
|
|
|
|
|
|
|
or croak |
110
|
|
|
|
|
|
|
"Error loading Log::Any. Please install it if you'd like to parse logs"; |
111
|
|
|
|
|
|
|
} ## end if ( $self->{_options}...) |
112
|
|
|
|
|
|
|
} ## end if (@_) |
113
|
0
|
|
|
|
|
0
|
return $self->{_options}->{parse_logs}; |
114
|
|
|
|
|
|
|
} ## end sub parse_logs |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
# Dry Run |
117
|
|
|
|
|
|
|
sub dry_run { |
118
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
119
|
0
|
0
|
|
|
|
0
|
if (@_) { $self->{_options}->{dry_run} = shift; } |
|
0
|
|
|
|
|
0
|
|
120
|
0
|
|
|
|
|
0
|
return $self->{_options}->{dry_run}; |
121
|
|
|
|
|
|
|
} ## end sub dry_run |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
# Get context |
124
|
|
|
|
|
|
|
sub get_context { |
125
|
8
|
|
|
8
|
1
|
333
|
my ( $self, $cmd ) = @_; |
126
|
8
|
|
|
|
|
14
|
my $context = {}; |
127
|
8
|
100
|
|
|
|
18
|
if ($cmd) { |
128
|
4
|
|
|
|
|
23
|
$context = { |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
# Global |
131
|
|
|
|
|
|
|
$self->{_context}->{global} |
132
|
4
|
|
|
|
|
21
|
? %{ $self->{_context}->{global} } |
133
|
|
|
|
|
|
|
: (), |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
# Command specific |
136
|
4
|
50
|
|
|
|
15
|
$self->{_context}->{$cmd} ? %{ $self->{_context}->{$cmd} } : (), |
|
|
50
|
|
|
|
|
|
137
|
|
|
|
|
|
|
}; |
138
|
|
|
|
|
|
|
} ## end if ($cmd) |
139
|
|
|
|
|
|
|
else { |
140
|
4
|
|
|
|
|
6
|
$context = $self->{_context}; |
141
|
|
|
|
|
|
|
} |
142
|
|
|
|
|
|
|
|
143
|
8
|
|
|
|
|
33
|
return $context; |
144
|
|
|
|
|
|
|
} ## end sub get_context |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
# Get error message |
147
|
0
|
|
|
0
|
0
|
0
|
sub errstr { return shift->{_errstr}; } |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
# Get return code |
150
|
0
|
|
|
0
|
0
|
0
|
sub exitval { return shift->{_exitval}; } |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
####################### |
153
|
|
|
|
|
|
|
# CASCM METHODS |
154
|
|
|
|
|
|
|
####################### |
155
|
|
|
|
|
|
|
|
156
|
0
|
|
|
0
|
0
|
0
|
sub haccess { return shift->_run( 'haccess', @_ ); } |
157
|
0
|
|
|
0
|
0
|
0
|
sub hap { return shift->_run( 'hap', @_ ); } |
158
|
0
|
|
|
0
|
0
|
0
|
sub har { return shift->_run( 'har', @_ ); } |
159
|
0
|
|
|
0
|
0
|
0
|
sub hauthsync { return shift->_run( 'hauthsync', @_ ); } |
160
|
0
|
|
|
0
|
0
|
0
|
sub hcbl { return shift->_run( 'hcbl', @_ ); } |
161
|
0
|
|
|
0
|
0
|
0
|
sub hccmrg { return shift->_run( 'hccmrg', @_ ); } |
162
|
0
|
|
|
0
|
0
|
0
|
sub hcrrlte { return shift->_run( 'hcrrlte', @_ ); } |
163
|
0
|
|
|
0
|
0
|
0
|
sub hchgtype { return shift->_run( 'hchgtype', @_ ); } |
164
|
0
|
|
|
0
|
0
|
0
|
sub hchu { return shift->_run( 'hchu', @_ ); } |
165
|
0
|
|
|
0
|
0
|
0
|
sub hci { return shift->_run( 'hci', @_ ); } |
166
|
0
|
|
|
0
|
0
|
0
|
sub hcmpview { return shift->_run( 'hcmpview', @_ ); } |
167
|
3
|
|
|
3
|
0
|
15
|
sub hco { return shift->_run( 'hco', @_ ); } |
168
|
0
|
|
|
0
|
0
|
0
|
sub hcp { return shift->_run( 'hcp', @_ ); } |
169
|
0
|
|
|
0
|
0
|
0
|
sub hcpj { return shift->_run( 'hcpj', @_ ); } |
170
|
0
|
|
|
0
|
0
|
0
|
sub hcropmrg { return shift->_run( 'hcropmrg', @_ ); } |
171
|
0
|
|
|
0
|
0
|
0
|
sub hcrtpath { return shift->_run( 'hcrtpath', @_ ); } |
172
|
0
|
|
|
0
|
0
|
0
|
sub hdbgctrl { return shift->_run( 'hdbgctrl', @_ ); } |
173
|
0
|
|
|
0
|
0
|
0
|
sub hdelss { return shift->_run( 'hdelss', @_ ); } |
174
|
0
|
|
|
0
|
0
|
0
|
sub hdlp { return shift->_run( 'hdlp', @_ ); } |
175
|
0
|
|
|
0
|
0
|
0
|
sub hdp { return shift->_run( 'hdp', @_ ); } |
176
|
0
|
|
|
0
|
0
|
0
|
sub hdv { return shift->_run( 'hdv', @_ ); } |
177
|
0
|
|
|
0
|
0
|
0
|
sub hexecp { return shift->_run( 'hexecp', @_ ); } |
178
|
0
|
|
|
0
|
0
|
0
|
sub hexpenv { return shift->_run( 'hexpenv', @_ ); } |
179
|
0
|
|
|
0
|
0
|
0
|
sub hfatt { return shift->_run( 'hfatt', @_ ); } |
180
|
0
|
|
|
0
|
0
|
0
|
sub hformsync { return shift->_run( 'hformsync', @_ ); } |
181
|
0
|
|
|
0
|
0
|
0
|
sub hft { return shift->_run( 'hft', @_ ); } |
182
|
0
|
|
|
0
|
0
|
0
|
sub hgetusg { return shift->_run( 'hgetusg', @_ ); } |
183
|
0
|
|
|
0
|
0
|
0
|
sub himpenv { return shift->_run( 'himpenv', @_ ); } |
184
|
0
|
|
|
0
|
0
|
0
|
sub hlr { return shift->_run( 'hlr', @_ ); } |
185
|
0
|
|
|
0
|
0
|
0
|
sub hlv { return shift->_run( 'hlv', @_ ); } |
186
|
0
|
|
|
0
|
0
|
0
|
sub hmvitm { return shift->_run( 'hmvitm', @_ ); } |
187
|
0
|
|
|
0
|
0
|
0
|
sub hmvpkg { return shift->_run( 'hmvpkg', @_ ); } |
188
|
0
|
|
|
0
|
0
|
0
|
sub hmvpth { return shift->_run( 'hmvpth', @_ ); } |
189
|
0
|
|
|
0
|
0
|
0
|
sub hpg { return shift->_run( 'hpg', @_ ); } |
190
|
0
|
|
|
0
|
0
|
0
|
sub hpkgunlk { return shift->_run( 'hpkgunlk', @_ ); } |
191
|
0
|
|
|
0
|
0
|
0
|
sub hpp { return shift->_run( 'hpp', @_ ); } |
192
|
0
|
|
|
0
|
0
|
0
|
sub hppolget { return shift->_run( 'hppolget', @_ ); } |
193
|
0
|
|
|
0
|
0
|
0
|
sub hppolset { return shift->_run( 'hppolset', @_ ); } |
194
|
0
|
|
|
0
|
0
|
0
|
sub hrefresh { return shift->_run( 'hrefresh', @_ ); } |
195
|
0
|
|
|
0
|
0
|
0
|
sub hrepedit { return shift->_run( 'hrepedit', @_ ); } |
196
|
0
|
|
|
0
|
0
|
0
|
sub hrepmngr { return shift->_run( 'hrepmngr', @_ ); } |
197
|
0
|
|
|
0
|
0
|
0
|
sub hri { return shift->_run( 'hri', @_ ); } |
198
|
0
|
|
|
0
|
0
|
0
|
sub hrmvpth { return shift->_run( 'hrmvpth', @_ ); } |
199
|
0
|
|
|
0
|
0
|
0
|
sub hrnitm { return shift->_run( 'hrnitm', @_ ); } |
200
|
0
|
|
|
0
|
0
|
0
|
sub hrnpth { return shift->_run( 'hrnpth', @_ ); } |
201
|
0
|
|
|
0
|
0
|
0
|
sub hrt { return shift->_run( 'hrt', @_ ); } |
202
|
0
|
|
|
0
|
0
|
0
|
sub hsigget { return shift->_run( 'hsigget', @_ ); } |
203
|
0
|
|
|
0
|
0
|
0
|
sub hsigset { return shift->_run( 'hsigset', @_ ); } |
204
|
0
|
|
|
0
|
0
|
0
|
sub hsmtp { return shift->_run( 'hsmtp', @_ ); } |
205
|
0
|
|
|
0
|
0
|
0
|
sub hspp { return shift->_run( 'hspp', @_ ); } |
206
|
0
|
|
|
0
|
0
|
0
|
sub hsql { return shift->_run( 'hsql', @_ ); } |
207
|
0
|
|
|
0
|
0
|
0
|
sub hsv { return shift->_run( 'hsv', @_ ); } |
208
|
0
|
|
|
0
|
0
|
0
|
sub hsync { return shift->_run( 'hsync', @_ ); } |
209
|
0
|
|
|
0
|
0
|
0
|
sub htakess { return shift->_run( 'htakess', @_ ); } |
210
|
0
|
|
|
0
|
0
|
0
|
sub hucache { return shift->_run( 'hucache', @_ ); } |
211
|
0
|
|
|
0
|
0
|
0
|
sub hudp { return shift->_run( 'hudp', @_ ); } |
212
|
0
|
|
|
0
|
0
|
0
|
sub hup { return shift->_run( 'hup', @_ ); } |
213
|
0
|
|
|
0
|
0
|
0
|
sub husrmgr { return shift->_run( 'husrmgr', @_ ); } |
214
|
0
|
|
|
0
|
0
|
0
|
sub husrunlk { return shift->_run( 'husrunlk', @_ ); } |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
####################### |
217
|
|
|
|
|
|
|
# INTERNAL METHODS |
218
|
|
|
|
|
|
|
####################### |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
# Object initialization |
221
|
|
|
|
|
|
|
sub _init { |
222
|
5
|
|
|
5
|
|
9
|
my $self = shift; |
223
|
5
|
|
|
|
|
10
|
my $options_ref = shift; |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
# Basic initliazation |
226
|
5
|
|
|
|
|
40
|
$self->{_options} = {}; |
227
|
5
|
|
|
|
|
15
|
$self->{_context} = {}; |
228
|
5
|
|
|
|
|
9
|
$self->{_errstr} = q(); |
229
|
5
|
|
|
|
|
9
|
$self->{_exitval} = 0; |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
# Make sure we have a option hash ref |
232
|
5
|
50
|
|
|
|
23
|
if ( ref $options_ref ne 'HASH' ) { croak "Hash reference expected"; } |
|
0
|
|
|
|
|
0
|
|
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
# Set default options |
235
|
5
|
|
|
|
|
17
|
my %default_options = ( |
236
|
|
|
|
|
|
|
'context_file' => 0, |
237
|
|
|
|
|
|
|
'dry_run' => 0, |
238
|
|
|
|
|
|
|
'parse_logs' => 0, |
239
|
|
|
|
|
|
|
); |
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
# Valid options |
242
|
5
|
|
|
|
|
15
|
my %valid_options = ( |
243
|
|
|
|
|
|
|
'context_file' => 1, |
244
|
|
|
|
|
|
|
'dry_run' => 1, |
245
|
|
|
|
|
|
|
'parse_logs' => 1, |
246
|
|
|
|
|
|
|
); |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
# Read options |
249
|
5
|
|
|
|
|
21
|
my %options = ( %default_options, %{$options_ref} ); |
|
5
|
|
|
|
|
51
|
|
250
|
5
|
|
|
|
|
21
|
foreach ( keys %options ) { |
251
|
15
|
50
|
|
|
|
50
|
croak "Invalid option $_" unless $valid_options{$_}; |
252
|
|
|
|
|
|
|
} |
253
|
5
|
|
|
|
|
13
|
$self->{_options} = \%options; |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
# Set context |
256
|
5
|
100
|
|
|
|
22
|
if ( $options{'context_file'} ) { |
257
|
1
|
50
|
|
|
|
6
|
$self->load_context( $options{'context_file'} ) |
258
|
|
|
|
|
|
|
or croak "Error Loading Context file : " . $self->errstr(); |
259
|
|
|
|
|
|
|
} ## end if ( $options{'context_file'...}) |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
# Check if we're parsing logs |
262
|
5
|
50
|
|
|
|
27
|
$self->parse_logs( $options{'parse_logs'} ) if $options{'parse_logs'}; |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
# Done initliazing |
265
|
5
|
|
|
|
|
22
|
return $self; |
266
|
|
|
|
|
|
|
} ## end sub _init |
267
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
# Set error |
269
|
|
|
|
|
|
|
sub _err { |
270
|
3
|
|
|
3
|
|
6
|
my $self = shift; |
271
|
3
|
|
|
|
|
6
|
my $msg = shift; |
272
|
3
|
|
|
|
|
9
|
$self->{_errstr} = $msg; |
273
|
3
|
|
|
|
|
22
|
return 1; |
274
|
|
|
|
|
|
|
} ## end sub _err |
275
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
# Set exitval |
277
|
|
|
|
|
|
|
sub _exitval { |
278
|
3
|
|
|
3
|
|
5
|
my ( $self, $rc ) = @_; |
279
|
3
|
50
|
|
|
|
11
|
$rc = 0 if not defined $rc; |
280
|
3
|
|
|
|
|
7
|
$self->{_exitval} = $rc; |
281
|
3
|
|
|
|
|
5
|
return 1; |
282
|
|
|
|
|
|
|
} ## end sub _exitval |
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
# Execute command |
285
|
|
|
|
|
|
|
sub _run { |
286
|
3
|
|
|
3
|
|
10
|
my ( $self, $cmd, @args ) = @_; |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
# Reset error |
289
|
3
|
|
|
|
|
323
|
$self->_err(q()); |
290
|
3
|
|
|
|
|
11
|
$self->_exitval(0); |
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
# Get Context & Options |
293
|
3
|
|
|
|
|
6
|
my $context = {}; |
294
|
3
|
|
|
|
|
42
|
( $context, @args ) = $self->_get_run_context( $cmd, @args ); |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
# Get options |
297
|
3
|
|
|
|
|
10
|
my $dry_run = delete $context->{dry_run}; |
298
|
3
|
|
|
|
|
6
|
my $parse_log = delete $context->{parse_logs}; |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
# Check if we're parsing logs |
301
|
3
|
|
|
|
|
3
|
my $default_log; |
302
|
3
|
50
|
|
|
|
10
|
if ($parse_log) { |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
# Init Log |
305
|
0
|
|
|
|
|
0
|
my $tmpfile = File::Temp->new( |
306
|
|
|
|
|
|
|
UNLINK => 1, |
307
|
|
|
|
|
|
|
); |
308
|
0
|
|
|
|
|
0
|
$default_log = $tmpfile->filename(); |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
# Remove existing 'o' & 'oa' from context |
311
|
0
|
0
|
|
|
|
0
|
delete $context->{'o'} if exists $context->{'o'}; |
312
|
0
|
0
|
|
|
|
0
|
delete $context->{'oa'} if exists $context->{'oa'}; |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
# Set default log |
315
|
0
|
|
|
|
|
0
|
$context->{'o'} = $default_log; |
316
|
|
|
|
|
|
|
} ## end if ($parse_log) |
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
# Build argument string |
319
|
3
|
|
|
|
|
5
|
my $arg_str = q(); |
320
|
3
|
100
|
|
|
|
17
|
if (@args) { |
321
|
2
|
|
|
|
|
5
|
$arg_str = join( ' ', map { "-arg=$_" } @args ); |
|
2
|
|
|
|
|
12
|
|
322
|
|
|
|
|
|
|
} |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
# Get option string for $cmd |
325
|
3
|
|
|
|
|
12
|
my $opt_str = $self->_get_option_str( $cmd, $context ); |
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
# Dry run |
328
|
3
|
50
|
|
|
|
9
|
if ($dry_run) { return "$cmd $arg_str $opt_str"; } |
|
3
|
|
|
|
|
38
|
|
329
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
# Prepare DI file |
331
|
0
|
|
|
|
|
0
|
my $DIF = File::Temp->new( UNLINK => 0 ); |
332
|
0
|
|
|
|
|
0
|
my $di_file = $DIF->filename; |
333
|
|
|
|
|
|
|
print( $DIF "$arg_str $opt_str" ) |
334
|
0
|
0
|
|
|
|
0
|
or do { $self->_err("Unable to write to $di_file") and return; }; |
|
0
|
0
|
|
|
|
0
|
|
335
|
0
|
|
|
|
|
0
|
close($DIF); |
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
# Run command |
338
|
0
|
|
|
|
|
0
|
my $cmd_str = "$cmd -di \"${di_file}\""; |
339
|
0
|
|
|
|
|
0
|
my $out = qx($cmd_str 2>&1); |
340
|
0
|
|
|
|
|
0
|
my $rc = $?; |
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
# Cleanup DI file if command didn't remove it |
343
|
0
|
0
|
|
|
|
0
|
if ( -f $di_file ) { unlink $di_file; } |
|
0
|
|
|
|
|
0
|
|
344
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
# Parse log |
346
|
0
|
0
|
|
|
|
0
|
_parse_log( $default_log, $parse_log ) if $parse_log; |
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
# Return |
349
|
0
|
|
|
|
|
0
|
return $self->_handle_error( $cmd, $rc, $out ); |
350
|
|
|
|
|
|
|
} ## end sub _run |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
# Get run context |
353
|
|
|
|
|
|
|
sub _get_run_context { |
354
|
3
|
|
|
3
|
|
8
|
my ( $self, $cmd, @args ) = @_; |
355
|
|
|
|
|
|
|
|
356
|
3
|
|
|
|
|
5
|
my $run_context = {}; |
357
|
3
|
100
|
|
|
|
13
|
if ( ref( $args[0] ) eq 'HASH' ) { $run_context = shift @args; } |
|
1
|
|
|
|
|
3
|
|
358
|
|
|
|
|
|
|
|
359
|
3
|
|
50
|
|
|
12
|
my $cmd_context = $self->get_context($cmd) || {}; |
360
|
3
|
|
|
|
|
3
|
my $context = { %{$cmd_context}, %{$run_context} }; |
|
3
|
|
|
|
|
10
|
|
|
3
|
|
|
|
|
11
|
|
361
|
|
|
|
|
|
|
|
362
|
3
|
50
|
|
|
|
18
|
$context->{dry_run} = $self->{_options}->{dry_run} |
363
|
|
|
|
|
|
|
if not exists $context->{dry_run}; |
364
|
3
|
50
|
|
|
|
14
|
$context->{parse_logs} = $self->{_options}->{parse_logs} |
365
|
|
|
|
|
|
|
if not exists $context->{parse_logs}; |
366
|
|
|
|
|
|
|
|
367
|
3
|
|
|
|
|
17
|
return ( $context, @args ); |
368
|
|
|
|
|
|
|
} ## end sub _get_run_context |
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
# Get option string |
371
|
|
|
|
|
|
|
sub _get_option_str { |
372
|
3
|
|
|
3
|
|
4
|
my $self = shift; |
373
|
3
|
|
|
|
|
4
|
my $cmd = shift; |
374
|
3
|
|
50
|
|
|
11
|
my $context = shift || {}; |
375
|
|
|
|
|
|
|
|
376
|
3
|
|
|
|
|
57
|
my @cmd_options = _get_cmd_options($cmd); |
377
|
|
|
|
|
|
|
|
378
|
3
|
|
|
|
|
14
|
my @opt_args = qw(); |
379
|
3
|
|
|
|
|
9
|
foreach my $option (@cmd_options) { |
380
|
126
|
100
|
|
|
|
322
|
next unless $context->{$option}; |
381
|
16
|
|
|
|
|
28
|
my $val = $context->{$option}; |
382
|
16
|
100
|
|
|
|
33
|
if ( $val eq '1' ) { push @opt_args, "-${option}"; } |
|
6
|
|
|
|
|
18
|
|
383
|
10
|
|
|
|
|
33
|
else { push @opt_args, "-${option}", $val; } |
384
|
|
|
|
|
|
|
} ## end foreach my $option (@cmd_options) |
385
|
|
|
|
|
|
|
|
386
|
3
|
|
|
|
|
29
|
return join( ' ', @opt_args ); |
387
|
|
|
|
|
|
|
} ## end sub _get_option_str |
388
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
# Command options |
390
|
|
|
|
|
|
|
sub _get_cmd_options { |
391
|
62
|
|
|
62
|
|
1849
|
my $cmd = shift; |
392
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
#<<< Don't touch this ... |
394
|
|
|
|
|
|
|
|
395
|
62
|
|
|
|
|
5169
|
my $options = { |
396
|
|
|
|
|
|
|
'common' => [qw(o v oa wts)], |
397
|
|
|
|
|
|
|
'haccess' => [qw(b eh en ft ha pw rn ug usr)], |
398
|
|
|
|
|
|
|
'hap' => [qw(b c eh en pn pw st rej usr)], |
399
|
|
|
|
|
|
|
'har' => [qw(b f m eh er pw mpw usr musr rport)], |
400
|
|
|
|
|
|
|
'hauthsync' => [qw(b eh pw usr)], |
401
|
|
|
|
|
|
|
'hcbl' => [qw(b eh en pw rp rw ss st add rdp rmr usr)], |
402
|
|
|
|
|
|
|
'hccmrg' => [qw(b p eh en ma mc pn pw st tb tt usr)], |
403
|
|
|
|
|
|
|
'hchgtype' => [qw(b q eh pw rp bin ext txt usr)], |
404
|
|
|
|
|
|
|
'hchu' => [qw(b eh pw npw usr ousr)], |
405
|
|
|
|
|
|
|
'hci' => [qw(b d p s bo cp de eh en er if nd ob op ot pn pw rm ro st tr uk ur vp dcp dvp rpw usr rusr rport)], |
406
|
|
|
|
|
|
|
'hcmpview' => [qw(b s eh pw en1 en2 st1 usr uv1 uv2 vn1 vn2 vp1 vp2 cidc ciic)], |
407
|
|
|
|
|
|
|
'hco' => [qw(b p r s bo br cp cu eh en er nt op pf pn po pw rm ro ss st sy tb to tr up vn vp ced dcp dvp nvf nvs rpw usr rusr rport replace)], |
408
|
|
|
|
|
|
|
'hcp' => [qw(b at eh en pn pw st usr)], |
409
|
|
|
|
|
|
|
'hcpj' => [qw(b eh pw act cpj cug dac ina npj tem usr)], |
410
|
|
|
|
|
|
|
'hcropmrg' => [qw(b eh mo p1 p2 pn pw en1 en2 plo st1 st2 usr vfs)], |
411
|
|
|
|
|
|
|
'hcrrlte' => [qw(b d eh en pw usr epid epname)], |
412
|
|
|
|
|
|
|
'hcrtpath' => [qw(b p de eh en ob ot pw rp st usr cipn)], |
413
|
|
|
|
|
|
|
'hdbgctrl' => [qw(b eh pw rm usr rport)], |
414
|
|
|
|
|
|
|
'hdelss' => [qw(b eh en pw usr)], |
415
|
|
|
|
|
|
|
'hdlp' => [qw(b eh en pn pw st usr pkgs)], |
416
|
|
|
|
|
|
|
'hdp' => [qw(b eh en pb pd pn pw st adp pdr usr vdr)], |
417
|
|
|
|
|
|
|
'hdv' => [qw(b s eh en pn pw st vp usr)], |
418
|
|
|
|
|
|
|
'hexecp' => [qw(m er ma pw prg syn usr args asyn rport)], |
419
|
|
|
|
|
|
|
'hexpenv' => [qw(b f eh en pw cug eac eug usr)], |
420
|
|
|
|
|
|
|
'hfatt' => [qw(b at cp eh er fn ft pw rm add fid get rem rpw usr comp rusr rport)], |
421
|
|
|
|
|
|
|
'hformsync' => [qw(b d f eh pw all hfd usr)], |
422
|
|
|
|
|
|
|
'hft' => [qw(a b fo fs)], |
423
|
|
|
|
|
|
|
'hgetusg' => [qw(b cu pu pw usr)], |
424
|
|
|
|
|
|
|
'himpenv' => [qw(b f eh pw iug usr)], |
425
|
|
|
|
|
|
|
'hlr' => [qw(b c f cp eh er pw rm rp rpw usr rcep rusr rport)], |
426
|
|
|
|
|
|
|
'hlv' => [qw(b s ac cd eh en pn pw ss st vn vp usr)], |
427
|
|
|
|
|
|
|
'hmvitm' => [qw(b p de eh en np ob ot pn pw st uk ur vp usr)], |
428
|
|
|
|
|
|
|
'hmvpkg' => [qw(b eh en ph pn pw st ten tst usr)], |
429
|
|
|
|
|
|
|
'hmvpth' => [qw(b p de eh en np ob ot pn pw st uk ur vp usr)], |
430
|
|
|
|
|
|
|
'hpg' => [qw(b bp eh en pg pw st app cpg dpg dpp usr)], |
431
|
|
|
|
|
|
|
'hpkgunlk' => [qw(b eh en pw usr)], |
432
|
|
|
|
|
|
|
'hpp' => [qw(b eh en pb pd pm pn pw st adp pdr usr vdr)], |
433
|
|
|
|
|
|
|
'hppolget' => [qw(b f eh gl pw usr)], |
434
|
|
|
|
|
|
|
'hppolset' => [qw(b f eh fc pw usr)], |
435
|
|
|
|
|
|
|
'hrefresh' => [qw(b iv pl pr ps pv st nst debug nolock)], |
436
|
|
|
|
|
|
|
'hrepedit' => [qw(b eh fo pw rp all usr ismv isren ppath tpath rnpath newname oldname)], |
437
|
|
|
|
|
|
|
'hrepmngr' => [qw(b c r co cp cr eh er fc ld mv nc nc pw rm rp all cep coe del drn drp dup isv mvs ren rpw srn srp upd usr appc gext ndac nmvs rext rusr noext rport addext appext remext addsgrp addugrp addvgrp newname oldname remsgrp remugrp remvgrp)], |
438
|
|
|
|
|
|
|
'hri' => [qw(b p de eh en ob ot pn pw st vp usr)], |
439
|
|
|
|
|
|
|
'hrmvpth' => [qw(b p de eh en ob ot pn pw st vp usr)], |
440
|
|
|
|
|
|
|
'hrnitm' => [qw(b p de eh en nn ob on ot pn pw st uk ur vp usr)], |
441
|
|
|
|
|
|
|
'hrnpth' => [qw(b p de eh en nn ob ot pn pw st uk ur vp usr)], |
442
|
|
|
|
|
|
|
'hrt' => [qw(b f m eh er pw mpw usr musr rport)], |
443
|
|
|
|
|
|
|
'hsigget' => [qw(a t v gl purge)], |
444
|
|
|
|
|
|
|
'hsigset' => [qw(purge context)], |
445
|
|
|
|
|
|
|
'hsmtp' => [qw(d f m p s cc bcc)], |
446
|
|
|
|
|
|
|
'hspp' => [qw(b s eh en fp pn pw st tp usr)], |
447
|
|
|
|
|
|
|
'hsql' => [qw(b f s t eh eh gl nh pw usr)], |
448
|
|
|
|
|
|
|
'hsv' => [qw(b p s eh en gl ib id io it iu iv pw st vp usr)], |
449
|
|
|
|
|
|
|
'hsync' => [qw(b av bo br cp eh en er fv il iv pl pn ps pv pw rm ss st sy tb to vp ced iol rpw usr excl rusr excls purge rport complete)], |
450
|
|
|
|
|
|
|
'htakess' => [qw(b p eh en pb pg pn po pw rs ss st ts ve vp abv usr)], |
451
|
|
|
|
|
|
|
'hucache' => [qw(b eh en er pw ss st vp rpw usr rusr purge rport cacheagent)], |
452
|
|
|
|
|
|
|
'hudp' => [qw(b ap eh en ip pn pw st usr)], |
453
|
|
|
|
|
|
|
'hup' => [qw(b p af at cf eh en ft nt pr pw rf afo apg del des npn rfo rpg usr)], |
454
|
|
|
|
|
|
|
'husrmgr' => [qw(b ad ae cf du eh nn ow pw cpw dlm swl usr)], |
455
|
|
|
|
|
|
|
'husrunlk' => [qw(b eh pw usr)], |
456
|
|
|
|
|
|
|
}; |
457
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
#>>> |
459
|
|
|
|
|
|
|
|
460
|
3053
|
|
|
|
|
2640
|
my @cmd_options = sort { lc $a cmp lc $b } |
|
62
|
|
|
|
|
112
|
|
461
|
62
|
|
|
|
|
145
|
( @{ $options->{common} }, @{ $options->{$cmd} } ); |
|
62
|
|
|
|
|
171
|
|
462
|
62
|
|
|
|
|
1894
|
return @cmd_options; |
463
|
|
|
|
|
|
|
} ## end sub _get_cmd_options |
464
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
# Handle error/return |
466
|
|
|
|
|
|
|
sub _handle_error { |
467
|
0
|
|
|
0
|
|
|
my ( $self, $cmd, $rc, $out ) = @_; |
468
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
# Standard cases |
470
|
0
|
|
|
|
|
|
my %error = ( |
471
|
|
|
|
|
|
|
'1' => "Command syntax for $cmd is incorrect." |
472
|
|
|
|
|
|
|
. ' Please check your context setting', |
473
|
|
|
|
|
|
|
'2' => 'Broker not connected', |
474
|
|
|
|
|
|
|
'3' => "$cmd failed", |
475
|
|
|
|
|
|
|
'4' => 'Unexpected error', |
476
|
|
|
|
|
|
|
'5' => 'Invalid login', |
477
|
|
|
|
|
|
|
'6' => 'Server or database down', |
478
|
|
|
|
|
|
|
'7' => 'Incorrect service pack level', |
479
|
|
|
|
|
|
|
'8' => 'Incompatible server version', |
480
|
|
|
|
|
|
|
'9' => 'Exposed password', |
481
|
|
|
|
|
|
|
'10' => 'Ambiguous arguments', |
482
|
|
|
|
|
|
|
'11' => 'Access denied', |
483
|
|
|
|
|
|
|
'12' => 'Pre-link failed', |
484
|
|
|
|
|
|
|
'13' => 'Post-link failed', |
485
|
|
|
|
|
|
|
); |
486
|
|
|
|
|
|
|
|
487
|
|
|
|
|
|
|
# Special cases |
488
|
0
|
0
|
|
|
|
|
if ( $cmd eq 'hchu' ) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
489
|
0
|
|
|
|
|
|
%error = ( |
490
|
|
|
|
|
|
|
%error, |
491
|
|
|
|
|
|
|
'94' => |
492
|
|
|
|
|
|
|
'Password changes executed from the command line using hchu are disabled when external authentication is enabled', |
493
|
|
|
|
|
|
|
); |
494
|
|
|
|
|
|
|
} ## end if ( $cmd eq 'hchu' ) |
495
|
|
|
|
|
|
|
elsif ( $cmd eq 'hco' ) { |
496
|
0
|
|
|
|
|
|
%error = ( |
497
|
|
|
|
|
|
|
%error, |
498
|
|
|
|
|
|
|
'14' => 'No version was found for the file name or pattern', |
499
|
|
|
|
|
|
|
); |
500
|
|
|
|
|
|
|
} ## end elsif ( $cmd eq 'hco' ) |
501
|
|
|
|
|
|
|
elsif ( $cmd eq 'hexecp' ) { |
502
|
0
|
|
|
|
|
|
%error = ( |
503
|
|
|
|
|
|
|
%error, |
504
|
|
|
|
|
|
|
'2' => |
505
|
|
|
|
|
|
|
'Broker not connected OR the invoked program did not return a value of its own', |
506
|
|
|
|
|
|
|
); |
507
|
|
|
|
|
|
|
} ## end elsif ( $cmd eq 'hexecp' ) |
508
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
# Cleanup command output |
510
|
0
|
0
|
|
|
|
|
if ($out) { |
511
|
0
|
|
|
|
|
|
my @lines; |
512
|
0
|
|
|
|
|
|
foreach my $line ( split( /\r\n|\r|\n/, $out ) ) { |
513
|
0
|
|
|
|
|
|
chomp $line; |
514
|
0
|
0
|
|
|
|
|
next unless $line; |
515
|
0
|
0
|
|
|
|
|
next if $line =~ /^[[:blank:]]$/; |
516
|
0
|
|
|
|
|
|
push @lines, $line; |
517
|
|
|
|
|
|
|
} ## end foreach my $line ( split( /\r\n|\r|\n/...)) |
518
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
# Reset |
520
|
0
|
|
|
|
|
|
$out = join( '. ', @lines ); |
521
|
|
|
|
|
|
|
} ## end if ($out) |
522
|
|
|
|
|
|
|
|
523
|
|
|
|
|
|
|
# Get error message |
524
|
0
|
|
|
|
|
|
my $msg; |
525
|
0
|
0
|
|
|
|
|
if ( $rc == -1 ) { |
|
|
0
|
|
|
|
|
|
526
|
0
|
|
|
|
|
|
$msg = "Failed to execute $cmd"; |
527
|
0
|
0
|
|
|
|
|
$msg .= " : $out" if $out; |
528
|
0
|
|
|
|
|
|
$self->_err($msg); |
529
|
0
|
|
|
|
|
|
return; |
530
|
|
|
|
|
|
|
} ## end if ( $rc == -1 ) |
531
|
|
|
|
|
|
|
elsif ( $rc > 0 ) { |
532
|
0
|
0
|
|
|
|
|
if ( $rc > 255 ) { $rc = $rc >> 8; } |
|
0
|
|
|
|
|
|
|
533
|
0
|
|
0
|
|
|
|
$msg = $error{$rc} || "Unknown error"; |
534
|
0
|
0
|
|
|
|
|
$msg .= " : $out" if $out; |
535
|
0
|
|
|
|
|
|
$self->_err($msg); |
536
|
0
|
|
|
|
|
|
return; |
537
|
|
|
|
|
|
|
} ## end elsif ( $rc > 0 ) |
538
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
# Return true |
540
|
0
|
|
|
|
|
|
return 1; |
541
|
|
|
|
|
|
|
} ## end sub _handle_error |
542
|
|
|
|
|
|
|
|
543
|
|
|
|
|
|
|
# Parse Log |
544
|
|
|
|
|
|
|
sub _parse_log { |
545
|
0
|
|
|
0
|
|
|
my ( $logfile, $category ) = @_; |
546
|
|
|
|
|
|
|
|
547
|
0
|
|
0
|
|
|
|
$category ||= 0; |
548
|
0
|
0
|
|
|
|
|
$category = __PACKAGE__ if ( $category eq '1' ); |
549
|
|
|
|
|
|
|
|
550
|
0
|
0
|
|
|
|
|
my $log |
551
|
|
|
|
|
|
|
= Log::Any->get_logger( $category ? ( category => $category ) : () ); |
552
|
|
|
|
|
|
|
|
553
|
0
|
0
|
|
|
|
|
if ( not -f $logfile ) { |
554
|
0
|
|
|
|
|
|
$log->error("Logfile $logfile does not exist"); |
555
|
0
|
|
|
|
|
|
return 1; |
556
|
|
|
|
|
|
|
} ## end if ( not -f $logfile ) |
557
|
|
|
|
|
|
|
|
558
|
|
|
|
|
|
|
open( my $L, '<', $logfile ) |
559
|
0
|
0
|
|
|
|
|
or do { $log->error("Unable to read $logfile") and return 1; }; |
|
0
|
0
|
|
|
|
|
|
560
|
0
|
|
|
|
|
|
while (<$L>) { |
561
|
0
|
|
|
|
|
|
my $line = $_; |
562
|
0
|
0
|
|
|
|
|
next unless defined $line; |
563
|
0
|
|
|
|
|
|
chomp $line; |
564
|
0
|
0
|
|
|
|
|
next unless $line; |
565
|
0
|
0
|
|
|
|
|
next if $line =~ /^[[:blank:]]*$/; |
566
|
|
|
|
|
|
|
|
567
|
0
|
0
|
|
|
|
|
if ( $line =~ s/^\s*E0\w{7}:\s*//x ) { $log->error($line); } |
|
0
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
568
|
0
|
|
|
|
|
|
elsif ( $line =~ s/^\s*W0\w{7}:\s*//x ) { $log->warn($line); } |
569
|
0
|
|
|
|
|
|
elsif ( $line =~ s/^\s*I0\w{7}:\s*//x ) { $log->info($line); } |
570
|
0
|
|
|
|
|
|
else { $log->info($line); } |
571
|
|
|
|
|
|
|
} ## end while (<$L>) |
572
|
0
|
|
|
|
|
|
close $L; |
573
|
0
|
0
|
|
|
|
|
unlink($logfile) or $log->warn("Unable to delete $logfile"); |
574
|
0
|
|
|
|
|
|
return 1; |
575
|
|
|
|
|
|
|
} ## end sub _parse_log |
576
|
|
|
|
|
|
|
|
577
|
|
|
|
|
|
|
####################### |
578
|
|
|
|
|
|
|
1; |
579
|
|
|
|
|
|
|
|
580
|
|
|
|
|
|
|
__END__ |