line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Provision::Unix::Utility; |
2
|
|
|
|
|
|
|
{ |
3
|
|
|
|
|
|
|
$Provision::Unix::Utility::VERSION = '1.07'; |
4
|
|
|
|
|
|
|
} |
5
|
|
|
|
|
|
|
# ABSTRACT: utility subroutines for sysadmin tasks |
6
|
|
|
|
|
|
|
|
7
|
2
|
|
|
2
|
|
3796
|
use strict; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
76
|
|
8
|
2
|
|
|
2
|
|
13
|
use warnings; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
60
|
|
9
|
|
|
|
|
|
|
|
10
|
2
|
|
|
2
|
|
10
|
use Cwd; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
153
|
|
11
|
2
|
|
|
2
|
|
10
|
use English qw( -no_match_vars ); |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
16
|
|
12
|
2
|
|
|
2
|
|
987
|
use File::Basename; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
250
|
|
13
|
2
|
|
|
2
|
|
2408
|
use File::Copy; |
|
2
|
|
|
|
|
6306
|
|
|
2
|
|
|
|
|
153
|
|
14
|
2
|
|
|
2
|
|
16
|
use File::Path 2.08 qw/ make_path /; |
|
2
|
|
|
|
|
54
|
|
|
2
|
|
|
|
|
118
|
|
15
|
2
|
|
|
2
|
|
12
|
use File::Spec; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
41
|
|
16
|
2
|
|
|
2
|
|
1041
|
use File::stat; |
|
2
|
|
|
|
|
10472
|
|
|
2
|
|
|
|
|
17
|
|
17
|
2
|
|
|
2
|
|
1167
|
use Params::Validate qw(:all); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
use Scalar::Util qw( openhandle ); |
19
|
|
|
|
|
|
|
use URI; |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
use lib 'lib'; |
22
|
|
|
|
|
|
|
use vars qw/ $log %std_opts /; |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
sub new { |
25
|
|
|
|
|
|
|
my $class = shift; |
26
|
|
|
|
|
|
|
my %p = validate( @_, |
27
|
|
|
|
|
|
|
{ 'log' => { type => OBJECT, optional => 1 }, |
28
|
|
|
|
|
|
|
fatal => { type => BOOLEAN, optional => 1, default => 1 }, |
29
|
|
|
|
|
|
|
debug => { type => BOOLEAN, optional => 1, default => 1 }, |
30
|
|
|
|
|
|
|
} |
31
|
|
|
|
|
|
|
); |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
$log = $p{'log'}; |
34
|
|
|
|
|
|
|
if ( ! $log ) { |
35
|
|
|
|
|
|
|
my @bits = split '::', $class; pop @bits; |
36
|
|
|
|
|
|
|
my $parent_class = join '::', grep { defined $_ } @bits; |
37
|
|
|
|
|
|
|
## no critic ( ProhibitStringyEval ) |
38
|
|
|
|
|
|
|
eval "require $parent_class"; |
39
|
|
|
|
|
|
|
## use critic |
40
|
|
|
|
|
|
|
$log = $parent_class->new(); |
41
|
|
|
|
|
|
|
}; |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
my $debug = $log->get_debug; # inherit from our parent |
44
|
|
|
|
|
|
|
my $fatal = $log->get_fatal; |
45
|
|
|
|
|
|
|
$debug = $p{debug} if defined $p{debug}; # explicity overridden |
46
|
|
|
|
|
|
|
$fatal = $p{fatal} if defined $p{fatal}; |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
my $self = { |
49
|
|
|
|
|
|
|
'log' => $log, |
50
|
|
|
|
|
|
|
debug => $debug, |
51
|
|
|
|
|
|
|
fatal => $fatal, |
52
|
|
|
|
|
|
|
}; |
53
|
|
|
|
|
|
|
bless $self, $class; |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
# globally scoped hash, populated with defaults as requested by the caller |
56
|
|
|
|
|
|
|
%std_opts = ( |
57
|
|
|
|
|
|
|
'test_ok' => { type => BOOLEAN, optional => 1 }, |
58
|
|
|
|
|
|
|
'fatal' => { type => BOOLEAN, optional => 1, default => $fatal }, |
59
|
|
|
|
|
|
|
'debug' => { type => BOOLEAN, optional => 1, default => $debug }, |
60
|
|
|
|
|
|
|
); |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
$log->audit( $class . sprintf( " loaded by %s, %s, %s", caller ) ); |
63
|
|
|
|
|
|
|
return $self; |
64
|
|
|
|
|
|
|
} |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
sub ask { |
67
|
|
|
|
|
|
|
my $self = shift; |
68
|
|
|
|
|
|
|
my $question = shift; |
69
|
|
|
|
|
|
|
my %p = validate( |
70
|
|
|
|
|
|
|
@_, |
71
|
|
|
|
|
|
|
{ default => { type => SCALAR|UNDEF, optional => 1 }, |
72
|
|
|
|
|
|
|
timeout => { type => SCALAR, optional => 1 }, |
73
|
|
|
|
|
|
|
password => { type => BOOLEAN, optional => 1, default => 0 }, |
74
|
|
|
|
|
|
|
test_ok => { type => BOOLEAN, optional => 1 }, |
75
|
|
|
|
|
|
|
} |
76
|
|
|
|
|
|
|
); |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
my $pass = $p{password}; |
79
|
|
|
|
|
|
|
my $default = $p{default}; |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
if ( ! $self->is_interactive() ) { |
82
|
|
|
|
|
|
|
$log->audit( "not running interactively, can not prompt!"); |
83
|
|
|
|
|
|
|
return $default; |
84
|
|
|
|
|
|
|
} |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
return $log->error( "ask called with \'$question\' which looks unsafe." ) |
87
|
|
|
|
|
|
|
if $question !~ m{\A \p{Any}* \z}xms; |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
my $response; |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
return $p{test_ok} if defined $p{test_ok}; |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
PROMPT: |
94
|
|
|
|
|
|
|
print "Please enter $question"; |
95
|
|
|
|
|
|
|
print " [$default]" if ( $default && !$pass ); |
96
|
|
|
|
|
|
|
print ": "; |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
system "stty -echo" if $pass; |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
if ( $p{timeout} ) { |
101
|
|
|
|
|
|
|
eval { |
102
|
|
|
|
|
|
|
local $SIG{ALRM} = sub { die "alarm\n" }; |
103
|
|
|
|
|
|
|
alarm $p{timeout}; |
104
|
|
|
|
|
|
|
$response = <STDIN>; |
105
|
|
|
|
|
|
|
alarm 0; |
106
|
|
|
|
|
|
|
}; |
107
|
|
|
|
|
|
|
if ($EVAL_ERROR) { |
108
|
|
|
|
|
|
|
$EVAL_ERROR eq "alarm\n" ? print "timed out!\n" : warn; |
109
|
|
|
|
|
|
|
} |
110
|
|
|
|
|
|
|
} |
111
|
|
|
|
|
|
|
else { |
112
|
|
|
|
|
|
|
$response = <STDIN>; |
113
|
|
|
|
|
|
|
} |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
if ( $pass ) { |
116
|
|
|
|
|
|
|
print "Please enter $question (confirm): "; |
117
|
|
|
|
|
|
|
my $response2 = <STDIN>; |
118
|
|
|
|
|
|
|
unless ( $response eq $response2 ) { |
119
|
|
|
|
|
|
|
print "\nPasswords don't match, try again.\n"; |
120
|
|
|
|
|
|
|
goto PROMPT; |
121
|
|
|
|
|
|
|
} |
122
|
|
|
|
|
|
|
system "stty echo"; |
123
|
|
|
|
|
|
|
print "\n"; |
124
|
|
|
|
|
|
|
} |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
chomp $response; |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
return $response if defined $response; # if they typed something, return it |
129
|
|
|
|
|
|
|
return $default if defined $default; # return the default, if available |
130
|
|
|
|
|
|
|
return ''; # return empty handed |
131
|
|
|
|
|
|
|
} |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
sub archive_file { |
134
|
|
|
|
|
|
|
my $self = shift; |
135
|
|
|
|
|
|
|
my $file = shift or return $log->error("missing filename in request"); |
136
|
|
|
|
|
|
|
my %p = validate( @_, |
137
|
|
|
|
|
|
|
{ %std_opts, |
138
|
|
|
|
|
|
|
'sudo' => { type => BOOLEAN, optional => 1, default => 1 }, |
139
|
|
|
|
|
|
|
'mode' => { type => SCALAR, optional => 1 }, |
140
|
|
|
|
|
|
|
destdir => { type => SCALAR, optional => 1 }, |
141
|
|
|
|
|
|
|
} |
142
|
|
|
|
|
|
|
); |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
my %args = ( debug => $p{debug}, fatal => $p{fatal} ); |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
return $log->error( "file ($file) is missing!", %args ) |
147
|
|
|
|
|
|
|
if !-e $file; |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
my $archive = $file . time; |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
if ( $p{destdir} && -d $p{destdir} ) { |
152
|
|
|
|
|
|
|
my ($vol,$dirs,$file_wo_path) = File::Spec->splitpath( $archive ); |
153
|
|
|
|
|
|
|
$archive = File::Spec->catfile( $p{destdir}, $file_wo_path ); |
154
|
|
|
|
|
|
|
}; |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
# see if we can write to both files (new & archive) with current user |
157
|
|
|
|
|
|
|
if ( $self->is_writable( $file, %args ) |
158
|
|
|
|
|
|
|
&& $self->is_writable( $archive, %args ) ) { |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
# we have permission, use perl's native copy |
161
|
|
|
|
|
|
|
copy( $file, $archive ); |
162
|
|
|
|
|
|
|
if ( -e $archive ) { |
163
|
|
|
|
|
|
|
$log->audit("archive_file: $file backed up to $archive"); |
164
|
|
|
|
|
|
|
$self->chmod( file => $file, mode => $p{mode}, %args ) if $p{mode}; |
165
|
|
|
|
|
|
|
return $archive; |
166
|
|
|
|
|
|
|
}; |
167
|
|
|
|
|
|
|
} |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
# we failed with existing permissions, try to escalate |
170
|
|
|
|
|
|
|
$self->archive_file_sudo( $file ) if ( $p{sudo} && $< != 0 ); |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
return $log->error( "backup of $file to $archive failed: $!", %args) |
173
|
|
|
|
|
|
|
if ! -e $archive; |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
$self->chmod( file => $file, mode => $p{mode}, %args ) if $p{mode}; |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
$log->audit("$file backed up to $archive"); |
178
|
|
|
|
|
|
|
return $archive; |
179
|
|
|
|
|
|
|
} |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
sub archive_file_sudo { |
182
|
|
|
|
|
|
|
my $self = shift; |
183
|
|
|
|
|
|
|
my ($file, $archive) = @_; |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
my $sudo = $self->sudo(); |
186
|
|
|
|
|
|
|
my $cp = $self->find_bin( 'cp',fatal=>0 ); |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
if ( $sudo && $cp ) { |
189
|
|
|
|
|
|
|
return $self->syscmd( "$sudo $cp $file $archive",fatal=>0 ); |
190
|
|
|
|
|
|
|
} |
191
|
|
|
|
|
|
|
$log->error( "archive_file: sudo or cp was missing, could not escalate.",fatal=>0); |
192
|
|
|
|
|
|
|
return; |
193
|
|
|
|
|
|
|
}; |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
sub chmod { |
196
|
|
|
|
|
|
|
my $self = shift; |
197
|
|
|
|
|
|
|
my %p = validate( |
198
|
|
|
|
|
|
|
@_, |
199
|
|
|
|
|
|
|
{ 'file' => { type => SCALAR, optional => 1, }, |
200
|
|
|
|
|
|
|
'file_or_dir' => { type => SCALAR, optional => 1, }, |
201
|
|
|
|
|
|
|
'dir' => { type => SCALAR, optional => 1, }, |
202
|
|
|
|
|
|
|
'mode' => { type => SCALAR, optional => 0, }, |
203
|
|
|
|
|
|
|
'sudo' => { type => BOOLEAN, optional => 1, default => 0 }, |
204
|
|
|
|
|
|
|
'fatal' => { type => BOOLEAN, optional => 1, default => 1 }, |
205
|
|
|
|
|
|
|
'debug' => { type => BOOLEAN, optional => 1, default => 1 }, |
206
|
|
|
|
|
|
|
'test_ok' => { type => BOOLEAN, optional => 1 }, |
207
|
|
|
|
|
|
|
} |
208
|
|
|
|
|
|
|
); |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
my $mode = $p{mode}; |
211
|
|
|
|
|
|
|
my %args = ( debug => $p{debug}, fatal => $p{fatal} ); |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
my $file = $p{file} || $p{file_or_dir} || $p{dir} |
214
|
|
|
|
|
|
|
or return $log->error( "invalid params to chmod in ". ref $self ); |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
if ( $p{sudo} ) { |
217
|
|
|
|
|
|
|
my $chmod = $self->find_bin( 'chmod', debug => 0 ); |
218
|
|
|
|
|
|
|
my $sudo = $self->sudo(); |
219
|
|
|
|
|
|
|
$self->syscmd( "$sudo $chmod $mode $file", debug => 0 ) |
220
|
|
|
|
|
|
|
or return $log->error( "couldn't chmod $file: $!", %args ); |
221
|
|
|
|
|
|
|
} |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
# note the conversion of ($mode) to an octal value. Very important! |
224
|
|
|
|
|
|
|
CORE::chmod( oct($mode), $file ) or |
225
|
|
|
|
|
|
|
return $log->error( "couldn't chmod $file: $!", %args); |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
$log->audit("chmod $mode $file"); |
228
|
|
|
|
|
|
|
} |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
sub chown { |
231
|
|
|
|
|
|
|
my $self = shift; |
232
|
|
|
|
|
|
|
my $file = shift; |
233
|
|
|
|
|
|
|
my %p = validate( @_, |
234
|
|
|
|
|
|
|
{ 'uid' => { type => SCALAR }, |
235
|
|
|
|
|
|
|
'gid' => { type => SCALAR }, |
236
|
|
|
|
|
|
|
'sudo' => { type => BOOLEAN, optional => 1 }, |
237
|
|
|
|
|
|
|
%std_opts, |
238
|
|
|
|
|
|
|
} |
239
|
|
|
|
|
|
|
); |
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
my ( $uid, $gid, $sudo ) = ( $p{uid}, $p{gid}, $p{sudo} ); |
242
|
|
|
|
|
|
|
my %args = ( debug => $p{debug}, fatal => $p{fatal} ); |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
$file or return $log->error( "missing file or dir", %args ); |
245
|
|
|
|
|
|
|
return $log->error( "file $file does not exist!", %args ) if ! -e $file; |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
$log->audit("chown: preparing to chown $uid $file"); |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
# sudo forces system chown instead of the perl builtin |
250
|
|
|
|
|
|
|
return $self->chown_system( $file, |
251
|
|
|
|
|
|
|
%args, |
252
|
|
|
|
|
|
|
user => $uid, |
253
|
|
|
|
|
|
|
group => $gid, |
254
|
|
|
|
|
|
|
) if $sudo; |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
my ( $nuid, $ngid ); # if uid or gid is not numeric, convert it |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
if ( $uid =~ /\A[0-9]+\z/ ) { |
259
|
|
|
|
|
|
|
$nuid = int($uid); |
260
|
|
|
|
|
|
|
$log->audit(" using $nuid from int($uid)"); |
261
|
|
|
|
|
|
|
} |
262
|
|
|
|
|
|
|
else { |
263
|
|
|
|
|
|
|
$nuid = getpwnam($uid); |
264
|
|
|
|
|
|
|
return $log->error( "failed to get uid for $uid", %args) if ! defined $nuid; |
265
|
|
|
|
|
|
|
$log->audit(" converted $uid to a number: $nuid"); |
266
|
|
|
|
|
|
|
} |
267
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
if ( $gid =~ /\A[0-9\-]+\z/ ) { |
269
|
|
|
|
|
|
|
$ngid = int( $gid ); |
270
|
|
|
|
|
|
|
$log->audit(" using $ngid from int($gid)"); |
271
|
|
|
|
|
|
|
} |
272
|
|
|
|
|
|
|
else { |
273
|
|
|
|
|
|
|
$ngid = getgrnam( $gid ); |
274
|
|
|
|
|
|
|
return $log->error( "failed to get gid for $gid", %args) if ! defined $ngid; |
275
|
|
|
|
|
|
|
$log->audit(" converted $gid to numeric: $ngid"); |
276
|
|
|
|
|
|
|
} |
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
chown( $nuid, $ngid, $file ) |
279
|
|
|
|
|
|
|
or return $log->error( "couldn't chown $file: $!",%args); |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
return 1; |
282
|
|
|
|
|
|
|
} |
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
sub chown_system { |
285
|
|
|
|
|
|
|
my $self = shift; |
286
|
|
|
|
|
|
|
my $dir = shift; |
287
|
|
|
|
|
|
|
my %p = validate( @_, |
288
|
|
|
|
|
|
|
{ 'user' => { type => SCALAR, optional => 0, }, |
289
|
|
|
|
|
|
|
'group' => { type => SCALAR, optional => 1, }, |
290
|
|
|
|
|
|
|
'recurse' => { type => BOOLEAN, optional => 1, }, |
291
|
|
|
|
|
|
|
%std_opts, |
292
|
|
|
|
|
|
|
} |
293
|
|
|
|
|
|
|
); |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
my ( $user, $group, $recurse ) = ( $p{user}, $p{group}, $p{recurse} ); |
296
|
|
|
|
|
|
|
my %args = ( debug => $p{debug}, fatal => $p{fatal} ); |
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
$dir or return $log->error( "missing file or dir", %args ); |
299
|
|
|
|
|
|
|
my $cmd = $self->find_bin( 'chown', %args ); |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
$cmd .= " -R" if $recurse; |
302
|
|
|
|
|
|
|
$cmd .= " $user"; |
303
|
|
|
|
|
|
|
$cmd .= ":$group" if $group; |
304
|
|
|
|
|
|
|
$cmd .= " $dir"; |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
$log->audit( "cmd: $cmd" ); |
307
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
$self->syscmd( $cmd, %args ) or |
309
|
|
|
|
|
|
|
return $log->error( "couldn't chown with $cmd: $!", %args); |
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
my $mess; |
312
|
|
|
|
|
|
|
$mess .= "Recursively " if $recurse; |
313
|
|
|
|
|
|
|
$mess .= "changed $dir to be owned by $user"; |
314
|
|
|
|
|
|
|
$log->audit( $mess ); |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
return 1; |
317
|
|
|
|
|
|
|
} |
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
sub clean_tmp_dir { |
320
|
|
|
|
|
|
|
my $self = shift; |
321
|
|
|
|
|
|
|
my %p = validate( |
322
|
|
|
|
|
|
|
@_, |
323
|
|
|
|
|
|
|
{ 'dir' => { type => SCALAR, optional => 0, }, |
324
|
|
|
|
|
|
|
'fatal' => { type => BOOLEAN, optional => 1, default => 1 }, |
325
|
|
|
|
|
|
|
'debug' => { type => BOOLEAN, optional => 1, default => 1 }, |
326
|
|
|
|
|
|
|
} |
327
|
|
|
|
|
|
|
); |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
my $dir = $p{dir}; |
330
|
|
|
|
|
|
|
my ($debug, $fatal) = ($p{debug}, $p{fatal}); |
331
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
my $before = cwd; # remember where we started |
333
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
return $log->error( "couldn't chdir to $dir: $!", fatal => $fatal ) |
335
|
|
|
|
|
|
|
if !chdir $dir; |
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
foreach ( $self->get_dir_files( dir => $dir ) ) { |
338
|
|
|
|
|
|
|
next unless $_; |
339
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
my ($file) = $_ =~ /^(.*)$/; |
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
$log->audit( "deleting file $file" ); |
343
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
if ( -f $file ) { |
345
|
|
|
|
|
|
|
unlink $file or |
346
|
|
|
|
|
|
|
$self->file_delete( file => $file, debug => $debug ); |
347
|
|
|
|
|
|
|
} |
348
|
|
|
|
|
|
|
elsif ( -d $file ) { |
349
|
|
|
|
|
|
|
use File::Path; |
350
|
|
|
|
|
|
|
rmtree $file or return $log->error( "couldn't delete $file"); |
351
|
|
|
|
|
|
|
} |
352
|
|
|
|
|
|
|
else { |
353
|
|
|
|
|
|
|
$log->audit( "Cannot delete unknown entity: $file" ); |
354
|
|
|
|
|
|
|
} |
355
|
|
|
|
|
|
|
} |
356
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
chdir $before; |
358
|
|
|
|
|
|
|
return 1; |
359
|
|
|
|
|
|
|
} |
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
sub cwd_source_dir { |
362
|
|
|
|
|
|
|
my $self = shift; |
363
|
|
|
|
|
|
|
my $dir = shift or die "missing dir in request\n"; |
364
|
|
|
|
|
|
|
my %p = validate( @_, |
365
|
|
|
|
|
|
|
{ 'src' => { type => SCALAR, optional => 1, }, |
366
|
|
|
|
|
|
|
'sudo' => { type => BOOLEAN, optional => 1, }, |
367
|
|
|
|
|
|
|
%std_opts, |
368
|
|
|
|
|
|
|
} |
369
|
|
|
|
|
|
|
); |
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
my ( $src, $sudo, ) = ( $p{src}, $p{sudo}, ); |
372
|
|
|
|
|
|
|
my %args = ( debug => $p{debug}, fatal => $p{fatal} ); |
373
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
return $log->error( "Something (other than a directory) is at $dir and " . |
375
|
|
|
|
|
|
|
"that's my build directory. Please remove it and try again!", %args ) |
376
|
|
|
|
|
|
|
if ( -e $dir && !-d $dir ); |
377
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
if ( !-d $dir ) { |
379
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
_try_mkdir( $dir ); # use the perl builtin mkdir |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
if ( !-d $dir ) { |
383
|
|
|
|
|
|
|
$log->audit( "trying again with system mkdir..."); |
384
|
|
|
|
|
|
|
$self->mkdir_system( dir => $dir, %args); |
385
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
if ( !-d $dir ) { |
387
|
|
|
|
|
|
|
$log->audit( "trying one last time with $sudo mkdir -p...."); |
388
|
|
|
|
|
|
|
$self->mkdir_system( dir => $dir, sudo => 1, %args) |
389
|
|
|
|
|
|
|
or return $log->error("Couldn't create $dir.", %args); |
390
|
|
|
|
|
|
|
} |
391
|
|
|
|
|
|
|
} |
392
|
|
|
|
|
|
|
} |
393
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
chdir $dir or return $log->error( "failed to cd to $dir: $!", %args); |
395
|
|
|
|
|
|
|
return 1; |
396
|
|
|
|
|
|
|
} |
397
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
sub _try_mkdir { |
399
|
|
|
|
|
|
|
my ( $dir ) = @_; |
400
|
|
|
|
|
|
|
mkpath( $dir, 0, oct('0755') ) |
401
|
|
|
|
|
|
|
or return $log->error( "mkdir $dir failed: $!"); |
402
|
|
|
|
|
|
|
$log->audit( "created $dir"); |
403
|
|
|
|
|
|
|
return 1; |
404
|
|
|
|
|
|
|
} |
405
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
sub extract_archive { |
407
|
|
|
|
|
|
|
my $self = shift; |
408
|
|
|
|
|
|
|
my $archive = shift or die "missing archive name"; |
409
|
|
|
|
|
|
|
my %p = validate( @_, { %std_opts } ); |
410
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
my %args = ( debug => $p{debug}, fatal => $p{fatal} ); |
412
|
|
|
|
|
|
|
my $r; |
413
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
if ( !-e $archive ) { |
415
|
|
|
|
|
|
|
if ( -e "$archive.tar.gz" ) { $archive = "$archive.tar.gz" } |
416
|
|
|
|
|
|
|
elsif ( -e "$archive.tgz" ) { $archive = "$archive.tgz" } |
417
|
|
|
|
|
|
|
elsif ( -e "$archive.tar.bz2" ) { $archive = "$archive.tar.bz2" } |
418
|
|
|
|
|
|
|
else { |
419
|
|
|
|
|
|
|
return $log->error( "file $archive is missing!", %args ); |
420
|
|
|
|
|
|
|
} |
421
|
|
|
|
|
|
|
} |
422
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
$log->audit("found $archive"); |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
$ENV{PATH} = '/bin:/usr/bin'; # do this or taint checks will blow up on `` |
426
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
return $log->error( "unknown archive type: $archive", %args ) |
428
|
|
|
|
|
|
|
if $archive !~ /[bz2|gz]$/; |
429
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
# find these binaries, we need them to inspect and expand the archive |
431
|
|
|
|
|
|
|
my $tar = $self->find_bin( 'tar', %args ); |
432
|
|
|
|
|
|
|
my $file = $self->find_bin( 'file', %args ); |
433
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
my %types = ( |
435
|
|
|
|
|
|
|
gzip => { bin => 'gunzip', content => 'gzip', }, |
436
|
|
|
|
|
|
|
bzip => { bin => 'bunzip2', content => 'b(un)?zip2', }, |
437
|
|
|
|
|
|
|
# on BSD bunzip2, on Linux bzip2 |
438
|
|
|
|
|
|
|
); |
439
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
my $type |
441
|
|
|
|
|
|
|
= $archive =~ /bz2$/ ? 'bzip' |
442
|
|
|
|
|
|
|
: $archive =~ /gz$/ ? 'gzip' |
443
|
|
|
|
|
|
|
: return $log->error( 'unknown archive type', %args); |
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
# make sure the archive contents match the file extension |
446
|
|
|
|
|
|
|
return $log->error( "$archive not a $type compressed file", %args) |
447
|
|
|
|
|
|
|
unless grep ( /$types{$type}{content}/, `$file $archive` ); |
448
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
my $bin = $self->find_bin( $types{$type}{bin}, %args); |
450
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
$self->syscmd( "$bin -c $archive | $tar -xf -" ) or return; |
452
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
$log->audit( "extracted $archive" ); |
454
|
|
|
|
|
|
|
return 1; |
455
|
|
|
|
|
|
|
} |
456
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
sub file_delete { |
458
|
|
|
|
|
|
|
my $self = shift; |
459
|
|
|
|
|
|
|
my %p = validate( @_, |
460
|
|
|
|
|
|
|
{ 'file' => { type => SCALAR }, |
461
|
|
|
|
|
|
|
'sudo' => { type => BOOLEAN, optional => 1, default => 0 }, |
462
|
|
|
|
|
|
|
%std_opts, |
463
|
|
|
|
|
|
|
} |
464
|
|
|
|
|
|
|
); |
465
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
my $file = $p{file}; |
467
|
|
|
|
|
|
|
my %args = ( debug => $p{debug}, fatal => $p{fatal} ); |
468
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
return $log->error( "$file does not exist", %args ) if !-e $file; |
470
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
if ( -w $file ) { |
472
|
|
|
|
|
|
|
$log->audit( "write permission to $file: ok" ); |
473
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
unlink $file or return $log->error( "failed to delete $file", %args ); |
475
|
|
|
|
|
|
|
|
476
|
|
|
|
|
|
|
$log->audit( "deleted: $file" ); |
477
|
|
|
|
|
|
|
return 1; |
478
|
|
|
|
|
|
|
} |
479
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
if ( !$p{sudo} ) { # all done |
481
|
|
|
|
|
|
|
return -e $file ? undef : 1; |
482
|
|
|
|
|
|
|
} |
483
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
my $err = "trying with system rm"; |
485
|
|
|
|
|
|
|
my $rm_command = $self->find_bin( "rm", %args ); |
486
|
|
|
|
|
|
|
$rm_command .= " -f $file"; |
487
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
if ( $< != 0 ) { # we're not running as root |
489
|
|
|
|
|
|
|
my $sudo = $self->sudo( %args ); |
490
|
|
|
|
|
|
|
$rm_command = "$sudo $rm_command"; |
491
|
|
|
|
|
|
|
$err .= " (sudo)"; |
492
|
|
|
|
|
|
|
} |
493
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
$self->syscmd( $rm_command, %args ) |
495
|
|
|
|
|
|
|
or return $log->error( $err, %args ); |
496
|
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
return -e $file ? 0 : 1; |
498
|
|
|
|
|
|
|
} |
499
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
sub file_is_newer { |
501
|
|
|
|
|
|
|
my $self = shift; |
502
|
|
|
|
|
|
|
my %p = validate( |
503
|
|
|
|
|
|
|
@_, |
504
|
|
|
|
|
|
|
{ f1 => { type => SCALAR }, |
505
|
|
|
|
|
|
|
f2 => { type => SCALAR }, |
506
|
|
|
|
|
|
|
%std_opts, |
507
|
|
|
|
|
|
|
} |
508
|
|
|
|
|
|
|
); |
509
|
|
|
|
|
|
|
|
510
|
|
|
|
|
|
|
my ( $file1, $file2 ) = ( $p{f1}, $p{f2} ); |
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
# get file attributes via stat |
513
|
|
|
|
|
|
|
# (dev,ino,mode,nlink,uid,gid,rdev,size,atime,mtime,ctime,blksize,blocks) |
514
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
$log->audit( "checking age of $file1 and $file2" ); |
516
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
my $stat1 = stat($file1)->mtime; |
518
|
|
|
|
|
|
|
my $stat2 = stat($file2)->mtime; |
519
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
$log->audit( "timestamps are $stat1 and $stat2"); |
521
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
return 1 if ( $stat2 > $stat1 ); |
523
|
|
|
|
|
|
|
return; |
524
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
# I could just: |
526
|
|
|
|
|
|
|
# |
527
|
|
|
|
|
|
|
# if ( stat($f1)[9] > stat($f2)[9] ) |
528
|
|
|
|
|
|
|
# |
529
|
|
|
|
|
|
|
# but that forces the reader to read the man page for stat |
530
|
|
|
|
|
|
|
# to see what's happening |
531
|
|
|
|
|
|
|
} |
532
|
|
|
|
|
|
|
|
533
|
|
|
|
|
|
|
sub file_read { |
534
|
|
|
|
|
|
|
my $self = shift; |
535
|
|
|
|
|
|
|
my $file = shift or return $log->error("missing filename in request"); |
536
|
|
|
|
|
|
|
my %p = validate( |
537
|
|
|
|
|
|
|
@_, |
538
|
|
|
|
|
|
|
{ 'max_lines' => { type => SCALAR, optional => 1 }, |
539
|
|
|
|
|
|
|
'max_length' => { type => SCALAR, optional => 1 }, |
540
|
|
|
|
|
|
|
'fatal' => { type => BOOLEAN, optional => 1, default => 1 }, |
541
|
|
|
|
|
|
|
'debug' => { type => BOOLEAN, optional => 1, default => 1 }, |
542
|
|
|
|
|
|
|
} |
543
|
|
|
|
|
|
|
); |
544
|
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
my ( $max_lines, $max_length ) = ( $p{max_lines}, $p{max_length} ); |
546
|
|
|
|
|
|
|
my %args = ( debug => $p{debug}, fatal => $p{fatal} ); |
547
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
return $log->error( "$file does not exist!", %args) if !-e $file; |
549
|
|
|
|
|
|
|
return $log->error( "$file is not readable", %args ) if !-r $file; |
550
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
open my $FILE, '<', $file or |
552
|
|
|
|
|
|
|
return $log->error( "could not open $file: $OS_ERROR", %args ); |
553
|
|
|
|
|
|
|
|
554
|
|
|
|
|
|
|
my ( $line, @lines ); |
555
|
|
|
|
|
|
|
|
556
|
|
|
|
|
|
|
if ( ! $max_lines) { |
557
|
|
|
|
|
|
|
chomp( @lines = <$FILE> ); |
558
|
|
|
|
|
|
|
close $FILE; |
559
|
|
|
|
|
|
|
return @lines; |
560
|
|
|
|
|
|
|
# TODO: make max_length work with slurp mode, without doing something ugly like |
561
|
|
|
|
|
|
|
# reading in the entire line and then truncating it. |
562
|
|
|
|
|
|
|
}; |
563
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
while ( my $i < $max_lines ) { |
565
|
|
|
|
|
|
|
if ($max_length) { $line = substr <$FILE>, 0, $max_length; } |
566
|
|
|
|
|
|
|
else { $line = <$FILE>; }; |
567
|
|
|
|
|
|
|
push @lines, $line; |
568
|
|
|
|
|
|
|
$i++; |
569
|
|
|
|
|
|
|
} |
570
|
|
|
|
|
|
|
chomp @lines; |
571
|
|
|
|
|
|
|
close $FILE; |
572
|
|
|
|
|
|
|
return @lines; |
573
|
|
|
|
|
|
|
} |
574
|
|
|
|
|
|
|
|
575
|
|
|
|
|
|
|
sub file_mode { |
576
|
|
|
|
|
|
|
my $self = shift; |
577
|
|
|
|
|
|
|
my %p = validate( |
578
|
|
|
|
|
|
|
@_, |
579
|
|
|
|
|
|
|
{ 'file' => { type => SCALAR }, |
580
|
|
|
|
|
|
|
'fatal' => { type => BOOLEAN, optional => 1, default => 1 }, |
581
|
|
|
|
|
|
|
'debug' => { type => BOOLEAN, optional => 1, default => 0 }, |
582
|
|
|
|
|
|
|
} |
583
|
|
|
|
|
|
|
); |
584
|
|
|
|
|
|
|
|
585
|
|
|
|
|
|
|
my $file = $p{file}; |
586
|
|
|
|
|
|
|
my %args = ( debug => $p{debug}, fatal => $p{fatal} ); |
587
|
|
|
|
|
|
|
|
588
|
|
|
|
|
|
|
return $log->error( "file '$file' does not exist!", %args) |
589
|
|
|
|
|
|
|
if !-e $file; |
590
|
|
|
|
|
|
|
|
591
|
|
|
|
|
|
|
# one way to get file mode (using File::mode) |
592
|
|
|
|
|
|
|
# my $raw_mode = stat($file)->[2]; |
593
|
|
|
|
|
|
|
## no critic |
594
|
|
|
|
|
|
|
my $mode = sprintf "%04o", stat($file)->[2] & 07777; |
595
|
|
|
|
|
|
|
|
596
|
|
|
|
|
|
|
# another way to get it |
597
|
|
|
|
|
|
|
# my $st = stat($file); |
598
|
|
|
|
|
|
|
# my $mode = sprintf "%lo", $st->mode & 07777; |
599
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
$log->audit( "file $file has mode: $mode" ); |
601
|
|
|
|
|
|
|
return $mode; |
602
|
|
|
|
|
|
|
} |
603
|
|
|
|
|
|
|
|
604
|
|
|
|
|
|
|
sub file_write { |
605
|
|
|
|
|
|
|
my $self = shift; |
606
|
|
|
|
|
|
|
my $file = shift or return $log->error("missing filename in request"); |
607
|
|
|
|
|
|
|
my %p = validate( |
608
|
|
|
|
|
|
|
@_, |
609
|
|
|
|
|
|
|
{ 'lines' => { type => ARRAYREF }, |
610
|
|
|
|
|
|
|
'append' => { type => BOOLEAN, optional => 1, default => 0 }, |
611
|
|
|
|
|
|
|
'mode' => { type => SCALAR, optional => 1 }, |
612
|
|
|
|
|
|
|
'fatal' => { type => BOOLEAN, optional => 1, default => $self->{fatal} }, |
613
|
|
|
|
|
|
|
'debug' => { type => BOOLEAN, optional => 1, default => $self->{debug} }, |
614
|
|
|
|
|
|
|
} |
615
|
|
|
|
|
|
|
); |
616
|
|
|
|
|
|
|
|
617
|
|
|
|
|
|
|
my $append = $p{append}; |
618
|
|
|
|
|
|
|
my $lines = $p{lines}; |
619
|
|
|
|
|
|
|
my %args = ( debug => $p{debug}, fatal => $p{fatal} ); |
620
|
|
|
|
|
|
|
|
621
|
|
|
|
|
|
|
return $log->error( "oops, $file is a directory", %args) if -d $file; |
622
|
|
|
|
|
|
|
return $log->error( "oops, $file is not writable", %args ) |
623
|
|
|
|
|
|
|
if ( ! $self->is_writable( $file, %args) ); |
624
|
|
|
|
|
|
|
|
625
|
|
|
|
|
|
|
my $m = "wrote"; |
626
|
|
|
|
|
|
|
my $write_mode = '>'; # (over)write |
627
|
|
|
|
|
|
|
|
628
|
|
|
|
|
|
|
if ( $append ) { |
629
|
|
|
|
|
|
|
$m = "appended"; |
630
|
|
|
|
|
|
|
$write_mode = '>>'; |
631
|
|
|
|
|
|
|
if ( -f $file ) { |
632
|
|
|
|
|
|
|
copy $file, "$file.tmp" or return $log->error( |
633
|
|
|
|
|
|
|
"couldn't create $file.tmp for safe append", %args ); |
634
|
|
|
|
|
|
|
}; |
635
|
|
|
|
|
|
|
}; |
636
|
|
|
|
|
|
|
|
637
|
|
|
|
|
|
|
open my $HANDLE, $write_mode, "$file.tmp" |
638
|
|
|
|
|
|
|
or return $log->error( "file_write: couldn't open $file: $!", %args ); |
639
|
|
|
|
|
|
|
|
640
|
|
|
|
|
|
|
my $c = 0; |
641
|
|
|
|
|
|
|
foreach ( @$lines ) { chomp; print $HANDLE "$_\n"; $c++ }; |
642
|
|
|
|
|
|
|
close $HANDLE or return $log->error( "couldn't close $file: $!", %args ); |
643
|
|
|
|
|
|
|
|
644
|
|
|
|
|
|
|
$log->audit( "file_write: $m $c lines to $file", %args ); |
645
|
|
|
|
|
|
|
|
646
|
|
|
|
|
|
|
move( "$file.tmp", $file ) |
647
|
|
|
|
|
|
|
or return $log->error(" unable to update $file", %args); |
648
|
|
|
|
|
|
|
|
649
|
|
|
|
|
|
|
# set file permissions mode if requested |
650
|
|
|
|
|
|
|
$self->chmod( file => $file, mode => $p{mode}, %args ) |
651
|
|
|
|
|
|
|
or return if $p{mode}; |
652
|
|
|
|
|
|
|
|
653
|
|
|
|
|
|
|
return 1; |
654
|
|
|
|
|
|
|
} |
655
|
|
|
|
|
|
|
|
656
|
|
|
|
|
|
|
sub files_diff { |
657
|
|
|
|
|
|
|
my $self = shift; |
658
|
|
|
|
|
|
|
my %p = validate( |
659
|
|
|
|
|
|
|
@_, |
660
|
|
|
|
|
|
|
{ f1 => { type => SCALAR }, |
661
|
|
|
|
|
|
|
f2 => { type => SCALAR }, |
662
|
|
|
|
|
|
|
type => { type => SCALAR, optional => 1, default => 'text' }, |
663
|
|
|
|
|
|
|
fatal => { type => BOOLEAN, optional => 1, default => $self->{fatal} }, |
664
|
|
|
|
|
|
|
debug => { type => BOOLEAN, optional => 1, default => $self->{debug} }, |
665
|
|
|
|
|
|
|
} |
666
|
|
|
|
|
|
|
); |
667
|
|
|
|
|
|
|
|
668
|
|
|
|
|
|
|
my ( $f1, $f2, $type, $debug ) = ( $p{f1}, $p{f2}, $p{type}, $p{debug} ); |
669
|
|
|
|
|
|
|
my %args = ( debug => $p{debug}, fatal => $p{fatal} ); |
670
|
|
|
|
|
|
|
|
671
|
|
|
|
|
|
|
if ( !-e $f1 || !-e $f2 ) { |
672
|
|
|
|
|
|
|
$log->error( "$f1 or $f2 does not exist!", %args ); |
673
|
|
|
|
|
|
|
return -1; |
674
|
|
|
|
|
|
|
}; |
675
|
|
|
|
|
|
|
|
676
|
|
|
|
|
|
|
return $self->files_diff_md5( $f1, $f2, \%args) |
677
|
|
|
|
|
|
|
if $type ne "text"; |
678
|
|
|
|
|
|
|
|
679
|
|
|
|
|
|
|
### TODO |
680
|
|
|
|
|
|
|
# use file here to make sure files are ASCII |
681
|
|
|
|
|
|
|
# |
682
|
|
|
|
|
|
|
$log->audit("comparing ascii files $f1 and $f2 using diff", %args); |
683
|
|
|
|
|
|
|
|
684
|
|
|
|
|
|
|
my $diff = $self->find_bin( 'diff', %args ); |
685
|
|
|
|
|
|
|
my $r = `$diff $f1 $f2`; |
686
|
|
|
|
|
|
|
chomp $r; |
687
|
|
|
|
|
|
|
return $r; |
688
|
|
|
|
|
|
|
}; |
689
|
|
|
|
|
|
|
|
690
|
|
|
|
|
|
|
sub files_diff_md5 { |
691
|
|
|
|
|
|
|
my $self = shift; |
692
|
|
|
|
|
|
|
my ($f1, $f2, $args) = @_; |
693
|
|
|
|
|
|
|
|
694
|
|
|
|
|
|
|
$log->audit("comparing $f1 and $f2 using md5", %$args); |
695
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
eval { require Digest::MD5 }; |
697
|
|
|
|
|
|
|
return $log->error( "couldn't load Digest::MD5!", %$args ) |
698
|
|
|
|
|
|
|
if $EVAL_ERROR; |
699
|
|
|
|
|
|
|
|
700
|
|
|
|
|
|
|
$log->audit( "\t Digest::MD5 loaded", %$args ); |
701
|
|
|
|
|
|
|
|
702
|
|
|
|
|
|
|
my @md5sums; |
703
|
|
|
|
|
|
|
|
704
|
|
|
|
|
|
|
foreach my $f ( $f1, $f2 ) { |
705
|
|
|
|
|
|
|
my ( $sum, $changed ); |
706
|
|
|
|
|
|
|
|
707
|
|
|
|
|
|
|
# if the md5 file exists |
708
|
|
|
|
|
|
|
if ( -f "$f.md5" ) { |
709
|
|
|
|
|
|
|
$sum = $self->file_read( "$f.md5", %$args ); |
710
|
|
|
|
|
|
|
$log->audit( " md5 file for $f exists", %$args ); |
711
|
|
|
|
|
|
|
} |
712
|
|
|
|
|
|
|
|
713
|
|
|
|
|
|
|
# if the md5 file is missing, invalid, or older than the file, recompute it |
714
|
|
|
|
|
|
|
if ( ! -f "$f.md5" or $sum !~ /[0-9a-f]+/i or |
715
|
|
|
|
|
|
|
$self->file_is_newer( f1 => "$f.md5", f2 => $f, %$args ) |
716
|
|
|
|
|
|
|
) |
717
|
|
|
|
|
|
|
{ |
718
|
|
|
|
|
|
|
my $ctx = Digest::MD5->new; |
719
|
|
|
|
|
|
|
open my $FILE, '<', $f; |
720
|
|
|
|
|
|
|
$ctx->addfile(*$FILE); |
721
|
|
|
|
|
|
|
$sum = $ctx->hexdigest; |
722
|
|
|
|
|
|
|
close $FILE; |
723
|
|
|
|
|
|
|
$changed++; |
724
|
|
|
|
|
|
|
$log->audit(" calculated md5: $sum", %$args); |
725
|
|
|
|
|
|
|
} |
726
|
|
|
|
|
|
|
|
727
|
|
|
|
|
|
|
push( @md5sums, $sum ); |
728
|
|
|
|
|
|
|
$self->file_write( "$f.md5", lines => [$sum], %$args ) if $changed; |
729
|
|
|
|
|
|
|
} |
730
|
|
|
|
|
|
|
|
731
|
|
|
|
|
|
|
return if $md5sums[0] eq $md5sums[1]; |
732
|
|
|
|
|
|
|
return 1; |
733
|
|
|
|
|
|
|
} |
734
|
|
|
|
|
|
|
|
735
|
|
|
|
|
|
|
sub find_bin { |
736
|
|
|
|
|
|
|
my $self = shift; |
737
|
|
|
|
|
|
|
my $bin = shift or die "missing argument to find_bin\n"; |
738
|
|
|
|
|
|
|
my %p = validate( |
739
|
|
|
|
|
|
|
@_, |
740
|
|
|
|
|
|
|
{ 'dir' => { type => SCALAR, optional => 1, }, |
741
|
|
|
|
|
|
|
%std_opts, |
742
|
|
|
|
|
|
|
}, |
743
|
|
|
|
|
|
|
); |
744
|
|
|
|
|
|
|
|
745
|
|
|
|
|
|
|
my $prefix = "/usr/local"; |
746
|
|
|
|
|
|
|
my %args = ( debug => $p{debug}, fatal => $p{fatal} ); |
747
|
|
|
|
|
|
|
|
748
|
|
|
|
|
|
|
if ( $bin =~ /^\// && -x $bin ) { # we got a full path |
749
|
|
|
|
|
|
|
$log->audit( "find_bin: found $bin", %args ); |
750
|
|
|
|
|
|
|
return $bin; |
751
|
|
|
|
|
|
|
}; |
752
|
|
|
|
|
|
|
|
753
|
|
|
|
|
|
|
my @prefixes; |
754
|
|
|
|
|
|
|
push @prefixes, $p{dir} if $p{dir}; |
755
|
|
|
|
|
|
|
push @prefixes, qw" |
756
|
|
|
|
|
|
|
/usr/local/bin /usr/local/sbin/ /opt/local/bin /opt/local/sbin |
757
|
|
|
|
|
|
|
$prefix/mysql/bin /bin /usr/bin /sbin /usr/sbin |
758
|
|
|
|
|
|
|
"; |
759
|
|
|
|
|
|
|
push @prefixes, cwd; |
760
|
|
|
|
|
|
|
|
761
|
|
|
|
|
|
|
my $found; |
762
|
|
|
|
|
|
|
foreach my $prefix ( @prefixes ) { |
763
|
|
|
|
|
|
|
if ( -x "$prefix/$bin" ) { |
764
|
|
|
|
|
|
|
$found = "$prefix/$bin" and last; |
765
|
|
|
|
|
|
|
}; |
766
|
|
|
|
|
|
|
}; |
767
|
|
|
|
|
|
|
|
768
|
|
|
|
|
|
|
if ($found) { |
769
|
|
|
|
|
|
|
$log->audit( "find_bin: found $found", %args); |
770
|
|
|
|
|
|
|
return $found; |
771
|
|
|
|
|
|
|
} |
772
|
|
|
|
|
|
|
|
773
|
|
|
|
|
|
|
return $log->error( "find_bin: could not find $bin", %args); |
774
|
|
|
|
|
|
|
} |
775
|
|
|
|
|
|
|
|
776
|
|
|
|
|
|
|
sub fstab_list { |
777
|
|
|
|
|
|
|
my $self = shift; |
778
|
|
|
|
|
|
|
my %p = validate( |
779
|
|
|
|
|
|
|
@_, |
780
|
|
|
|
|
|
|
{ 'fatal' => { type => BOOLEAN, optional => 1, default => 1 }, |
781
|
|
|
|
|
|
|
'debug' => { type => BOOLEAN, optional => 1, default => 1 }, |
782
|
|
|
|
|
|
|
} |
783
|
|
|
|
|
|
|
); |
784
|
|
|
|
|
|
|
|
785
|
|
|
|
|
|
|
if ( $OSNAME eq "darwin" ) { |
786
|
|
|
|
|
|
|
return ['fstab not used on Darwin!']; |
787
|
|
|
|
|
|
|
} |
788
|
|
|
|
|
|
|
|
789
|
|
|
|
|
|
|
my $fstab = "/etc/fstab"; |
790
|
|
|
|
|
|
|
if ( !-e $fstab ) { |
791
|
|
|
|
|
|
|
print "fstab_list: FAILURE: $fstab does not exist!\n" if $p{debug}; |
792
|
|
|
|
|
|
|
return; |
793
|
|
|
|
|
|
|
} |
794
|
|
|
|
|
|
|
|
795
|
|
|
|
|
|
|
my $grep = $self->find_bin( "grep", debug => 0 ); |
796
|
|
|
|
|
|
|
my @fstabs = `$grep -v cdr $fstab`; |
797
|
|
|
|
|
|
|
|
798
|
|
|
|
|
|
|
# foreach my $fstab (@fstabs) |
799
|
|
|
|
|
|
|
# {} |
800
|
|
|
|
|
|
|
# my @fields = split(" ", $fstab); |
801
|
|
|
|
|
|
|
# #print "device: $fields[0] mount: $fields[1]\n"; |
802
|
|
|
|
|
|
|
# {}; |
803
|
|
|
|
|
|
|
# print "\n\n END of fstabs\n\n"; |
804
|
|
|
|
|
|
|
|
805
|
|
|
|
|
|
|
return \@fstabs; |
806
|
|
|
|
|
|
|
} |
807
|
|
|
|
|
|
|
|
808
|
|
|
|
|
|
|
sub get_cpan_config { |
809
|
|
|
|
|
|
|
|
810
|
|
|
|
|
|
|
my $ftp = `which ftp`; chomp $ftp; |
811
|
|
|
|
|
|
|
my $gzip = `which gzip`; chomp $gzip; |
812
|
|
|
|
|
|
|
my $unzip = `which unzip`; chomp $unzip; |
813
|
|
|
|
|
|
|
my $tar = `which tar`; chomp $tar; |
814
|
|
|
|
|
|
|
my $make = `which make`; chomp $make; |
815
|
|
|
|
|
|
|
my $wget = `which wget`; chomp $wget; |
816
|
|
|
|
|
|
|
|
817
|
|
|
|
|
|
|
return |
818
|
|
|
|
|
|
|
{ |
819
|
|
|
|
|
|
|
'build_cache' => q[10], |
820
|
|
|
|
|
|
|
'build_dir' => qq[$ENV{HOME}/.cpan/build], |
821
|
|
|
|
|
|
|
'cache_metadata' => q[1], |
822
|
|
|
|
|
|
|
'cpan_home' => qq[$ENV{HOME}/.cpan], |
823
|
|
|
|
|
|
|
'ftp' => $ftp, |
824
|
|
|
|
|
|
|
'ftp_proxy' => q[], |
825
|
|
|
|
|
|
|
'getcwd' => q[cwd], |
826
|
|
|
|
|
|
|
'gpg' => q[], |
827
|
|
|
|
|
|
|
'gzip' => $gzip, |
828
|
|
|
|
|
|
|
'histfile' => qq[$ENV{HOME}/.cpan/histfile], |
829
|
|
|
|
|
|
|
'histsize' => q[100], |
830
|
|
|
|
|
|
|
'http_proxy' => q[], |
831
|
|
|
|
|
|
|
'inactivity_timeout' => q[5], |
832
|
|
|
|
|
|
|
'index_expire' => q[1], |
833
|
|
|
|
|
|
|
'inhibit_startup_message' => q[1], |
834
|
|
|
|
|
|
|
'keep_source_where' => qq[$ENV{HOME}/.cpan/sources], |
835
|
|
|
|
|
|
|
'lynx' => q[], |
836
|
|
|
|
|
|
|
'make' => $make, |
837
|
|
|
|
|
|
|
'make_arg' => q[], |
838
|
|
|
|
|
|
|
'make_install_arg' => q[], |
839
|
|
|
|
|
|
|
'makepl_arg' => q[], |
840
|
|
|
|
|
|
|
'ncftp' => q[], |
841
|
|
|
|
|
|
|
'ncftpget' => q[], |
842
|
|
|
|
|
|
|
'no_proxy' => q[], |
843
|
|
|
|
|
|
|
'pager' => q[less], |
844
|
|
|
|
|
|
|
'prerequisites_policy' => q[follow], |
845
|
|
|
|
|
|
|
'scan_cache' => q[atstart], |
846
|
|
|
|
|
|
|
'shell' => q[/bin/csh], |
847
|
|
|
|
|
|
|
'tar' => $tar, |
848
|
|
|
|
|
|
|
'term_is_latin' => q[1], |
849
|
|
|
|
|
|
|
'unzip' => $unzip, |
850
|
|
|
|
|
|
|
'urllist' => [ 'http://www.perl.com/CPAN/', 'ftp://cpan.cs.utah.edu/pub/CPAN/', 'ftp://mirrors.kernel.org/pub/CPAN', 'ftp://osl.uoregon.edu/CPAN/', 'http://cpan.yahoo.com/' ], |
851
|
|
|
|
|
|
|
'wget' => $wget, |
852
|
|
|
|
|
|
|
}; |
853
|
|
|
|
|
|
|
|
854
|
|
|
|
|
|
|
} |
855
|
|
|
|
|
|
|
|
856
|
|
|
|
|
|
|
sub get_dir_files { |
857
|
|
|
|
|
|
|
my $self = shift; |
858
|
|
|
|
|
|
|
my %p = validate( |
859
|
|
|
|
|
|
|
@_, |
860
|
|
|
|
|
|
|
{ 'dir' => { type => SCALAR, optional => 0, }, |
861
|
|
|
|
|
|
|
'fatal' => { type => BOOLEAN, optional => 1, default => 1 }, |
862
|
|
|
|
|
|
|
'debug' => { type => BOOLEAN, optional => 1, default => 1 }, |
863
|
|
|
|
|
|
|
} |
864
|
|
|
|
|
|
|
); |
865
|
|
|
|
|
|
|
|
866
|
|
|
|
|
|
|
my ( $dir, $fatal, $debug ) = ( $p{dir}, $p{fatal}, $p{debug} ); |
867
|
|
|
|
|
|
|
my %args = ( debug => $p{debug}, fatal => $p{fatal} ); |
868
|
|
|
|
|
|
|
|
869
|
|
|
|
|
|
|
my @files; |
870
|
|
|
|
|
|
|
|
871
|
|
|
|
|
|
|
return $log->error( "dir $dir is not a directory!", %args) |
872
|
|
|
|
|
|
|
if ! -d $dir; |
873
|
|
|
|
|
|
|
|
874
|
|
|
|
|
|
|
opendir D, $dir or return $log->error( "couldn't open $dir: $!", %args ); |
875
|
|
|
|
|
|
|
|
876
|
|
|
|
|
|
|
while ( defined( my $f = readdir(D) ) ) { |
877
|
|
|
|
|
|
|
next if $f =~ /^\.\.?$/; |
878
|
|
|
|
|
|
|
push @files, "$dir/$f"; |
879
|
|
|
|
|
|
|
} |
880
|
|
|
|
|
|
|
|
881
|
|
|
|
|
|
|
closedir(D); |
882
|
|
|
|
|
|
|
|
883
|
|
|
|
|
|
|
return @files; |
884
|
|
|
|
|
|
|
} |
885
|
|
|
|
|
|
|
|
886
|
|
|
|
|
|
|
sub get_my_ips { |
887
|
|
|
|
|
|
|
|
888
|
|
|
|
|
|
|
############################################ |
889
|
|
|
|
|
|
|
# Usage : @list_of_ips_ref = $util->get_my_ips(); |
890
|
|
|
|
|
|
|
# Purpose : get a list of IP addresses on local interfaces |
891
|
|
|
|
|
|
|
# Returns : an arrayref of IP addresses |
892
|
|
|
|
|
|
|
# Parameters : only - can be one of: first, last |
893
|
|
|
|
|
|
|
# : exclude_locahost (all 127.0 addresses) |
894
|
|
|
|
|
|
|
# : exclude_internals (192.168, 10., 169., 172.) |
895
|
|
|
|
|
|
|
# : exclude_ipv6 |
896
|
|
|
|
|
|
|
# Comments : exclude options are boolean and enabled by default. |
897
|
|
|
|
|
|
|
# tested on Mac OS X and FreeBSD |
898
|
|
|
|
|
|
|
|
899
|
|
|
|
|
|
|
my $self = shift; |
900
|
|
|
|
|
|
|
my %p = validate( |
901
|
|
|
|
|
|
|
@_, |
902
|
|
|
|
|
|
|
{ 'only' => { type => SCALAR, optional => 1, default => 0 }, |
903
|
|
|
|
|
|
|
'exclude_localhost' => |
904
|
|
|
|
|
|
|
{ type => BOOLEAN, optional => 1, default => 1 }, |
905
|
|
|
|
|
|
|
'exclude_internals' => |
906
|
|
|
|
|
|
|
{ type => BOOLEAN, optional => 1, default => 1 }, |
907
|
|
|
|
|
|
|
'exclude_ipv6' => |
908
|
|
|
|
|
|
|
{ type => BOOLEAN, optional => 1, default => 1 }, |
909
|
|
|
|
|
|
|
'fatal' => { type => BOOLEAN, optional => 1, default => 1 }, |
910
|
|
|
|
|
|
|
'debug' => { type => BOOLEAN, optional => 1, default => 1 }, |
911
|
|
|
|
|
|
|
} |
912
|
|
|
|
|
|
|
); |
913
|
|
|
|
|
|
|
|
914
|
|
|
|
|
|
|
my $debug = $p{debug}; |
915
|
|
|
|
|
|
|
my $only = $p{only}; |
916
|
|
|
|
|
|
|
|
917
|
|
|
|
|
|
|
my $ifconfig = $self->find_bin( "ifconfig", debug => 0 ); |
918
|
|
|
|
|
|
|
|
919
|
|
|
|
|
|
|
my $once = 0; |
920
|
|
|
|
|
|
|
|
921
|
|
|
|
|
|
|
TRY: |
922
|
|
|
|
|
|
|
my @ips = grep {/inet/} `$ifconfig`; chomp @ips; |
923
|
|
|
|
|
|
|
@ips = grep {!/inet6/} @ips if $p{exclude_ipv6}; |
924
|
|
|
|
|
|
|
@ips = grep {!/inet 127\.0\.0/} @ips if $p{exclude_localhost}; |
925
|
|
|
|
|
|
|
@ips = grep {!/inet (192\.168\.|10\.|172\.16\.|169\.254\.)/} @ips |
926
|
|
|
|
|
|
|
if $p{exclude_internals}; |
927
|
|
|
|
|
|
|
|
928
|
|
|
|
|
|
|
# this keeps us from failing if the box has only internal IPs |
929
|
|
|
|
|
|
|
if ( @ips < 1 || $ips[0] eq "" ) { |
930
|
|
|
|
|
|
|
warn "you really don't have any public IPs?!" if $debug; |
931
|
|
|
|
|
|
|
$p{exclude_internals} = 0; |
932
|
|
|
|
|
|
|
$once++; |
933
|
|
|
|
|
|
|
goto TRY if ( $once < 2 ); |
934
|
|
|
|
|
|
|
} |
935
|
|
|
|
|
|
|
|
936
|
|
|
|
|
|
|
foreach ( @ips ) { ($_) = $_ =~ m/inet ([\d\.]+)\s/; }; |
937
|
|
|
|
|
|
|
|
938
|
|
|
|
|
|
|
return [ $ips[0] ] if $only eq 'first'; |
939
|
|
|
|
|
|
|
return [ $ips[-1] ] if $only eq 'last'; |
940
|
|
|
|
|
|
|
return \@ips; |
941
|
|
|
|
|
|
|
} |
942
|
|
|
|
|
|
|
|
943
|
|
|
|
|
|
|
sub get_the_date { |
944
|
|
|
|
|
|
|
my $self = shift; |
945
|
|
|
|
|
|
|
my %p = validate( |
946
|
|
|
|
|
|
|
@_, |
947
|
|
|
|
|
|
|
{ 'bump' => { type => SCALAR, optional => 1, }, |
948
|
|
|
|
|
|
|
'fatal' => { type => BOOLEAN, optional => 1, default => $self->{fatal} }, |
949
|
|
|
|
|
|
|
'debug' => { type => BOOLEAN, optional => 1, default => $self->{debug} }, |
950
|
|
|
|
|
|
|
} |
951
|
|
|
|
|
|
|
); |
952
|
|
|
|
|
|
|
|
953
|
|
|
|
|
|
|
my $bump = $p{bump} || 0; |
954
|
|
|
|
|
|
|
my %args = ( debug => $p{debug}, fatal => $p{fatal} ); |
955
|
|
|
|
|
|
|
|
956
|
|
|
|
|
|
|
my $time = time; |
957
|
|
|
|
|
|
|
my $mess = "get_the_date time: " . time; |
958
|
|
|
|
|
|
|
|
959
|
|
|
|
|
|
|
$bump = $bump * 86400 if $bump; |
960
|
|
|
|
|
|
|
my $offset_time = time - $bump; |
961
|
|
|
|
|
|
|
$mess .= ", (selected $offset_time)" if $time != $offset_time; |
962
|
|
|
|
|
|
|
|
963
|
|
|
|
|
|
|
# load Date::Format to get the time2str function |
964
|
|
|
|
|
|
|
eval { require Date::Format }; |
965
|
|
|
|
|
|
|
if ( !$EVAL_ERROR ) { |
966
|
|
|
|
|
|
|
|
967
|
|
|
|
|
|
|
my $ss = Date::Format::time2str( "%S", ($offset_time) ); |
968
|
|
|
|
|
|
|
my $mn = Date::Format::time2str( "%M", ($offset_time) ); |
969
|
|
|
|
|
|
|
my $hh = Date::Format::time2str( "%H", ($offset_time) ); |
970
|
|
|
|
|
|
|
my $dd = Date::Format::time2str( "%d", ($offset_time) ); |
971
|
|
|
|
|
|
|
my $mm = Date::Format::time2str( "%m", ($offset_time) ); |
972
|
|
|
|
|
|
|
my $yy = Date::Format::time2str( "%Y", ($offset_time) ); |
973
|
|
|
|
|
|
|
my $lm = Date::Format::time2str( "%m", ( $offset_time - 2592000 ) ); |
974
|
|
|
|
|
|
|
|
975
|
|
|
|
|
|
|
$log->audit( "$mess, $yy/$mm/$dd $hh:$mn", %args); |
976
|
|
|
|
|
|
|
return $dd, $mm, $yy, $lm, $hh, $mn, $ss; |
977
|
|
|
|
|
|
|
} |
978
|
|
|
|
|
|
|
|
979
|
|
|
|
|
|
|
# 0 1 2 3 4 5 6 7 8 |
980
|
|
|
|
|
|
|
# ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = |
981
|
|
|
|
|
|
|
# localtime(time); |
982
|
|
|
|
|
|
|
# 4 = month + 1 ( see perldoc localtime) |
983
|
|
|
|
|
|
|
# 5 = year + 1900 "" |
984
|
|
|
|
|
|
|
|
985
|
|
|
|
|
|
|
my @fields = localtime($offset_time); |
986
|
|
|
|
|
|
|
|
987
|
|
|
|
|
|
|
my $ss = sprintf( "%02i", $fields[0] ); # seconds |
988
|
|
|
|
|
|
|
my $mn = sprintf( "%02i", $fields[1] ); # minutes |
989
|
|
|
|
|
|
|
my $hh = sprintf( "%02i", $fields[2] ); # hours (24 hour clock) |
990
|
|
|
|
|
|
|
|
991
|
|
|
|
|
|
|
my $dd = sprintf( "%02i", $fields[3] ); # day of month |
992
|
|
|
|
|
|
|
my $mm = sprintf( "%02i", $fields[4] + 1 ); # month |
993
|
|
|
|
|
|
|
my $yy = ( $fields[5] + 1900 ); # year |
994
|
|
|
|
|
|
|
|
995
|
|
|
|
|
|
|
$log->audit( "$mess, $yy/$mm/$dd $hh:$mn", %args ); |
996
|
|
|
|
|
|
|
return $dd, $mm, $yy, undef, $hh, $mn, $ss; |
997
|
|
|
|
|
|
|
} |
998
|
|
|
|
|
|
|
|
999
|
|
|
|
|
|
|
sub get_mounted_drives { |
1000
|
|
|
|
|
|
|
my $self = shift; |
1001
|
|
|
|
|
|
|
my %p = validate( @_, { %std_opts } ); |
1002
|
|
|
|
|
|
|
my %args = ( debug => $p{debug}, fatal => $p{fatal} ); |
1003
|
|
|
|
|
|
|
|
1004
|
|
|
|
|
|
|
my $mount = $self->find_bin( 'mount', %args ); |
1005
|
|
|
|
|
|
|
|
1006
|
|
|
|
|
|
|
-x $mount or return $log->error( "I couldn't find mount!", %args ); |
1007
|
|
|
|
|
|
|
|
1008
|
|
|
|
|
|
|
$ENV{PATH} = ""; |
1009
|
|
|
|
|
|
|
my %hash; |
1010
|
|
|
|
|
|
|
foreach (`$mount`) { |
1011
|
|
|
|
|
|
|
my ( $d, $m ) = $_ =~ /^(.*) on (.*) \(/; |
1012
|
|
|
|
|
|
|
|
1013
|
|
|
|
|
|
|
#if ( $m =~ /^\// && $d =~ /^\// ) # mount drives that begin with / |
1014
|
|
|
|
|
|
|
if ( $m && $m =~ /^\// ) { # only mounts that begin with / |
1015
|
|
|
|
|
|
|
$log->audit( "adding: $m \t $d" ) if $p{debug}; |
1016
|
|
|
|
|
|
|
$hash{$m} = $d; |
1017
|
|
|
|
|
|
|
} |
1018
|
|
|
|
|
|
|
} |
1019
|
|
|
|
|
|
|
return \%hash; |
1020
|
|
|
|
|
|
|
} |
1021
|
|
|
|
|
|
|
|
1022
|
|
|
|
|
|
|
sub get_url { |
1023
|
|
|
|
|
|
|
my $self = shift; |
1024
|
|
|
|
|
|
|
my $url = shift; |
1025
|
|
|
|
|
|
|
my %p = validate( |
1026
|
|
|
|
|
|
|
@_, |
1027
|
|
|
|
|
|
|
{ dir => { type => SCALAR, optional => 1 }, |
1028
|
|
|
|
|
|
|
timeout => { type => SCALAR, optional => 1 }, |
1029
|
|
|
|
|
|
|
%std_opts, |
1030
|
|
|
|
|
|
|
} |
1031
|
|
|
|
|
|
|
); |
1032
|
|
|
|
|
|
|
|
1033
|
|
|
|
|
|
|
my $dir = $p{dir}; |
1034
|
|
|
|
|
|
|
my %args = ( debug => $p{debug}, fatal => $p{fatal} ); |
1035
|
|
|
|
|
|
|
|
1036
|
|
|
|
|
|
|
my ($ua, $response); |
1037
|
|
|
|
|
|
|
## no critic ( ProhibitStringyEval ) |
1038
|
|
|
|
|
|
|
eval "require LWP::Simple"; |
1039
|
|
|
|
|
|
|
## use critic |
1040
|
|
|
|
|
|
|
return $self->get_url_system( $url, %p ) if $EVAL_ERROR; |
1041
|
|
|
|
|
|
|
|
1042
|
|
|
|
|
|
|
my $uri = URI->new($url); |
1043
|
|
|
|
|
|
|
my @parts = $uri->path_segments; |
1044
|
|
|
|
|
|
|
my $file = $parts[-1]; # everything after the last / in the URL |
1045
|
|
|
|
|
|
|
my $file_path = $file; |
1046
|
|
|
|
|
|
|
$file_path = "$dir/$file" if $dir; |
1047
|
|
|
|
|
|
|
|
1048
|
|
|
|
|
|
|
$log->audit( "fetching $url" ); |
1049
|
|
|
|
|
|
|
eval { $response = LWP::Simple::mirror($url, $file_path ); }; |
1050
|
|
|
|
|
|
|
|
1051
|
|
|
|
|
|
|
if ( $response ) { |
1052
|
|
|
|
|
|
|
if ( $response == 404 ) { |
1053
|
|
|
|
|
|
|
return $log->error( "file not found ($url)", %args ); |
1054
|
|
|
|
|
|
|
} |
1055
|
|
|
|
|
|
|
elsif ($response == 304 ) { |
1056
|
|
|
|
|
|
|
$log->audit( "result 304: file is up-to-date" ); |
1057
|
|
|
|
|
|
|
} |
1058
|
|
|
|
|
|
|
elsif ( $response == 200 ) { |
1059
|
|
|
|
|
|
|
$log->audit( "result 200: file download ok" ); |
1060
|
|
|
|
|
|
|
} |
1061
|
|
|
|
|
|
|
else { |
1062
|
|
|
|
|
|
|
$log->error( "unhandled response: $response", fatal => 0 ); |
1063
|
|
|
|
|
|
|
}; |
1064
|
|
|
|
|
|
|
}; |
1065
|
|
|
|
|
|
|
|
1066
|
|
|
|
|
|
|
return if ! -e $file_path; |
1067
|
|
|
|
|
|
|
return $response; |
1068
|
|
|
|
|
|
|
} |
1069
|
|
|
|
|
|
|
|
1070
|
|
|
|
|
|
|
sub get_url_system { |
1071
|
|
|
|
|
|
|
my $self = shift; |
1072
|
|
|
|
|
|
|
my $url = shift; |
1073
|
|
|
|
|
|
|
my %p = validate( |
1074
|
|
|
|
|
|
|
@_, |
1075
|
|
|
|
|
|
|
{ dir => { type => SCALAR, optional => 1 }, |
1076
|
|
|
|
|
|
|
timeout => { type => SCALAR, optional => 1, }, |
1077
|
|
|
|
|
|
|
%std_opts, |
1078
|
|
|
|
|
|
|
} |
1079
|
|
|
|
|
|
|
); |
1080
|
|
|
|
|
|
|
|
1081
|
|
|
|
|
|
|
my $dir = $p{dir}; |
1082
|
|
|
|
|
|
|
my $debug = $p{debug}; |
1083
|
|
|
|
|
|
|
my %args = ( debug => $p{debug}, fatal => $p{fatal} ); |
1084
|
|
|
|
|
|
|
|
1085
|
|
|
|
|
|
|
my ($fetchbin, $found); |
1086
|
|
|
|
|
|
|
if ( $OSNAME eq "freebsd" ) { |
1087
|
|
|
|
|
|
|
$fetchbin = $self->find_bin( 'fetch', %args); |
1088
|
|
|
|
|
|
|
if ( $fetchbin && -x $fetchbin ) { |
1089
|
|
|
|
|
|
|
$found = $fetchbin; |
1090
|
|
|
|
|
|
|
$found .= " -q" if !$debug; |
1091
|
|
|
|
|
|
|
} |
1092
|
|
|
|
|
|
|
} |
1093
|
|
|
|
|
|
|
elsif ( $OSNAME eq "darwin" ) { |
1094
|
|
|
|
|
|
|
$fetchbin = $self->find_bin( 'curl', %args ); |
1095
|
|
|
|
|
|
|
if ( $fetchbin && -x $fetchbin ) { |
1096
|
|
|
|
|
|
|
$found = "$fetchbin -O"; |
1097
|
|
|
|
|
|
|
$found .= " -s " if !$debug; |
1098
|
|
|
|
|
|
|
} |
1099
|
|
|
|
|
|
|
} |
1100
|
|
|
|
|
|
|
|
1101
|
|
|
|
|
|
|
if ( !$found ) { |
1102
|
|
|
|
|
|
|
$fetchbin = $self->find_bin( 'wget', %args); |
1103
|
|
|
|
|
|
|
$found = $fetchbin if $fetchbin && -x $fetchbin; |
1104
|
|
|
|
|
|
|
} |
1105
|
|
|
|
|
|
|
|
1106
|
|
|
|
|
|
|
return $log->error( "Failed to fetch $url.\n\tCouldn't find wget. Please install it.", %args ) |
1107
|
|
|
|
|
|
|
if !$found; |
1108
|
|
|
|
|
|
|
|
1109
|
|
|
|
|
|
|
my $fetchcmd = "$found $url"; |
1110
|
|
|
|
|
|
|
|
1111
|
|
|
|
|
|
|
my $timeout = $p{timeout} || 0; |
1112
|
|
|
|
|
|
|
if ( ! $timeout ) { |
1113
|
|
|
|
|
|
|
$self->syscmd( $fetchcmd, %args ) or return; |
1114
|
|
|
|
|
|
|
my $uri = URI->new($url); |
1115
|
|
|
|
|
|
|
my @parts = $uri->path_segments; |
1116
|
|
|
|
|
|
|
my $file = $parts[-1]; # everything after the last / in the URL |
1117
|
|
|
|
|
|
|
if ( -e $file && $dir && -d $dir ) { |
1118
|
|
|
|
|
|
|
$log->audit("moving file $file to $dir" ); |
1119
|
|
|
|
|
|
|
move $file, "$dir/$file"; |
1120
|
|
|
|
|
|
|
return 1; |
1121
|
|
|
|
|
|
|
}; |
1122
|
|
|
|
|
|
|
}; |
1123
|
|
|
|
|
|
|
|
1124
|
|
|
|
|
|
|
my $r; |
1125
|
|
|
|
|
|
|
eval { |
1126
|
|
|
|
|
|
|
local $SIG{ALRM} = sub { die "alarm\n" }; |
1127
|
|
|
|
|
|
|
alarm $timeout; |
1128
|
|
|
|
|
|
|
$r = $self->syscmd( $fetchcmd, %args ); |
1129
|
|
|
|
|
|
|
alarm 0; |
1130
|
|
|
|
|
|
|
}; |
1131
|
|
|
|
|
|
|
|
1132
|
|
|
|
|
|
|
if ($EVAL_ERROR) { # propagate unexpected errors |
1133
|
|
|
|
|
|
|
print "timed out!\n" if $EVAL_ERROR eq "alarm\n"; |
1134
|
|
|
|
|
|
|
return $log->error( $EVAL_ERROR, %args ); |
1135
|
|
|
|
|
|
|
} |
1136
|
|
|
|
|
|
|
|
1137
|
|
|
|
|
|
|
return $log->error( "error executing $fetchcmd", %args) if !$r; |
1138
|
|
|
|
|
|
|
return 1; |
1139
|
|
|
|
|
|
|
} |
1140
|
|
|
|
|
|
|
|
1141
|
|
|
|
|
|
|
sub install_if_changed { |
1142
|
|
|
|
|
|
|
my $self = shift; |
1143
|
|
|
|
|
|
|
my %p = validate( |
1144
|
|
|
|
|
|
|
@_, |
1145
|
|
|
|
|
|
|
{ newfile => { type => SCALAR, optional => 0, }, |
1146
|
|
|
|
|
|
|
existing=> { type => SCALAR, optional => 0, }, |
1147
|
|
|
|
|
|
|
mode => { type => SCALAR, optional => 1, }, |
1148
|
|
|
|
|
|
|
uid => { type => SCALAR, optional => 1, }, |
1149
|
|
|
|
|
|
|
gid => { type => SCALAR, optional => 1, }, |
1150
|
|
|
|
|
|
|
sudo => { type => BOOLEAN, optional => 1, default => 0 }, |
1151
|
|
|
|
|
|
|
notify => { type => BOOLEAN, optional => 1, }, |
1152
|
|
|
|
|
|
|
email => { type => SCALAR, optional => 1, default => 'postmaster' }, |
1153
|
|
|
|
|
|
|
clean => { type => BOOLEAN, optional => 1, default => 1 }, |
1154
|
|
|
|
|
|
|
archive => { type => BOOLEAN, optional => 1, default => 0 }, |
1155
|
|
|
|
|
|
|
fatal => { type => BOOLEAN, optional => 1, default => $self->{fatal} }, |
1156
|
|
|
|
|
|
|
debug => { type => BOOLEAN, optional => 1, default => $self->{debug} }, |
1157
|
|
|
|
|
|
|
}, |
1158
|
|
|
|
|
|
|
); |
1159
|
|
|
|
|
|
|
|
1160
|
|
|
|
|
|
|
my ( $newfile, $existing, $mode, $uid, $gid, $email) = ( |
1161
|
|
|
|
|
|
|
$p{newfile}, $p{existing}, $p{mode}, $p{uid}, $p{gid}, $p{email} ); |
1162
|
|
|
|
|
|
|
my ($debug, $sudo, $notify ) = ($p{debug}, $p{sudo}, $p{notify} ); |
1163
|
|
|
|
|
|
|
my %args = ( debug => $p{debug}, fatal => $p{fatal} ); |
1164
|
|
|
|
|
|
|
|
1165
|
|
|
|
|
|
|
if ( $newfile !~ /\// ) { |
1166
|
|
|
|
|
|
|
# relative filename given |
1167
|
|
|
|
|
|
|
$log->audit( "relative filename given, use complete paths " |
1168
|
|
|
|
|
|
|
. "for more predicatable results!\n" |
1169
|
|
|
|
|
|
|
. "working directory is " . cwd() ); |
1170
|
|
|
|
|
|
|
} |
1171
|
|
|
|
|
|
|
|
1172
|
|
|
|
|
|
|
return $log->error( "file ($newfile) does not exist", %args ) |
1173
|
|
|
|
|
|
|
if !-e $newfile; |
1174
|
|
|
|
|
|
|
|
1175
|
|
|
|
|
|
|
return $log->error( "file ($newfile) is not a file", %args ) |
1176
|
|
|
|
|
|
|
if !-f $newfile; |
1177
|
|
|
|
|
|
|
|
1178
|
|
|
|
|
|
|
# make sure existing and new are writable |
1179
|
|
|
|
|
|
|
if ( !$self->is_writable( $existing, fatal => 0 ) |
1180
|
|
|
|
|
|
|
|| !$self->is_writable( $newfile, fatal => 0 ) ) { |
1181
|
|
|
|
|
|
|
|
1182
|
|
|
|
|
|
|
# root does not have permission, sudo won't do any good |
1183
|
|
|
|
|
|
|
return $log->error("no write permission", %args) if $UID == 0; |
1184
|
|
|
|
|
|
|
|
1185
|
|
|
|
|
|
|
if ( $sudo ) { |
1186
|
|
|
|
|
|
|
$sudo = $self->find_bin( 'sudo', %args ) or |
1187
|
|
|
|
|
|
|
return $log->error( "you are not root, sudo was not found, and you don't have permission to write to $newfile or $existing" ); |
1188
|
|
|
|
|
|
|
} |
1189
|
|
|
|
|
|
|
} |
1190
|
|
|
|
|
|
|
|
1191
|
|
|
|
|
|
|
my $diffie; |
1192
|
|
|
|
|
|
|
if ( -f $existing ) { |
1193
|
|
|
|
|
|
|
$diffie = $self->files_diff( %args, |
1194
|
|
|
|
|
|
|
f1 => $newfile, |
1195
|
|
|
|
|
|
|
f2 => $existing, |
1196
|
|
|
|
|
|
|
type => "text", |
1197
|
|
|
|
|
|
|
) or do { |
1198
|
|
|
|
|
|
|
$log->audit( "$existing is already up-to-date.", %args); |
1199
|
|
|
|
|
|
|
unlink $newfile if $p{clean}; |
1200
|
|
|
|
|
|
|
return 2; |
1201
|
|
|
|
|
|
|
}; |
1202
|
|
|
|
|
|
|
}; |
1203
|
|
|
|
|
|
|
|
1204
|
|
|
|
|
|
|
$log->audit("checking $existing", %args); |
1205
|
|
|
|
|
|
|
|
1206
|
|
|
|
|
|
|
$self->chown( $newfile, |
1207
|
|
|
|
|
|
|
uid => $uid, |
1208
|
|
|
|
|
|
|
gid => $gid, |
1209
|
|
|
|
|
|
|
sudo => $sudo, |
1210
|
|
|
|
|
|
|
%args |
1211
|
|
|
|
|
|
|
) |
1212
|
|
|
|
|
|
|
if ( $uid && $gid ); # set file ownership on the new file |
1213
|
|
|
|
|
|
|
|
1214
|
|
|
|
|
|
|
# set file permissions on the new file |
1215
|
|
|
|
|
|
|
$self->chmod( |
1216
|
|
|
|
|
|
|
file_or_dir => $existing, |
1217
|
|
|
|
|
|
|
mode => $mode, |
1218
|
|
|
|
|
|
|
sudo => $sudo, |
1219
|
|
|
|
|
|
|
%args |
1220
|
|
|
|
|
|
|
) |
1221
|
|
|
|
|
|
|
if ( -e $existing && $mode ); |
1222
|
|
|
|
|
|
|
|
1223
|
|
|
|
|
|
|
$self->install_if_changed_notify( $notify, $email, $existing, $diffie); |
1224
|
|
|
|
|
|
|
$self->archive_file( $existing, %args) if ( -e $existing && $p{archive} ); |
1225
|
|
|
|
|
|
|
$self->install_if_changed_copy( $sudo, $newfile, $existing, $p{clean}, \%args ); |
1226
|
|
|
|
|
|
|
|
1227
|
|
|
|
|
|
|
$self->chown( $existing, |
1228
|
|
|
|
|
|
|
uid => $uid, |
1229
|
|
|
|
|
|
|
gid => $gid, |
1230
|
|
|
|
|
|
|
sudo => $sudo, |
1231
|
|
|
|
|
|
|
%args |
1232
|
|
|
|
|
|
|
) if ( $uid && $gid ); # set ownership on new existing file |
1233
|
|
|
|
|
|
|
|
1234
|
|
|
|
|
|
|
$self->chmod( |
1235
|
|
|
|
|
|
|
file_or_dir => $existing, |
1236
|
|
|
|
|
|
|
mode => $mode, |
1237
|
|
|
|
|
|
|
sudo => $sudo, |
1238
|
|
|
|
|
|
|
%args |
1239
|
|
|
|
|
|
|
) |
1240
|
|
|
|
|
|
|
if $mode; # set file permissions (paranoid) |
1241
|
|
|
|
|
|
|
|
1242
|
|
|
|
|
|
|
$log->audit( " updated $existing" ); |
1243
|
|
|
|
|
|
|
return 1; |
1244
|
|
|
|
|
|
|
} |
1245
|
|
|
|
|
|
|
|
1246
|
|
|
|
|
|
|
sub install_if_changed_copy { |
1247
|
|
|
|
|
|
|
my $self = shift; |
1248
|
|
|
|
|
|
|
my ( $sudo, $newfile, $existing, $clean, $args ) = @_; |
1249
|
|
|
|
|
|
|
|
1250
|
|
|
|
|
|
|
# install the new file |
1251
|
|
|
|
|
|
|
if ($sudo) { |
1252
|
|
|
|
|
|
|
my $cp = $self->find_bin( 'cp', %$args ); |
1253
|
|
|
|
|
|
|
|
1254
|
|
|
|
|
|
|
# back up the existing file |
1255
|
|
|
|
|
|
|
$self->syscmd( "$sudo $cp $existing $existing.bak", %$args) |
1256
|
|
|
|
|
|
|
if -e $existing; |
1257
|
|
|
|
|
|
|
|
1258
|
|
|
|
|
|
|
# install the new one |
1259
|
|
|
|
|
|
|
if ( $clean ) { |
1260
|
|
|
|
|
|
|
my $mv = $self->find_bin( 'mv' ); |
1261
|
|
|
|
|
|
|
$self->syscmd( "$sudo $mv $newfile $existing", %$args); |
1262
|
|
|
|
|
|
|
} |
1263
|
|
|
|
|
|
|
else { |
1264
|
|
|
|
|
|
|
$self->syscmd( "$sudo $cp $newfile $existing",%$args); |
1265
|
|
|
|
|
|
|
} |
1266
|
|
|
|
|
|
|
} |
1267
|
|
|
|
|
|
|
else { |
1268
|
|
|
|
|
|
|
|
1269
|
|
|
|
|
|
|
# back up the existing file |
1270
|
|
|
|
|
|
|
copy( $existing, "$existing.bak" ) if -e $existing; |
1271
|
|
|
|
|
|
|
|
1272
|
|
|
|
|
|
|
if ( $clean ) { |
1273
|
|
|
|
|
|
|
move( $newfile, $existing ) or |
1274
|
|
|
|
|
|
|
return $log->error( "failed copy $newfile to $existing", %$args); |
1275
|
|
|
|
|
|
|
} |
1276
|
|
|
|
|
|
|
else { |
1277
|
|
|
|
|
|
|
copy( $newfile, $existing ) or |
1278
|
|
|
|
|
|
|
return $log->error( "failed copy $newfile to $existing", %$args ); |
1279
|
|
|
|
|
|
|
} |
1280
|
|
|
|
|
|
|
} |
1281
|
|
|
|
|
|
|
}; |
1282
|
|
|
|
|
|
|
|
1283
|
|
|
|
|
|
|
sub install_if_changed_notify { |
1284
|
|
|
|
|
|
|
|
1285
|
|
|
|
|
|
|
my ($self, $notify, $email, $existing, $diffie) = @_; |
1286
|
|
|
|
|
|
|
|
1287
|
|
|
|
|
|
|
return if ! $notify; |
1288
|
|
|
|
|
|
|
return if ! -f $existing; |
1289
|
|
|
|
|
|
|
|
1290
|
|
|
|
|
|
|
# email diffs to admin |
1291
|
|
|
|
|
|
|
|
1292
|
|
|
|
|
|
|
eval { require Mail::Send; }; |
1293
|
|
|
|
|
|
|
|
1294
|
|
|
|
|
|
|
return $log->error( "could not send notice, Mail::Send is not installed!", fatal => 0) |
1295
|
|
|
|
|
|
|
if $EVAL_ERROR; |
1296
|
|
|
|
|
|
|
|
1297
|
|
|
|
|
|
|
my $msg = Mail::Send->new; |
1298
|
|
|
|
|
|
|
$msg->subject("$existing updated by $0"); |
1299
|
|
|
|
|
|
|
$msg->to($email); |
1300
|
|
|
|
|
|
|
my $email_message = $msg->open; |
1301
|
|
|
|
|
|
|
|
1302
|
|
|
|
|
|
|
print $email_message "This message is to notify you that $existing has been altered. The difference between the new file and the old one is:\n\n$diffie"; |
1303
|
|
|
|
|
|
|
|
1304
|
|
|
|
|
|
|
$email_message->close; |
1305
|
|
|
|
|
|
|
}; |
1306
|
|
|
|
|
|
|
|
1307
|
|
|
|
|
|
|
sub install_from_source { |
1308
|
|
|
|
|
|
|
my $self = shift; |
1309
|
|
|
|
|
|
|
my %p = validate( |
1310
|
|
|
|
|
|
|
@_, |
1311
|
|
|
|
|
|
|
{ 'site' => { type => SCALAR, optional => 0, }, |
1312
|
|
|
|
|
|
|
'url' => { type => SCALAR, optional => 0, }, |
1313
|
|
|
|
|
|
|
'package' => { type => SCALAR, optional => 0, }, |
1314
|
|
|
|
|
|
|
'targets' => { type => ARRAYREF, optional => 1, }, |
1315
|
|
|
|
|
|
|
'patches' => { type => ARRAYREF, optional => 1, }, |
1316
|
|
|
|
|
|
|
'patch_url' => { type => SCALAR, optional => 1, }, |
1317
|
|
|
|
|
|
|
'patch_args' => { type => SCALAR, optional => 1, }, |
1318
|
|
|
|
|
|
|
'source_dir' => { type => SCALAR, optional => 1, }, |
1319
|
|
|
|
|
|
|
'source_sub_dir' => { type => SCALAR, optional => 1, }, |
1320
|
|
|
|
|
|
|
'bintest' => { type => SCALAR, optional => 1, }, |
1321
|
|
|
|
|
|
|
%std_opts, |
1322
|
|
|
|
|
|
|
}, |
1323
|
|
|
|
|
|
|
); |
1324
|
|
|
|
|
|
|
|
1325
|
|
|
|
|
|
|
return $p{test_ok} if defined $p{test_ok}; |
1326
|
|
|
|
|
|
|
|
1327
|
|
|
|
|
|
|
my %args = ( debug => $p{debug}, fatal => $p{fatal} ); |
1328
|
|
|
|
|
|
|
my ( $site, $url, $package, $targets, $patches, $debug, $bintest ) = |
1329
|
|
|
|
|
|
|
( $p{site}, $p{url}, $p{package}, |
1330
|
|
|
|
|
|
|
$p{targets}, $p{patches}, $p{debug}, $p{bintest} ); |
1331
|
|
|
|
|
|
|
|
1332
|
|
|
|
|
|
|
my $patch_args = $p{patch_args} || ''; |
1333
|
|
|
|
|
|
|
my $src = $p{source_dir} || "/usr/local/src"; |
1334
|
|
|
|
|
|
|
$src .= "/$p{source_sub_dir}" if $p{source_sub_dir}; |
1335
|
|
|
|
|
|
|
|
1336
|
|
|
|
|
|
|
my $original_directory = cwd; |
1337
|
|
|
|
|
|
|
|
1338
|
|
|
|
|
|
|
$self->cwd_source_dir( $src, %args ); |
1339
|
|
|
|
|
|
|
|
1340
|
|
|
|
|
|
|
if ( $bintest && $self->find_bin( $bintest, fatal => 0, debug => 0 ) ) { |
1341
|
|
|
|
|
|
|
return if ! $self->yes_or_no( |
1342
|
|
|
|
|
|
|
"$bintest exists, suggesting that " |
1343
|
|
|
|
|
|
|
. "$package is installed. Do you want to reinstall?", |
1344
|
|
|
|
|
|
|
timeout => 60, |
1345
|
|
|
|
|
|
|
); |
1346
|
|
|
|
|
|
|
} |
1347
|
|
|
|
|
|
|
|
1348
|
|
|
|
|
|
|
$log->audit( "install_from_source: building $package in $src"); |
1349
|
|
|
|
|
|
|
|
1350
|
|
|
|
|
|
|
$self->install_from_source_cleanup($package,$src) or return; |
1351
|
|
|
|
|
|
|
$self->install_from_source_get_files($package,$site,$url,$p{patch_url},$patches) or return; |
1352
|
|
|
|
|
|
|
|
1353
|
|
|
|
|
|
|
$self->extract_archive( $package ) |
1354
|
|
|
|
|
|
|
or return $log->error( "Couldn't expand $package: $!", %args ); |
1355
|
|
|
|
|
|
|
|
1356
|
|
|
|
|
|
|
# cd into the package directory |
1357
|
|
|
|
|
|
|
my $sub_path; |
1358
|
|
|
|
|
|
|
if ( -d $package ) { |
1359
|
|
|
|
|
|
|
chdir $package or |
1360
|
|
|
|
|
|
|
return $log->error( "FAILED to chdir $package!", %args ); |
1361
|
|
|
|
|
|
|
} |
1362
|
|
|
|
|
|
|
else { |
1363
|
|
|
|
|
|
|
|
1364
|
|
|
|
|
|
|
# some packages (like daemontools) unpack within an enclosing directory |
1365
|
|
|
|
|
|
|
$sub_path = `find ./ -name $package`; # tainted data |
1366
|
|
|
|
|
|
|
chomp $sub_path; |
1367
|
|
|
|
|
|
|
($sub_path) = $sub_path =~ /^([-\w\/.]+)$/; # untaint it |
1368
|
|
|
|
|
|
|
|
1369
|
|
|
|
|
|
|
$log->audit( "found sources in $sub_path" ) if $sub_path; |
1370
|
|
|
|
|
|
|
return $log->error( "FAILED to find $package sources!",fatal=>0) |
1371
|
|
|
|
|
|
|
unless ( -d $sub_path && chdir($sub_path) ); |
1372
|
|
|
|
|
|
|
} |
1373
|
|
|
|
|
|
|
|
1374
|
|
|
|
|
|
|
$self->install_from_source_apply_patches($src, $patches, $patch_args) or return; |
1375
|
|
|
|
|
|
|
|
1376
|
|
|
|
|
|
|
# set default build targets if none are provided |
1377
|
|
|
|
|
|
|
if ( !@$targets[0] ) { |
1378
|
|
|
|
|
|
|
$log->audit( "\tusing default targets (./configure, make, make install)" ); |
1379
|
|
|
|
|
|
|
@$targets = ( "./configure", "make", "make install" ); |
1380
|
|
|
|
|
|
|
} |
1381
|
|
|
|
|
|
|
|
1382
|
|
|
|
|
|
|
my $msg = "install_from_source: using targets\n"; |
1383
|
|
|
|
|
|
|
foreach (@$targets) { $msg .= "\t$_\n" }; |
1384
|
|
|
|
|
|
|
$log->audit( $msg ) if $debug; |
1385
|
|
|
|
|
|
|
|
1386
|
|
|
|
|
|
|
# build the program |
1387
|
|
|
|
|
|
|
foreach my $target (@$targets) { |
1388
|
|
|
|
|
|
|
|
1389
|
|
|
|
|
|
|
if ( $target =~ /^cd (.*)$/ ) { |
1390
|
|
|
|
|
|
|
$log->audit( "cwd: " . cwd . " -> " . $1 ); |
1391
|
|
|
|
|
|
|
chdir($1) or return $log->error( "couldn't chdir $1: $!", %args); |
1392
|
|
|
|
|
|
|
next; |
1393
|
|
|
|
|
|
|
} |
1394
|
|
|
|
|
|
|
|
1395
|
|
|
|
|
|
|
$self->syscmd( $target, debug => $debug ) or |
1396
|
|
|
|
|
|
|
return $log->error( "pwd: " . cwd . "\n$target failed: $!", %args ); |
1397
|
|
|
|
|
|
|
} |
1398
|
|
|
|
|
|
|
|
1399
|
|
|
|
|
|
|
# clean up the build sources |
1400
|
|
|
|
|
|
|
chdir $src; |
1401
|
|
|
|
|
|
|
$self->syscmd( "rm -rf $package", debug => $debug ) if -d $package; |
1402
|
|
|
|
|
|
|
|
1403
|
|
|
|
|
|
|
$self->syscmd( "rm -rf $package/$sub_path", %args ) |
1404
|
|
|
|
|
|
|
if defined $sub_path && -d "$package/$sub_path"; |
1405
|
|
|
|
|
|
|
|
1406
|
|
|
|
|
|
|
chdir $original_directory; |
1407
|
|
|
|
|
|
|
return 1; |
1408
|
|
|
|
|
|
|
} |
1409
|
|
|
|
|
|
|
|
1410
|
|
|
|
|
|
|
sub install_from_source_apply_patches { |
1411
|
|
|
|
|
|
|
my $self = shift; |
1412
|
|
|
|
|
|
|
my ($src, $patches,$patch_args) = @_; |
1413
|
|
|
|
|
|
|
|
1414
|
|
|
|
|
|
|
return 1 if ! $patches; |
1415
|
|
|
|
|
|
|
return 1 if ! $patches->[0]; |
1416
|
|
|
|
|
|
|
|
1417
|
|
|
|
|
|
|
my $patchbin = $self->find_bin( "patch" ); |
1418
|
|
|
|
|
|
|
foreach my $patch (@$patches) { |
1419
|
|
|
|
|
|
|
$self->syscmd( "$patchbin $patch_args < $src/$patch" ) |
1420
|
|
|
|
|
|
|
or return $log->error("failed to apply patch $patch"); |
1421
|
|
|
|
|
|
|
} |
1422
|
|
|
|
|
|
|
return 1; |
1423
|
|
|
|
|
|
|
}; |
1424
|
|
|
|
|
|
|
|
1425
|
|
|
|
|
|
|
sub install_from_source_cleanup { |
1426
|
|
|
|
|
|
|
my $self = shift; |
1427
|
|
|
|
|
|
|
my ($package,$src) = @_; |
1428
|
|
|
|
|
|
|
|
1429
|
|
|
|
|
|
|
# make sure there are no previous sources in the way |
1430
|
|
|
|
|
|
|
return 1 if ! -d $package; |
1431
|
|
|
|
|
|
|
|
1432
|
|
|
|
|
|
|
$self->source_warning( |
1433
|
|
|
|
|
|
|
package => $package, |
1434
|
|
|
|
|
|
|
clean => 1, |
1435
|
|
|
|
|
|
|
src => $src, |
1436
|
|
|
|
|
|
|
) or return $log->error( "OK then, skipping install.", fatal => 0); |
1437
|
|
|
|
|
|
|
|
1438
|
|
|
|
|
|
|
print "install_from_source: removing previous build sources.\n"; |
1439
|
|
|
|
|
|
|
return $self->syscmd( "rm -rf $package-*" ); |
1440
|
|
|
|
|
|
|
}; |
1441
|
|
|
|
|
|
|
|
1442
|
|
|
|
|
|
|
sub install_from_source_get_files { |
1443
|
|
|
|
|
|
|
my $self = shift; |
1444
|
|
|
|
|
|
|
my ($package,$site,$url,$patch_url,$patches) = @_; |
1445
|
|
|
|
|
|
|
|
1446
|
|
|
|
|
|
|
$self->sources_get( |
1447
|
|
|
|
|
|
|
package => $package, |
1448
|
|
|
|
|
|
|
site => $site, |
1449
|
|
|
|
|
|
|
path => $url, |
1450
|
|
|
|
|
|
|
) or return; |
1451
|
|
|
|
|
|
|
|
1452
|
|
|
|
|
|
|
if ( ! $patches || ! $patches->[0] ) { |
1453
|
|
|
|
|
|
|
$log->audit( "install_from_source: no patches to fetch." ); |
1454
|
|
|
|
|
|
|
return 1; |
1455
|
|
|
|
|
|
|
}; |
1456
|
|
|
|
|
|
|
|
1457
|
|
|
|
|
|
|
return $log->error( "oops! You supplied patch names to apply without a URL!") |
1458
|
|
|
|
|
|
|
if ! $patch_url; |
1459
|
|
|
|
|
|
|
|
1460
|
|
|
|
|
|
|
|
1461
|
|
|
|
|
|
|
foreach my $patch (@$patches) { |
1462
|
|
|
|
|
|
|
next if ! $patch; |
1463
|
|
|
|
|
|
|
next if -e $patch; |
1464
|
|
|
|
|
|
|
|
1465
|
|
|
|
|
|
|
$log->audit( "install_from_source: fetching patch from $url"); |
1466
|
|
|
|
|
|
|
my $url = "$patch_url/$patch"; |
1467
|
|
|
|
|
|
|
$self->get_url( $url ) |
1468
|
|
|
|
|
|
|
or return $log->error( "could not fetch $url" ); |
1469
|
|
|
|
|
|
|
}; |
1470
|
|
|
|
|
|
|
|
1471
|
|
|
|
|
|
|
return 1; |
1472
|
|
|
|
|
|
|
}; |
1473
|
|
|
|
|
|
|
|
1474
|
|
|
|
|
|
|
sub install_package { |
1475
|
|
|
|
|
|
|
my ($self, $app, $info) = @_; |
1476
|
|
|
|
|
|
|
|
1477
|
|
|
|
|
|
|
if ( lc($OSNAME) eq 'freebsd' ) { |
1478
|
|
|
|
|
|
|
|
1479
|
|
|
|
|
|
|
my $portname = $info->{port} |
1480
|
|
|
|
|
|
|
or return $log->error( "skipping install of $app b/c port dir not set.", fatal => 0); |
1481
|
|
|
|
|
|
|
|
1482
|
|
|
|
|
|
|
if (`/usr/sbin/pkg_info | /usr/bin/grep $app`) { |
1483
|
|
|
|
|
|
|
print "$app is installed.\n"; |
1484
|
|
|
|
|
|
|
return 1; |
1485
|
|
|
|
|
|
|
} |
1486
|
|
|
|
|
|
|
|
1487
|
|
|
|
|
|
|
print "installing $app\n"; |
1488
|
|
|
|
|
|
|
my $portdir = glob("/usr/ports/*/$portname"); |
1489
|
|
|
|
|
|
|
|
1490
|
|
|
|
|
|
|
return $log->error( "oops, couldn't find port $app at '$portname'") |
1491
|
|
|
|
|
|
|
if ( ! -d $portdir || ! chdir $portdir ); |
1492
|
|
|
|
|
|
|
|
1493
|
|
|
|
|
|
|
system "make install clean" |
1494
|
|
|
|
|
|
|
and return $log->error( "'make install clean' failed for port $app", fatal => 0); |
1495
|
|
|
|
|
|
|
return 1; |
1496
|
|
|
|
|
|
|
}; |
1497
|
|
|
|
|
|
|
|
1498
|
|
|
|
|
|
|
if ( lc($OSNAME) eq 'linux' ) { |
1499
|
|
|
|
|
|
|
my $rpm = $info->{rpm} or return $log->error("skipping install of $app b/c rpm not set", fatal => 0); |
1500
|
|
|
|
|
|
|
my $yum = '/usr/bin/yum'; |
1501
|
|
|
|
|
|
|
return $log->error( "couldn't find yum, skipping install.", fatal => 0) |
1502
|
|
|
|
|
|
|
if ! -x $yum; |
1503
|
|
|
|
|
|
|
return system "$yum install $rpm"; |
1504
|
|
|
|
|
|
|
}; |
1505
|
|
|
|
|
|
|
|
1506
|
|
|
|
|
|
|
$log->error(" no package support for $OSNAME "); |
1507
|
|
|
|
|
|
|
} |
1508
|
|
|
|
|
|
|
|
1509
|
|
|
|
|
|
|
sub install_module { |
1510
|
|
|
|
|
|
|
my ($self, $module, %info) = @_; |
1511
|
|
|
|
|
|
|
|
1512
|
|
|
|
|
|
|
my $debug = defined $info{debug} ? $info{debug} : 1; |
1513
|
|
|
|
|
|
|
|
1514
|
|
|
|
|
|
|
## no critic ( ProhibitStringyEval ) |
1515
|
|
|
|
|
|
|
eval "use $module"; |
1516
|
|
|
|
|
|
|
## use critic |
1517
|
|
|
|
|
|
|
if ( ! $EVAL_ERROR ) { |
1518
|
|
|
|
|
|
|
$log->audit( "$module is already installed.",debug=>$debug ); |
1519
|
|
|
|
|
|
|
}; |
1520
|
|
|
|
|
|
|
|
1521
|
|
|
|
|
|
|
if ( lc($OSNAME) eq 'darwin' ) { |
1522
|
|
|
|
|
|
|
$self->install_module_darwin( $module ) and return 1; |
1523
|
|
|
|
|
|
|
} |
1524
|
|
|
|
|
|
|
elsif ( lc($OSNAME) eq 'freebsd' ) { |
1525
|
|
|
|
|
|
|
$self->install_module_freebsd( $module, \%info) and return 1; |
1526
|
|
|
|
|
|
|
} |
1527
|
|
|
|
|
|
|
elsif ( lc($OSNAME) eq 'linux' ) { |
1528
|
|
|
|
|
|
|
$self->install_module_linux( $module, \%info) and return 1; |
1529
|
|
|
|
|
|
|
}; |
1530
|
|
|
|
|
|
|
|
1531
|
|
|
|
|
|
|
$self->install_module_cpan( $module ); |
1532
|
|
|
|
|
|
|
|
1533
|
|
|
|
|
|
|
## no critic ( ProhibitStringyEval ) |
1534
|
|
|
|
|
|
|
eval "use $module"; |
1535
|
|
|
|
|
|
|
## use critic |
1536
|
|
|
|
|
|
|
if ( ! $EVAL_ERROR ) { |
1537
|
|
|
|
|
|
|
$log->audit( "$module is installed." ); |
1538
|
|
|
|
|
|
|
return 1; |
1539
|
|
|
|
|
|
|
}; |
1540
|
|
|
|
|
|
|
return; |
1541
|
|
|
|
|
|
|
} |
1542
|
|
|
|
|
|
|
|
1543
|
|
|
|
|
|
|
sub install_module_cpan { |
1544
|
|
|
|
|
|
|
my $self = shift; |
1545
|
|
|
|
|
|
|
my ($module, $version) = @_; |
1546
|
|
|
|
|
|
|
|
1547
|
|
|
|
|
|
|
print " from CPAN..."; |
1548
|
|
|
|
|
|
|
require CPAN; |
1549
|
|
|
|
|
|
|
|
1550
|
|
|
|
|
|
|
# some Linux distros break CPAN by auto/preconfiguring it with no URL mirrors. |
1551
|
|
|
|
|
|
|
# this works around that annoying little habit |
1552
|
|
|
|
|
|
|
no warnings; |
1553
|
|
|
|
|
|
|
$CPAN::Config = get_cpan_config(); |
1554
|
|
|
|
|
|
|
use warnings; |
1555
|
|
|
|
|
|
|
|
1556
|
|
|
|
|
|
|
if ( $module eq 'Provision::Unix' && $version ) { |
1557
|
|
|
|
|
|
|
$module =~ s/\:\:/\-/g; |
1558
|
|
|
|
|
|
|
$module = "M/MS/MSIMERSON/$module-$version.tar.gz"; |
1559
|
|
|
|
|
|
|
} |
1560
|
|
|
|
|
|
|
CPAN::Shell->install($module); |
1561
|
|
|
|
|
|
|
} |
1562
|
|
|
|
|
|
|
|
1563
|
|
|
|
|
|
|
sub install_module_darwin { |
1564
|
|
|
|
|
|
|
my $self = shift; |
1565
|
|
|
|
|
|
|
my $module = shift; |
1566
|
|
|
|
|
|
|
|
1567
|
|
|
|
|
|
|
my $dport = '/opt/local/bin/port'; |
1568
|
|
|
|
|
|
|
return $log->error( "Darwin ports is not installed!", fatal => 0) |
1569
|
|
|
|
|
|
|
if ! -x $dport; |
1570
|
|
|
|
|
|
|
|
1571
|
|
|
|
|
|
|
my $port = "p5-$module"; |
1572
|
|
|
|
|
|
|
$port =~ s/::/-/g; |
1573
|
|
|
|
|
|
|
system "sudo $dport install $port" or return 1; |
1574
|
|
|
|
|
|
|
return; |
1575
|
|
|
|
|
|
|
}; |
1576
|
|
|
|
|
|
|
|
1577
|
|
|
|
|
|
|
sub install_module_freebsd { |
1578
|
|
|
|
|
|
|
my $self = shift; |
1579
|
|
|
|
|
|
|
my ($module, $info) = @_; |
1580
|
|
|
|
|
|
|
|
1581
|
|
|
|
|
|
|
my $portname = $info->{port}; # optional override |
1582
|
|
|
|
|
|
|
if ( ! $portname ) { |
1583
|
|
|
|
|
|
|
$portname = "p5-$module"; |
1584
|
|
|
|
|
|
|
$portname =~ s/::/-/g; |
1585
|
|
|
|
|
|
|
}; |
1586
|
|
|
|
|
|
|
|
1587
|
|
|
|
|
|
|
my $r = `/usr/sbin/pkg_info | /usr/bin/grep $portname`; |
1588
|
|
|
|
|
|
|
return $log->audit( "$module is installed as $r") if $r; |
1589
|
|
|
|
|
|
|
|
1590
|
|
|
|
|
|
|
my $portdir = glob("/usr/ports/*/$portname"); |
1591
|
|
|
|
|
|
|
|
1592
|
|
|
|
|
|
|
if ( $portdir && -d $portdir && chdir $portdir ) { |
1593
|
|
|
|
|
|
|
$log->audit( "installing $module from ports ($portdir)" ); |
1594
|
|
|
|
|
|
|
system "make clean && make install clean"; |
1595
|
|
|
|
|
|
|
return 1; |
1596
|
|
|
|
|
|
|
} |
1597
|
|
|
|
|
|
|
return; |
1598
|
|
|
|
|
|
|
} |
1599
|
|
|
|
|
|
|
|
1600
|
|
|
|
|
|
|
sub install_module_from_src { |
1601
|
|
|
|
|
|
|
my $self = shift; |
1602
|
|
|
|
|
|
|
my %p = validate( @_, { |
1603
|
|
|
|
|
|
|
module => { type=>SCALAR, optional=>0, }, |
1604
|
|
|
|
|
|
|
archive => { type=>SCALAR, optional=>0, }, |
1605
|
|
|
|
|
|
|
site => { type=>SCALAR, optional=>0, }, |
1606
|
|
|
|
|
|
|
url => { type=>SCALAR, optional=>0, }, |
1607
|
|
|
|
|
|
|
src => { type=>SCALAR, optional=>1, default=>'/usr/local/src' }, |
1608
|
|
|
|
|
|
|
targets => { type=>ARRAYREF,optional=>1, }, |
1609
|
|
|
|
|
|
|
%std_opts, |
1610
|
|
|
|
|
|
|
}, |
1611
|
|
|
|
|
|
|
); |
1612
|
|
|
|
|
|
|
|
1613
|
|
|
|
|
|
|
my ( $module, $site, $url, $src, $targets ) |
1614
|
|
|
|
|
|
|
= ( $p{module}, $p{site}, $p{url}, $p{src}, $p{targets} ); |
1615
|
|
|
|
|
|
|
my %args = ( debug => $p{debug}, fatal => $p{fatal} ); |
1616
|
|
|
|
|
|
|
|
1617
|
|
|
|
|
|
|
$self->cwd_source_dir( $src, %args ); |
1618
|
|
|
|
|
|
|
|
1619
|
|
|
|
|
|
|
$log->audit( "checking for previous build attempts."); |
1620
|
|
|
|
|
|
|
if ( -d $module ) { |
1621
|
|
|
|
|
|
|
if ( ! $self->source_warning( package=>$module, src=>$src, %args ) ) { |
1622
|
|
|
|
|
|
|
print "\nokay, skipping install.\n"; |
1623
|
|
|
|
|
|
|
return; |
1624
|
|
|
|
|
|
|
} |
1625
|
|
|
|
|
|
|
$self->syscmd( cmd => "rm -rf $module", %args ); |
1626
|
|
|
|
|
|
|
} |
1627
|
|
|
|
|
|
|
|
1628
|
|
|
|
|
|
|
$self->sources_get( |
1629
|
|
|
|
|
|
|
site => $site, |
1630
|
|
|
|
|
|
|
path => $url, |
1631
|
|
|
|
|
|
|
package => $p{'archive'} || $module, |
1632
|
|
|
|
|
|
|
%args, |
1633
|
|
|
|
|
|
|
) or return; |
1634
|
|
|
|
|
|
|
|
1635
|
|
|
|
|
|
|
$self->extract_archive( $module ) or return; |
1636
|
|
|
|
|
|
|
|
1637
|
|
|
|
|
|
|
my $found; |
1638
|
|
|
|
|
|
|
print "looking for $module in $src..."; |
1639
|
|
|
|
|
|
|
foreach my $file ( $self->get_dir_files( dir => $src ) ) { |
1640
|
|
|
|
|
|
|
|
1641
|
|
|
|
|
|
|
next if ! -d $file; # only check directories |
1642
|
|
|
|
|
|
|
next if $file !~ /$module/; |
1643
|
|
|
|
|
|
|
|
1644
|
|
|
|
|
|
|
print "found: $file\n"; |
1645
|
|
|
|
|
|
|
$found++; |
1646
|
|
|
|
|
|
|
chdir $file; |
1647
|
|
|
|
|
|
|
|
1648
|
|
|
|
|
|
|
unless ( @$targets[0] && @$targets[0] ne "" ) { |
1649
|
|
|
|
|
|
|
$log->audit( "using default targets." ); |
1650
|
|
|
|
|
|
|
$targets = [ "perl Makefile.PL", "make", "make install" ]; |
1651
|
|
|
|
|
|
|
} |
1652
|
|
|
|
|
|
|
|
1653
|
|
|
|
|
|
|
print "building with targets " . join( ", ", @$targets ) . "\n"; |
1654
|
|
|
|
|
|
|
foreach (@$targets) { |
1655
|
|
|
|
|
|
|
return $log->error( "$_ failed!", %args) |
1656
|
|
|
|
|
|
|
if ! $self->syscmd( cmd => $_ , %args); |
1657
|
|
|
|
|
|
|
} |
1658
|
|
|
|
|
|
|
|
1659
|
|
|
|
|
|
|
chdir('..'); |
1660
|
|
|
|
|
|
|
$self->syscmd( cmd => "rm -rf $file", debug=>0); |
1661
|
|
|
|
|
|
|
last; |
1662
|
|
|
|
|
|
|
} |
1663
|
|
|
|
|
|
|
|
1664
|
|
|
|
|
|
|
return $found; |
1665
|
|
|
|
|
|
|
} |
1666
|
|
|
|
|
|
|
|
1667
|
|
|
|
|
|
|
sub install_module_linux { |
1668
|
|
|
|
|
|
|
my $self = shift; |
1669
|
|
|
|
|
|
|
my ($module, $info ) = @_; |
1670
|
|
|
|
|
|
|
my $rpm = $info->{rpm}; |
1671
|
|
|
|
|
|
|
if ( $rpm ) { |
1672
|
|
|
|
|
|
|
my $portname = "perl-$rpm"; |
1673
|
|
|
|
|
|
|
$portname =~ s/::/-/g; |
1674
|
|
|
|
|
|
|
my $yum = '/usr/bin/yum'; |
1675
|
|
|
|
|
|
|
system "$yum -y install $portname" if -x $yum; |
1676
|
|
|
|
|
|
|
} |
1677
|
|
|
|
|
|
|
}; |
1678
|
|
|
|
|
|
|
|
1679
|
|
|
|
|
|
|
sub is_interactive { |
1680
|
|
|
|
|
|
|
|
1681
|
|
|
|
|
|
|
## no critic |
1682
|
|
|
|
|
|
|
# borrowed from IO::Interactive |
1683
|
|
|
|
|
|
|
my $self = shift; |
1684
|
|
|
|
|
|
|
my ($out_handle) = ( @_, select ); # Default to default output handle |
1685
|
|
|
|
|
|
|
|
1686
|
|
|
|
|
|
|
# Not interactive if output is not to terminal... |
1687
|
|
|
|
|
|
|
return if not -t $out_handle; |
1688
|
|
|
|
|
|
|
|
1689
|
|
|
|
|
|
|
# If *ARGV is opened, we're interactive if... |
1690
|
|
|
|
|
|
|
if ( openhandle * ARGV ) { |
1691
|
|
|
|
|
|
|
|
1692
|
|
|
|
|
|
|
# ...it's currently opened to the magic '-' file |
1693
|
|
|
|
|
|
|
return -t *STDIN if defined $ARGV && $ARGV eq '-'; |
1694
|
|
|
|
|
|
|
|
1695
|
|
|
|
|
|
|
# ...it's at end-of-file and the next file is the magic '-' file |
1696
|
|
|
|
|
|
|
return @ARGV > 0 && $ARGV[0] eq '-' && -t *STDIN if eof *ARGV; |
1697
|
|
|
|
|
|
|
|
1698
|
|
|
|
|
|
|
# ...it's directly attached to the terminal |
1699
|
|
|
|
|
|
|
return -t *ARGV; |
1700
|
|
|
|
|
|
|
}; |
1701
|
|
|
|
|
|
|
|
1702
|
|
|
|
|
|
|
# If *ARGV isn't opened, it will be interactive if *STDIN is attached |
1703
|
|
|
|
|
|
|
# to a terminal and either there are no files specified on the command line |
1704
|
|
|
|
|
|
|
# or if there are files and the first is the magic '-' file |
1705
|
|
|
|
|
|
|
return -t *STDIN && ( @ARGV == 0 || $ARGV[0] eq '-' ); |
1706
|
|
|
|
|
|
|
} |
1707
|
|
|
|
|
|
|
|
1708
|
|
|
|
|
|
|
sub is_process_running { |
1709
|
|
|
|
|
|
|
my ( $self, $process ) = @_; |
1710
|
|
|
|
|
|
|
|
1711
|
|
|
|
|
|
|
## no critic ( ProhibitStringyEval ) |
1712
|
|
|
|
|
|
|
eval "require Proc::ProcessTable"; |
1713
|
|
|
|
|
|
|
## use critic |
1714
|
|
|
|
|
|
|
if ( ! $EVAL_ERROR ) { |
1715
|
|
|
|
|
|
|
my $i = 0; |
1716
|
|
|
|
|
|
|
my $t = Proc::ProcessTable->new(); |
1717
|
|
|
|
|
|
|
if ( scalar @{ $t->table } ) { |
1718
|
|
|
|
|
|
|
foreach my $p ( @{ $t->table } ) { |
1719
|
|
|
|
|
|
|
$i++ if ( $p->cmndline =~ m/$process/i ); |
1720
|
|
|
|
|
|
|
}; |
1721
|
|
|
|
|
|
|
return $i; |
1722
|
|
|
|
|
|
|
}; |
1723
|
|
|
|
|
|
|
}; |
1724
|
|
|
|
|
|
|
|
1725
|
|
|
|
|
|
|
my $ps = $self->find_bin( 'ps', debug => 0 ); |
1726
|
|
|
|
|
|
|
|
1727
|
|
|
|
|
|
|
if ( lc($OSNAME) =~ /solaris/i ) { $ps .= ' -ef'; } |
1728
|
|
|
|
|
|
|
elsif ( lc($OSNAME) =~ /irix/i ) { $ps .= ' -ef'; } |
1729
|
|
|
|
|
|
|
elsif ( lc($OSNAME) =~ /linux/i ) { $ps .= ' -efw'; } |
1730
|
|
|
|
|
|
|
else { $ps .= ' axww'; }; |
1731
|
|
|
|
|
|
|
|
1732
|
|
|
|
|
|
|
my @procs = `$ps`; |
1733
|
|
|
|
|
|
|
chomp @procs; |
1734
|
|
|
|
|
|
|
return scalar grep {/$process/i} @procs; |
1735
|
|
|
|
|
|
|
} |
1736
|
|
|
|
|
|
|
|
1737
|
|
|
|
|
|
|
sub is_readable { |
1738
|
|
|
|
|
|
|
my $self = shift; |
1739
|
|
|
|
|
|
|
my $file = shift or die "missing file or dir name\n"; |
1740
|
|
|
|
|
|
|
my %p = validate( @_, { %std_opts } ); |
1741
|
|
|
|
|
|
|
|
1742
|
|
|
|
|
|
|
my %args = ( debug => $p{debug}, fatal => $p{fatal} ); |
1743
|
|
|
|
|
|
|
|
1744
|
|
|
|
|
|
|
-e $file or return $log->error( "$file does not exist.", %args); |
1745
|
|
|
|
|
|
|
-r $file or return $log->error( "$file is not readable by you (" |
1746
|
|
|
|
|
|
|
. getpwuid($>) |
1747
|
|
|
|
|
|
|
. "). You need to fix this, using chown or chmod.", %args); |
1748
|
|
|
|
|
|
|
|
1749
|
|
|
|
|
|
|
return 1; |
1750
|
|
|
|
|
|
|
} |
1751
|
|
|
|
|
|
|
|
1752
|
|
|
|
|
|
|
sub is_writable { |
1753
|
|
|
|
|
|
|
my $self = shift; |
1754
|
|
|
|
|
|
|
my $file = shift or die "missing file or dir name\n"; |
1755
|
|
|
|
|
|
|
|
1756
|
|
|
|
|
|
|
my %p = validate( @_, { %std_opts } ); |
1757
|
|
|
|
|
|
|
my %args = ( debug => $p{debug}, fatal => $p{fatal} ); |
1758
|
|
|
|
|
|
|
|
1759
|
|
|
|
|
|
|
my $nl = "\n"; |
1760
|
|
|
|
|
|
|
$nl = "<br>" if ( $ENV{GATEWAY_INTERFACE} ); |
1761
|
|
|
|
|
|
|
|
1762
|
|
|
|
|
|
|
if ( !-e $file ) { |
1763
|
|
|
|
|
|
|
|
1764
|
|
|
|
|
|
|
my ( $base, $path, $suffix ) = fileparse($file); |
1765
|
|
|
|
|
|
|
|
1766
|
|
|
|
|
|
|
return $log->error( "is_writable: $path not writable by " |
1767
|
|
|
|
|
|
|
. getpwuid($>) |
1768
|
|
|
|
|
|
|
. "$nl$nl", %args) if (-e $path && !-w $path); |
1769
|
|
|
|
|
|
|
return 1; |
1770
|
|
|
|
|
|
|
} |
1771
|
|
|
|
|
|
|
|
1772
|
|
|
|
|
|
|
return $log->error( " $file not writable by " . getpwuid($>) . "$nl$nl", %args ) if ! -w $file; |
1773
|
|
|
|
|
|
|
|
1774
|
|
|
|
|
|
|
$log->audit( "$file is writable" ); |
1775
|
|
|
|
|
|
|
return 1; |
1776
|
|
|
|
|
|
|
} |
1777
|
|
|
|
|
|
|
|
1778
|
|
|
|
|
|
|
sub logfile_append { |
1779
|
|
|
|
|
|
|
my $self = shift; |
1780
|
|
|
|
|
|
|
my %p = validate( |
1781
|
|
|
|
|
|
|
@_, |
1782
|
|
|
|
|
|
|
{ 'file' => { type => SCALAR, optional => 0, }, |
1783
|
|
|
|
|
|
|
'lines' => { type => ARRAYREF, optional => 0, }, |
1784
|
|
|
|
|
|
|
'prog' => { type => BOOLEAN, optional => 1, default => 0, }, |
1785
|
|
|
|
|
|
|
%std_opts, |
1786
|
|
|
|
|
|
|
}, |
1787
|
|
|
|
|
|
|
); |
1788
|
|
|
|
|
|
|
|
1789
|
|
|
|
|
|
|
my ( $file, $lines ) = ( $p{file}, $p{lines} ); |
1790
|
|
|
|
|
|
|
my %args = ( debug => $p{debug}, fatal => $p{fatal} ); |
1791
|
|
|
|
|
|
|
|
1792
|
|
|
|
|
|
|
my ( $dd, $mm, $yy, $lm, $hh, $mn, $ss ) = $self->get_the_date( %args ); |
1793
|
|
|
|
|
|
|
|
1794
|
|
|
|
|
|
|
open my $LOG_FILE, '>>', $file |
1795
|
|
|
|
|
|
|
or return $log->error( "couldn't open $file: $OS_ERROR", %args); |
1796
|
|
|
|
|
|
|
|
1797
|
|
|
|
|
|
|
print $LOG_FILE "$yy-$mm-$dd $hh:$mn:$ss $p{prog} "; |
1798
|
|
|
|
|
|
|
|
1799
|
|
|
|
|
|
|
my $i; |
1800
|
|
|
|
|
|
|
foreach (@$lines) { print $LOG_FILE "$_ "; $i++ } |
1801
|
|
|
|
|
|
|
|
1802
|
|
|
|
|
|
|
print $LOG_FILE "\n"; |
1803
|
|
|
|
|
|
|
close $LOG_FILE; |
1804
|
|
|
|
|
|
|
|
1805
|
|
|
|
|
|
|
$log->audit( "logfile_append wrote $i lines to $file", %args ); |
1806
|
|
|
|
|
|
|
return 1; |
1807
|
|
|
|
|
|
|
} |
1808
|
|
|
|
|
|
|
|
1809
|
|
|
|
|
|
|
sub mail_toaster { |
1810
|
|
|
|
|
|
|
my $self = shift; |
1811
|
|
|
|
|
|
|
$self->install_module( 'Mail::Toaster' ); |
1812
|
|
|
|
|
|
|
} |
1813
|
|
|
|
|
|
|
|
1814
|
|
|
|
|
|
|
sub mkdir_system { |
1815
|
|
|
|
|
|
|
my $self = shift; |
1816
|
|
|
|
|
|
|
my %p = validate( |
1817
|
|
|
|
|
|
|
@_, |
1818
|
|
|
|
|
|
|
{ 'dir' => { type => SCALAR, optional => 0, }, |
1819
|
|
|
|
|
|
|
'mode' => { type => SCALAR, optional => 1, }, |
1820
|
|
|
|
|
|
|
'sudo' => { type => BOOLEAN, optional => 1, default => 0 }, |
1821
|
|
|
|
|
|
|
%std_opts, |
1822
|
|
|
|
|
|
|
} |
1823
|
|
|
|
|
|
|
); |
1824
|
|
|
|
|
|
|
|
1825
|
|
|
|
|
|
|
my ( $dir, $mode ) = ( $p{dir}, $p{mode} ); |
1826
|
|
|
|
|
|
|
my %args = ( debug => $p{debug}, fatal => $p{fatal} ); |
1827
|
|
|
|
|
|
|
|
1828
|
|
|
|
|
|
|
return $log->audit( "mkdir_system: $dir already exists.") if -d $dir; |
1829
|
|
|
|
|
|
|
|
1830
|
|
|
|
|
|
|
my $mkdir = $self->find_bin( 'mkdir', %args) or return; |
1831
|
|
|
|
|
|
|
|
1832
|
|
|
|
|
|
|
# if we are root, just do it (no sudo nonsense) |
1833
|
|
|
|
|
|
|
if ( $< == 0 ) { |
1834
|
|
|
|
|
|
|
$self->syscmd( "$mkdir -p $dir", %args) or return; |
1835
|
|
|
|
|
|
|
$self->chmod( dir => $dir, mode => $mode, %args ) if $mode; |
1836
|
|
|
|
|
|
|
|
1837
|
|
|
|
|
|
|
return 1 if -d $dir; |
1838
|
|
|
|
|
|
|
return $log->error( "failed to create $dir", %args); |
1839
|
|
|
|
|
|
|
} |
1840
|
|
|
|
|
|
|
|
1841
|
|
|
|
|
|
|
if ( $p{sudo} ) { |
1842
|
|
|
|
|
|
|
my $sudo = $self->sudo(); |
1843
|
|
|
|
|
|
|
|
1844
|
|
|
|
|
|
|
$log->audit( "trying $sudo $mkdir -p $dir"); |
1845
|
|
|
|
|
|
|
$self->syscmd( "$sudo $mkdir -p $dir", %args); |
1846
|
|
|
|
|
|
|
|
1847
|
|
|
|
|
|
|
$log->audit( "setting ownership to $<."); |
1848
|
|
|
|
|
|
|
my $chown = $self->find_bin( 'chown', %args); |
1849
|
|
|
|
|
|
|
$self->syscmd( "$sudo $chown $< $dir", %args); |
1850
|
|
|
|
|
|
|
|
1851
|
|
|
|
|
|
|
$self->chmod( dir => $dir, mode => $mode, sudo => $sudo, %args) |
1852
|
|
|
|
|
|
|
if $mode; |
1853
|
|
|
|
|
|
|
return -d $dir ? 1 : 0; |
1854
|
|
|
|
|
|
|
} |
1855
|
|
|
|
|
|
|
|
1856
|
|
|
|
|
|
|
$log->audit( "trying mkdir -p $dir" ); |
1857
|
|
|
|
|
|
|
|
1858
|
|
|
|
|
|
|
# no root and no sudo, just try and see what happens |
1859
|
|
|
|
|
|
|
$self->syscmd( "$mkdir -p $dir", %args ) or return; |
1860
|
|
|
|
|
|
|
|
1861
|
|
|
|
|
|
|
$self->chmod( dir => $dir, mode => $mode, %args) if $mode; |
1862
|
|
|
|
|
|
|
|
1863
|
|
|
|
|
|
|
return $log->audit( "mkdir_system created $dir" ) if -d $dir; |
1864
|
|
|
|
|
|
|
return $log->error( '', %args ); |
1865
|
|
|
|
|
|
|
} |
1866
|
|
|
|
|
|
|
|
1867
|
|
|
|
|
|
|
sub path_parse { |
1868
|
|
|
|
|
|
|
|
1869
|
|
|
|
|
|
|
# code left here for reference, use File::Basename instead |
1870
|
|
|
|
|
|
|
my ( $self, $dir ) = @_; |
1871
|
|
|
|
|
|
|
|
1872
|
|
|
|
|
|
|
# if it ends with a /, chop if off |
1873
|
|
|
|
|
|
|
if ( $dir =~ q{/$} ) { chop $dir } |
1874
|
|
|
|
|
|
|
|
1875
|
|
|
|
|
|
|
# get the position of the last / in the path |
1876
|
|
|
|
|
|
|
my $rindex = rindex( $dir, "/" ); |
1877
|
|
|
|
|
|
|
|
1878
|
|
|
|
|
|
|
# grabs everything up to the last / |
1879
|
|
|
|
|
|
|
my $updir = substr( $dir, 0, $rindex ); |
1880
|
|
|
|
|
|
|
$rindex++; |
1881
|
|
|
|
|
|
|
|
1882
|
|
|
|
|
|
|
# matches from the last / char +1 to the end of string |
1883
|
|
|
|
|
|
|
my $curdir = substr( $dir, $rindex ); |
1884
|
|
|
|
|
|
|
|
1885
|
|
|
|
|
|
|
return $updir, $curdir; |
1886
|
|
|
|
|
|
|
} |
1887
|
|
|
|
|
|
|
|
1888
|
|
|
|
|
|
|
sub check_pidfile { |
1889
|
|
|
|
|
|
|
my $self = shift; |
1890
|
|
|
|
|
|
|
my $file = shift; |
1891
|
|
|
|
|
|
|
my %p = validate( @_, { %std_opts } ); |
1892
|
|
|
|
|
|
|
|
1893
|
|
|
|
|
|
|
my %args = ( debug => $p{debug}, fatal => $p{fatal} ); |
1894
|
|
|
|
|
|
|
|
1895
|
|
|
|
|
|
|
return $log->error( "missing filename", %args) if ! $file; |
1896
|
|
|
|
|
|
|
return $log->error( "$file is not a regular file", %args) |
1897
|
|
|
|
|
|
|
if ( -e $file && !-f $file ); |
1898
|
|
|
|
|
|
|
|
1899
|
|
|
|
|
|
|
# test if file & enclosing directory is writable, revert to /tmp if not |
1900
|
|
|
|
|
|
|
$self->is_writable( $file, %args) |
1901
|
|
|
|
|
|
|
or do { |
1902
|
|
|
|
|
|
|
my ( $base, $path, $suffix ) = fileparse($file); |
1903
|
|
|
|
|
|
|
$log->audit( "NOTICE: using /tmp for file, $path is not writable!", %args); |
1904
|
|
|
|
|
|
|
$file = "/tmp/$base"; |
1905
|
|
|
|
|
|
|
}; |
1906
|
|
|
|
|
|
|
|
1907
|
|
|
|
|
|
|
# if it does not exist |
1908
|
|
|
|
|
|
|
if ( !-e $file ) { |
1909
|
|
|
|
|
|
|
$log->audit( "writing process id $PROCESS_ID to $file..."); |
1910
|
|
|
|
|
|
|
$self->file_write( $file, lines => [$PROCESS_ID], %args) and return $file; |
1911
|
|
|
|
|
|
|
}; |
1912
|
|
|
|
|
|
|
|
1913
|
|
|
|
|
|
|
my $age = time() - stat($file)->mtime; |
1914
|
|
|
|
|
|
|
|
1915
|
|
|
|
|
|
|
if ( $age < 1200 ) { # less than 20 minutes old |
1916
|
|
|
|
|
|
|
return $log->error( "check_pidfile: $file is " . $age / 60 |
1917
|
|
|
|
|
|
|
. " minutes old and might still be running. If it is not running," |
1918
|
|
|
|
|
|
|
. " please remove the file (rm $file).", %args); |
1919
|
|
|
|
|
|
|
} |
1920
|
|
|
|
|
|
|
elsif ( $age < 3600 ) { # 1 hour |
1921
|
|
|
|
|
|
|
return $log->error( "check_pidfile: $file is " . $age / 60 |
1922
|
|
|
|
|
|
|
. " minutes old and might still be running. If it is not running," |
1923
|
|
|
|
|
|
|
. " please remove the pidfile. (rm $file)", %args); |
1924
|
|
|
|
|
|
|
} |
1925
|
|
|
|
|
|
|
else { |
1926
|
|
|
|
|
|
|
$log->audit( "check_pidfile: $file is $age seconds old, ignoring.", %args); |
1927
|
|
|
|
|
|
|
} |
1928
|
|
|
|
|
|
|
|
1929
|
|
|
|
|
|
|
return $file; |
1930
|
|
|
|
|
|
|
} |
1931
|
|
|
|
|
|
|
|
1932
|
|
|
|
|
|
|
sub provision_unix { |
1933
|
|
|
|
|
|
|
my $self = shift; |
1934
|
|
|
|
|
|
|
$self->install_module( 'Provision::Unix' ); |
1935
|
|
|
|
|
|
|
} |
1936
|
|
|
|
|
|
|
|
1937
|
|
|
|
|
|
|
sub regexp_test { |
1938
|
|
|
|
|
|
|
my $self = shift; |
1939
|
|
|
|
|
|
|
my %p = validate( |
1940
|
|
|
|
|
|
|
@_, |
1941
|
|
|
|
|
|
|
{ 'exp' => { type => SCALAR }, |
1942
|
|
|
|
|
|
|
'string' => { type => SCALAR }, |
1943
|
|
|
|
|
|
|
'pbp' => { type => BOOLEAN, optional => 1, default => 0 }, |
1944
|
|
|
|
|
|
|
'debug' => { type => BOOLEAN, optional => 1, default => $self->{debug} }, |
1945
|
|
|
|
|
|
|
}, |
1946
|
|
|
|
|
|
|
); |
1947
|
|
|
|
|
|
|
|
1948
|
|
|
|
|
|
|
my $debug = $p{debug}; |
1949
|
|
|
|
|
|
|
my ( $exp, $string, $pbp ) = ( $p{exp}, $p{string}, $p{pbp} ); |
1950
|
|
|
|
|
|
|
|
1951
|
|
|
|
|
|
|
if ($pbp) { |
1952
|
|
|
|
|
|
|
if ( $string =~ m{($exp)}xms ) { |
1953
|
|
|
|
|
|
|
print "\t Matched pbp: |$`<$&>$'|\n" if $debug; |
1954
|
|
|
|
|
|
|
return $1; |
1955
|
|
|
|
|
|
|
} |
1956
|
|
|
|
|
|
|
else { |
1957
|
|
|
|
|
|
|
print "\t No match.\n" if $debug; |
1958
|
|
|
|
|
|
|
return; |
1959
|
|
|
|
|
|
|
} |
1960
|
|
|
|
|
|
|
} |
1961
|
|
|
|
|
|
|
|
1962
|
|
|
|
|
|
|
if ( $string =~ m{($exp)} ) { |
1963
|
|
|
|
|
|
|
print "\t Matched: |$`<$&>$'|\n" if $debug; |
1964
|
|
|
|
|
|
|
return $1; |
1965
|
|
|
|
|
|
|
} |
1966
|
|
|
|
|
|
|
|
1967
|
|
|
|
|
|
|
print "\t No match.\n" if $debug; |
1968
|
|
|
|
|
|
|
return; |
1969
|
|
|
|
|
|
|
} |
1970
|
|
|
|
|
|
|
|
1971
|
|
|
|
|
|
|
sub sources_get { |
1972
|
|
|
|
|
|
|
my $self = shift; |
1973
|
|
|
|
|
|
|
my %p = validate( |
1974
|
|
|
|
|
|
|
@_, |
1975
|
|
|
|
|
|
|
{ 'package' => { type => SCALAR, optional => 0 }, |
1976
|
|
|
|
|
|
|
site => { type => SCALAR, optional => 0 }, |
1977
|
|
|
|
|
|
|
path => { type => SCALAR, optional => 1 }, |
1978
|
|
|
|
|
|
|
%std_opts, |
1979
|
|
|
|
|
|
|
}, |
1980
|
|
|
|
|
|
|
); |
1981
|
|
|
|
|
|
|
|
1982
|
|
|
|
|
|
|
my ( $package, $site, $path ) = ( $p{package}, $p{site}, $p{path} ); |
1983
|
|
|
|
|
|
|
my %args = ( debug => $p{debug}, fatal => $p{fatal} ); |
1984
|
|
|
|
|
|
|
|
1985
|
|
|
|
|
|
|
$log->audit( "sources_get: fetching $package from site $site\n\t path: $path"); |
1986
|
|
|
|
|
|
|
|
1987
|
|
|
|
|
|
|
my @extensions = qw/ tar.gz tgz tar.bz2 tbz2 /; |
1988
|
|
|
|
|
|
|
|
1989
|
|
|
|
|
|
|
my $filet = $self->find_bin( 'file', %args) or return; |
1990
|
|
|
|
|
|
|
my $grep = $self->find_bin( 'grep', %args) or return; |
1991
|
|
|
|
|
|
|
|
1992
|
|
|
|
|
|
|
foreach my $ext (@extensions) { |
1993
|
|
|
|
|
|
|
|
1994
|
|
|
|
|
|
|
my $tarball = "$package.$ext"; |
1995
|
|
|
|
|
|
|
next if !-e $tarball; |
1996
|
|
|
|
|
|
|
$log->audit( " found $tarball!") if -e $tarball; |
1997
|
|
|
|
|
|
|
|
1998
|
|
|
|
|
|
|
if (`$filet $tarball | $grep compress`) { |
1999
|
|
|
|
|
|
|
$self->yes_or_no( "$tarball exists, shall I use it?: ") |
2000
|
|
|
|
|
|
|
and return $log->audit( " ok, using existing archive: $tarball"); |
2001
|
|
|
|
|
|
|
} |
2002
|
|
|
|
|
|
|
|
2003
|
|
|
|
|
|
|
$self->file_delete( file => $tarball, %args ); |
2004
|
|
|
|
|
|
|
} |
2005
|
|
|
|
|
|
|
|
2006
|
|
|
|
|
|
|
foreach my $ext (@extensions) { |
2007
|
|
|
|
|
|
|
my $tarball = "$package.$ext"; |
2008
|
|
|
|
|
|
|
|
2009
|
|
|
|
|
|
|
$log->audit( "sources_get: fetching $site$path/$tarball"); |
2010
|
|
|
|
|
|
|
|
2011
|
|
|
|
|
|
|
$self->get_url( "$site$path/$tarball", fatal => 0) |
2012
|
|
|
|
|
|
|
or return $log->error( "couldn't fetch $site$path/$tarball", %args); |
2013
|
|
|
|
|
|
|
|
2014
|
|
|
|
|
|
|
next if ! -e $tarball; |
2015
|
|
|
|
|
|
|
|
2016
|
|
|
|
|
|
|
$log->audit( " sources_get: testing $tarball "); |
2017
|
|
|
|
|
|
|
|
2018
|
|
|
|
|
|
|
if (`$filet $tarball | $grep zip`) { |
2019
|
|
|
|
|
|
|
$log->audit( " sources_get: looks good!"); |
2020
|
|
|
|
|
|
|
return 1; |
2021
|
|
|
|
|
|
|
}; |
2022
|
|
|
|
|
|
|
|
2023
|
|
|
|
|
|
|
$log->audit( " oops, is not [b|g]zipped data!"); |
2024
|
|
|
|
|
|
|
$self->file_delete( file => $tarball, %args); |
2025
|
|
|
|
|
|
|
} |
2026
|
|
|
|
|
|
|
|
2027
|
|
|
|
|
|
|
return $log->error( "unable to get $package", %args ); |
2028
|
|
|
|
|
|
|
} |
2029
|
|
|
|
|
|
|
|
2030
|
|
|
|
|
|
|
sub source_warning { |
2031
|
|
|
|
|
|
|
my $self = shift; |
2032
|
|
|
|
|
|
|
my %p = validate( |
2033
|
|
|
|
|
|
|
@_, |
2034
|
|
|
|
|
|
|
{ 'package' => { type => SCALAR, }, |
2035
|
|
|
|
|
|
|
'clean' => { type => BOOLEAN, optional => 1, default => 1 }, |
2036
|
|
|
|
|
|
|
'src' => { |
2037
|
|
|
|
|
|
|
type => SCALAR, |
2038
|
|
|
|
|
|
|
optional => 1, |
2039
|
|
|
|
|
|
|
default => "/usr/local/src" |
2040
|
|
|
|
|
|
|
}, |
2041
|
|
|
|
|
|
|
'timeout' => { type => SCALAR, optional => 1, default => 60 }, |
2042
|
|
|
|
|
|
|
%std_opts, |
2043
|
|
|
|
|
|
|
}, |
2044
|
|
|
|
|
|
|
); |
2045
|
|
|
|
|
|
|
|
2046
|
|
|
|
|
|
|
my ( $package, $src ) = ( $p{package}, $p{src} ); |
2047
|
|
|
|
|
|
|
my %args = ( debug => $p{debug}, fatal => $p{fatal} ); |
2048
|
|
|
|
|
|
|
|
2049
|
|
|
|
|
|
|
return $log->audit( "$package sources not present.", %args ) if !-d $package; |
2050
|
|
|
|
|
|
|
|
2051
|
|
|
|
|
|
|
if ( -e $package ) { |
2052
|
|
|
|
|
|
|
print " |
2053
|
|
|
|
|
|
|
$package sources are already present, indicating that you've already |
2054
|
|
|
|
|
|
|
installed $package. If you want to reinstall it, remove the existing |
2055
|
|
|
|
|
|
|
sources (rm -r $src/$package) and re-run this script\n\n"; |
2056
|
|
|
|
|
|
|
return if !$p{clean}; |
2057
|
|
|
|
|
|
|
} |
2058
|
|
|
|
|
|
|
|
2059
|
|
|
|
|
|
|
if ( !$self->yes_or_no( "\n\tMay I remove the sources for you?", timeout => $p{timeout} ) ) { |
2060
|
|
|
|
|
|
|
print "\nOK then, skipping $package install.\n\n"; |
2061
|
|
|
|
|
|
|
return; |
2062
|
|
|
|
|
|
|
}; |
2063
|
|
|
|
|
|
|
|
2064
|
|
|
|
|
|
|
$log->audit( " wd: " . cwd ); |
2065
|
|
|
|
|
|
|
$log->audit( " deleting $src/$package"); |
2066
|
|
|
|
|
|
|
|
2067
|
|
|
|
|
|
|
return $log->error( "failed to delete $package: $OS_ERROR", %args ) |
2068
|
|
|
|
|
|
|
if ! rmtree "$src/$package"; |
2069
|
|
|
|
|
|
|
return 1; |
2070
|
|
|
|
|
|
|
} |
2071
|
|
|
|
|
|
|
|
2072
|
|
|
|
|
|
|
sub sudo { |
2073
|
|
|
|
|
|
|
my $self = shift; |
2074
|
|
|
|
|
|
|
my %p = validate( @_, { %std_opts } ); |
2075
|
|
|
|
|
|
|
|
2076
|
|
|
|
|
|
|
# if we are running as root via $< |
2077
|
|
|
|
|
|
|
if ( $REAL_USER_ID == 0 ) { |
2078
|
|
|
|
|
|
|
$log->audit( "sudo: you are root, sudo isn't necessary."); |
2079
|
|
|
|
|
|
|
return ''; # return an empty string, purposefully |
2080
|
|
|
|
|
|
|
} |
2081
|
|
|
|
|
|
|
|
2082
|
|
|
|
|
|
|
my $sudo; |
2083
|
|
|
|
|
|
|
my $path_to_sudo = $self->find_bin( 'sudo', fatal => 0 ); |
2084
|
|
|
|
|
|
|
|
2085
|
|
|
|
|
|
|
# sudo is installed |
2086
|
|
|
|
|
|
|
if ( $path_to_sudo && -x $path_to_sudo ) { |
2087
|
|
|
|
|
|
|
$log->audit( "sudo: sudo was found at $path_to_sudo."); |
2088
|
|
|
|
|
|
|
return "$path_to_sudo -p 'Password for %u@%h:'"; |
2089
|
|
|
|
|
|
|
} |
2090
|
|
|
|
|
|
|
|
2091
|
|
|
|
|
|
|
$log->audit( "\nWARNING: Couldn't find sudo. This may not be a problem but some features require root permissions and will not work without them. Having sudo can allow legitimate and limited root permission to non-root users. Some features of Mail::Toaster may not work as expected without it.\n"); |
2092
|
|
|
|
|
|
|
|
2093
|
|
|
|
|
|
|
# try installing sudo |
2094
|
|
|
|
|
|
|
$self->yes_or_no( "may I try to install sudo?", timeout => 20 ) or do { |
2095
|
|
|
|
|
|
|
print "very well then, skipping along.\n"; |
2096
|
|
|
|
|
|
|
return ""; |
2097
|
|
|
|
|
|
|
}; |
2098
|
|
|
|
|
|
|
|
2099
|
|
|
|
|
|
|
-x $self->find_bin( "sudo", fatal => 0 ) or |
2100
|
|
|
|
|
|
|
$self->install_from_source( |
2101
|
|
|
|
|
|
|
package => 'sudo-1.6.9p17', |
2102
|
|
|
|
|
|
|
site => 'http://www.courtesan.com', |
2103
|
|
|
|
|
|
|
url => '/sudo/', |
2104
|
|
|
|
|
|
|
targets => [ './configure', 'make', 'make install' ], |
2105
|
|
|
|
|
|
|
patches => '', |
2106
|
|
|
|
|
|
|
debug => 1, |
2107
|
|
|
|
|
|
|
); |
2108
|
|
|
|
|
|
|
|
2109
|
|
|
|
|
|
|
# can we find it now? |
2110
|
|
|
|
|
|
|
$path_to_sudo = $self->find_bin( "sudo" ); |
2111
|
|
|
|
|
|
|
|
2112
|
|
|
|
|
|
|
if ( !-x $path_to_sudo ) { |
2113
|
|
|
|
|
|
|
print "sudo install failed!"; |
2114
|
|
|
|
|
|
|
return ''; |
2115
|
|
|
|
|
|
|
} |
2116
|
|
|
|
|
|
|
|
2117
|
|
|
|
|
|
|
return "$path_to_sudo -p 'Password for %u@%h:'"; |
2118
|
|
|
|
|
|
|
} |
2119
|
|
|
|
|
|
|
|
2120
|
|
|
|
|
|
|
sub syscmd { |
2121
|
|
|
|
|
|
|
my $self = shift; |
2122
|
|
|
|
|
|
|
my $cmd = shift or die "missing command!\n"; |
2123
|
|
|
|
|
|
|
my %p = validate( |
2124
|
|
|
|
|
|
|
@_, |
2125
|
|
|
|
|
|
|
{ 'timeout' => { type => SCALAR, optional => 1 }, |
2126
|
|
|
|
|
|
|
%std_opts, |
2127
|
|
|
|
|
|
|
}, |
2128
|
|
|
|
|
|
|
); |
2129
|
|
|
|
|
|
|
|
2130
|
|
|
|
|
|
|
my %args = ( debug => $p{debug}, fatal => $p{fatal} ); |
2131
|
|
|
|
|
|
|
|
2132
|
|
|
|
|
|
|
$log->audit("syscmd: $cmd"); |
2133
|
|
|
|
|
|
|
|
2134
|
|
|
|
|
|
|
my ( $is_safe, $tainted, $bin, @args ); |
2135
|
|
|
|
|
|
|
|
2136
|
|
|
|
|
|
|
# separate the program from its arguments |
2137
|
|
|
|
|
|
|
if ( $cmd =~ m/\s+/xm ) { |
2138
|
|
|
|
|
|
|
($cmd) = $cmd =~ /^\s*(.*?)\s*$/; # trim lead/trailing whitespace |
2139
|
|
|
|
|
|
|
@args = split /\s+/, $cmd; # split on whitespace |
2140
|
|
|
|
|
|
|
$bin = shift @args; |
2141
|
|
|
|
|
|
|
$is_safe++; |
2142
|
|
|
|
|
|
|
$log->audit("\tprogram: $bin, args : " . join ' ', @args ); |
2143
|
|
|
|
|
|
|
} |
2144
|
|
|
|
|
|
|
else { |
2145
|
|
|
|
|
|
|
# does not not contain a ./ pattern |
2146
|
|
|
|
|
|
|
if ( $cmd !~ m{\./} ) { $bin = $cmd; $is_safe++; }; |
2147
|
|
|
|
|
|
|
} |
2148
|
|
|
|
|
|
|
|
2149
|
|
|
|
|
|
|
if ( $is_safe && !$bin ) { |
2150
|
|
|
|
|
|
|
return $log->error("command is not safe! BAILING OUT!", %args); |
2151
|
|
|
|
|
|
|
} |
2152
|
|
|
|
|
|
|
|
2153
|
|
|
|
|
|
|
my $message; |
2154
|
|
|
|
|
|
|
$message .= "syscmd: bin is <$bin>" if $bin; |
2155
|
|
|
|
|
|
|
$message .= " (safe)" if $is_safe; |
2156
|
|
|
|
|
|
|
$log->audit($message, %args ); |
2157
|
|
|
|
|
|
|
|
2158
|
|
|
|
|
|
|
if ( $bin && !-e $bin ) { # $bin is set, but we have not found it |
2159
|
|
|
|
|
|
|
$bin = $self->find_bin( $bin, fatal => 0, debug => 0 ) |
2160
|
|
|
|
|
|
|
or return $log->error( "$bin was not found", %args); |
2161
|
|
|
|
|
|
|
} |
2162
|
|
|
|
|
|
|
unshift @args, $bin; |
2163
|
|
|
|
|
|
|
|
2164
|
|
|
|
|
|
|
require Scalar::Util; |
2165
|
|
|
|
|
|
|
$tainted++ if Scalar::Util::tainted($cmd); |
2166
|
|
|
|
|
|
|
|
2167
|
|
|
|
|
|
|
my $before_path = $ENV{PATH}; |
2168
|
|
|
|
|
|
|
|
2169
|
|
|
|
|
|
|
# instead of dying, maybe try setting a |
2170
|
|
|
|
|
|
|
# very restrictive PATH? I'll err on the side of safety |
2171
|
|
|
|
|
|
|
# $ENV{PATH} = ''; |
2172
|
|
|
|
|
|
|
return $log->error( "syscmd request has tainted data", %args) |
2173
|
|
|
|
|
|
|
if ( $tainted && !$is_safe ); |
2174
|
|
|
|
|
|
|
|
2175
|
|
|
|
|
|
|
if ($is_safe) { |
2176
|
|
|
|
|
|
|
my $prefix = "/usr/local"; # restrict the path |
2177
|
|
|
|
|
|
|
$prefix = "/opt/local" if -d "/opt/local"; |
2178
|
|
|
|
|
|
|
$ENV{PATH} = "/bin:/sbin:/usr/bin:/usr/sbin:$prefix/bin:$prefix/sbin"; |
2179
|
|
|
|
|
|
|
} |
2180
|
|
|
|
|
|
|
|
2181
|
|
|
|
|
|
|
my $r; |
2182
|
|
|
|
|
|
|
eval { |
2183
|
|
|
|
|
|
|
if ( defined $p{timeout} ) { |
2184
|
|
|
|
|
|
|
local $SIG{ALRM} = sub { die "alarm\n" }; |
2185
|
|
|
|
|
|
|
alarm $p{timeout}; |
2186
|
|
|
|
|
|
|
}; |
2187
|
|
|
|
|
|
|
#$r = system $cmd; |
2188
|
|
|
|
|
|
|
$r = `$cmd 2>&1`; |
2189
|
|
|
|
|
|
|
alarm 0 if defined $p{timeout}; |
2190
|
|
|
|
|
|
|
}; |
2191
|
|
|
|
|
|
|
|
2192
|
|
|
|
|
|
|
if ($EVAL_ERROR) { |
2193
|
|
|
|
|
|
|
if ( $EVAL_ERROR eq "alarm\n" ) { |
2194
|
|
|
|
|
|
|
$log->audit("timed out"); |
2195
|
|
|
|
|
|
|
} |
2196
|
|
|
|
|
|
|
else { |
2197
|
|
|
|
|
|
|
return $log->error( "unknown error '$EVAL_ERROR'", %args); |
2198
|
|
|
|
|
|
|
} |
2199
|
|
|
|
|
|
|
} |
2200
|
|
|
|
|
|
|
$ENV{PATH} = $before_path; # set PATH back to original value |
2201
|
|
|
|
|
|
|
|
2202
|
|
|
|
|
|
|
my @caller = caller; |
2203
|
|
|
|
|
|
|
return $self->syscmd_exit_code( $r, $CHILD_ERROR, \@caller, \%args ); |
2204
|
|
|
|
|
|
|
} |
2205
|
|
|
|
|
|
|
|
2206
|
|
|
|
|
|
|
sub syscmd_exit_code { |
2207
|
|
|
|
|
|
|
my $self = shift; |
2208
|
|
|
|
|
|
|
my ($r, $err, $caller, $args) = @_; |
2209
|
|
|
|
|
|
|
|
2210
|
|
|
|
|
|
|
$log->audit( "r: $r" ); |
2211
|
|
|
|
|
|
|
|
2212
|
|
|
|
|
|
|
my $exit_code = sprintf ("%d", $err >> 8); |
2213
|
|
|
|
|
|
|
return 1 if $exit_code == 0; # success |
2214
|
|
|
|
|
|
|
|
2215
|
|
|
|
|
|
|
#print 'error # ' . $ERRNO . "\n"; # $! == $ERRNO |
2216
|
|
|
|
|
|
|
$log->error( "$err: $r",fatal=>0); |
2217
|
|
|
|
|
|
|
|
2218
|
|
|
|
|
|
|
if ( $err == -1 ) { # check $? for "normal" errors |
2219
|
|
|
|
|
|
|
$log->error( "failed to execute: $ERRNO", fatal=>0); |
2220
|
|
|
|
|
|
|
} |
2221
|
|
|
|
|
|
|
elsif ( $err & 127 ) { # check for core dump |
2222
|
|
|
|
|
|
|
printf "child died with signal %d, %s coredump\n", ( $? & 127 ), |
2223
|
|
|
|
|
|
|
( $? & 128 ) ? 'with' : 'without'; |
2224
|
|
|
|
|
|
|
} |
2225
|
|
|
|
|
|
|
|
2226
|
|
|
|
|
|
|
return $log->error( "$err: $r", location => join( ", ", @$caller ), %$args ); |
2227
|
|
|
|
|
|
|
}; |
2228
|
|
|
|
|
|
|
|
2229
|
|
|
|
|
|
|
sub yes_or_no { |
2230
|
|
|
|
|
|
|
my $self = shift; |
2231
|
|
|
|
|
|
|
my $question = shift; |
2232
|
|
|
|
|
|
|
my %p = validate( |
2233
|
|
|
|
|
|
|
@_, |
2234
|
|
|
|
|
|
|
{ 'timeout' => { type => SCALAR, optional => 1 }, |
2235
|
|
|
|
|
|
|
'debug' => { type => BOOLEAN, optional => 1, default => 1 }, |
2236
|
|
|
|
|
|
|
'force' => { type => BOOLEAN, optional => 1, default => 0 }, |
2237
|
|
|
|
|
|
|
}, |
2238
|
|
|
|
|
|
|
); |
2239
|
|
|
|
|
|
|
|
2240
|
|
|
|
|
|
|
|
2241
|
|
|
|
|
|
|
# for 'make test' testing |
2242
|
|
|
|
|
|
|
return 1 if $question eq "test"; |
2243
|
|
|
|
|
|
|
|
2244
|
|
|
|
|
|
|
# force if interactivity testing is not working properly. |
2245
|
|
|
|
|
|
|
if ( !$p{force} && !$self->is_interactive ) { |
2246
|
|
|
|
|
|
|
warn "not running interactively, can't prompt!"; |
2247
|
|
|
|
|
|
|
return; |
2248
|
|
|
|
|
|
|
} |
2249
|
|
|
|
|
|
|
|
2250
|
|
|
|
|
|
|
my $response; |
2251
|
|
|
|
|
|
|
|
2252
|
|
|
|
|
|
|
print "\nYou have $p{timeout} seconds to respond.\n" if $p{timeout}; |
2253
|
|
|
|
|
|
|
print "\n\t\t$question"; |
2254
|
|
|
|
|
|
|
|
2255
|
|
|
|
|
|
|
# I wish I knew why this is not working correctly |
2256
|
|
|
|
|
|
|
# eval { local $SIG{__DIE__}; require Term::ReadKey }; |
2257
|
|
|
|
|
|
|
# if ($@) { # |
2258
|
|
|
|
|
|
|
# require Term::ReadKey; |
2259
|
|
|
|
|
|
|
# Term::ReadKey->import(); |
2260
|
|
|
|
|
|
|
# print "yay, Term::ReadKey is present! Are you pleased? (y/n):\n"; |
2261
|
|
|
|
|
|
|
# use Term::Readkey; |
2262
|
|
|
|
|
|
|
# ReadMode 4; |
2263
|
|
|
|
|
|
|
# while ( not defined ($key = ReadKey(-1))) |
2264
|
|
|
|
|
|
|
# { # no key yet } |
2265
|
|
|
|
|
|
|
# print "Got key $key\n"; |
2266
|
|
|
|
|
|
|
# ReadMode 0; |
2267
|
|
|
|
|
|
|
# }; |
2268
|
|
|
|
|
|
|
|
2269
|
|
|
|
|
|
|
if ( $p{timeout} ) { |
2270
|
|
|
|
|
|
|
eval { |
2271
|
|
|
|
|
|
|
local $SIG{ALRM} = sub { die "alarm\n" }; |
2272
|
|
|
|
|
|
|
alarm $p{timeout}; |
2273
|
|
|
|
|
|
|
do { |
2274
|
|
|
|
|
|
|
print "(y/n): "; |
2275
|
|
|
|
|
|
|
$response = lc(<STDIN>); |
2276
|
|
|
|
|
|
|
chomp($response); |
2277
|
|
|
|
|
|
|
} until ( $response eq "n" || $response eq "y" ); |
2278
|
|
|
|
|
|
|
alarm 0; |
2279
|
|
|
|
|
|
|
}; |
2280
|
|
|
|
|
|
|
|
2281
|
|
|
|
|
|
|
if ($@) { |
2282
|
|
|
|
|
|
|
$@ eq "alarm\n" ? print "timed out!\n" : warn; |
2283
|
|
|
|
|
|
|
} |
2284
|
|
|
|
|
|
|
|
2285
|
|
|
|
|
|
|
return ($response && $response eq "y") ? 1 : 0; |
2286
|
|
|
|
|
|
|
} |
2287
|
|
|
|
|
|
|
|
2288
|
|
|
|
|
|
|
do { |
2289
|
|
|
|
|
|
|
print "(y/n): "; |
2290
|
|
|
|
|
|
|
$response = lc(<STDIN>); |
2291
|
|
|
|
|
|
|
chomp($response); |
2292
|
|
|
|
|
|
|
} until ( $response eq "n" || $response eq "y" ); |
2293
|
|
|
|
|
|
|
|
2294
|
|
|
|
|
|
|
return ($response eq "y") ? 1 : 0; |
2295
|
|
|
|
|
|
|
} |
2296
|
|
|
|
|
|
|
|
2297
|
|
|
|
|
|
|
1; |
2298
|
|
|
|
|
|
|
|
2299
|
|
|
|
|
|
|
__END__ |
2300
|
|
|
|
|
|
|
|
2301
|
|
|
|
|
|
|
=pod |
2302
|
|
|
|
|
|
|
|
2303
|
|
|
|
|
|
|
=encoding UTF-8 |
2304
|
|
|
|
|
|
|
|
2305
|
|
|
|
|
|
|
=head1 NAME |
2306
|
|
|
|
|
|
|
|
2307
|
|
|
|
|
|
|
Provision::Unix::Utility - utility subroutines for sysadmin tasks |
2308
|
|
|
|
|
|
|
|
2309
|
|
|
|
|
|
|
=head1 VERSION |
2310
|
|
|
|
|
|
|
|
2311
|
|
|
|
|
|
|
version 1.07 |
2312
|
|
|
|
|
|
|
|
2313
|
|
|
|
|
|
|
=head1 SYNOPSIS |
2314
|
|
|
|
|
|
|
|
2315
|
|
|
|
|
|
|
use Provision::Unix::Utility; |
2316
|
|
|
|
|
|
|
my $util = Provision::Unix::Utility->new; |
2317
|
|
|
|
|
|
|
|
2318
|
|
|
|
|
|
|
$util->file_write($file, lines=> @lines); |
2319
|
|
|
|
|
|
|
|
2320
|
|
|
|
|
|
|
This is just one of the many handy little methods I have amassed here. Rather than try to remember all of the best ways to code certain functions and then attempt to remember them, I have consolidated years of experience and countless references from Learning Perl, Programming Perl, Perl Best Practices, and many other sources into these subroutines. |
2321
|
|
|
|
|
|
|
|
2322
|
|
|
|
|
|
|
=head1 DESCRIPTION |
2323
|
|
|
|
|
|
|
|
2324
|
|
|
|
|
|
|
This Utility module is my most frequently used one. Each method has documentation but in general, all methods accept as input a list of key value pairs (named parameters). |
2325
|
|
|
|
|
|
|
|
2326
|
|
|
|
|
|
|
=head1 DIAGNOSTICS |
2327
|
|
|
|
|
|
|
|
2328
|
|
|
|
|
|
|
All methods set and return error codes (0 = fail, 1 = success) unless otherwise stated. |
2329
|
|
|
|
|
|
|
|
2330
|
|
|
|
|
|
|
Unless otherwise mentioned, all methods accept two additional parameters: |
2331
|
|
|
|
|
|
|
|
2332
|
|
|
|
|
|
|
debug - to print status and verbose error messages, set debug=>1. |
2333
|
|
|
|
|
|
|
fatal - die on errors. This is the default, set fatal=>0 to override. |
2334
|
|
|
|
|
|
|
|
2335
|
|
|
|
|
|
|
=head1 DEPENDENCIES |
2336
|
|
|
|
|
|
|
|
2337
|
|
|
|
|
|
|
Perl. |
2338
|
|
|
|
|
|
|
Scalar::Util - built-in as of perl 5.8 |
2339
|
|
|
|
|
|
|
|
2340
|
|
|
|
|
|
|
Almost nothing else. A few of the methods do require certain things, like extract_archive requires tar and file. But in general, this package (Provision::Unix::Utility) should run flawlessly on any UNIX-like system. Because I recycle this package in other places (not just Provision::Unix), I avoid creating dependencies here. |
2341
|
|
|
|
|
|
|
|
2342
|
|
|
|
|
|
|
=head1 METHODS |
2343
|
|
|
|
|
|
|
|
2344
|
|
|
|
|
|
|
=over |
2345
|
|
|
|
|
|
|
|
2346
|
|
|
|
|
|
|
=item new |
2347
|
|
|
|
|
|
|
|
2348
|
|
|
|
|
|
|
To use any of the methods below, you must first create a utility object. The methods can be accessed via the utility object. |
2349
|
|
|
|
|
|
|
|
2350
|
|
|
|
|
|
|
############################################ |
2351
|
|
|
|
|
|
|
# Usage : use Provision::Unix::Utility; |
2352
|
|
|
|
|
|
|
# : my $util = Provision::Unix::Utility->new; |
2353
|
|
|
|
|
|
|
# Purpose : create a new Provision::Unix::Utility object |
2354
|
|
|
|
|
|
|
# Returns : a bona fide object |
2355
|
|
|
|
|
|
|
# Parameters : none |
2356
|
|
|
|
|
|
|
############################################ |
2357
|
|
|
|
|
|
|
|
2358
|
|
|
|
|
|
|
=item ask |
2359
|
|
|
|
|
|
|
|
2360
|
|
|
|
|
|
|
Get a response from the user. If the user responds, their response is returned. If not, then the default response is returned. If no default was supplied, 0 is returned. |
2361
|
|
|
|
|
|
|
|
2362
|
|
|
|
|
|
|
############################################ |
2363
|
|
|
|
|
|
|
# Usage : my $ask = $util->ask( "Would you like fries with that", |
2364
|
|
|
|
|
|
|
# default => "SuperSized!", |
2365
|
|
|
|
|
|
|
# timeout => 30 |
2366
|
|
|
|
|
|
|
# ); |
2367
|
|
|
|
|
|
|
# Purpose : prompt the user for information |
2368
|
|
|
|
|
|
|
# |
2369
|
|
|
|
|
|
|
# Returns : S - the users response (if not empty) or |
2370
|
|
|
|
|
|
|
# : S - the default ask or |
2371
|
|
|
|
|
|
|
# : S - an empty string |
2372
|
|
|
|
|
|
|
# |
2373
|
|
|
|
|
|
|
# Parameters |
2374
|
|
|
|
|
|
|
# Required : S - question - what to ask |
2375
|
|
|
|
|
|
|
# Optional : S - default - a default answer |
2376
|
|
|
|
|
|
|
# : I - timeout - how long to wait for a response |
2377
|
|
|
|
|
|
|
# Throws : no exceptions |
2378
|
|
|
|
|
|
|
# See Also : yes_or_no |
2379
|
|
|
|
|
|
|
|
2380
|
|
|
|
|
|
|
=item extract_archive |
2381
|
|
|
|
|
|
|
|
2382
|
|
|
|
|
|
|
Decompresses a variety of archive formats using your systems built in tools. |
2383
|
|
|
|
|
|
|
|
2384
|
|
|
|
|
|
|
############### extract_archive ################## |
2385
|
|
|
|
|
|
|
# Usage : $util->extract_archive( 'example.tar.bz2' ); |
2386
|
|
|
|
|
|
|
# Purpose : test the archiver, determine its contents, and then |
2387
|
|
|
|
|
|
|
# use the best available means to expand it. |
2388
|
|
|
|
|
|
|
# Returns : 0 - failure, 1 - success |
2389
|
|
|
|
|
|
|
# Parameters : S - archive - a bz2, gz, or tgz file to decompress |
2390
|
|
|
|
|
|
|
|
2391
|
|
|
|
|
|
|
=item cwd_source_dir |
2392
|
|
|
|
|
|
|
|
2393
|
|
|
|
|
|
|
Changes the current working directory to the supplied one. Creates it if it does not exist. Tries to create the directory using perl's builtin mkdir, then the system mkdir, and finally the system mkdir with sudo. |
2394
|
|
|
|
|
|
|
|
2395
|
|
|
|
|
|
|
############ cwd_source_dir ################### |
2396
|
|
|
|
|
|
|
# Usage : $util->cwd_source_dir( "/usr/local/src" ); |
2397
|
|
|
|
|
|
|
# Purpose : prepare a location to build source files in |
2398
|
|
|
|
|
|
|
# Returns : 0 - failure, 1 - success |
2399
|
|
|
|
|
|
|
# Parameters : S - dir - a directory to build programs in |
2400
|
|
|
|
|
|
|
|
2401
|
|
|
|
|
|
|
=item check_homedir_ownership |
2402
|
|
|
|
|
|
|
|
2403
|
|
|
|
|
|
|
Checks the ownership on all home directories to see if they are owned by their respective users in /etc/password. Offers to repair the permissions on incorrectly owned directories. This is useful when someone that knows better does something like "chown -R user /home /user" and fouls things up. |
2404
|
|
|
|
|
|
|
|
2405
|
|
|
|
|
|
|
######### check_homedir_ownership ############ |
2406
|
|
|
|
|
|
|
# Usage : $util->check_homedir_ownership(); |
2407
|
|
|
|
|
|
|
# Purpose : repair user homedir ownership |
2408
|
|
|
|
|
|
|
# Returns : 0 - failure, 1 - success |
2409
|
|
|
|
|
|
|
# Parameters : |
2410
|
|
|
|
|
|
|
# Optional : I - auto - no prompts, just fix everything |
2411
|
|
|
|
|
|
|
# See Also : sysadmin |
2412
|
|
|
|
|
|
|
|
2413
|
|
|
|
|
|
|
Comments: Auto mode should be run with great caution. Run it first to see the results and then, if everything looks good, run in auto mode to do the actual repairs. |
2414
|
|
|
|
|
|
|
|
2415
|
|
|
|
|
|
|
=item check_pidfile |
2416
|
|
|
|
|
|
|
|
2417
|
|
|
|
|
|
|
check_pidfile is a process management method. It will check to make sure an existing pidfile does not exist and if not, it will create the pidfile. |
2418
|
|
|
|
|
|
|
|
2419
|
|
|
|
|
|
|
$pidfile = $util->check_pidfile( "/var/run/program.pid" ); |
2420
|
|
|
|
|
|
|
|
2421
|
|
|
|
|
|
|
The above example is all you need to do to add process checking (avoiding multiple daemons running at the same time) to a program or script. This is used in toaster-watcher.pl. toaster-watcher normally completes a run in a few seconds and is run every 5 minutes. |
2422
|
|
|
|
|
|
|
|
2423
|
|
|
|
|
|
|
However, toaster-watcher can be configured to do things like expire old messages from maildirs and feed spam through a processor like sa-learn. This can take a long time on a large mail system so we don't want multiple instances of toaster-watcher running. |
2424
|
|
|
|
|
|
|
|
2425
|
|
|
|
|
|
|
result: |
2426
|
|
|
|
|
|
|
the path to the pidfile (on success). |
2427
|
|
|
|
|
|
|
|
2428
|
|
|
|
|
|
|
Example: |
2429
|
|
|
|
|
|
|
|
2430
|
|
|
|
|
|
|
my $pidfile = $util->check_pidfile( "/var/run/changeme.pid" ); |
2431
|
|
|
|
|
|
|
unless ($pidfile) { |
2432
|
|
|
|
|
|
|
warn "WARNING: couldn't create a process id file!: $!\n"; |
2433
|
|
|
|
|
|
|
exit 0; |
2434
|
|
|
|
|
|
|
}; |
2435
|
|
|
|
|
|
|
|
2436
|
|
|
|
|
|
|
do_a_bunch_of_cool_stuff; |
2437
|
|
|
|
|
|
|
unlink $pidfile; |
2438
|
|
|
|
|
|
|
|
2439
|
|
|
|
|
|
|
=item chown_system |
2440
|
|
|
|
|
|
|
|
2441
|
|
|
|
|
|
|
The advantage this sub has over a Pure Perl implementation is that it can utilize sudo to gain elevated permissions that we might not otherwise have. |
2442
|
|
|
|
|
|
|
|
2443
|
|
|
|
|
|
|
############### chown_system ################# |
2444
|
|
|
|
|
|
|
# Usage : $util->chown_system( "/tmp/example", user=>'matt' ); |
2445
|
|
|
|
|
|
|
# Purpose : change the ownership of a file or directory |
2446
|
|
|
|
|
|
|
# Returns : 0 - failure, 1 - success |
2447
|
|
|
|
|
|
|
# Parameters : S - dir - the directory to chown |
2448
|
|
|
|
|
|
|
# : S - user - a system username |
2449
|
|
|
|
|
|
|
# Optional : S - group - a sytem group name |
2450
|
|
|
|
|
|
|
# : I - recurse - include all files/folders in directory? |
2451
|
|
|
|
|
|
|
# Comments : Uses the system chown binary |
2452
|
|
|
|
|
|
|
# See Also : n/a |
2453
|
|
|
|
|
|
|
|
2454
|
|
|
|
|
|
|
=item clean_tmp_dir |
2455
|
|
|
|
|
|
|
|
2456
|
|
|
|
|
|
|
############## clean_tmp_dir ################ |
2457
|
|
|
|
|
|
|
# Usage : $util->clean_tmp_dir( dir=>$dir ); |
2458
|
|
|
|
|
|
|
# Purpose : clean up old build stuff before rebuilding |
2459
|
|
|
|
|
|
|
# Returns : 0 - failure, 1 - success |
2460
|
|
|
|
|
|
|
# Parameters : S - $dir - a directory or file. |
2461
|
|
|
|
|
|
|
# Throws : die on failure |
2462
|
|
|
|
|
|
|
# Comments : Running this will delete its contents. Be careful! |
2463
|
|
|
|
|
|
|
|
2464
|
|
|
|
|
|
|
=item get_mounted_drives |
2465
|
|
|
|
|
|
|
|
2466
|
|
|
|
|
|
|
############# get_mounted_drives ############ |
2467
|
|
|
|
|
|
|
# Usage : my $mounts = $util->get_mounted_drives(); |
2468
|
|
|
|
|
|
|
# Purpose : Uses mount to fetch a list of mounted drive/partitions |
2469
|
|
|
|
|
|
|
# Returns : a hashref of mounted slices and their mount points. |
2470
|
|
|
|
|
|
|
|
2471
|
|
|
|
|
|
|
=item archive_file |
2472
|
|
|
|
|
|
|
|
2473
|
|
|
|
|
|
|
############### archive_file ################# |
2474
|
|
|
|
|
|
|
# Purpose : Make a backup copy of a file by copying the file to $file.timestamp. |
2475
|
|
|
|
|
|
|
# Usage : my $archived_file = $util->archive_file( $file ); |
2476
|
|
|
|
|
|
|
# Returns : the filename of the backup file, or 0 on failure. |
2477
|
|
|
|
|
|
|
# Parameters : S - file - the filname to be backed up |
2478
|
|
|
|
|
|
|
# Comments : none |
2479
|
|
|
|
|
|
|
|
2480
|
|
|
|
|
|
|
=item chmod |
2481
|
|
|
|
|
|
|
|
2482
|
|
|
|
|
|
|
Set the permissions (ugo-rwx) of a file. Will use the native perl methods (by default) but can also use system calls and prepend sudo if additional permissions are needed. |
2483
|
|
|
|
|
|
|
|
2484
|
|
|
|
|
|
|
$util->chmod( |
2485
|
|
|
|
|
|
|
file_or_dir => '/etc/resolv.conf', |
2486
|
|
|
|
|
|
|
mode => '0755', |
2487
|
|
|
|
|
|
|
sudo => $sudo |
2488
|
|
|
|
|
|
|
) |
2489
|
|
|
|
|
|
|
|
2490
|
|
|
|
|
|
|
arguments required: |
2491
|
|
|
|
|
|
|
file_or_dir - a file or directory to alter permission on |
2492
|
|
|
|
|
|
|
mode - the permissions (numeric) |
2493
|
|
|
|
|
|
|
|
2494
|
|
|
|
|
|
|
arguments optional: |
2495
|
|
|
|
|
|
|
sudo - the output of $util->sudo |
2496
|
|
|
|
|
|
|
fatal - die on errors? (default: on) |
2497
|
|
|
|
|
|
|
debug |
2498
|
|
|
|
|
|
|
|
2499
|
|
|
|
|
|
|
result: |
2500
|
|
|
|
|
|
|
0 - failure |
2501
|
|
|
|
|
|
|
1 - success |
2502
|
|
|
|
|
|
|
|
2503
|
|
|
|
|
|
|
=item chown |
2504
|
|
|
|
|
|
|
|
2505
|
|
|
|
|
|
|
Set the ownership (user and group) of a file. Will use the native perl methods (by default) but can also use system calls and prepend sudo if additional permissions are needed. |
2506
|
|
|
|
|
|
|
|
2507
|
|
|
|
|
|
|
$util->chown( |
2508
|
|
|
|
|
|
|
file_or_dir => '/etc/resolv.conf', |
2509
|
|
|
|
|
|
|
uid => 'root', |
2510
|
|
|
|
|
|
|
gid => 'wheel', |
2511
|
|
|
|
|
|
|
sudo => 1 |
2512
|
|
|
|
|
|
|
); |
2513
|
|
|
|
|
|
|
|
2514
|
|
|
|
|
|
|
arguments required: |
2515
|
|
|
|
|
|
|
file_or_dir - a file or directory to alter permission on |
2516
|
|
|
|
|
|
|
uid - the uid or user name |
2517
|
|
|
|
|
|
|
gid - the gid or group name |
2518
|
|
|
|
|
|
|
|
2519
|
|
|
|
|
|
|
arguments optional: |
2520
|
|
|
|
|
|
|
file - alias for file_or_dir |
2521
|
|
|
|
|
|
|
dir - alias for file_or_dir |
2522
|
|
|
|
|
|
|
sudo - the output of $util->sudo |
2523
|
|
|
|
|
|
|
fatal - die on errors? (default: on) |
2524
|
|
|
|
|
|
|
debug |
2525
|
|
|
|
|
|
|
|
2526
|
|
|
|
|
|
|
result: |
2527
|
|
|
|
|
|
|
0 - failure |
2528
|
|
|
|
|
|
|
1 - success |
2529
|
|
|
|
|
|
|
|
2530
|
|
|
|
|
|
|
=item file_delete |
2531
|
|
|
|
|
|
|
|
2532
|
|
|
|
|
|
|
############################################ |
2533
|
|
|
|
|
|
|
# Usage : $util->file_delete( file=>$file ); |
2534
|
|
|
|
|
|
|
# Purpose : Deletes a file. |
2535
|
|
|
|
|
|
|
# Returns : 0 - failure, 1 - success |
2536
|
|
|
|
|
|
|
# Parameters |
2537
|
|
|
|
|
|
|
# Required : file - a file path |
2538
|
|
|
|
|
|
|
# Comments : none |
2539
|
|
|
|
|
|
|
# See Also : |
2540
|
|
|
|
|
|
|
|
2541
|
|
|
|
|
|
|
Uses unlink if we have appropriate permissions, otherwise uses a system rm call, using sudo if it is not being run as root. This sub will try very hard to delete the file! |
2542
|
|
|
|
|
|
|
|
2543
|
|
|
|
|
|
|
=item get_url |
2544
|
|
|
|
|
|
|
|
2545
|
|
|
|
|
|
|
$util->get_url( $url, debug=>1 ); |
2546
|
|
|
|
|
|
|
|
2547
|
|
|
|
|
|
|
Use the standard URL fetching utility (fetch, curl, wget) for your OS to download a file from the $url handed to us. |
2548
|
|
|
|
|
|
|
|
2549
|
|
|
|
|
|
|
arguments required: |
2550
|
|
|
|
|
|
|
url - the fully qualified URL |
2551
|
|
|
|
|
|
|
|
2552
|
|
|
|
|
|
|
arguments optional: |
2553
|
|
|
|
|
|
|
timeout - the maximum amount of time to try |
2554
|
|
|
|
|
|
|
fatal |
2555
|
|
|
|
|
|
|
debug |
2556
|
|
|
|
|
|
|
|
2557
|
|
|
|
|
|
|
result: |
2558
|
|
|
|
|
|
|
1 - success |
2559
|
|
|
|
|
|
|
0 - failure |
2560
|
|
|
|
|
|
|
|
2561
|
|
|
|
|
|
|
=item file_is_newer |
2562
|
|
|
|
|
|
|
|
2563
|
|
|
|
|
|
|
compares the mtime on two files to determine if one is newer than another. |
2564
|
|
|
|
|
|
|
|
2565
|
|
|
|
|
|
|
=item file_mode |
2566
|
|
|
|
|
|
|
|
2567
|
|
|
|
|
|
|
usage: |
2568
|
|
|
|
|
|
|
my @lines = "1", "2", "3"; # named array |
2569
|
|
|
|
|
|
|
$util->file_write ( "/tmp/foo", lines=>\@lines ); |
2570
|
|
|
|
|
|
|
or |
2571
|
|
|
|
|
|
|
$util->file_write ( "/tmp/foo", lines=>['1','2','3'] ); # anon arrayref |
2572
|
|
|
|
|
|
|
|
2573
|
|
|
|
|
|
|
required arguments: |
2574
|
|
|
|
|
|
|
mode - the files permissions mode |
2575
|
|
|
|
|
|
|
|
2576
|
|
|
|
|
|
|
arguments optional: |
2577
|
|
|
|
|
|
|
fatal |
2578
|
|
|
|
|
|
|
debug |
2579
|
|
|
|
|
|
|
|
2580
|
|
|
|
|
|
|
result: |
2581
|
|
|
|
|
|
|
0 - failure |
2582
|
|
|
|
|
|
|
1 - success |
2583
|
|
|
|
|
|
|
|
2584
|
|
|
|
|
|
|
=item file_read |
2585
|
|
|
|
|
|
|
|
2586
|
|
|
|
|
|
|
Reads in a file, and returns it in an array. All lines in the array are chomped. |
2587
|
|
|
|
|
|
|
|
2588
|
|
|
|
|
|
|
my @lines = $util->file_read( $file, max_lines=>100 ) |
2589
|
|
|
|
|
|
|
|
2590
|
|
|
|
|
|
|
arguments required: |
2591
|
|
|
|
|
|
|
file - the file to read in |
2592
|
|
|
|
|
|
|
|
2593
|
|
|
|
|
|
|
arguments optional: |
2594
|
|
|
|
|
|
|
max_lines - integer - max number of lines |
2595
|
|
|
|
|
|
|
max_length - integer - maximum length of a line |
2596
|
|
|
|
|
|
|
fatal |
2597
|
|
|
|
|
|
|
debug |
2598
|
|
|
|
|
|
|
|
2599
|
|
|
|
|
|
|
result: |
2600
|
|
|
|
|
|
|
0 - failure |
2601
|
|
|
|
|
|
|
success - returns an array with the files contents, one line per array element |
2602
|
|
|
|
|
|
|
|
2603
|
|
|
|
|
|
|
=item file_write |
2604
|
|
|
|
|
|
|
|
2605
|
|
|
|
|
|
|
usage: |
2606
|
|
|
|
|
|
|
my @lines = "1", "2", "3"; # named array |
2607
|
|
|
|
|
|
|
$util->file_write ( "/tmp/foo", lines=>\@lines ); |
2608
|
|
|
|
|
|
|
or |
2609
|
|
|
|
|
|
|
$util->file_write ( "/tmp/foo", lines=>['1','2','3'] ); # anon arrayref |
2610
|
|
|
|
|
|
|
|
2611
|
|
|
|
|
|
|
required arguments: |
2612
|
|
|
|
|
|
|
file - the file path you want to write to |
2613
|
|
|
|
|
|
|
lines - an arrayref. Each array element will be a line in the file |
2614
|
|
|
|
|
|
|
|
2615
|
|
|
|
|
|
|
arguments optional: |
2616
|
|
|
|
|
|
|
fatal |
2617
|
|
|
|
|
|
|
debug |
2618
|
|
|
|
|
|
|
|
2619
|
|
|
|
|
|
|
result: |
2620
|
|
|
|
|
|
|
0 - failure |
2621
|
|
|
|
|
|
|
1 - success |
2622
|
|
|
|
|
|
|
|
2623
|
|
|
|
|
|
|
=item files_diff |
2624
|
|
|
|
|
|
|
|
2625
|
|
|
|
|
|
|
Determine if the files are different. $type is assumed to be text unless you set it otherwise. For anthing but text files, we do a MD5 checksum on the files to determine if they are different or not. |
2626
|
|
|
|
|
|
|
|
2627
|
|
|
|
|
|
|
$util->files_diff( f1=>$file1,f2=>$file2,type=>'text',debug=>1 ); |
2628
|
|
|
|
|
|
|
|
2629
|
|
|
|
|
|
|
if ( $util->files_diff( f1=>"foo", f2=>"bar" ) ) |
2630
|
|
|
|
|
|
|
{ |
2631
|
|
|
|
|
|
|
print "different!\n"; |
2632
|
|
|
|
|
|
|
}; |
2633
|
|
|
|
|
|
|
|
2634
|
|
|
|
|
|
|
required arguments: |
2635
|
|
|
|
|
|
|
f1 - the first file to compare |
2636
|
|
|
|
|
|
|
f2 - the second file to compare |
2637
|
|
|
|
|
|
|
|
2638
|
|
|
|
|
|
|
arguments optional: |
2639
|
|
|
|
|
|
|
type - the type of file (text or binary) |
2640
|
|
|
|
|
|
|
fatal |
2641
|
|
|
|
|
|
|
debug |
2642
|
|
|
|
|
|
|
|
2643
|
|
|
|
|
|
|
result: |
2644
|
|
|
|
|
|
|
0 - files are the same |
2645
|
|
|
|
|
|
|
1 - files are different |
2646
|
|
|
|
|
|
|
-1 - error. |
2647
|
|
|
|
|
|
|
|
2648
|
|
|
|
|
|
|
=item find_bin |
2649
|
|
|
|
|
|
|
|
2650
|
|
|
|
|
|
|
Check all the "normal" locations for a binary that should be on the system and returns the full path to the binary. |
2651
|
|
|
|
|
|
|
|
2652
|
|
|
|
|
|
|
$util->find_bin( 'dos2unix', dir=>'/opt/local/bin' ); |
2653
|
|
|
|
|
|
|
|
2654
|
|
|
|
|
|
|
Example: |
2655
|
|
|
|
|
|
|
|
2656
|
|
|
|
|
|
|
my $apachectl = $util->find_bin( "apachectl", dir=>"/usr/local/sbin" ); |
2657
|
|
|
|
|
|
|
|
2658
|
|
|
|
|
|
|
|
2659
|
|
|
|
|
|
|
arguments required: |
2660
|
|
|
|
|
|
|
bin - the name of the program (its filename) |
2661
|
|
|
|
|
|
|
|
2662
|
|
|
|
|
|
|
arguments optional: |
2663
|
|
|
|
|
|
|
dir - a directory to check first |
2664
|
|
|
|
|
|
|
fatal |
2665
|
|
|
|
|
|
|
debug |
2666
|
|
|
|
|
|
|
|
2667
|
|
|
|
|
|
|
results: |
2668
|
|
|
|
|
|
|
0 - failure |
2669
|
|
|
|
|
|
|
success will return the full path to the binary. |
2670
|
|
|
|
|
|
|
|
2671
|
|
|
|
|
|
|
=item get_file |
2672
|
|
|
|
|
|
|
|
2673
|
|
|
|
|
|
|
an alias for get_url for legacy purposes. Do not use. |
2674
|
|
|
|
|
|
|
|
2675
|
|
|
|
|
|
|
=item get_my_ips |
2676
|
|
|
|
|
|
|
|
2677
|
|
|
|
|
|
|
returns an arrayref of IP addresses on local interfaces. |
2678
|
|
|
|
|
|
|
|
2679
|
|
|
|
|
|
|
=item is_process_running |
2680
|
|
|
|
|
|
|
|
2681
|
|
|
|
|
|
|
Verify if a process is running or not. |
2682
|
|
|
|
|
|
|
|
2683
|
|
|
|
|
|
|
$util->is_process_running($process) ? print "yes" : print "no"; |
2684
|
|
|
|
|
|
|
|
2685
|
|
|
|
|
|
|
$process is the name as it would appear in the process table. |
2686
|
|
|
|
|
|
|
|
2687
|
|
|
|
|
|
|
=item is_readable |
2688
|
|
|
|
|
|
|
|
2689
|
|
|
|
|
|
|
############################################ |
2690
|
|
|
|
|
|
|
# Usage : $util->is_readable( file=>$file ); |
2691
|
|
|
|
|
|
|
# Purpose : ???? |
2692
|
|
|
|
|
|
|
# Returns : 0 = no (not reabable), 1 = yes |
2693
|
|
|
|
|
|
|
# Parameters : S - file - a path name to a file |
2694
|
|
|
|
|
|
|
# Throws : no exceptions |
2695
|
|
|
|
|
|
|
# Comments : none |
2696
|
|
|
|
|
|
|
# See Also : n/a |
2697
|
|
|
|
|
|
|
|
2698
|
|
|
|
|
|
|
result: |
2699
|
|
|
|
|
|
|
0 - no (file is not readable) |
2700
|
|
|
|
|
|
|
1 - yes (file is readable) |
2701
|
|
|
|
|
|
|
|
2702
|
|
|
|
|
|
|
=item is_writable |
2703
|
|
|
|
|
|
|
|
2704
|
|
|
|
|
|
|
If the file exists, it checks to see if it is writable. If the file does not exist, it checks to see if the enclosing directory is writable. |
2705
|
|
|
|
|
|
|
|
2706
|
|
|
|
|
|
|
############################################ |
2707
|
|
|
|
|
|
|
# Usage : $util->is_writable("/tmp/boogers"); |
2708
|
|
|
|
|
|
|
# Purpose : make sure a file is writable |
2709
|
|
|
|
|
|
|
# Returns : 0 - no (not writable), 1 - yes (is writeable) |
2710
|
|
|
|
|
|
|
# Parameters : S - file - a path name to a file |
2711
|
|
|
|
|
|
|
# Throws : no exceptions |
2712
|
|
|
|
|
|
|
|
2713
|
|
|
|
|
|
|
=item fstab_list |
2714
|
|
|
|
|
|
|
|
2715
|
|
|
|
|
|
|
############ fstab_list ################### |
2716
|
|
|
|
|
|
|
# Usage : $util->fstab_list; |
2717
|
|
|
|
|
|
|
# Purpose : Fetch a list of drives that are mountable from /etc/fstab. |
2718
|
|
|
|
|
|
|
# Returns : an arrayref |
2719
|
|
|
|
|
|
|
# Comments : used in backup.pl |
2720
|
|
|
|
|
|
|
# See Also : n/a |
2721
|
|
|
|
|
|
|
|
2722
|
|
|
|
|
|
|
=item get_dir_files |
2723
|
|
|
|
|
|
|
|
2724
|
|
|
|
|
|
|
$util->get_dir_files( dir=>$dir, debug=>1 ) |
2725
|
|
|
|
|
|
|
|
2726
|
|
|
|
|
|
|
required arguments: |
2727
|
|
|
|
|
|
|
dir - a directory |
2728
|
|
|
|
|
|
|
|
2729
|
|
|
|
|
|
|
optional arguments: |
2730
|
|
|
|
|
|
|
fatal |
2731
|
|
|
|
|
|
|
debug |
2732
|
|
|
|
|
|
|
|
2733
|
|
|
|
|
|
|
result: |
2734
|
|
|
|
|
|
|
an array of files names contained in that directory. |
2735
|
|
|
|
|
|
|
0 - failure |
2736
|
|
|
|
|
|
|
|
2737
|
|
|
|
|
|
|
=item get_the_date |
2738
|
|
|
|
|
|
|
|
2739
|
|
|
|
|
|
|
Returns the date split into a easy to work with set of strings. |
2740
|
|
|
|
|
|
|
|
2741
|
|
|
|
|
|
|
$util->get_the_date( bump=>$bump, debug=>$debug ) |
2742
|
|
|
|
|
|
|
|
2743
|
|
|
|
|
|
|
required arguments: |
2744
|
|
|
|
|
|
|
none |
2745
|
|
|
|
|
|
|
|
2746
|
|
|
|
|
|
|
optional arguments: |
2747
|
|
|
|
|
|
|
bump - the offset (in days) to subtract from the date. |
2748
|
|
|
|
|
|
|
debug |
2749
|
|
|
|
|
|
|
|
2750
|
|
|
|
|
|
|
result: (array with the following elements) |
2751
|
|
|
|
|
|
|
$dd = day |
2752
|
|
|
|
|
|
|
$mm = month |
2753
|
|
|
|
|
|
|
$yy = year |
2754
|
|
|
|
|
|
|
$lm = last month |
2755
|
|
|
|
|
|
|
$hh = hours |
2756
|
|
|
|
|
|
|
$mn = minutes |
2757
|
|
|
|
|
|
|
$ss = seconds |
2758
|
|
|
|
|
|
|
|
2759
|
|
|
|
|
|
|
my ($dd, $mm, $yy, $lm, $hh, $mn, $ss) = $util->get_the_date(); |
2760
|
|
|
|
|
|
|
|
2761
|
|
|
|
|
|
|
=item install_from_source |
2762
|
|
|
|
|
|
|
|
2763
|
|
|
|
|
|
|
usage: |
2764
|
|
|
|
|
|
|
|
2765
|
|
|
|
|
|
|
$util->install_from_source( |
2766
|
|
|
|
|
|
|
package => 'simscan-1.07', |
2767
|
|
|
|
|
|
|
site => 'http://www.inter7.com', |
2768
|
|
|
|
|
|
|
url => '/simscan/', |
2769
|
|
|
|
|
|
|
targets => ['./configure', 'make', 'make install'], |
2770
|
|
|
|
|
|
|
patches => '', |
2771
|
|
|
|
|
|
|
debug => 1, |
2772
|
|
|
|
|
|
|
); |
2773
|
|
|
|
|
|
|
|
2774
|
|
|
|
|
|
|
Downloads and installs a program from sources. |
2775
|
|
|
|
|
|
|
|
2776
|
|
|
|
|
|
|
required arguments: |
2777
|
|
|
|
|
|
|
conf - hashref - mail-toaster.conf settings. |
2778
|
|
|
|
|
|
|
site - |
2779
|
|
|
|
|
|
|
url - |
2780
|
|
|
|
|
|
|
package - |
2781
|
|
|
|
|
|
|
|
2782
|
|
|
|
|
|
|
optional arguments: |
2783
|
|
|
|
|
|
|
targets - arrayref - defaults to [./configure, make, make install]. |
2784
|
|
|
|
|
|
|
patches - arrayref - patch(es) to apply to the sources before compiling |
2785
|
|
|
|
|
|
|
patch_args - |
2786
|
|
|
|
|
|
|
source_sub_dir - a subdirectory within the sources build directory |
2787
|
|
|
|
|
|
|
bintest - check the usual places for an executable binary. If found, it will assume the software is already installed and require confirmation before re-installing. |
2788
|
|
|
|
|
|
|
debug |
2789
|
|
|
|
|
|
|
fatal |
2790
|
|
|
|
|
|
|
|
2791
|
|
|
|
|
|
|
result: |
2792
|
|
|
|
|
|
|
1 - success |
2793
|
|
|
|
|
|
|
0 - failure |
2794
|
|
|
|
|
|
|
|
2795
|
|
|
|
|
|
|
=item install_from_source_php |
2796
|
|
|
|
|
|
|
|
2797
|
|
|
|
|
|
|
Downloads a PHP program and installs it. This function is not completed due to lack o interest. |
2798
|
|
|
|
|
|
|
|
2799
|
|
|
|
|
|
|
=item is_interactive |
2800
|
|
|
|
|
|
|
|
2801
|
|
|
|
|
|
|
tests to determine if the running process is attached to a terminal. |
2802
|
|
|
|
|
|
|
|
2803
|
|
|
|
|
|
|
=item logfile_append |
2804
|
|
|
|
|
|
|
|
2805
|
|
|
|
|
|
|
$util->logfile_append( file=>$file, lines=>\@lines ) |
2806
|
|
|
|
|
|
|
|
2807
|
|
|
|
|
|
|
Pass a filename and an array ref and it will append a timestamp and the array contents to the file. Here's a working example: |
2808
|
|
|
|
|
|
|
|
2809
|
|
|
|
|
|
|
$util->logfile_append( file=>$file, prog=>"proggy", lines=>["Starting up", "Shutting down"] ) |
2810
|
|
|
|
|
|
|
|
2811
|
|
|
|
|
|
|
That will append a line like this to the log file: |
2812
|
|
|
|
|
|
|
|
2813
|
|
|
|
|
|
|
2004-11-12 23:20:06 proggy Starting up |
2814
|
|
|
|
|
|
|
2004-11-12 23:20:06 proggy Shutting down |
2815
|
|
|
|
|
|
|
|
2816
|
|
|
|
|
|
|
arguments required: |
2817
|
|
|
|
|
|
|
file - the log file to append to |
2818
|
|
|
|
|
|
|
prog - the name of the application |
2819
|
|
|
|
|
|
|
lines - arrayref - elements are events to log. |
2820
|
|
|
|
|
|
|
|
2821
|
|
|
|
|
|
|
arguments optional: |
2822
|
|
|
|
|
|
|
fatal |
2823
|
|
|
|
|
|
|
debug |
2824
|
|
|
|
|
|
|
|
2825
|
|
|
|
|
|
|
result: |
2826
|
|
|
|
|
|
|
1 - success |
2827
|
|
|
|
|
|
|
0 - failure |
2828
|
|
|
|
|
|
|
|
2829
|
|
|
|
|
|
|
=item mailtoaster |
2830
|
|
|
|
|
|
|
|
2831
|
|
|
|
|
|
|
$util->mailtoaster(); |
2832
|
|
|
|
|
|
|
|
2833
|
|
|
|
|
|
|
Downloads and installs Mail::Toaster. |
2834
|
|
|
|
|
|
|
|
2835
|
|
|
|
|
|
|
=item mkdir_system |
2836
|
|
|
|
|
|
|
|
2837
|
|
|
|
|
|
|
$util->mkdir_system( dir => $dir, debug=>$debug ); |
2838
|
|
|
|
|
|
|
|
2839
|
|
|
|
|
|
|
creates a directory using the system mkdir binary. Can also make levels of directories (-p) and utilize sudo if necessary to escalate. |
2840
|
|
|
|
|
|
|
|
2841
|
|
|
|
|
|
|
=item regexp_test |
2842
|
|
|
|
|
|
|
|
2843
|
|
|
|
|
|
|
Prints out a string with the regexp match bracketed. Credit to Damien Conway from Perl Best Practices. |
2844
|
|
|
|
|
|
|
|
2845
|
|
|
|
|
|
|
Example: |
2846
|
|
|
|
|
|
|
$util->regexp_test( |
2847
|
|
|
|
|
|
|
exp => 'toast', |
2848
|
|
|
|
|
|
|
string => 'mailtoaster rocks', |
2849
|
|
|
|
|
|
|
); |
2850
|
|
|
|
|
|
|
|
2851
|
|
|
|
|
|
|
arguments required: |
2852
|
|
|
|
|
|
|
exp - the regular expression |
2853
|
|
|
|
|
|
|
string - the string you are applying the regexp to |
2854
|
|
|
|
|
|
|
|
2855
|
|
|
|
|
|
|
result: |
2856
|
|
|
|
|
|
|
printed string highlighting the regexp match |
2857
|
|
|
|
|
|
|
|
2858
|
|
|
|
|
|
|
=item source_warning |
2859
|
|
|
|
|
|
|
|
2860
|
|
|
|
|
|
|
Checks to see if the old build sources are present. If they are, offer to remove them. |
2861
|
|
|
|
|
|
|
|
2862
|
|
|
|
|
|
|
Usage: |
2863
|
|
|
|
|
|
|
|
2864
|
|
|
|
|
|
|
$util->source_warning( |
2865
|
|
|
|
|
|
|
package => "Provision-Unix-0.96", |
2866
|
|
|
|
|
|
|
clean => 1, |
2867
|
|
|
|
|
|
|
src => "/usr/local/src" |
2868
|
|
|
|
|
|
|
); |
2869
|
|
|
|
|
|
|
|
2870
|
|
|
|
|
|
|
arguments required: |
2871
|
|
|
|
|
|
|
package - the name of the packages directory |
2872
|
|
|
|
|
|
|
|
2873
|
|
|
|
|
|
|
arguments optional: |
2874
|
|
|
|
|
|
|
src - the source directory to build in (/usr/local/src) |
2875
|
|
|
|
|
|
|
clean - do we try removing the existing sources? (enabled) |
2876
|
|
|
|
|
|
|
timeout - how long to wait for an answer (60 seconds) |
2877
|
|
|
|
|
|
|
|
2878
|
|
|
|
|
|
|
result: |
2879
|
|
|
|
|
|
|
1 - removed |
2880
|
|
|
|
|
|
|
0 - failure, package exists and needs to be removed. |
2881
|
|
|
|
|
|
|
|
2882
|
|
|
|
|
|
|
=item sources_get |
2883
|
|
|
|
|
|
|
|
2884
|
|
|
|
|
|
|
Tries to download a set of sources files from the site and url provided. It will try first fetching a gzipped tarball and if that files, a bzipped tarball. As new formats are introduced, I will expand the support for them here. |
2885
|
|
|
|
|
|
|
|
2886
|
|
|
|
|
|
|
usage: |
2887
|
|
|
|
|
|
|
$self->sources_get( |
2888
|
|
|
|
|
|
|
package => 'simscan-1.07', |
2889
|
|
|
|
|
|
|
site => 'http://www.inter7.com', |
2890
|
|
|
|
|
|
|
path => '/simscan/', |
2891
|
|
|
|
|
|
|
) |
2892
|
|
|
|
|
|
|
|
2893
|
|
|
|
|
|
|
arguments required: |
2894
|
|
|
|
|
|
|
package - the software package name |
2895
|
|
|
|
|
|
|
site - the host to fetch it from |
2896
|
|
|
|
|
|
|
url - the path to the package on $site |
2897
|
|
|
|
|
|
|
|
2898
|
|
|
|
|
|
|
arguments optional: |
2899
|
|
|
|
|
|
|
conf - hashref - values from toaster-watcher.conf |
2900
|
|
|
|
|
|
|
debug |
2901
|
|
|
|
|
|
|
|
2902
|
|
|
|
|
|
|
This sub proved quite useful during 2005 as many packages began to be distributed in bzip format instead of the traditional gzip. |
2903
|
|
|
|
|
|
|
|
2904
|
|
|
|
|
|
|
=item sudo |
2905
|
|
|
|
|
|
|
|
2906
|
|
|
|
|
|
|
my $sudo = $util->sudo(); |
2907
|
|
|
|
|
|
|
|
2908
|
|
|
|
|
|
|
$util->syscmd( "$sudo rm /etc/root-owned-file" ); |
2909
|
|
|
|
|
|
|
|
2910
|
|
|
|
|
|
|
Often you want to run a script as an unprivileged user. However, the script may need elevated privileges for a plethora of reasons. Rather than running the script suid, or as root, configure sudo allowing the script to run system commands with appropriate permissions. |
2911
|
|
|
|
|
|
|
|
2912
|
|
|
|
|
|
|
If sudo is not installed and you're running as root, it'll offer to install sudo for you. This is recommended, as is properly configuring sudo. |
2913
|
|
|
|
|
|
|
|
2914
|
|
|
|
|
|
|
arguments required: |
2915
|
|
|
|
|
|
|
|
2916
|
|
|
|
|
|
|
arguments optional: |
2917
|
|
|
|
|
|
|
debug |
2918
|
|
|
|
|
|
|
|
2919
|
|
|
|
|
|
|
result: |
2920
|
|
|
|
|
|
|
0 - failure |
2921
|
|
|
|
|
|
|
on success, the full path to the sudo binary |
2922
|
|
|
|
|
|
|
|
2923
|
|
|
|
|
|
|
=item syscmd |
2924
|
|
|
|
|
|
|
|
2925
|
|
|
|
|
|
|
Just a little wrapper around system calls, that returns any failure codes and prints out the error(s) if present. A bit of sanity testing is also done to make sure the command to execute is safe. |
2926
|
|
|
|
|
|
|
|
2927
|
|
|
|
|
|
|
my $r = $util->syscmd( "gzip /tmp/example.txt" ); |
2928
|
|
|
|
|
|
|
$r ? print "ok!\n" : print "not ok.\n"; |
2929
|
|
|
|
|
|
|
|
2930
|
|
|
|
|
|
|
arguments required: |
2931
|
|
|
|
|
|
|
cmd - the command to execute |
2932
|
|
|
|
|
|
|
|
2933
|
|
|
|
|
|
|
arguments optional: |
2934
|
|
|
|
|
|
|
debug |
2935
|
|
|
|
|
|
|
fatal |
2936
|
|
|
|
|
|
|
|
2937
|
|
|
|
|
|
|
result |
2938
|
|
|
|
|
|
|
the exit status of the program you called. |
2939
|
|
|
|
|
|
|
|
2940
|
|
|
|
|
|
|
=item _try_mkdir |
2941
|
|
|
|
|
|
|
|
2942
|
|
|
|
|
|
|
try creating a directory using perl's builtin mkdir. |
2943
|
|
|
|
|
|
|
|
2944
|
|
|
|
|
|
|
=item yes_or_no |
2945
|
|
|
|
|
|
|
|
2946
|
|
|
|
|
|
|
my $r = $util->yes_or_no( |
2947
|
|
|
|
|
|
|
"Would you like fries with that?", |
2948
|
|
|
|
|
|
|
timeout => 30 |
2949
|
|
|
|
|
|
|
); |
2950
|
|
|
|
|
|
|
|
2951
|
|
|
|
|
|
|
$r ? print "fries are in the bag\n" : print "no fries!\n"; |
2952
|
|
|
|
|
|
|
|
2953
|
|
|
|
|
|
|
arguments required: |
2954
|
|
|
|
|
|
|
none. |
2955
|
|
|
|
|
|
|
|
2956
|
|
|
|
|
|
|
arguments optional: |
2957
|
|
|
|
|
|
|
question - the question to ask |
2958
|
|
|
|
|
|
|
timeout - how long to wait for an answer (in seconds) |
2959
|
|
|
|
|
|
|
|
2960
|
|
|
|
|
|
|
result: |
2961
|
|
|
|
|
|
|
0 - negative (or null) |
2962
|
|
|
|
|
|
|
1 - success (affirmative) |
2963
|
|
|
|
|
|
|
|
2964
|
|
|
|
|
|
|
=back |
2965
|
|
|
|
|
|
|
|
2966
|
|
|
|
|
|
|
=head1 TODO |
2967
|
|
|
|
|
|
|
|
2968
|
|
|
|
|
|
|
make all errors raise exceptions |
2969
|
|
|
|
|
|
|
write test cases for every method |
2970
|
|
|
|
|
|
|
comments. always needs more comments. |
2971
|
|
|
|
|
|
|
|
2972
|
|
|
|
|
|
|
=head1 SEE ALSO |
2973
|
|
|
|
|
|
|
|
2974
|
|
|
|
|
|
|
The following are all man/perldoc pages: |
2975
|
|
|
|
|
|
|
|
2976
|
|
|
|
|
|
|
Provision::Unix |
2977
|
|
|
|
|
|
|
|
2978
|
|
|
|
|
|
|
=head1 AUTHOR |
2979
|
|
|
|
|
|
|
|
2980
|
|
|
|
|
|
|
Matt Simerson <msimerson@cpan.org> |
2981
|
|
|
|
|
|
|
|
2982
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
2983
|
|
|
|
|
|
|
|
2984
|
|
|
|
|
|
|
This software is copyright (c) 2014 by The Network People, Inc.. |
2985
|
|
|
|
|
|
|
|
2986
|
|
|
|
|
|
|
This is free software; you can redistribute it and/or modify it under |
2987
|
|
|
|
|
|
|
the same terms as the Perl 5 programming language system itself. |
2988
|
|
|
|
|
|
|
|
2989
|
|
|
|
|
|
|
=cut |