line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Provision::Unix; |
2
|
|
|
|
|
|
|
# ABSTRACT: provision hosting accounts on unix systems |
3
|
|
|
|
|
|
|
|
4
|
14
|
|
|
14
|
|
583205
|
use strict; |
|
14
|
|
|
|
|
33
|
|
|
14
|
|
|
|
|
479
|
|
5
|
14
|
|
|
14
|
|
70
|
use warnings; |
|
14
|
|
|
|
|
26
|
|
|
14
|
|
|
|
|
5102
|
|
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
our $VERSION = '1.05'; |
8
|
|
|
|
|
|
|
|
9
|
14
|
|
|
14
|
|
11355
|
use Config::Tiny; |
|
14
|
|
|
|
|
14896
|
|
|
14
|
|
|
|
|
471
|
|
10
|
14
|
|
|
14
|
|
83
|
use Cwd; |
|
14
|
|
|
|
|
21
|
|
|
14
|
|
|
|
|
1136
|
|
11
|
14
|
|
|
14
|
|
6595
|
use Data::Dumper; |
|
14
|
|
|
|
|
69717
|
|
|
14
|
|
|
|
|
795
|
|
12
|
14
|
|
|
14
|
|
88
|
use English qw( -no_match_vars ); |
|
14
|
|
|
|
|
30
|
|
|
14
|
|
|
|
|
111
|
|
13
|
14
|
|
|
14
|
|
19707
|
use Params::Validate qw(:all); |
|
14
|
|
|
|
|
172214
|
|
|
14
|
|
|
|
|
3458
|
|
14
|
14
|
|
|
14
|
|
130
|
use Scalar::Util qw( openhandle ); |
|
14
|
|
|
|
|
25
|
|
|
14
|
|
|
|
|
27478
|
|
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
sub new { |
17
|
13
|
|
|
13
|
1
|
2993
|
my $class = shift; |
18
|
13
|
|
|
|
|
790
|
my %p = validate( |
19
|
|
|
|
|
|
|
@_, |
20
|
|
|
|
|
|
|
{ file => { type => SCALAR, optional => 1, }, |
21
|
|
|
|
|
|
|
fatal => { type => SCALAR, optional => 1, default => 1 }, |
22
|
|
|
|
|
|
|
debug => { type => SCALAR, optional => 1, default => 1 }, |
23
|
|
|
|
|
|
|
} |
24
|
|
|
|
|
|
|
); |
25
|
|
|
|
|
|
|
|
26
|
13
|
|
50
|
|
|
182
|
my $file = $p{file} || 'provision.conf'; |
27
|
13
|
|
|
|
|
31
|
my $debug = $p{debug}; |
28
|
13
|
|
|
|
|
72
|
my $ts = get_datetime_from_epoch(); |
29
|
13
|
|
|
|
|
229
|
my $self = { |
30
|
|
|
|
|
|
|
debug => $debug, |
31
|
|
|
|
|
|
|
fatal => $p{fatal}, |
32
|
|
|
|
|
|
|
config => undef, |
33
|
|
|
|
|
|
|
errors => [], # errors get appended here |
34
|
|
|
|
|
|
|
audit => [ # status messages accumulate here |
35
|
|
|
|
|
|
|
"launched at $ts", |
36
|
|
|
|
|
|
|
$class . sprintf( " loaded by %s, %s, %s", caller ), |
37
|
|
|
|
|
|
|
], |
38
|
|
|
|
|
|
|
last_audit => 0, |
39
|
|
|
|
|
|
|
last_error => 0, |
40
|
|
|
|
|
|
|
util => undef, |
41
|
|
|
|
|
|
|
}; |
42
|
|
|
|
|
|
|
|
43
|
13
|
|
|
|
|
48
|
bless( $self, $class ); |
44
|
13
|
|
|
|
|
77
|
my $config = $self->find_config( file => $file, debug => $debug, fatal => 0 ); |
45
|
13
|
50
|
|
|
|
138
|
if ( $config ) { |
46
|
13
|
|
|
|
|
600
|
$self->{config} = Config::Tiny->read( $config ); |
47
|
|
|
|
|
|
|
} |
48
|
|
|
|
|
|
|
else { |
49
|
0
|
|
|
|
|
0
|
warn "could not find $file. Installing it in your local etc directory.\n"; |
50
|
|
|
|
|
|
|
}; |
51
|
|
|
|
|
|
|
|
52
|
13
|
|
|
|
|
21192
|
return $self; |
53
|
|
|
|
|
|
|
} |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
sub audit { |
56
|
294
|
|
|
294
|
1
|
1018
|
my $self = shift; |
57
|
294
|
|
|
|
|
1507
|
my $mess = shift; |
58
|
|
|
|
|
|
|
|
59
|
294
|
50
|
|
|
|
1050
|
if ($mess) { |
60
|
294
|
|
|
|
|
630
|
push @{ $self->{audit} }, $mess; |
|
294
|
|
|
|
|
1828
|
|
61
|
294
|
50
|
|
|
|
1992
|
print STDERR "$mess\n" if $self->{debug}; |
62
|
|
|
|
|
|
|
} |
63
|
|
|
|
|
|
|
|
64
|
294
|
|
|
|
|
1254
|
return $self->{audit}; |
65
|
|
|
|
|
|
|
} |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
sub dump_audit { |
68
|
13
|
|
|
13
|
1
|
1475
|
my $self = shift; |
69
|
13
|
|
|
|
|
50
|
my $last_line = $self->{last_audit}; |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
# we already dumped everything |
72
|
13
|
50
|
|
|
|
26
|
return if $last_line == scalar @{ $self->{audit} }; |
|
13
|
|
|
|
|
80
|
|
73
|
|
|
|
|
|
|
|
74
|
13
|
|
|
|
|
1730
|
print STDERR "\n\t\t\tAudit History Report \n\n"; |
75
|
13
|
|
|
|
|
43
|
my $i = 0; |
76
|
13
|
|
|
|
|
30
|
foreach ( @{ $self->{audit} } ) { |
|
13
|
|
|
|
|
81
|
|
77
|
1159
|
|
|
|
|
1325
|
$i++; |
78
|
1159
|
100
|
|
|
|
3106
|
next if $i < $last_line; |
79
|
205
|
|
|
|
|
20717
|
print STDERR "\t$_\n"; |
80
|
|
|
|
|
|
|
}; |
81
|
13
|
|
|
|
|
67
|
$self->{last_audit} = $i; |
82
|
13
|
|
|
|
|
45
|
return; |
83
|
|
|
|
|
|
|
}; |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
sub dump_errors { |
86
|
10
|
|
|
10
|
0
|
26
|
my $self = shift; |
87
|
10
|
|
|
|
|
22
|
my $last_line = $self->{last_error}; |
88
|
|
|
|
|
|
|
|
89
|
10
|
50
|
|
|
|
18
|
return if $last_line == scalar @{ $self->{errors} }; # everything dumped |
|
10
|
|
|
|
|
80
|
|
90
|
|
|
|
|
|
|
|
91
|
10
|
|
|
|
|
725
|
print STDERR "\n\t\t\t Error History Report \n\n"; |
92
|
10
|
|
|
|
|
32
|
my $i = 0; |
93
|
10
|
|
|
|
|
17
|
foreach ( @{ $self->{errors} } ) { |
|
10
|
|
|
|
|
40
|
|
94
|
53
|
|
|
|
|
66
|
$i++; |
95
|
53
|
100
|
|
|
|
141
|
next if $i < $last_line; |
96
|
21
|
|
|
|
|
1357
|
print STDERR "ERROR: '$_->{errmsg}' \t\t at $_->{errloc}\n"; |
97
|
|
|
|
|
|
|
}; |
98
|
10
|
|
|
|
|
503
|
print "\n"; |
99
|
10
|
|
|
|
|
35
|
$self->{last_error} = $i; |
100
|
10
|
|
|
|
|
32
|
return; |
101
|
|
|
|
|
|
|
}; |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
sub error { |
104
|
43
|
|
|
43
|
1
|
197
|
my $self = shift; |
105
|
43
|
|
|
|
|
174
|
my $message = shift; |
106
|
43
|
|
|
|
|
4316
|
my %p = validate( |
107
|
|
|
|
|
|
|
@_, |
108
|
|
|
|
|
|
|
{ 'location' => { type => SCALAR, optional => 1, }, |
109
|
|
|
|
|
|
|
'fatal' => { type => BOOLEAN, optional => 1, default => 1 }, |
110
|
|
|
|
|
|
|
'debug' => { type => BOOLEAN, optional => 1, default => 1 }, |
111
|
|
|
|
|
|
|
}, |
112
|
|
|
|
|
|
|
); |
113
|
|
|
|
|
|
|
|
114
|
43
|
|
|
|
|
531
|
my $debug = $p{debug}; |
115
|
43
|
|
|
|
|
166
|
my $fatal = $p{fatal}; |
116
|
43
|
|
|
|
|
105
|
my $location = $p{location}; |
117
|
|
|
|
|
|
|
|
118
|
43
|
100
|
|
|
|
255
|
if ( $message ) { |
119
|
42
|
|
|
|
|
369
|
my @caller = caller; |
120
|
42
|
|
|
|
|
123
|
push @{ $self->{audit} }, $message; |
|
42
|
|
|
|
|
327
|
|
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
# append message to $self->error stack |
123
|
42
|
|
66
|
|
|
108
|
push @{ $self->{errors} }, |
|
42
|
|
|
|
|
943
|
|
124
|
|
|
|
|
|
|
{ |
125
|
|
|
|
|
|
|
errmsg => $message, |
126
|
|
|
|
|
|
|
errloc => $location || join( ", ", $caller[0], $caller[2] ), |
127
|
|
|
|
|
|
|
}; |
128
|
|
|
|
|
|
|
} |
129
|
|
|
|
|
|
|
else { |
130
|
1
|
|
|
|
|
8
|
$message = $self->get_last_error(); |
131
|
|
|
|
|
|
|
} |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
# print audit and error results to stderr |
134
|
43
|
100
|
|
|
|
246
|
if ( $debug ) { |
135
|
10
|
|
|
|
|
59
|
$self->dump_audit(); |
136
|
10
|
|
|
|
|
47
|
$self->dump_errors(); |
137
|
|
|
|
|
|
|
} |
138
|
|
|
|
|
|
|
|
139
|
43
|
100
|
|
|
|
242
|
if ( $fatal ) { |
140
|
2
|
50
|
|
|
|
23
|
if ( ! $debug ) { |
141
|
0
|
|
|
|
|
0
|
$self->dump_audit(); # dump if err is fatal and debug is not set |
142
|
0
|
|
|
|
|
0
|
$self->dump_errors(); |
143
|
|
|
|
|
|
|
}; |
144
|
2
|
|
|
|
|
83
|
die "FATAL ERROR"; |
145
|
|
|
|
|
|
|
}; |
146
|
41
|
|
|
|
|
1774
|
return; |
147
|
|
|
|
|
|
|
} |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
sub find_config { |
150
|
20
|
|
|
20
|
1
|
400
|
my $self = shift; |
151
|
20
|
|
|
|
|
1170
|
my %p = validate( |
152
|
|
|
|
|
|
|
@_, |
153
|
|
|
|
|
|
|
{ 'file' => { type => SCALAR, }, |
154
|
|
|
|
|
|
|
'etcdir' => { type => SCALAR | UNDEF, optional => 1, }, |
155
|
|
|
|
|
|
|
'fatal' => { type => SCALAR, optional => 1, default => 1 }, |
156
|
|
|
|
|
|
|
'debug' => { type => SCALAR, optional => 1, default => 1 }, |
157
|
|
|
|
|
|
|
} |
158
|
|
|
|
|
|
|
); |
159
|
|
|
|
|
|
|
|
160
|
19
|
|
|
|
|
171
|
my $file = $p{file}; |
161
|
19
|
|
|
|
|
42
|
my $etcdir = $p{etcdir}; |
162
|
19
|
|
|
|
|
137
|
my $fatal = $self->{fatal} = $p{fatal}; |
163
|
19
|
|
|
|
|
54
|
my $debug = $self->{debug} = $p{debug}; |
164
|
|
|
|
|
|
|
|
165
|
19
|
|
|
|
|
164
|
$self->audit("searching for config $file"); |
166
|
|
|
|
|
|
|
|
167
|
19
|
100
|
|
|
|
64
|
return $self->_find_readable( $file, $etcdir ) if $etcdir; |
168
|
|
|
|
|
|
|
|
169
|
17
|
|
|
|
|
61
|
my @etc_dirs = qw{ /opt/local/etc /usr/local/etc /etc etc }; |
170
|
|
|
|
|
|
|
|
171
|
17
|
|
|
|
|
102399
|
my $working_directory = cwd; |
172
|
17
|
|
|
|
|
442
|
push @etc_dirs, $working_directory; |
173
|
|
|
|
|
|
|
|
174
|
17
|
|
|
|
|
673
|
my $r = $self->_find_readable( $file, @etc_dirs ); |
175
|
17
|
100
|
|
|
|
217
|
return $r if $r; |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
# try $file-dist in the working dir |
178
|
16
|
100
|
|
|
|
414
|
if ( -r "./$file-dist" ) { |
179
|
14
|
|
|
|
|
370
|
$self->audit("\tfound $file-dist in ./"); |
180
|
14
|
|
|
|
|
761
|
return "$working_directory/$file-dist"; |
181
|
|
|
|
|
|
|
} |
182
|
|
|
|
|
|
|
|
183
|
2
|
|
|
|
|
45
|
return $self->error( "could not find $file", |
184
|
|
|
|
|
|
|
fatal => $fatal, |
185
|
|
|
|
|
|
|
debug => $debug, |
186
|
|
|
|
|
|
|
); |
187
|
|
|
|
|
|
|
} |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
sub get_datetime_from_epoch { |
190
|
13
|
|
|
13
|
0
|
28
|
my ( $self, $time ) = @_; |
191
|
13
|
|
33
|
|
|
1433
|
my @lt = localtime( $time || time() ); |
192
|
13
|
|
|
|
|
153
|
return sprintf '%04d-%02d-%02d %02d:%02d:%02d', $lt[5] + 1900, $lt[4] + 1, |
193
|
|
|
|
|
|
|
$lt[3], $lt[2], $lt[1], $lt[0]; |
194
|
|
|
|
|
|
|
} |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
sub get_dns { |
197
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
198
|
0
|
0
|
|
|
|
0
|
return $self->{dns} if ref $self->{dns}; |
199
|
0
|
|
|
|
|
0
|
require Provision::Unix::DNS; |
200
|
0
|
|
|
|
|
0
|
$self->{dns} = Provision::Unix::DNS->new( |
201
|
|
|
|
|
|
|
prov => $self, |
202
|
|
|
|
|
|
|
debug => $self->{debug}, |
203
|
|
|
|
|
|
|
); |
204
|
0
|
|
|
|
|
0
|
return $self->{dns}; |
205
|
|
|
|
|
|
|
}; |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
sub get_debug { |
208
|
15
|
|
|
15
|
0
|
106
|
my ($self, $debug) = @_; |
209
|
15
|
50
|
|
|
|
122
|
return $debug if defined $debug; |
210
|
15
|
|
|
|
|
72
|
return $self->{debug}; |
211
|
|
|
|
|
|
|
}; |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
sub get_errors { |
214
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
215
|
0
|
|
|
|
|
0
|
return $self->{errors}; |
216
|
|
|
|
|
|
|
} |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
sub get_fatal { |
219
|
15
|
|
|
15
|
0
|
40
|
my ($self, $fatal) = @_; |
220
|
15
|
50
|
|
|
|
58
|
return $fatal if defined $fatal; |
221
|
15
|
|
|
|
|
130
|
return $self->{fatal}; |
222
|
|
|
|
|
|
|
}; |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
sub get_last_error { |
225
|
1
|
|
|
1
|
1
|
5
|
my $self = shift; |
226
|
1
|
50
|
|
|
|
3
|
return $self->{errors}[-1]->{errmsg} if scalar @{ $self->{errors} }; |
|
1
|
|
|
|
|
11
|
|
227
|
0
|
|
|
|
|
0
|
return; |
228
|
|
|
|
|
|
|
} |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
sub get_util { |
231
|
14
|
|
|
14
|
0
|
49
|
my $self = shift; |
232
|
14
|
100
|
|
|
|
86
|
return $self->{util} if ref $self->{util}; |
233
|
11
|
|
|
|
|
9270
|
require Provision::Unix::Utility; |
234
|
11
|
|
|
|
|
203
|
$self->{util} = Provision::Unix::Utility->new( |
235
|
|
|
|
|
|
|
'log' => $self, |
236
|
|
|
|
|
|
|
debug => $self->{debug}, |
237
|
|
|
|
|
|
|
); |
238
|
11
|
|
|
|
|
89
|
return $self->{util}; |
239
|
|
|
|
|
|
|
}; |
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
sub get_version { |
242
|
1
|
|
|
1
|
0
|
1884
|
print "Provision::Unix version $VERSION\n"; |
243
|
1
|
|
|
|
|
14
|
return $VERSION; |
244
|
|
|
|
|
|
|
}; |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
sub progress { |
247
|
4
|
|
|
4
|
0
|
41
|
my $self = shift; |
248
|
4
|
|
|
|
|
457
|
my %p = validate( |
249
|
|
|
|
|
|
|
@_, |
250
|
|
|
|
|
|
|
{ 'num' => { type => SCALAR }, |
251
|
|
|
|
|
|
|
'desc' => { type => SCALAR, optional => 1 }, |
252
|
|
|
|
|
|
|
'err' => { type => SCALAR, optional => 1 }, |
253
|
|
|
|
|
|
|
}, |
254
|
|
|
|
|
|
|
); |
255
|
|
|
|
|
|
|
|
256
|
4
|
|
|
|
|
65
|
my $num = $p{num}; |
257
|
4
|
|
|
|
|
21
|
my $desc = $p{desc}; |
258
|
4
|
|
|
|
|
19
|
my $err = $p{err}; |
259
|
|
|
|
|
|
|
|
260
|
4
|
|
50
|
|
|
37
|
my $msg_length = length $desc || 0; |
261
|
4
|
|
|
|
|
21
|
my $to_print = 10; |
262
|
4
|
|
|
|
|
24
|
my $max_print = 70 - $msg_length; |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
# if err, print and return |
265
|
4
|
50
|
|
|
|
158
|
if ( $err ) { |
266
|
0
|
0
|
|
|
|
0
|
if ( length( $err ) == 1 ) { |
267
|
0
|
|
|
|
|
0
|
foreach my $error ( @{ $self->{errors} } ) { |
|
0
|
|
|
|
|
0
|
|
268
|
0
|
|
|
|
|
0
|
print {*STDERR} "\n$error->{errloc}\t$error->{errmsg}\n"; |
|
0
|
|
|
|
|
0
|
|
269
|
|
|
|
|
|
|
} |
270
|
|
|
|
|
|
|
} |
271
|
|
|
|
|
|
|
else { |
272
|
0
|
|
|
|
|
0
|
print {*STDERR} "\n\t$err\n"; |
|
0
|
|
|
|
|
0
|
|
273
|
|
|
|
|
|
|
} |
274
|
0
|
|
|
|
|
0
|
return $self->error( $err, fatal => 0, debug => 0 ); |
275
|
|
|
|
|
|
|
} |
276
|
|
|
|
|
|
|
|
277
|
4
|
50
|
|
|
|
48
|
if ( $msg_length > 54 ) { |
278
|
0
|
|
|
|
|
0
|
die "max message length is 55 chars\n"; |
279
|
|
|
|
|
|
|
} |
280
|
|
|
|
|
|
|
|
281
|
4
|
|
|
|
|
16
|
print {*STDERR} "\r["; |
|
4
|
|
|
|
|
834
|
|
282
|
4
|
|
|
|
|
48
|
foreach ( 1 .. $num ) { |
283
|
31
|
|
|
|
|
65
|
print {*STDERR} "="; |
|
31
|
|
|
|
|
3579
|
|
284
|
31
|
|
|
|
|
514
|
$to_print--; |
285
|
31
|
|
|
|
|
306
|
$max_print--; |
286
|
|
|
|
|
|
|
} |
287
|
|
|
|
|
|
|
|
288
|
4
|
|
|
|
|
39
|
while ($to_print) { |
289
|
9
|
|
|
|
|
14
|
print {*STDERR} "."; |
|
9
|
|
|
|
|
474
|
|
290
|
9
|
|
|
|
|
20
|
$to_print--; |
291
|
9
|
|
|
|
|
20
|
$max_print--; |
292
|
|
|
|
|
|
|
} |
293
|
|
|
|
|
|
|
|
294
|
4
|
|
|
|
|
17
|
print {*STDERR} "] $desc"; |
|
4
|
|
|
|
|
224
|
|
295
|
4
|
|
|
|
|
31
|
while ($max_print) { |
296
|
188
|
|
|
|
|
377
|
print {*STDERR} " "; |
|
188
|
|
|
|
|
11182
|
|
297
|
188
|
|
|
|
|
848
|
$max_print--; |
298
|
|
|
|
|
|
|
} |
299
|
|
|
|
|
|
|
|
300
|
4
|
100
|
|
|
|
35
|
if ( $num == 10 ) { print {*STDERR} "\n" } |
|
3
|
|
|
|
|
16
|
|
|
3
|
|
|
|
|
202
|
|
301
|
|
|
|
|
|
|
|
302
|
4
|
|
|
|
|
313
|
return 1; |
303
|
|
|
|
|
|
|
} |
304
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
sub _find_readable { |
306
|
102
|
|
|
102
|
|
409
|
my $self = shift; |
307
|
102
|
|
|
|
|
268
|
my $file = shift; |
308
|
102
|
100
|
|
|
|
672
|
my $dir = shift or return; # breaks recursion at end of @_ |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
#$self->audit("looking for $file in $dir") if $self->{debug}; |
311
|
|
|
|
|
|
|
|
312
|
85
|
100
|
|
|
|
6147
|
if ( -r "$dir/$file" ) { |
313
|
14
|
|
|
14
|
|
101
|
no warnings; |
|
14
|
|
|
|
|
34
|
|
|
14
|
|
|
|
|
5495
|
|
314
|
2
|
|
|
|
|
28
|
$self->audit("\tfound in $dir"); |
315
|
2
|
|
|
|
|
20
|
return "$dir/$file"; # we have succeeded |
316
|
|
|
|
|
|
|
} |
317
|
|
|
|
|
|
|
|
318
|
83
|
100
|
|
|
|
2195
|
if ( -d $dir ) { |
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
# warn about directories we don't have read access to |
321
|
49
|
50
|
|
|
|
1673
|
if ( !-r $dir ) { |
322
|
0
|
|
|
|
|
0
|
$self->error( "$dir is not readable", fatal => 0 ); |
323
|
|
|
|
|
|
|
} |
324
|
|
|
|
|
|
|
else { |
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
# warn about files that exist but aren't readable |
327
|
49
|
50
|
|
|
|
1082
|
if ( -e "$dir/$file" ) { |
328
|
0
|
|
|
|
|
0
|
$self->error( "$dir/$file is not readable", |
329
|
|
|
|
|
|
|
fatal => 0 |
330
|
|
|
|
|
|
|
); |
331
|
|
|
|
|
|
|
} |
332
|
|
|
|
|
|
|
} |
333
|
|
|
|
|
|
|
} |
334
|
|
|
|
|
|
|
|
335
|
83
|
|
|
|
|
1066
|
return $self->_find_readable( $file, @_ ); |
336
|
|
|
|
|
|
|
} |
337
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
1; |
340
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
=pod |
344
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
=head1 NAME |
346
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
Provision::Unix - provision hosting accounts on unix systems |
348
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
=head1 VERSION |
350
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
version 1.06 |
352
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
=head1 SYNOPSIS |
354
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
use Provision::Unix; |
356
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
my $foo = Provision::Unix->new(); |
358
|
|
|
|
|
|
|
... |
359
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
prov_dns --action=create --zone=example.com |
361
|
|
|
|
|
|
|
prov_user --action=create --username=matt --pass='neat0app!' |
362
|
|
|
|
|
|
|
prov_virtual --action=create --name=testVPS |
363
|
|
|
|
|
|
|
prov_web --action=create --vhost=www.example.com |
364
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
=head1 DESCRIPTION |
366
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
Provision::Unix is a suite of applications to create, modify, and destroy |
368
|
|
|
|
|
|
|
accounts on Unix systems in a reliable and consistent manner. |
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
Command line scripts are provided for humans to perform provisioning actions |
371
|
|
|
|
|
|
|
by hand. See the documentation included in each of the prov_* scripts. |
372
|
|
|
|
|
|
|
Programmers and automated systems should be loading the Provision::Unix |
373
|
|
|
|
|
|
|
modules and calling the methods directly. The API provided by each method is |
374
|
|
|
|
|
|
|
stable and only changes when additional parameters are added. |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
The types of accounts that can be provisioned are organized by class with each |
377
|
|
|
|
|
|
|
class including a standard set of methods. All classes support at least |
378
|
|
|
|
|
|
|
create and destroy. Additional common methods are: modify, enable, and disable. |
379
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
Each class (DNS, User, VirtualOS, Web) has a general module that |
381
|
|
|
|
|
|
|
contains the logic for selecting and dispatching requests to sub-classes which |
382
|
|
|
|
|
|
|
are implementation specific. Selecting and dispatching is done based on the |
383
|
|
|
|
|
|
|
environment and configuration file settings at run time. |
384
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
For example, Provision::Unix::DNS contains all the general logic for dns |
386
|
|
|
|
|
|
|
operations (create a zone, record, alias, etc). Subclasses contain |
387
|
|
|
|
|
|
|
specific information such as how to provision a DNS record for nictool, |
388
|
|
|
|
|
|
|
BIND, or tinydns. |
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
Not all specific modules are fully implemented yet. |
391
|
|
|
|
|
|
|
Ex: Provision::Unix::VirtualOS::Linux::Xen is fully implemented, |
392
|
|
|
|
|
|
|
where Provision::Unix::VirtualOS::FreeBSD::Jail is not. |
393
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
Browse the perl modules to see which modules are available. |
395
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
=head1 NAME |
397
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
Provision::Unix - provision accounts on unix systems |
399
|
|
|
|
|
|
|
|
400
|
|
|
|
|
|
|
=head1 Programming Conventions |
401
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
All functions/methods adhere to the following: |
403
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
=head2 Exception Handling |
405
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
Errors throw exceptions. This can be overridden by calling the method with fatal=0. If you do so, you must write code to handle the errors. |
407
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
This call will throw an exception since it cannot find the file. |
409
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
$util->file_read('/etc/oopsie_a_typo'); |
411
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
Setting fatal will cause it to return undef instead: |
413
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
$util->file_read('/etc/oopsie_a_typo', fatal=>0); |
415
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
=head2 Warnings and Messages |
417
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
Methods have an optional debug parameter that defaults to enabled. Often, that means methods spit out more messages than you want. Supress them by setting debug=0. |
419
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
Supressed messages are not lost! All error messages are stored in $prov->errors and all status messages are in $prov->audit. You can dump those arrays any time to to inspect the status or error messages. A handy way to do so is: |
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
$prov->error('test breakpoint'); |
423
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
That will dump the contents of $prov->audit and $prov->errors and then terminate your program. If you want your program to continue after calling $prov->error, just set fatal=0. |
425
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
$prov->error('test breakpoint', fatal => 0); |
427
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
=head1 FUNCTIONS |
429
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
=head2 new |
431
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
Creates and returns a new Provision::Unix object. |
433
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
As part of initialization, new() finds and reads in provision.conf from /[opt/usr]/local/etc, /etc, and the current working directory. |
435
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
=head2 audit |
437
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
audit is a method that appends messages to an internal audit log. Rather than spewing messages to stdout or stderr, they are stored as a list. The list can can be inspected by calling $prov->audit or it can be printed by calling $prov->dump_audit. |
439
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
$prov->audit("knob fitzerbaum twiddled to setting 5"); |
441
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
If the debug option is set ($prov->{debug}), audit messages are also printed to stderr. |
443
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
returns an arrayref of audit messages. |
445
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
=head2 dump_audit |
447
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
dump_audit prints out any audit/status messages that have accumulated since the last time dump_audit was called. It is particularly useful for RPC agents that poll for status updates during long running processes. |
449
|
|
|
|
|
|
|
|
450
|
|
|
|
|
|
|
=head2 dump_error |
451
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
Same as dump_audit, except dumps the error history report. |
453
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
=head2 error |
455
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
Whenever a method runs into an unexpected condition, it should call $prov->error with a human intelligible error message. It should also specify whether the error is merely a warning or a fatal condition. Errors are considered fatal unless otherwise specified. |
457
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
Examples: |
459
|
|
|
|
|
|
|
|
460
|
|
|
|
|
|
|
$prov->error( 'could not write to file /etc/passwd' ); |
461
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
This error is fatal and will throw an exception, after printing the contents of the audit log and the last error message to stderr. |
463
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
A very helpful thing to do is call error with a location as well: |
465
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
$prov->error( 'could not write to file /etc/passwd', |
467
|
|
|
|
|
|
|
location => join( ", ", caller ), |
468
|
|
|
|
|
|
|
); |
469
|
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
Doing so will tell reveal in the error log exactly where the error was encountered as well as who called the method. The latter is more likely where the error exists, making location a very beneficial parameter. |
471
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
=head2 find_config |
473
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
This sub is used to determine which configuration file to use. The general logic is as follows: |
475
|
|
|
|
|
|
|
|
476
|
|
|
|
|
|
|
If the etc dir and file name are provided and the file exists, use it. |
477
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
If that fails, then go prowling around the drive and look in all the usual places, in order of preference: |
479
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
/opt/local/etc/ |
481
|
|
|
|
|
|
|
/usr/local/etc/ |
482
|
|
|
|
|
|
|
/etc |
483
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
Finally, if none of those work, then check the working directory for the named .conf file, or a .conf-dist. |
485
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
Example: |
487
|
|
|
|
|
|
|
my $conf = $util->find_config ( |
488
|
|
|
|
|
|
|
file => 'example.conf', |
489
|
|
|
|
|
|
|
etcdir => '/usr/local/etc', |
490
|
|
|
|
|
|
|
) |
491
|
|
|
|
|
|
|
|
492
|
|
|
|
|
|
|
arguments required: |
493
|
|
|
|
|
|
|
file - the .conf file to read in |
494
|
|
|
|
|
|
|
|
495
|
|
|
|
|
|
|
arguments optional: |
496
|
|
|
|
|
|
|
etcdir - the etc directory to prefer |
497
|
|
|
|
|
|
|
debug |
498
|
|
|
|
|
|
|
fatal |
499
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
result: |
501
|
|
|
|
|
|
|
0 - failure |
502
|
|
|
|
|
|
|
the path to $file |
503
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
=head2 get_last_error |
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
prints and returns the last error encountered. |
507
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
=head1 BUGS |
509
|
|
|
|
|
|
|
|
510
|
|
|
|
|
|
|
Please report any bugs or feature requests to C<bug-unix-provision at rt.cpan.org>, or through the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Provision-Unix>. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes. |
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
=head1 SUPPORT |
513
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
You can find documentation for this module with the perldoc command. |
515
|
|
|
|
|
|
|
|
516
|
|
|
|
|
|
|
perldoc Provision::Unix |
517
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
You can also look for information at: |
519
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
=over 4 |
521
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
=item * RT: CPAN's request tracker |
523
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Provision-Unix> |
525
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
=item * AnnoCPAN: Annotated CPAN documentation |
527
|
|
|
|
|
|
|
|
528
|
|
|
|
|
|
|
L<http://annocpan.org/dist/Provision-Unix> |
529
|
|
|
|
|
|
|
|
530
|
|
|
|
|
|
|
=item * CPAN Ratings |
531
|
|
|
|
|
|
|
|
532
|
|
|
|
|
|
|
L<http://cpanratings.perl.org/d/Provision-Unix> |
533
|
|
|
|
|
|
|
|
534
|
|
|
|
|
|
|
=item * Search CPAN |
535
|
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
L<http://search.cpan.org/dist/Provision-Unix> |
537
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
=back |
539
|
|
|
|
|
|
|
|
540
|
|
|
|
|
|
|
=head1 AUTHOR |
541
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
Matt Simerson <msimerson@cpan.org> |
543
|
|
|
|
|
|
|
|
544
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
545
|
|
|
|
|
|
|
|
546
|
|
|
|
|
|
|
This software is copyright (c) 2013 by The Network People, Inc.. |
547
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
This is free software; you can redistribute it and/or modify it under |
549
|
|
|
|
|
|
|
the same terms as the Perl 5 programming language system itself. |
550
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
=cut |
552
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
|
554
|
|
|
|
|
|
|
__END__ |
555
|
|
|
|
|
|
|
|
556
|
|
|
|
|
|
|
|