Hide Show 40 lines of Pod
our
$VERSION
=
'1.16.0'
;
@EXPORT
=
qw(list_files ls
unlink rm rmdir mkdir stat readlink symlink ln rename mv chdir cd cp
chown chgrp chmod
is_file is_dir is_readable is_writeable is_writable is_symlink
df du
mount umount
glob)
;
use
vars
qw(%file_handles)
;
Hide Show 14 lines of Pod
sub
symlink
{
my
(
$from
,
$to
) =
@_
;
$from
= resolv_path(
$from
);
$to
= resolv_path(
$to
);
Rex::get_current_connection()->{reporter}
->report_resource_start(
type
=>
"symlink"
,
name
=>
$to
);
my
$fs
= Rex::Interface::Fs->create;
if
(
$fs
->is_symlink(
$to
) &&
$fs
->
readlink
(
$to
) eq
$from
) {
Rex::get_current_connection()->{reporter}->report(
changed
=> 0, );
}
else
{
$fs
->ln(
$from
,
$to
) or
die
(
"Can't link $from -> $to"
);
Rex::get_current_connection()->{reporter}
->report(
changed
=> 1,
message
=>
"Symlink created: $from -> $to."
);
}
Rex::get_current_connection()->{reporter}
->report_resource_end(
type
=>
"symlink"
,
name
=>
$to
);
return
1;
}
Hide Show 6 lines of Pod
sub
ln {
&symlink
(
@_
);
}
Hide Show 10 lines of Pod
sub
unlink
{
my
@files
=
@_
;
my
$f
;
if
(
ref
$files
[0] eq
"ARRAY"
) {
$f
=
$files
[0];
}
else
{
$f
= \
@files
;
}
if
(
scalar
@{
$f
} == 1 ) {
my
$file
= resolv_path
$f
->[0];
my
$fs
= Rex::Interface::Fs->create;
Rex::get_current_connection()->{reporter}
->report_resource_start(
type
=>
"unlink"
,
name
=>
$file
);
if
(
$fs
->is_file(
$file
) ||
$fs
->is_symlink(
$file
) ) {
$fs
->
unlink
(
$file
);
my
$tmp_path
= Rex::Config->get_tmp_dir;
if
(
$file
!~ m/^\Q
$tmp_path
\E[\/\\][a-z]+\.tmp$/ ) {
Rex::get_current_connection()->{reporter}
->report(
changed
=> 1,
message
=>
"File $file removed."
);
}
}
else
{
Rex::get_current_connection()->{reporter}->report(
changed
=> 0, );
}
Rex::get_current_connection()->{reporter}
->report_resource_end(
type
=>
"unlink"
,
name
=>
$file
);
}
else
{
&unlink
(
$_
)
for
@{
$f
};
}
}
Hide Show 6 lines of Pod
sub
rm {
&unlink
(
@_
);
}
Hide Show 18 lines of Pod
sub
rmdir
{
my
@dirs
=
@_
;
my
$d
;
if
(
ref
$dirs
[0] eq
"ARRAY"
) {
$d
=
$dirs
[0];
}
else
{
$d
= \
@dirs
;
}
if
(
scalar
@{
$d
} == 1 ) {
my
$dir
= resolv_path
$d
->[0];
my
$fs
= Rex::Interface::Fs->create;
Rex::get_current_connection()->{reporter}
->report_resource_start(
type
=>
"rmdir"
,
name
=>
$dir
);
if
( !
$fs
->is_dir(
$dir
) &&
$dir
!~ m/[\*\[]/ ) {
Rex::get_current_connection()->{reporter}->report(
changed
=> 0, );
}
else
{
$fs
->
rmdir
(
$dir
);
if
(
$fs
->is_dir(
$dir
) ) {
die
"Can't remove $dir."
;
}
Rex::get_current_connection()->{reporter}
->report(
changed
=> 1,
message
=>
"Directory $dir removed."
);
}
Rex::get_current_connection()->{reporter}
->report_resource_end(
type
=>
"rmdir"
,
name
=>
$dir
);
}
else
{
&rmdir
(
$_
)
for
@{
$d
};
}
}
Hide Show 41 lines of Pod
sub
mkdir
{
Rex::Logger::debug(
"Creating directory $_[0]"
);
my
$dir
=
shift
;
$dir
= resolv_path(
$dir
);
my
$options
= {
@_
};
$options
->{on_change} //=
sub
{ };
Rex::get_current_connection()->{reporter}
->report_resource_start(
type
=>
"mkdir"
,
name
=>
$dir
);
my
$fs
= Rex::Interface::Fs->create;
my
$not_created
= 0;
my
%old_stat
;
my
$changed
= 0;
if
(
$fs
->is_dir(
$dir
) ) {
$not_created
= 1;
%old_stat
=
&stat
(
$dir
);
}
my
$mode
=
$options
->{
"mode"
} || 755;
my
$owner
=
$options
->{
"owner"
} ||
""
;
my
$group
=
$options
->{
"group"
} ||
""
;
my
$not_recursive
=
$options
->{
"not_recursive"
} || 0;
if
(
$not_recursive
) {
if
( !
$fs
->
mkdir
(
$dir
) ) {
Rex::Logger::debug(
"Can't create directory $dir"
);
die
(
"Can't create directory $dir"
);
}
&chown
(
$owner
,
$dir
)
if
$owner
;
&chgrp
(
$group
,
$dir
)
if
$group
;
&chmod
(
$mode
,
$dir
)
if
$mode
;
}
else
{
if
( !Rex::Helper::File::Spec->file_name_is_absolute(
$dir
) ) {
$dir
= Rex::Helper::File::Spec->rel2abs(
$dir
);
}
my
@directories
= __splitdir(
$dir
);
my
$path_so_far
=
shift
@directories
;
for
my
$part
(
@directories
) {
$path_so_far
= Rex::Helper::File::Spec->
join
(
$path_so_far
,
$part
);
if
( !is_dir(
$path_so_far
) && !is_file(
$path_so_far
) ) {
if
( !
$fs
->
mkdir
(
$path_so_far
) ) {
Rex::Logger::debug(
"Can't create directory $dir"
);
die
(
"Can't create directory $dir"
);
}
&chown
(
$owner
,
$path_so_far
)
if
$owner
;
&chgrp
(
$group
,
$path_so_far
)
if
$group
;
&chmod
(
$mode
,
$path_so_far
)
if
$mode
;
}
}
}
my
%new_stat
=
&stat
(
$dir
);
if
( !
$not_created
) {
Rex::get_current_connection()->{reporter}
->report(
changed
=> 1,
message
=>
"Directory created."
);
$changed
= 1;
}
if
(
%old_stat
&&
$old_stat
{uid} !=
$new_stat
{uid} ) {
Rex::get_current_connection()->{reporter}
->report(
changed
=> 1,
message
=>
"Owner updated."
);
$changed
= 1;
}
if
(
%old_stat
&&
$old_stat
{gid} !=
$new_stat
{gid} ) {
Rex::get_current_connection()->{reporter}
->report(
changed
=> 1,
message
=>
"Group updated."
);
$changed
= 1;
}
if
(
%old_stat
&&
$old_stat
{mode} ne
$new_stat
{mode} ) {
Rex::get_current_connection()->{reporter}
->report(
changed
=> 1,
message
=>
"Mode updated."
);
$changed
= 1;
}
if
(
$changed
== 0 ) {
Rex::get_current_connection()->{reporter}->report(
changed
=> 0, );
}
else
{
$options
->{on_change}->(
$dir
);
}
Rex::get_current_connection()->{reporter}
->report_resource_end(
type
=>
"mkdir"
,
name
=>
$dir
);
return
1;
}
sub
__splitdir {
return
Rex::Helper::File::Spec->splitdir(
shift
);
}
Hide Show 16 lines of Pod
sub
chown
{
my
(
$user
,
$file
,
@opts
) =
@_
;
$file
= resolv_path(
$file
);
my
$fs
= Rex::Interface::Fs->create;
$fs
->
chown
(
$user
,
$file
,
@opts
) or
die
(
"Can't chown $file"
);
}
Hide Show 16 lines of Pod
sub
chgrp {
my
(
$group
,
$file
,
@opts
) =
@_
;
$file
= resolv_path(
$file
);
my
$fs
= Rex::Interface::Fs->create;
$fs
->chgrp(
$group
,
$file
,
@opts
) or
die
(
"Can't chgrp $file"
);
}
Hide Show 16 lines of Pod
sub
chmod
{
my
(
$mode
,
$file
,
@opts
) =
@_
;
$file
= resolv_path(
$file
);
my
$fs
= Rex::Interface::Fs->create;
$fs
->
chmod
(
$mode
,
$file
,
@opts
) or
die
(
"Can't chmod $file"
);
}
Hide Show 10 lines of Pod
sub
rename
{
my
(
$old
,
$new
) =
@_
;
$old
= resolv_path(
$old
);
$new
= resolv_path(
$new
);
Rex::get_current_connection()->{reporter}
->report_resource_start(
type
=>
"rename"
,
name
=>
"$old -> $new"
);
my
$fs
= Rex::Interface::Fs->create;
my
$old_present
= 0;
if
(
$fs
->is_file(
$old
) ||
$fs
->is_dir(
$old
) ||
$fs
->is_symlink(
$old
) ) {
$old_present
= 1;
}
Rex::Logger::debug(
"Renaming $old to $new"
);
if
( !
$fs
->
rename
(
$old
,
$new
) ) {
Rex::Logger::info(
"Rename failed ($old -> $new)"
);
die
(
"Rename failed $old -> $new"
);
}
my
$new_present
= 0;
if
(
$fs
->is_file(
$new
) ||
$fs
->is_dir(
$new
) ||
$fs
->is_symlink(
$new
) ) {
$new_present
= 1;
}
my
$old_absent
= 0;
if
( !(
$fs
->is_file(
$old
) ||
$fs
->is_dir(
$old
) ||
$fs
->is_symlink(
$old
) ) ) {
$old_absent
= 1;
}
if
(
$old_present
== 1 &&
$new_present
== 1 &&
$old_absent
== 1 ) {
Rex::get_current_connection()->{reporter}->report(
changed
=> 1 );
}
else
{
Rex::get_current_connection()->{reporter}->report(
changed
=> 0 );
}
Rex::get_current_connection()->{reporter}
->report_resource_end(
type
=>
"rename"
,
name
=>
"$old -> $new"
);
}
Hide Show 6 lines of Pod
sub
mv {
return
&rename
(
@_
);
}
Hide Show 10 lines of Pod
sub
cp {
my
(
$source
,
$dest
) =
@_
;
$source
= resolv_path(
$source
);
$dest
= resolv_path(
$dest
);
Rex::get_current_connection()->{reporter}
->report_resource_start(
type
=>
"cp"
,
name
=>
"$source -> $dest"
);
my
$fs
= Rex::Interface::Fs->create;
my
$new_present
= 0;
if
(
$fs
->is_file(
$source
) &&
$fs
->is_dir(
$dest
) ) {
$dest
=
"$dest/"
. basename
$source
;
}
if
(
$fs
->is_file(
$dest
) ||
$fs
->is_dir(
$dest
) ||
$fs
->is_symlink(
$dest
) ) {
$new_present
= 1;
}
if
( !
$fs
->cp(
$source
,
$dest
) ) {
die
(
"Copy failed from $source to $dest"
);
}
if
(
$new_present
== 0 ) {
Rex::get_current_connection()->{reporter}->report(
changed
=> 1, );
}
else
{
Rex::get_current_connection()->{reporter}->report(
changed
=> 0, );
}
Rex::get_current_connection()->{reporter}
->report_resource_end(
type
=>
"cp"
,
name
=>
"$source -> $dest"
);
}
Hide Show 16 lines of Pod
sub
list_files {
my
$path
=
shift
;
$path
= resolv_path(
$path
);
my
$fs
= Rex::Interface::Fs->create;
my
@ret
=
$fs
->ls(
$path
);
return
@ret
;
}
Hide Show 6 lines of Pod
sub
ls {
return
list_files(
@_
);
}
Hide Show 29 lines of Pod
sub
stat
{
my
(
$file
) =
@_
;
$file
= resolv_path(
$file
);
my
%ret
;
Rex::Logger::debug(
"Getting fs stat from $file"
);
my
$fs
= Rex::Interface::Fs->create;
my
@stat
=
$fs
->
stat
(
$file
);
die
(
"Can't stat $file"
)
if
( !
defined
$stat
[0] &&
scalar
@stat
== 1 );
if
(
scalar
@stat
% 2 ) {
Rex::Logger::debug(
'stat output: '
.
join
', '
,
@stat
);
die
(
'stat returned odd number of elements'
);
}
%ret
=
@stat
;
return
%ret
;
}
Hide Show 17 lines of Pod
sub
is_file {
my
(
$file
) =
@_
;
$file
= resolv_path(
$file
);
my
$fs
= Rex::Interface::Fs->create;
return
$fs
->is_file(
$file
);
}
Hide Show 17 lines of Pod
sub
is_dir {
my
(
$path
) =
@_
;
$path
= resolv_path(
$path
);
my
$fs
= Rex::Interface::Fs->create;
return
$fs
->is_dir(
$path
);
}
Hide Show 17 lines of Pod
sub
is_symlink {
my
(
$path
) =
@_
;
$path
= resolv_path(
$path
);
my
$fs
= Rex::Interface::Fs->create;
return
$fs
->is_symlink(
$path
);
}
Hide Show 17 lines of Pod
sub
is_readable {
my
(
$file
) =
@_
;
$file
= resolv_path(
$file
);
Rex::Logger::debug(
"Checking if $file is readable"
);
my
$fs
= Rex::Interface::Fs->create;
return
$fs
->is_readable(
$file
);
}
Hide Show 17 lines of Pod
sub
is_writable {
my
(
$file
) =
@_
;
$file
= resolv_path(
$file
);
Rex::Logger::debug(
"Checking if $file is writable"
);
my
$fs
= Rex::Interface::Fs->create;
return
$fs
->is_writable(
$file
);
}
Hide Show 6 lines of Pod
sub
is_writeable {
is_writable(
@_
);
}
Hide Show 17 lines of Pod
sub
readlink
{
my
(
$file
) =
@_
;
$file
= resolv_path(
$file
);
Rex::Logger::debug(
"Reading link of $file"
);
my
$fs
= Rex::Interface::Fs->create;
my
$link
=
$fs
->
readlink
(
$file
);
unless
(
$link
) {
Rex::Logger::debug(
"readlink: $file is not a link."
);
die
(
"readlink: $file is not a link."
);
}
return
$link
;
}
Hide Show 12 lines of Pod
sub
chdir
{
Rex::Logger::debug(
"chdir behaviour will be changed in the future."
);
CORE::
chdir
(
$_
[0] );
}
Hide Show 6 lines of Pod
sub
cd {
&chdir
(
$_
[0] );
}
Hide Show 13 lines of Pod
sub
df {
my
(
$dev
) =
@_
;
my
$ret
= {};
$dev
||=
""
;
my
$exec
= Rex::Interface::Exec->create;
my
(
$out
,
$err
) =
$exec
->
exec
(
"df $dev 2>/dev/null"
);
my
@lines
=
split
( /\r?\n/,
$out
);
$ret
= _parse_df(
@lines
);
if
(
$dev
) {
if
(
keys
%$ret
== 1 ) {
(
$dev
) =
keys
%$ret
;
}
return
$ret
->{
$dev
};
}
return
$ret
;
}
sub
_parse_df {
my
@lines
=
@_
;
chomp
@lines
;
my
$ret
= {};
shift
@lines
;
my
$current_fs
=
""
;
for
my
$line
(
@lines
) {
my
(
$fs
,
$size
,
$used
,
$free
,
$use_per
,
$mounted_on
) =
split
( /\s+/,
$line
, 6 );
$current_fs
=
$fs
if
$fs
;
if
( !
$size
) {
next
;
}
$ret
->{
$current_fs
} = {
size
=>
$size
,
used
=>
$used
,
free
=>
$free
,
used_perc
=>
$use_per
,
mounted_on
=>
$mounted_on
};
}
return
$ret
;
}
Hide Show 12 lines of Pod
sub
du {
my
(
$path
) =
@_
;
$path
= resolv_path(
$path
);
my
$exec
= Rex::Interface::Exec->create;
my
@lines
=
$exec
->
exec
(
"du -s $path"
);
my
(
$du
) = (
$lines
[0] =~ m/^(\d+)/ );
return
$du
;
}
Hide Show 30 lines of Pod
sub
mount {
my
(
$device
,
$mount_point
,
@options
) =
@_
;
my
$option
= {
@options
};
if
(
defined
$option
->{fs} ) {
Rex::Logger::info(
'The `fs` option of the mount command has been deprecated in favor of the `type` option. Please update your task.'
,
'warn'
);
if
( !
defined
$option
->{type} ) {
$option
->{type} =
$option
->{fs};
}
else
{
Rex::Logger::info(
'Both `fs` and `type` options have been specified for mount command. Preferring `type`.'
,
'warn'
);
}
}
delete
$option
->{fs};
Rex::get_current_connection()->{reporter}
->report_resource_start(
type
=>
"mount"
,
name
=>
"$mount_point"
);
$option
->{ensure} ||=
"present"
;
if
(
$option
->{ensure} eq
"absent"
) {
&umount
(
$mount_point
,
device
=>
$device
,
on_change
=>
(
exists
$option
->{on_change} ?
$option
->{on_change} :
undef
)
);
}
else
{
if
(
$option
->{ensure} eq
"persistent"
) {
$option
->{persistent} = 1;
}
my
$changed
= 0;
my
$exec
= Rex::Interface::Exec->create;
my
(
$m_out
,
$m_err
) =
$exec
->
exec
(
"mount"
);
my
@mounted
=
split
( /\r?\n/,
$m_out
);
my
(
$already_mounted
) =
grep
{ m/
$device
on
$mount_point
/ }
@mounted
;
if
(
$already_mounted
) {
Rex::Logger::debug(
"Device ($device) already mounted on $mount_point."
);
$changed
= 0;
}
my
$cmd
=
sprintf
(
"mount %s %s %s %s"
,
$option
->{type} ?
"-t "
.
$option
->{type} :
""
,
$option
->{
"options"
}
?
" -o "
.
join
(
","
, @{
$option
->{
"options"
} } )
:
""
,
$device
,
$mount_point
);
unless
(
$already_mounted
) {
$exec
->
exec
(
$cmd
);
if
( $? != 0 ) {
die
(
"Mount failed of $mount_point"
); }
$changed
= 1;
Rex::get_current_connection()->{reporter}->report(
changed
=> 1,
message
=>
"Device $device mounted on $mount_point."
);
}
if
(
exists
$option
->{persistent} ) {
if
( !
exists
$option
->{type} ) {
my
(
$out
,
$err
) =
$exec
->
exec
(
"mount"
);
my
@output
=
split
( /\r?\n/,
$out
);
my
(
$line
) =
grep
{ /^
$device
/ }
@output
;
my
(
$_d
,
$_o
,
$_p
,
$_t
,
$fs_type
) =
split
( /\s+/,
$line
);
$option
->{type} =
$fs_type
;
my
(
$_options
) = (
$line
=~ m/\((.+?)\)/ );
$option
->{options} =
$_options
;
}
my
$fh
= Rex::Interface::File->create;
my
$old_md5
= md5(
"/etc/fstab"
);
if
( !
$fh
->
open
(
"<"
,
"/etc/fstab"
) ) {
Rex::Logger::debug(
"Can't open /etc/fstab for reading."
);
die
(
"Can't open /etc/fstab for reading."
);
}
my
$f
= Rex::FS::File->new(
fh
=>
$fh
);
my
@content
=
$f
->read_all;
$f
->
close
;
my
@new_content
=
grep
{ !/^
$device
\s/ }
@content
;
$option
->{options} ||=
"defaults"
;
if
(
ref
(
$option
->{options} ) eq
"ARRAY"
) {
my
$mountops
=
join
(
","
, @{
$option
->{
"options"
} } );
if
(
$option
->{label} ) {
push
(
@new_content
,
"LABEL="
.
$option
->{label}
.
"\t$mount_point\t$option->{type}\t$mountops\t0 0\n"
);
}
else
{
push
(
@new_content
,
"$device\t$mount_point\t$option->{type}\t$mountops\t0 0\n"
);
}
}
else
{
if
(
$option
->{label} ) {
push
(
@new_content
,
"LABEL="
.
$option
->{label}
.
"\t$mount_point\t$option->{type}\t$option->{options}\t0 0\n"
);
}
else
{
push
(
@new_content
,
"$device\t$mount_point\t$option->{type}\t$option->{options}\t0 0\n"
);
}
}
$fh
= Rex::Interface::File->create;
if
( !
$fh
->
open
(
">"
,
"/etc/fstab"
) ) {
Rex::Logger::debug(
"Can't open /etc/fstab for writing."
);
die
(
"Can't open /etc/fstab for writing."
);
}
$f
= Rex::FS::File->new(
fh
=>
$fh
);
$f
->
write
(
join
(
"\n"
,
@new_content
) );
$f
->
close
;
my
$new_md5
= md5(
"/etc/fstab"
);
if
(
$new_md5
ne
$old_md5
) {
Rex::get_current_connection()->{reporter}
->report(
changed
=> 1,
message
=>
"File /etc/fstab updated."
);
$changed
= 1;
}
}
if
(
$changed
== 1 ) {
if
(
exists
$option
->{on_change} &&
ref
$option
->{on_change} eq
"CODE"
) {
$option
->{on_change}->(
$device
,
$mount_point
);
}
}
}
Rex::get_current_connection()->{reporter}
->report_resource_end(
type
=>
"mount"
,
name
=>
"$mount_point"
);
}
Hide Show 10 lines of Pod
sub
umount {
my
(
$mount_point
,
%option
) =
@_
;
my
$device
;
if
(
exists
$option
{device} ) {
$device
=
$option
{device};
}
my
$exec
= Rex::Interface::Exec->create;
Rex::get_current_connection()->{reporter}
->report_resource_start(
type
=>
"umount"
,
name
=>
"$mount_point"
);
my
$changed
= 0;
my
(
$m_out
,
$m_err
) =
$exec
->
exec
(
"mount"
);
my
@mounted
=
split
( /\r?\n/,
$m_out
);
my
$already_mounted
;
if
(
$device
) {
(
$already_mounted
) =
grep
{ m/
$device
on
$mount_point
/ }
@mounted
;
}
else
{
(
$already_mounted
) =
grep
{ m/on
$mount_point
/ }
@mounted
;
}
if
(
$already_mounted
) {
$exec
->
exec
(
"umount $mount_point"
);
if
( $? != 0 ) {
die
(
"Umount failed of $mount_point"
); }
$changed
= 1;
}
if
(
$changed
) {
if
(
exists
$option
{on_change} &&
ref
$option
{on_change} eq
"CODE"
) {
$option
{on_change}->(
$mount_point
,
%option
);
}
Rex::get_current_connection()->{reporter}
->report(
changed
=> 1,
message
=>
"Unmounted $mount_point."
);
}
Rex::get_current_connection()->{reporter}
->report_resource_end(
type
=>
"umount"
,
name
=>
"$mount_point"
);
}
Hide Show 12 lines of Pod
sub
glob
{
my
(
$glob
) =
@_
;
$glob
= resolv_path(
$glob
);
my
$fs
= Rex::Interface::Fs->create;
return
$fs
->
glob
(
$glob
);
}
1;