$Config::Model::Backend::Systemd::Unit::VERSION
=
'0.257.1'
;
use
5.020;
use
feature
qw/postderef signatures/
;
no
warnings
qw/experimental::postderef experimental::signatures/
;
has
_has_system_file
=> (
is
=>
'rw'
,
isa
=>
'Bool'
,
default
=> 0,
);
my
$logger
= get_logger(
"Backend::Systemd::Unit"
);
my
$user_logger
= get_logger(
"User"
);
sub
get_unit_info (
$self
,
$file_path
) {
my
$unit_type
=
$self
->node->element_name;
my
$unit_name
=
$self
->node->index_value;
my
$app
=
$self
->instance->application;
my
(
$trash
,
$app_type
) =
split
/-/,
$app
;
if
(
my
$fp
=
$file_path
->basename) {
my
(
$n
,
$t
) =
split
/\./,
$fp
;
$unit_type
||=
$t
;
$unit_name
||=
$n
;
}
$unit_type
||=
$app_type
if
(
$app_type
and
$app_type
ne
'user'
);
Config::Model::Exception::User->throw(
object
=>
$self
,
error
=>
"Unknown unit type. Please add type to file name. e.g. "
.
$file_path
->basename.
".service or socket..."
)
unless
$unit_type
;
if
(
$app
!~ /^systemd(-user)?$/ and
$app
!~ /^systemd-
$unit_type
/) {
Config::Model::Exception::User->throw(
objet
=>
$self
->node,
error
=>
"Unit type $unit_type does not match app $app"
);
}
return
(
$unit_name
,
$unit_type
);
}
around
read
=>
sub
(
$orig
,
$self
,
%args
) {
$args
{comment_delimiter} =
"#;"
;
if
(
$self
->instance->application =~ /-file$/) {
return
1
unless
$args
{file_path}->
exists
;
return
$self
->load_ini_file(
$orig
,
%args
);
}
my
(
$unit_name
,
$unit_type
) =
$self
->get_unit_info(
$args
{file_path});
my
$app
=
$self
->instance->application;
my
@default_directories
;
if
(
$app
!~ /-user$/ or not
$args
{file_path}->
exists
) {
@default_directories
=
$self
->default_directories;
}
$self
->node->instance->layered_start;
my
$root
=
$args
{root} || path(
'/'
);
my
$cwd
=
$args
{root} || path(
'.'
);
my
$found_unit
= 0;
foreach
my
$layer
(
@default_directories
) {
my
$local_root
=
$layer
=~ m!^/! ?
$root
:
$cwd
;
my
$layer_dir
=
$local_root
->child(
$layer
);
next
unless
$layer_dir
->is_dir;
my
$layer_file
=
$layer_dir
->child(
$unit_name
.
'.'
.
$unit_type
);
next
unless
$layer_file
->
exists
;
$user_logger
->
warn
(
"Reading unit '$unit_type' '$unit_name' from '$layer_file'."
);
$self
->load_ini_file(
$orig
,
%args
,
file_path
=>
$layer_file
);
$found_unit
++;
}
$self
->node->instance->layered_stop;
if
(
$found_unit
) {
$self
->_has_system_file(1);
}
else
{
$user_logger
->
warn
(
"Could not find unit files for $unit_type name $unit_name"
);
}
my
$service_path
;
if
(
$app
=~ /-user$/ and
$args
{file_path}->
exists
) {
$service_path
=
$args
{file_path} ;
}
else
{
$service_path
=
$args
{file_path}->parent->child(
"$unit_name.$unit_type.d/override.conf"
);
}
if
(
$service_path
->
exists
and
$service_path
->realpath eq
'/dev/null'
) {
$logger
->debug(
"skipping unit $unit_type name $unit_name from $service_path"
);
}
elsif
(
$service_path
->
exists
) {
$logger
->debug(
"reading unit $unit_type name $unit_name from $service_path"
);
$self
->load_ini_file(
$orig
,
%args
,
file_path
=>
$service_path
);
}
return
1;
};
sub
load_ini_file (
$self
,
$orig_read
,
%args
) {
$logger
->debug(
"opening file '"
.
$args
{file_path}.
"' to read"
);
my
$res
=
$self
->
$orig_read
(
%args
);
die
"failed "
.
$args
{file_path}.
" read"
unless
$res
;
return
;
};
sub
load_data (
$self
,
%args
) {
my
$check
=
$args
{check};
my
$data
=
$args
{data} ;
my
$disp_leaf
=
sub
{
my
(
$scanner
,
$data
,
$node
,
$element_name
,
$index
,
$leaf_object
) =
@_
;
if
(
ref
(
$data
) eq
'ARRAY'
) {
Config::Model::Exception::User->throw(
object
=>
$leaf_object
,
error
=>
"Cannot store twice the same value ('"
.
join
(
"', '"
,
@$data
).
"'). "
.
"Is '$element_name' line duplicated in config file ? "
.
"You can use -force option to load value '"
.
$data
->[-1].
"'."
)
if
$check
eq
'yes'
;
$data
=
$data
->[-1];
}
if
(
$leaf_object
->value_type eq
'boolean'
) {
$data
=
'yes'
if
$data
eq
'on'
;
$data
=
'no'
if
$data
eq
'off'
;
}
$leaf_object
->store(
value
=>
$data
,
check
=>
$check
);
} ;
my
$unit_cb
=
sub
{
my
(
$scanner
,
$data_ref
,
$node
,
@elements
) =
@_
;
foreach
my
$elt
(
@elements
) {
my
$unit_data
=
delete
$data_ref
->{
$elt
};
next
unless
defined
$unit_data
;
$scanner
->scan_element(
$unit_data
,
$node
,
$elt
) ;
}
foreach
my
$elt
(
sort
keys
%$data_ref
) {
my
$unit_data
=
$data_ref
->{
$elt
};
$scanner
->scan_element(
$unit_data
,
$node
,
$elt
) ;
}
};
my
$list_cb
=
sub
{
my
(
$scanner
,
$data
,
$node
,
$element_name
,
@idx
) =
@_
;
my
$list_ref
=
ref
(
$data
) ?
$data
: [
$data
];
my
$list_obj
=
$node
->fetch_element(
name
=>
$element_name
,
check
=>
$check
);
foreach
my
$d
(
@$list_ref
) {
$list_obj
->
push
(
$d
);
}
};
my
$scan
= Config::Model::ObjTreeScanner-> new (
node_content_cb
=>
$unit_cb
,
list_element_cb
=>
$list_cb
,
leaf_cb
=>
$disp_leaf
,
) ;
$scan
->scan_node(
$data
,
$self
->node) ;
return
;
}
around
'write'
=>
sub
(
$orig
,
$self
,
%args
) {
if
(
$self
->node->grab_value(
'disable'
)) {
my
$fp
=
$args
{file_path};
if
(
$fp
->realpath ne
'/dev/null'
) {
$user_logger
->
warn
(
"symlinking file $fp to /dev/null"
);
$fp
->remove;
symlink
(
'/dev/null'
,
$fp
->stringify);
}
return
1;
}
my
(
$unit_name
,
$unit_type
) =
$self
->get_unit_info(
$args
{file_path});
my
$app
=
$self
->instance->application;
my
$service_path
;
if
(
$app
=~ /-(user|file)$/ and not
$self
->_has_system_file) {
$service_path
=
$args
{file_path};
$logger
->debug(
"writing unit to $service_path"
);
$self
->
$orig
(
%args
,
file_path
=>
$service_path
);
}
else
{
my
$dir
=
$args
{file_path}->parent->child(
"$unit_name.$unit_type.d"
);
$dir
->mkpath({
mode
=>
oct
(755) });
$service_path
=
$dir
->child(
'override.conf'
);
$logger
->debug(
"writing unit to $service_path"
);
$self
->
$orig
(
%args
,
file_path
=>
$service_path
);
if
(
scalar
$dir
->children == 0) {
$logger
->
warn
(
"Removing empty dir $dir"
);
rmdir
$dir
;
}
}
return
1;
};
around
_write_leaf
=>
sub
(
$orig
,
$self
,
$args
,
$node
,
$elt
) {
if
(
$elt
eq
'disable'
) {
return
''
;
}
else
{
return
$self
->
$orig
(
$args
,
$node
,
$elt
);
}
};
no
Mouse ;
__PACKAGE__->meta->make_immutable ;
1;