@ -19,12 +19,10 @@ if (($running_directory =~ /^\./) && ($ENV{PWD}))
my $anvil = Anvil::Tools->new();
my $manage_tunnel = $anvil->data->{path}{exe}{'anvil-manage-tunnel'};
my $echo = $anvil->data->{path}{exe}{'echo'};
my $grep = $anvil->data->{path}{exe}{'grep'};
my $kill = $anvil->data->{path}{exe}{'kill'};
my $pgrep = $anvil->data->{path}{exe}{'pgrep'};
my $ps = $anvil->data->{path}{exe}{'ps'};
my $ss = $anvil->data->{path}{exe}{'ss'};
my $sed = $anvil->data->{path}{exe}{'sed'};
my $websockify = $anvil->data->{path}{exe}{'websockify'};
@ -42,7 +40,6 @@ if (not $anvil->data->{sys}{database}{connections})
my $switch_debug = $anvil->data->{switches}{'debug'};
my $open = $anvil->data->{switches}{'open'};
my $prepare = $anvil->data->{switches}{'prepare'};
my $server = $anvil->data->{switches}{'server'};
my $server_uuid = $anvil->data->{switches}{'server-uuid'};
my $server_vnc_port = $anvil->data->{switches}{'server-vnc-port'};
@ -75,12 +72,6 @@ if ($server_uuid)
$anvil->nice_exit({ exit_code => $rcode });
}
elsif ($prepare)
{
my ($rcode) = prepare_tp_processes({ debug => $switch_debug });
$anvil->nice_exit({ exit_code => $rcode });
}
$anvil->nice_exit({ exit_code => 0 });
@ -106,36 +97,11 @@ sub build_find_available_port_call
return (0, $call);
}
sub build_tunnel_call
sub build_vncport_variable_name
{
my $parameters = shift;
my $ctl_cmd = $parameters->{ctl_cmd} // "forward";
my $ctl_path = $parameters->{ctl_path};
my $debug = $parameters->{debug} || 3;
my $lport = $parameters->{lport};
my $rport = $parameters->{rport};
my $svr_uuid = $parameters->{svr_uuid};
my $tunnel_ls_path = $parameters->{tunnel_ls_path};
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => $debug, list => $parameters, prefix => "build_tunnel_call" });
return (1) if ( (not defined $ctl_path)
|| (not defined $lport)
|| (not defined $rport) );
my ($svr_uuid) = @_;
my $ls_path_opt = defined $tunnel_ls_path ? "--tunnel-ls-path '$tunnel_ls_path'" : "";
my $ls_prefix_opt = defined $svr_uuid ? "--tunnel-ls-prefix '$svr_uuid'" : "";
my $call = "$manage_tunnel --child --ctl-cmd $ctl_cmd --ctl-path '$ctl_path' --debug $debug --forward-lport $lport --forward-rport $rport $ls_path_opt $ls_prefix_opt";
return (0, $call);
}
sub build_tunnel_variable_name
{
my ($svr_uuid, $tp_target_uuid) = @_;
return "sshctl::${svr_uuid}::${tp_target_uuid}";
return "server::${svr_uuid}::vncport";
}
sub call
@ -174,40 +140,6 @@ sub find_available_port
return call({ call => $call, debug => $debug });
}
sub find_ws_processes
{
my $parameters = shift;
my $debug = $parameters->{debug} || 3;
my $ps_name = $parameters->{ps_name} // "websockify";
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => $debug, list => $parameters, prefix => "find_ws_processes" });
my $ps_call = "$pgrep -a '$ps_name' | $sed -En 's/^([[:digit:]]+).*${ps_name}[[:space:]:]+([[:digit:]]+)[[:space:]:]+([[:digit:]]+).*\$/\\1,\\2,\\3/p'";
my ($rcode, $output) = call({ call => $ps_call, debug => $debug });
return (1) if ($rcode);
my $result = { pids => {}, sources => {}, targets => {} };
foreach my $line (split(/\n/, $output))
{
chomp($line);
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => $debug, list => { ws_line => $line } });
my ($pid, $sport, $tport) = split(/,/, $line);
my $process = { pid => $pid, sport => $sport, tport => $tport };
set_ws_process({ debug => $debug, entry => $process, entries => $result });
}
$anvil->Log->entry({ source => $THIS_FILE, line => __LINE__, level => $debug, raw => prettify($result, "ws_processes") });
return (0, $result);
}
sub find_server_vnc_port
{
my $parameters = shift;
@ -215,11 +147,13 @@ sub find_server_vnc_port
my $svr_uuid = $parameters->{svr_uuid};
my $svr_vnc_port = $parameters->{svr_vnc_port};
return (0, $svr_vnc_port) if ( defined $svr_vnc_port || is_int($svr_vnc_port) );
return (0, $svr_vnc_port) if (is_int($svr_vnc_port));
# If we don't have the server's VNC port, find it in its qemu-kvm process.
($rcode, my $svr_processes) = $anvil->Server->find_processes({ debug => $debug });
my ($rcode, $svr_processes) = $anvil->Server->find_processes({ debug => $debug });
return (1) if ($rcode);
my $svr_process = $svr_processes->{uuids}{$svr_uuid};
my $svr_vnc_alive = $svr_process->{vnc_alive};
@ -229,177 +163,48 @@ sub find_server_vnc_port
return (0, $svr_process->{vnc_port});
}
sub find_tp _processes
sub find_ws _processes
{
my $parameters = shift;
my $debug = $parameters->{debug} || 3;
my $ps_name = $parameters->{ps_name} // "anvil-manage-tunnel ";
my $ps_name = $parameters->{ps_name} // "websockify ";
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => $debug, list => $parameters, prefix => "find_tp _processes" });
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => $debug, list => $parameters, prefix => "find_ws _processes" });
my $ps_call = "$pgrep -af '$ps_name' | $sed -En 's/^([[:digit:]]+).*--target[[:space:]]+([^[:space:]]+).*--ctl-path[[:space:]]+([^[:space:]]+).*--tunnel-ls-path[[:space:]]+([^[:space:]]+).*\$/\\1,\\2,\\3,\\4 /p'";
my $ps_call = "$pgrep -a '$ps_name' | $sed -En 's/^([[:digit:]]+).*${ps_name}[[:space:]:]+([[:digit:]]+)[[:space:]:]+([[:digit:]]+).*\$/\\1,\\2,\\3 /p'";
my ($rcode, $output) = call({ call => $ps_call, debug => $debug });
return (1) if ($rcode);
my $result = { pids => {}, targets => {} };
my $result = { pids => {}, sources => {}, targets => {} };
foreach my $line (split(/\n/, $output))
{
chomp($line);
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => $debug, list => { tp_line => $line } });
my ($pid, $target, $ctl_path, $tunnel_ls_path) = split(/,/, $line);
my $process = { ctl_path => $ctl_path, pid => $pid, target => $target, tunnel_ls_path => $tunnel_ls_path };
set_tp_process({ debug => $debug, entry => $process, entries => $result });
}
$anvil->Log->entry({ source => $THIS_FILE, line => __LINE__, level => $debug, raw => prettify($result, "tp_processes") });
return (0, $result);
}
sub find_tunnels
{
my $parameters = shift;
my $debug = $parameters->{debug} || 3;
my $tunnel_ls_path = $parameters->{tunnel_ls_path};
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => $debug, list => $parameters, prefix => "find_tunnels" });
return (1) if (not defined $tunnel_ls_path);
my $result = { server_uuids => {}, lports => {} };
return (0, $result) if (not -e $tunnel_ls_path);
open(my $tunnel_ls_fh, "< :encoding(UTF-8)", $tunnel_ls_path) or return (1);
while (my $line = <$tunnel_ls_fh>)
{
chomp($line);
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => $debug, list => { tunnel_line => $line } });
my ($svr_uuid, $forward, $port_a, $port_b) = $line =~ /^([^\s]+)-([L|R]).*:(\d+):.*:(\d+)$/;
my $lport = $port_a;
my $rport = $port_b;
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => $debug, list => { ws_line => $line } });
if ($forward eq "R")
{
$lport = $port_b;
$rport = $port_a;
}
my ($pid, $sport, $tport) = split(/,/, $line);
my $tunnel = { lport => $lport, rport => $rport, server_uuid => $svr_uuid };
my $process = { pid => $pid, sport => $sport, tport => $tport };
set_tunnel({ debug => $debug, entry => $tunnel , entries => $result });
set_ws_process({ debug => $debug, entry => $process, entries => $result });
}
close($tunnel_ls_fh) or return (1);
$anvil->Log->entry({ source => $THIS_FILE, line => __LINE__, level => $debug, raw => prettify($result, "tunnels") });
$anvil->Log->entry({ source => $THIS_FILE, line => __LINE__, level => $debug, raw => prettify($result, "ws_processes") });
return (0, $result);
}
sub get_strikers
{
my $parameters = shift;
my $debug = $parameters->{debug} || 3;
my $query = "
SELECT host_name, host_uuid
FROM hosts
WHERE host_status = 'online' AND host_type = 'striker'
;";
my $rows = $anvil->Database->query({ query => $query, source => $THIS_FILE, line => __LINE__ });
my $strikers = { names => {}, uuids => {} };
foreach my $row (@{$rows})
{
my $host_name = $row->[0];
my $host_uuid = $row->[1];
$strikers->{uuids}{$host_uuid} = { name => $host_name, uuid => $host_uuid };
$strikers->{names}{$host_name} = $host_uuid;
}
$anvil->Log->entry({ source => $THIS_FILE, line => __LINE__, level => $debug, raw => prettify($strikers, "strikers") });
return (0, $strikers);
}
sub get_tunnel_variable
{
my $parameters = shift;
my $svr_uuid = $parameters->{svr_uuid};
my $tp_target_uuid = $parameters->{tp_target_uuid};
my $variable_name = build_tunnel_variable_name($svr_uuid, $tp_target_uuid);
my $query = "
SELECT variable_value
FROM variables
WHERE
variable_name = ".$anvil->Database->quote($variable_name)."
AND
variable_source_table = 'hosts'
AND
variable_source_uuid = ".$anvil->Database->quote($tp_target_uuid)."
;";
my $rows = $anvil->Database->query({ query => $query, source => $THIS_FILE, line => __LINE__ });
return (1) if (not @{$rows});
my $end_port = $rows->[0]->[0];
return (0, int($end_port));
}
sub is_int
{
return $_[0] =~ /^\d+$/;
return defined $_[0] && $_[0] =~ /^\d+$/;
}
sub is_uuid_v4
{
return $_[0] =~ /[a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}/;
}
sub prepare_tp_processes
{
my $parameters = shift;
my $debug = $parameters->{debug} || 3;
my $common_params = { debug => $debug };
my $rcode;
($rcode, my $hosts) = get_strikers($common_params);
return ($rcode) if ($rcode);
($rcode, my $tp_processes) = find_tp_processes($common_params);
return ($rcode) if ($rcode);
foreach my $host_uuid (keys %{$hosts->{uuids}})
{
my $host_name = $hosts->{uuids}{$host_uuid}{name};
($rcode, my $tp_pid) = start_tp({ tp_processes => $tp_processes, tp_target => $host_name, %$common_params });
return ($rcode) if ($rcode);
}
return (0);
return defined $_[0] && $_[0] =~ /[a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}/;
}
sub prettify
@ -445,81 +250,20 @@ sub set_entry
return (0);
}
sub set_tp_process
{
my $parameters = shift;
$parameters->{handle_delete} = sub {
my ($pid, $process, $processes) = @_;
$process = $processes->{pids}{$pid};
my $target = $process->{target};
delete $processes->{pids}{$pid};
delete $processes->{targets}{$target};
};
$parameters->{handle_set} = sub {
my ($pid, $process, $processes) = @_;
$pid = $process->{pid};
my $target = $process->{target};
$processes->{pids}{$pid} = $process;
$processes->{targets}{$target} = $pid;
};
return set_entry($parameters);
}
sub set_tunnel
{
my $parameters = shift;
$parameters->{handle_delete} = sub {
my ($svr_uuid, $tunnel, $tunnels) = @_;
$tunnel = $tunnels->{server_uuids}{$svr_uuid};
my $lport = $tunnel->{lport};
delete $tunnels->{server_uuids}{$svr_uuid};
delete $tunnels->{lports}{$lport};
};
$parameters->{handle_set} = sub {
my ($svr_uuid, $tunnel, $tunnels) = @_;
$svr_uuid = $tunnel->{server_uuid};
my $lport = $tunnel->{lport};
$tunnels->{server_uuids}{$svr_uuid} = $tunnel;
$tunnels->{lports}{$lport} = $svr_uuid;
};
return set_entry($parameters);
}
sub set_tunnel_variable
sub set_vncport_variable
{
my $parameters = shift;
my $debug = $parameters->{debug} || 3;
my $end_port = $parameters->{end_port};
my $svr_uuid = $parameters->{svr_uuid};
my $tp_target_uuid = $parameters->{tp_target_uuid};
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => $debug, list => $parameters, prefix => "set_tunnel _variable" });
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => $debug, list => $parameters, prefix => "set_vncport_variable" });
my ($variable_uuid) = $anvil->Database->insert_or_update_variables({
file => $THIS_FILE,
line => __LINE__,
variable_name => build_tunnel_variable_name($svr_uuid, $tp_target_uuid),
variable_source_table => "hosts",
variable_source_uuid => $tp_target_uuid,
variable_value => $end_port,
file => $THIS_FILE,
line => __LINE__,
variable_name => build_vncport_variable_name($svr_uuid),
variable_value => $end_port,
});
return (1) if (not is_uuid_v4($variable_uuid));
@ -585,177 +329,13 @@ sub start_pipe
return ($rcode) if ($rcode);
($rcode, my $hosts) = get_strikers($common_params);
return ($rcode) if ($rcode);
($rcode, my $tp_processes) = find_tp_processes($common_params);
return ($rcode) if ($rcode);
my $ws_process = $ws_processes->{pids}{$ws_pid};
foreach my $host_uuid (keys %{$hosts->{uuids}})
{
my $host_name = $hosts->{uuids}{$host_uuid}{name};
my $tp_pid = $tp_processes->{targets}{$host_name};
return (1) if (not defined $tp_pid);
my $tp_process = $tp_processes->{pids}{$tp_pid};
($rcode, my $tunnels) = find_tunnels({ tunnel_ls_path => $tp_process->{tunnel_ls_path}, %$common_params });
return ($rcode) if ($rcode);
($rcode) = start_tunnel({
svr_uuid => $svr_uuid,
tp_ctl_path => $tp_process->{ctl_path},
tp_target => $host_name,
tp_target_uuid => $host_uuid,
tp_tunnel_ls_path => $tp_process->{tunnel_ls_path},
tunnels => $tunnels,
ws_sport => $ws_process->{sport},
%$common_params,
});
return ($rcode) if ($rcode);
my $tunnel = $tunnels->{server_uuids}{$svr_uuid};
($rcode) = set_tunnel_variable({
debug => $debug,
end_port => $tunnel->{rport},
svr_uuid => $svr_uuid,
tp_target_uuid => $host_uuid,
%$common_params,
});
if ($rcode)
{
stop_tunnel({ %$tunnel, tp_ctl_path => $tp_process->{ctl_path}, %$common_params });
return ($rcode);
}
}
($rcode) = set_vncport_variable({ end_port => $ws_process->{sport}, svr_uuid => $svr_uuid, %$common_params });
return (0);
}
sub start_tp
{
my $parameters = shift;
my $debug = $parameters->{debug} || 3;
my $tp_ctl_path = $parameters->{tp_ctl_path};
my $tp_processes = $parameters->{tp_processes};
my $tp_target = $parameters->{tp_target};
my $tp_tunnel_ls_path = $parameters->{tp_tunnel_ls_path};
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => $debug, list => $parameters, prefix => "start_tp" });
return (1) if ( (not defined $tp_processes) || (not defined $tp_target) );
my $tp_ctl_name = "sshctl-${tp_target}";
$tp_ctl_path //= $ENV{HOME}."/.libnet-openssh-perl/${tp_ctl_name}";
$tp_tunnel_ls_path //= $anvil->data->{path}{'directories'}{'tmp'}."/${tp_ctl_name}-tunnel-ls";
my $existing_tp_pid = $tp_processes->{targets}{$tp_target};
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => $debug, list => { existing_tp_pid => $existing_tp_pid } });
return (0, $existing_tp_pid) if (defined $existing_tp_pid);
my $tp_call = "$manage_tunnel --debug $debug --target $tp_target --ctl-path '$tp_ctl_path' --tunnel-ls-path '$tp_tunnel_ls_path'";
my ($start_rcode, $start_ps) = call({ background => 1, call => $tp_call, debug => $debug });
return (1) if ($start_rcode);
my $tp_pid = $start_ps->pid;
my $tp_process = { ctl_path => $tp_ctl_path, pid => $tp_pid, target => $tp_target, tunnel_ls_path => $tp_tunnel_ls_path };
set_tp_process({ debug => $debug, entry => $tp_process, entries => $tp_processes });
return (0, $tp_pid);
}
sub start_tunnel
{
my $parameters = shift;
my $debug = $parameters->{debug};
my $svr_uuid = $parameters->{svr_uuid};
my $tp_ctl_path = $parameters->{tp_ctl_path};
my $tp_target = $parameters->{tp_target};
my $tp_target_uuid = $parameters->{tp_target_uuid};
my $tp_tunnel_ls_path = $parameters->{tp_tunnel_ls_path};
my $tunnels = $parameters->{tunnels};
my $ws_sport = $parameters->{ws_sport};
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => $debug, list => $parameters, prefix => "start_tunnel" });
return (1) if ( (not defined $svr_uuid)
|| (not defined $tp_target)
|| (not defined $tp_target_uuid)
|| (not defined $tp_ctl_path) || ($tp_ctl_path eq "") || (not -e $tp_ctl_path)
|| (not defined $tunnels)
|| (not defined $ws_sport) );
my $existing_tunnel = $tunnels->{server_uuids}{$svr_uuid};
$anvil->Log->entry({ source => $THIS_FILE, line => __LINE__, level => $debug, raw => prettify($existing_tunnel, "existing_tunnel") });
return (0, $svr_uuid) if (defined $existing_tunnel);
my $build_rcode;
my $sh_call;
# ----- Try to find a usable port on the target host.
($build_rcode, $sh_call) = build_find_available_port_call({ start => $ws_sport });
return (1) if ($build_rcode);
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => $debug, list => { find_available_port_call => $sh_call } });
my ($tunnel_rport, $find_error, $find_rcode) = $anvil->Remote->call({
no_cache => 1,
ossh_opts => [ ctl_path => $tp_ctl_path, external_master => 1 ],
shell_call => $sh_call,
target => "0.0.0.0",
});
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => $debug, list => {
find_error => $find_error,
find_rcode => $find_rcode,
tunnel_rport => $tunnel_rport,
} });
return (1) if ($find_rcode);
# -----
($build_rcode, $sh_call) = build_tunnel_call({
ctl_path => $tp_ctl_path,
debug => $debug,
lport => $ws_sport,
rport => $tunnel_rport,
svr_uuid => $svr_uuid,
tunnel_ls_path => $tp_tunnel_ls_path,
});
return (1) if ($build_rcode);
my ($start_rcode) = call({ call => $sh_call, debug => $debug });
return (1) if ($start_rcode);
my $tunnel = { lport => $ws_sport, rport => $tunnel_rport, server_uuid => $svr_uuid };
set_tunnel({ debug => $debug, entry => $tunnel, entries => $tunnels });
return (0, $svr_uuid);
}
sub start_ws
{
my $parameters = shift;
@ -767,7 +347,7 @@ sub start_ws
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => $debug, list => $parameters, prefix => "start_ws" });
return (1) if ( (not defined $ws_processes)
|| (not defined $svr_vnc_port) || (not is_int($svr_vnc_port))
|| (not is_int($svr_vnc_port))
|| (not is_int($ws_sport_offset)) );
my $existing_ws_pid = $ws_processes->{targets}{$svr_vnc_port};
@ -801,9 +381,8 @@ sub stop_pipe
my $debug = $parameters->{debug} || 3;
my $svr_uuid = $parameters->{svr_uuid};
my $svr_vnc_port = $parameters->{svr_vnc_port};
my $tunnels = $parameters->{tunnels};
return (1) if ( ( not is_uuid_v4($svr_uuid)) || (not defined $tunnels) );
return (1) if (not is_uuid_v4($svr_uuid));
my $common_params = { debug => $debug };
@ -819,76 +398,7 @@ sub stop_pipe
my $ws_pid = $ws_processes->{targets}{$svr_vnc_port};
stop_ws({ ws_pid => $ws_pid, ws_processes => $ws_processes });
($rcode, my $hosts) = get_strikers($common_params);
return ($rcode) if ($rcode);
($rcode, my $tp_processes) = find_tp_processes($common_params);
return ($rcode) if ($rcode);
foreach my $host_uuid (keys %{$hosts->{uuids}})
{
my $host_name = $hosts->{uuids}{$host_uuid}{name};
my $tp_pid = $tp_processes->{targets}{$host_name};
my $tp_process = $tp_processes->{pids}{$tp_pid};
my $tunnel = $tunnels->{server_uuids}{$svr_uuid};
next if (not defined $tunnel);
stop_tunnel({
%$tunnel,
tp_ctl_path => $tp_process->{ctl_path},
tp_tunnel_ls_path => $tp_process->{tunnel_ls_path},
%$common_params
});
}
return (0);
}
sub stop_tp
{
my $parameters = shift;
my $debug = $parameters->{debug} || 3;
my $tp_pid = $parameters->{tp_pid};
my $tp_processes = $parameters->{tp_processes};
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => $debug, list => $parameters, prefix => "stop_tp" });
call({ debug => $debug, call => "$kill $tp_pid || $kill -9 $tp_pid" });
set_tp_process({ debug => $debug, id => $tp_pid, entries => $tp_processes });
}
sub stop_tunnel
{
my $parameters = shift;
my $debug = $parameters->{debug} || 3;
my $lport = $parameters->{lport};
my $rport = $parameters->{rport};
my $tp_ctl_path = $parameters->{tp_ctl_path};
my $tp_tunnel_ls_path = $parameters->{tp_tunnel_ls_path};
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => $debug, list => $parameters, prefix => "stop_tunnel" });
my ($build_rcode, $call) = build_tunnel_call({
ctl_cmd => "cancel",
ctl_path => $tp_ctl_path,
debug => $debug,
lport => $lport,
rport => $rport,
tunnel_ls_path => $tp_tunnel_ls_path,
});
return (1) if ($build_rcode);
my ($stop_rcode) = call({ call => $call, debug => $debug });
return (1) if ($stop_rcode);
stop_ws({ ws_pid => $ws_pid, ws_processes => $ws_processes, %$common_params });
return (0);
}
@ -902,7 +412,11 @@ sub stop_ws
$anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => $debug, list => $parameters, prefix => "stop_ws" });
return (1) if ( (not is_int($ws_pid)) || (not defined $ws_processes) );
call({ debug => $debug, call => "$kill $ws_pid || $kill -9 $ws_pid" });
set_ws_process({ debug => $debug, id => $ws_pid, entries => $ws_processes });
return (0);
}