package POEDaemon::TCPServer::Main::Cmds::Admin; use strict; use warnings FATAL => 'all'; no warnings 'redefine'; use Data::Dumper; use POSIX qw(strftime); use POE; use POEDaemon; sub states { return $_[0], [qw( tcpserver_client_commands_admin )]; } sub tcpserver_client_commands_admin { my ($kernel, $session, $heap, $args) = @_[KERNEL, SESSION, HEAP, ARG0]; return unless ref $args eq 'HASH'; my $wheel_id = $args->{wheel_id} || return; my $input = $args->{input}; return unless defined $input; my $client = $heap->{tcpserver}->{connections}->{$wheel_id}; return unless $client && $client->{admin}; my $sock_wheel_id = $client->{sock_wheel_id} || return; my $cipher = $client->{cipher} || return; my $cn = $client->{cn} || return; my ($output, $no_output_log); if ($input =~ /^\s*gpio(?:_|\s+)(\d{1,3}|all)(?:=([01t]))?\s*$/i) { my $pin = $1; my $status = $2; my $use_new_gpio = 1; my $use_new_output_format = 1; my @valid_pins = sort {$a <=> $b} @{cfg->{change_gpio_pins} || []}; my $valid_pins = { map { $_ => 1 } @valid_pins }; if (!$heap->{gpio} && !$use_new_gpio) { $output = $use_new_output_format ? 'GPIO system failure' : 'ERR gpio system failure'; } elsif ($pin eq 'all') { my @lines = (); foreach my $real_pin (@valid_pins) { my $value = $heap->{gpiochange}->{pinstatus}->{$real_pin}->{value}; $value = 0 unless defined $value; push @lines, sprintf "%s%s=%s", ($use_new_output_format ? '' : 'gpio_'), $real_pin, $value; } $output = sprintf "%s %s", ($use_new_output_format ? 'GPIO' : 'OK'), join(' ', @lines); } elsif ($valid_pins->{$pin}) { if (defined $status) { if ($status eq GPIO_TOGGLE) { my $new_async_status; if (!defined $heap->{gpiochange}->{pinstatus}->{$pin}->{value} || $heap->{gpiochange}->{pinstatus}->{$pin}->{value} == GPIO_LOW) { $new_async_status = GPIO_HIGH; } elsif ($heap->{gpiochange}->{pinstatus}->{$pin}->{value} == GPIO_HIGH) { $new_async_status = GPIO_LOW; } $kernel->yield(gpiochange_set => { pin => $pin, value => $new_async_status, }); $output = sprintf "%s%s=%s", ($use_new_output_format ? 'GPIO ' : 'OK gpio_'), $pin, $new_async_status; } else { my $gpio_setval; if ($status == GPIO_HIGH) { $gpio_setval = GPIO_HIGH; } elsif ($status == GPIO_LOW) { $gpio_setval = GPIO_LOW; } $kernel->yield(gpiochange_set => { pin => $pin, value => $gpio_setval, }); $output = sprintf "%s%s=%s", ($use_new_output_format ? 'GPIO ' : 'OK gpio_'), $pin, $status; } } } else { $output = $use_new_output_format ? 'GPIO invalid pin' : 'ERR invalid pin'; } } elsif ($input =~ /^\s*pwm\s+([12][ab]|all)(?:=(\d{1,4}))?\s*$/i) { my $pwm = $1; my $value = $2; my @valid_pwms = sort @{cfg->{valid_pwms} || []}; my $valid_pwms = { map { $_ => 1 } @valid_pwms }; if ($pwm eq 'all') { my @pwms = (); foreach my $name (@valid_pwms) { if ($name =~ /^([12])([ab])$/i) { my $group = $1; my $port = uc $2; my $value = $heap->{sysctlset}->{status}->{sprintf "dev.am335x_pwm.%s.duty%s", $group, $port}; if (defined $value) { push @pwms, sprintf "%s=%s", $name, $value; } else { push @pwms, sprintf "%s=value_error", $name; } } else { push @pwms, sprintf "%s=name_error", $name; } } $output = join(' ', @pwms); } elsif ($valid_pwms->{$pwm}) { if ($pwm =~ /^([12])([ab])$/i) { my $group = $1; my $port = uc $2; if (defined $value) { if ($value >= 0 && $value <= 1000) { $kernel->yield(sysctlset => { name => sprintf("dev.am335x_pwm.%s.duty%s", $group, $port), value => $value, }); $output = sprintf "%s=%s", $pwm, $value; } else { $output = sprintf "value error %s=%s", $pwm, $value; } } else { my $new_value = $heap->{sysctlset}->{status}->{sprintf "dev.am335x_pwm.%s.duty%s", $group, $port}; if (defined $new_value) { $output = sprintf "%s=%s", $pwm, $new_value; } else { $output = sprintf "value error %s", $pwm; } } } else { $output = sprintf "name error %s", $pwm; } } else { $output = 'invalid name'; } $output = sprintf "PWM %s", $output; } elsif ($input =~ /^\s*adc\s+([0-7]|all)\s*$/i && cfg->{valid_adcs}) { my $adc = $1; my @adc_list = ($adc); @adc_list = sort {$a <=> $b} @{cfg->{valid_adcs} || []} if $adc eq 'all'; foreach my $num (@adc_list) { my $name = sprintf "dev.ti_adc.0.ain.%s.input", $num; $kernel->yield(sysctlget => { sysctl => $name, source_tcpclient_wheel_id => $wheel_id, }); } } elsif ($input =~ /^\s*ping\s+(.{0,100})\s*$/i) { my $args = $1; if (defined $args) { $output = sprintf "PONG %s", $args; } else { $output = 'PONG'; } } elsif ($input =~ /^\s*poll\s+(start|stop)\s*$/i && cfg->{poll_gpio_pins}) { my $action = lc $1; if ($action eq 'start') { delete $heap->{gpio_poll_all_ignore}; } elsif ($action eq 'stop') { $heap->{gpio_poll_all_ignore} = 1; } $output = sprintf "POLL %s", $action; } elsif ($input =~ /^\s*kill\s*$/i) { $kernel->yield(shutdown => 'tcpserver client kill command'); $output = 'KILL'; } elsif ($input =~ /^\s*restart\s*$/i) { my $argv0_orig = $heap->{argv0_orig}; if ($argv0_orig =~ /^[a-z0-9\/\.\-]{3,255}$/i) { $kernel->yield(restart => { source_wheel_id => $wheel_id, }); } else { $output = sprintf "RESTART fail, invalid argv0_orig = '%s'", $argv0_orig; } } elsif ($input =~ /^\s*r(?:eload)?\s*(clear|force)?\s*$/i) { my $args = lc($1 || ''); $kernel->yield(reload => { source_wheel_id => $wheel_id, subcmd => $args, }); } elsif ($input =~ /^\s*sysreboot\s*$/i && cfg->{tcpserver_command_sysreboot_enabled}) { $kernel->yield(wheelrun_exec => { prog => [ 'util/sysreboot-suidwrapper', ], }); $kernel->yield(shutdown => 'tcpserver client sysreboot command'); $output = 'SYSREBOOT'; } elsif ($input =~ /^\s*load\s*$/i) { $kernel->yield(sysctlget => { sysctl => 'vm.loadavg', source_tcpclient_wheel_id => $wheel_id, }); } elsif ($input =~ /^\s*t(?:ime)?\s*$/i) { $output = sprintf "TIME %s", time_hires; } elsif ($input =~ /^\s*u(?:ptime)?\s*$/i) { $output = sprintf "UPTIME %s", duration_exact time - $^T; } elsif ($input =~ /^\s*heap\s*$/i) { $no_output_log = 1; $output = sprintf "HEAP\n%s", Dumper $heap; } elsif ($input =~ /^\s*client\s*$/i) { my ($input_idle, $output_idle); if ($client->{last_input_time}) { $input_idle = concise_duration time_hires - $client->{last_input_time}; } if ($client->{last_output_time}) { $output_idle = concise_duration time_hires - $client->{last_output_time}; } my $server = $heap->{tcpserver}->{servers}->{$sock_wheel_id}; my $server_sslstatus; if (exists $server->{ssl} && defined $server->{ssl}) { $server_sslstatus = $server->{ssl}; } $output = sprintf "CLIENT sock=%s id=%s addr=%s port=%s inputidle=%s outputidle=%s ssl=%s%s%s", $sock_wheel_id, $wheel_id, $client->{client_addr}, $client->{client_port}, $input_idle || 'n/a', $output_idle || 'n/a', $server_sslstatus || 'n/a', ($cipher ? sprintf " cipher=%s", $cipher : ''), (defined $cn ? sprintf " cn=%s", $cn : ''); } elsif ($input =~ /^\s*c(?:lients)?(?:\s+(\w{1,16}))?\s*$/i) { my $arg = lc($1 || ''); my $multiline = 1; $multiline = 0 if $arg =~ /^\s*sl?\s*$/i; my $oneline; $oneline = 1 if $arg =~ /^\s*1\s*$/i; my @clients; foreach my $curr_wheel_id (sort {$a <=> $b} keys %{$heap->{tcpserver}->{connections}}) { my $curr_client = $heap->{tcpserver}->{connections}->{$curr_wheel_id}; my $curr_sock_wheel_id = $curr_client->{sock_wheel_id}; if ($oneline) { push @clients, sprintf "%s sock#%s client#%s addr=%s port=%s", ($curr_wheel_id == $wheel_id ? '*' : ' '), $curr_sock_wheel_id || 'n/a', $curr_wheel_id, $curr_client->{client_addr}, $curr_client->{client_port}; next; } my $sysver; my @append = (); my $conntime = concise_duration time_hires - $curr_client->{connect_time}; push @append, sprintf "conntime=%s", $conntime; foreach (qw(input output)) { my $time = $curr_client->{sprintf "last_%s_time", $_}; next unless defined $time && $time =~ /^\d+(?:\.\d+)?$/; push @append, sprintf "%sidle=%s", $_, concise_duration time_hires - $time; } if ($curr_client->{ssl}) { push @append, sprintf "ssl=%s", $curr_client->{ssl}; } if ($curr_client->{ssl_handshake_time}) { my $hstime = concise_duration $curr_client->{ssl_handshake_time} - $curr_client->{connect_time}; push @append, sprintf "sslhstime=%s", $hstime; } if ($curr_client->{ssl_version}) { push @append, sprintf "sslver=%s", $curr_client->{ssl_version}; } if ($curr_client->{cipher}) { push @append, sprintf "cipher=%s", $curr_client->{cipher}; } if (defined $curr_client->{cn}) { push @append, sprintf "cn=%s", $curr_client->{cn}; } foreach (qw(poeclient fastcgi admin http websocket)) { next unless $curr_client->{$_}; push @append, sprintf "%s=%s", $_, $curr_client->{$_}; } foreach (qw(systemupgrade_requested systemupgrade_active systemupgrade_exitcode sysreboot_requested)) { next unless defined $curr_client->{$_}; next if /^systemupgrade_(?:active|exitcode)$/ && $curr_client->{$_} =~ m|^n/a$|i; my $short_name = $_; $short_name =~ s/^sys(?:tem)?//; push @append, sprintf "%s=%s", $short_name, $curr_client->{$_}; } if (defined $curr_client->{input_line_count}) { push @append, sprintf "inlines=%s", $curr_client->{input_line_count}; } if (defined $curr_client->{last_input_line_speed}) { push @append, sprintf "inlinespeed=%.3f/s", $curr_client->{last_input_line_speed}; } if (defined $curr_client->{output_line_count}) { push @append, sprintf "outlines=%s", $curr_client->{output_line_count}; } if (defined $curr_client->{last_output_line_speed}) { push @append, sprintf "outlinespeed=%.3f/s", $curr_client->{last_output_line_speed}; } if (defined $curr_client->{httprequest_count}) { push @append, sprintf "httpreq=%s", $curr_client->{httprequest_count}; } if (defined $curr_client->{last_httprequest_speed} && !($curr_client->{websocket} && !$curr_client->{last_httprequest_speed})) { push @append, sprintf "httpreqspeed=%.3f/s", $curr_client->{last_httprequest_speed}; } if (defined $curr_client->{input_message_count}) { push @append, sprintf "inmsgs=%s", $curr_client->{input_message_count}; } if (defined $curr_client->{last_input_message_speed}) { push @append, sprintf "inmsgspeed=%.3f/s", $curr_client->{last_input_message_speed}; } if (defined $curr_client->{output_message_count}) { push @append, sprintf "outmsgs=%s", $curr_client->{output_message_count}; } if (defined $curr_client->{last_output_message_speed}) { push @append, sprintf "outmsgspeed=%.3f/s", $curr_client->{last_output_message_speed}; } foreach (qw(rtt rtt_min rtt_max)) { my $curr_item = $curr_client->{sprintf "%s_%s", $curr_client->{websocket} ? 'websocket' : 'poeclient', $_}; next unless $curr_item; push @append, sprintf "%s=%.3fms", $_, $curr_item * 1000; } if (0) { foreach (qw(rtt rtt_min rtt_max)) { my $curr_item = $curr_client->{sprintf "%s_%s", $curr_client->{websocket} ? 'websocket' : 'poeclient', $_}; next unless $curr_item; my $name = $_; $name =~ s/^rtt/lag/; push @append, sprintf "%s=%.3fms", $name, ($curr_item / 2) * 1000; } } if ($curr_client->{poeclient_loadavg} && $curr_client->{poeclient_loadavg} =~ /^(\d{1,64}(?:\.\d{1,64})?)\s+(\d{1,64}(?:\.\d{1,64})?)\s+(\d{1,64}(?:\.\d{1,64})?)$/) { push @append, sprintf "loadavg=%.2f,%.2f,%.2f", $1, $2, $3; } if ($curr_client->{poeclient_sysuptime} && $curr_client->{poeclient_sysuptime} =~ /^\d+(?:\.\d+)?$/) { my $sysup = concise_duration $curr_client->{poeclient_sysuptime}; push @append, sprintf "sysup=%s", $sysup; } if (defined $curr_client->{poeclient_sysinittime}) { push @append, sprintf "sysinit=%s", concise_duration_exact $curr_client->{poeclient_sysinittime}; } if (defined $curr_client->{poeclient_poeinittime}) { push @append, sprintf "poeinit=%s", concise_duration_exact $curr_client->{poeclient_poeinittime}; } if (exists $curr_client->{i2csysutil}) { if (defined $curr_client->{i2csysutil}->{pmic_status}) { my $power = $curr_client->{i2csysutil}->{pmic_status}; $power =~ s/^.+,//; $power =~ s/pwr//gi; $power =~ s/\|/&/; push @append, sprintf "power=%s", $power; } if (defined $curr_client->{i2csysutil}->{eeprom_model}) { my $model = $curr_client->{i2csysutil}->{eeprom_model}; push @append, sprintf "model=%s", $model; } } if ($curr_client->{poeclient_ubootversion}) { push @append, sprintf "ubver=%s", $curr_client->{poeclient_ubootversion}; } if ($curr_client->{poeclient_ubootbootcount}) { push @append, sprintf "bootcount=%s", $curr_client->{poeclient_ubootbootcount}; } if ($curr_client->{poeclient_compilerversion}) { push @append, sprintf "compiler=%s", $curr_client->{poeclient_compilerversion}; } if ($curr_client->{poeclient_sysversion}) { $sysver = $curr_client->{poeclient_sysversion}; $sysver =~ s/\x20{4}/\x20/; $sysver =~ s/\x00//; push @append, sprintf "sysver=%s", $sysver; } if ($curr_client->{poeclient_rootfs}) { push @append, sprintf "rootfs=%s", $curr_client->{poeclient_rootfs}; } if ($curr_client->{poeclient_ntpsyspeer}) { push @append, sprintf "ntpsyspeer=%s", $curr_client->{poeclient_ntpsyspeer}; } if ($curr_client->{adc}) { my $adc_list = $curr_client->{adc}; foreach (sort {$a <=> $b} keys %$adc_list) { my $adc = $adc_list->{$_}; next unless ref $adc eq 'HASH'; push @append, sprintf "adc%s=%smV", $_, $adc->{mv}; if ($_ == 7 && defined $sysver && $sysver =~ /(?:BBB|BEAGLEBONE)/) { push @append, sprintf "vdd_3v3b(adc%s*2)=%.3fV", $_, ($adc->{mv} * 2) / 1000; } } } my %gpios_free = %{$curr_client->{poeclient_readyargs}->{allowed_gpio} || {}} if exists $curr_client->{poeclient_readyargs}; if (exists $curr_client->{poeclient_pins}) { foreach my $key (qw(in in_interrupt out out_ws2801)) { my $curr_item = $curr_client->{poeclient_pins}->{$key}; if ($key eq 'out_ws2801') { next unless ref $curr_item eq 'ARRAY' && @$curr_item; } else { next unless ref $curr_item eq 'HASH' && %$curr_item; } my @curr_pins = $key eq 'out_ws2801' ? @$curr_item : keys %$curr_item; delete $gpios_free{$_} foreach @curr_pins; if ($key eq 'out_ws2801') { push @append, sprintf "gpio_%s=clock:%s,data:%s(%s)", $key, $curr_pins[0], $curr_pins[1], $#curr_pins + 1; } else { push @append, sprintf "gpio_%s=%s(%s)", $key, join(',', sort {$a <=> $b} @curr_pins), $#curr_pins + 1; } } } if (%gpios_free) { my @curr_pins = keys %gpios_free; push @append, sprintf "gpio_free=%s(%s)", join(',', sort {$a <=> $b} @curr_pins), $#curr_pins + 1; } if (exists $curr_client->{poeclient_readyargs}) { foreach (qw(pwm adc gpio)) { my $key = $_; $key =~ s/^/allowed_/; my $curr_item = $curr_client->{poeclient_readyargs}->{$key}; my @items = keys %$curr_item; next unless ref $curr_item eq 'HASH' && %$curr_item; push @append, sprintf "%s=%s(%s)", $_, join(',', $_ eq 'pwm' ? sort @items : sort {$a <=> $b} @items), $#items + 1; } } push @append, sprintf "useragent=%s", $curr_client->{http_clientinfo}->{useragent} if exists $curr_client->{http_clientinfo} && $curr_client->{http_clientinfo}->{useragent}; my $line = sprintf "%s sock#%s client#%s addr=%s port=%s%s", ($curr_wheel_id == $wheel_id ? '*' : ' '), $curr_sock_wheel_id || 'n/a', $curr_wheel_id, $curr_client->{client_addr}, $curr_client->{client_port}, (@append ? sprintf " %s", join(' ', @append) : ''); $line =~ s/\s+((?:sslver|gpio_(?:in(?:_interrupt)?|out(?:_ws2801)?|free)|pwm|adc|gpio|adc0|rtt|inlines|useragent)=)/\n\x20\x20$1/gi if $multiline; push @clients, sprintf "%s%s", $line, $multiline ? "\n" : ''; } $output = sprintf "CLIENTS\n%s%s", $multiline ? "\n" : '', join("\n", @clients); } elsif ($input =~ /^\s*s(?:ervers)?\s*$/i) { my @servers; foreach my $curr_sock_wheel_id (sort {$a <=> $b} keys %{$heap->{tcpserver}->{servers}}) { my $curr_server = $heap->{tcpserver}->{servers}->{$curr_sock_wheel_id}; my $socket_domain = $curr_server->{socket_domain}; my $proto_string = ''; my $socket_protocol = $curr_server->{socket_protocol}; $proto_string = sprintf " proto=%s", $socket_protocol if $socket_protocol; my @extra_args; foreach (qw(ssl http)) { next unless exists $curr_server->{$_} && defined $curr_server->{$_}; push @extra_args, sprintf "%s=%s", $_, $curr_server->{$_}; } push @servers, sprintf "%s sock#%s domain=%s%s addr=%s port=%s%slocal_family=%s local_addr=%s local_port=%s", ($curr_sock_wheel_id == $sock_wheel_id ? '*' : ' '), $curr_sock_wheel_id, $socket_domain, $proto_string, $curr_server->{bind_address}, $curr_server->{bind_port}, (@extra_args ? sprintf " %s ", join ' ', @extra_args : ' '), $curr_server->{sockinfo_local}->{family} || 'n/a', $curr_server->{sockinfo_local}->{addr} || 'n/a', $curr_server->{sockinfo_local}->{port} || 'n/a'; } $output = sprintf "SERVERS\n%s", join("\n", @servers); } elsif ($input =~ /^\s*pins\s*$/i) { $output = sprintf "PINS in=%s adcin=%s out=%s pwmout=%s", join(',', sort {$a <=> $b} @{cfg->{poll_gpio_pins} || []}), join(',', sort {$a <=> $b} @{cfg->{valid_adcs} || []}), join(',', sort {$a <=> $b} @{cfg->{change_gpio_pins} || []}), join(',', sort @{cfg->{valid_pwms} || []}); } elsif ($input =~ /^\s*w(?:atch)?\s*$/i) { $client->{gpio_watch} = 1; $output = 'WATCH'; } elsif ($input =~ /^\s*log\s*$/i) { $no_output_log = 1; $output = sprintf "LOG\n%s", join("\n", @{logbuf()}); } elsif ($input =~ /^\s*l(?:atch)?(?:\s+(.+))?\s*$/i) { my $args = $1 || ''; my $real_cmd = 'latch'; my ($real_args, $reset); if ($args =~ /^\s*r(?:eset)?\s*$/i) { $real_args = 'reset'; $reset = 1; } my @out; foreach my $latch_type (sort keys %{$heap->{eventsystem}->{latch}}) { foreach my $latch_cn (sort keys %{$heap->{eventsystem}->{latch}->{$latch_type}}) { foreach my $latch_pin (sort keys %{$heap->{eventsystem}->{latch}->{$latch_type}->{$latch_cn}}) { my $latch_item = $heap->{eventsystem}->{latch}->{$latch_type}->{$latch_cn}->{$latch_pin}; next unless $latch_item; my $latch_time = $latch_item->{time}; my $latch_value = $latch_item->{value}; next unless $latch_time; my $map_key = join ':', $latch_type, $latch_cn, $latch_pin; my $item_name = cfg->{event_namemap_reverse}->{input}->{$map_key}; push @out, sprintf "%s[%s %s] latch, '%s' / '%s:%s:%s' = '%s'", ($reset ? 'delete ' : ''), strftime("%F %T %z", localtime $latch_time), $latch_time, $item_name || 'n/a', $latch_type, $latch_cn, $latch_pin, $latch_value; } } } $heap->{eventsystem}->{latch} = {} if $reset; $output = sprintf "%s%s\n%s", uc $real_cmd, (defined $real_args ? sprintf " %s", $real_args : ''), join("\n", @out) || 'empty'; } elsif ($input =~ /^\s*rgpio\s+([a-z-]{1,256}):(\d{1,3})=(.)\s*$/i) { my $rgpio_cn = $1; my $rgpio_pin = $2; my $rgpio_value = lc $3; if ($rgpio_value eq GPIO_TOGGLE || $rgpio_value eq GPIO_HIGH || $rgpio_value eq GPIO_LOW) { $kernel->yield(eventsystem_output_remotegpio => { type => 'remote-gpio', cn => $rgpio_cn, pin => $rgpio_pin, value => $rgpio_value, time => time_hires, }); } } elsif ($input =~ /^\s*e(?:ventmap)?(?:\s+(.+))?\s*$/i) { my $args = $1 || ''; my ($times, $evmap_in, $evmap_out, $args_fullname, $line_format); my (@out, @lines_evmap_in, @lines_evmap_out); my $evmap_in_written_items = {}; my $evmap_out_written_items = {}; my $nm_in = cfg->{event_namemap_reverse}->{input} || {}; my $nm_out = cfg->{event_namemap_reverse}->{output} || {}; if ($args =~ /^\s*t(?:imes?)?\s*$/i) { $times = 1; $args_fullname = 'times'; } elsif ($args =~ /^\s*i(?:nput)?\s*$/i) { $evmap_in = 1; $args_fullname = 'input'; } elsif ($args =~ /^\s*o(?:utput)?\s*$/i) { $evmap_out = 1; $args_fullname = 'output'; } elsif ($args =~ /^\s*i(?:nput)?o(?:utput)?\s*$/i) { $evmap_in = 1; $evmap_out = 1; $args_fullname = 'inputoutput'; } else { $args_fullname = ''; } if ($times) { $line_format = "%-25s %11s %34s %-25s %4s %34s"; push @out, sprintf $line_format, 'input', 'curr', 'duration, last change time', 'output', 'curr', 'duration, last change time'; } else { if ($evmap_in && $evmap_out) { $line_format = "%-30s %-30s %22s %34s"; push @lines_evmap_in, sprintf $line_format, 'name', 'input', 'curr', 'duration, last change time'; push @lines_evmap_out, sprintf $line_format, 'name', 'output', 'curr', 'duration, last change time'; } elsif ($evmap_in) { $line_format = "%-30s %-30s %22s %34s"; push @lines_evmap_in, sprintf $line_format, 'name', 'input', 'curr', 'duration, last change time'; } elsif ($evmap_out) { $line_format = "%-30s %-30s %22s %34s"; push @lines_evmap_out, sprintf $line_format, 'name', 'output', 'curr', 'duration, last change time'; } else { $line_format = "%-30s %11s %10s -> %-30s %4s %6s %s"; push @out, sprintf $line_format, 'input', 'curr', 'trigval', 'output', 'curr', 'outval', 'out options'; } } foreach my $in_key (sort keys %{cfg->{eventmap}}) { my $output_handlers_found; next unless ref cfg->{eventmap}->{$in_key} eq 'HASH'; my $in_key_ = $in_key; if (defined $in_key_) { unless ($evmap_in || $evmap_out) { foreach ($in_key_) { s/^(r)emote(-(?:gpio|adc):)/$1$2/i; s/^(f)ast(cgi:)/$1$2/; } } } my ($i_value_act, $i_time_act); if ($in_key =~ /^remote-gpio:/) { my ($i_type, $i_cn, $i_pin) = split ':', $in_key; if (defined $i_type && defined $i_cn && defined $i_pin) { $i_value_act = $heap->{eventsystem}->{input}->{$i_type}->{$i_cn}->{$i_pin}->{value}; $i_time_act = $heap->{eventsystem}->{input}->{$i_type}->{$i_cn}->{$i_pin}->{time}; } } elsif ($in_key =~ /^remote-gpiointerrupt:/) { my ($i_type, $i_cn, $i_pin) = split ':', $in_key; if (defined $i_type && defined $i_cn && defined $i_pin) { $i_value_act = $heap->{eventsystem}->{input_interrupt}->{$i_type}->{$i_cn}->{$i_pin}->{value}; $i_time_act = $heap->{eventsystem}->{input_interrupt}->{$i_type}->{$i_cn}->{$i_pin}->{time}; } } elsif ($in_key =~ /^remote-adc:/) { my ($i_type, $i_cn, $i_adc) = split ':', $in_key; if (defined $i_type && defined $i_cn && defined $i_adc) { $i_value_act = $heap->{eventsystem}->{input}->{$i_type}->{$i_cn}->{$i_adc}->{mv}; $i_time_act = $heap->{eventsystem}->{input}->{$i_type}->{$i_cn}->{$i_adc}->{time}; } } elsif ($in_key =~ /^fastcgi:/) { my ($i_type, $i_name) = split ':', $in_key; if (defined $i_type && defined $i_name) { $i_value_act = $heap->{eventsystem}->{input}->{$i_type}->{$i_name}->{value}; $i_time_act = $heap->{eventsystem}->{input}->{$i_type}->{$i_name}->{time}; } } elsif ($in_key eq 'sun') { $i_value_act = $heap->{eventsystem}->{input}->{$in_key}->{value}; $i_time_act = $heap->{eventsystem}->{input}->{$in_key}->{time}; } elsif ($in_key eq 'time') { $i_value_act = $heap->{eventsystem}->{input}->{$in_key}->{datetime}; $i_time_act = $heap->{eventsystem}->{input}->{$in_key}->{time}; } if (defined $i_value_act) { if ($in_key eq 'sun') { foreach ($i_value_act) { s/^1$/RISE/; s/^0$/SET/; } } elsif ($in_key eq 'time') { unless ($evmap_in || $evmap_out) { $i_value_act =~ s/^\d{4}-\d{2}-\d{2}\s+/*/; } } else { foreach ($i_value_act) { s/^1$/ON/; s/^0$/OFF/; s/^t$/TOGGLE/i; } } } if (defined $i_time_act) { my $dur = concise_duration time_hires - $i_time_act; $i_time_act = sprintf "%s, %s", $dur, strftime("%F %T %z", localtime $i_time_act), } foreach my $in_value (sort keys %{cfg->{eventmap}->{$in_key}}) { my $output_handler_items_found; $output_handlers_found = 1; next unless ref cfg->{eventmap}->{$in_key}->{$in_value} eq 'ARRAY'; my $in_value_ = $in_value; if ($in_key eq 'sun') { foreach ($in_value_) { s/^1$/RISE/; s/^0$/SET/; } } elsif ($in_key eq 'time') { unless ($evmap_in || $evmap_out) { foreach ($in_value_) { s/^\.{4}-\.{2}-\.{2}\s+/*/; s/\s+\.{2}:\.{2}:\.{2}$/*/; } } } else { foreach ($in_value_) { s/^1$/ON/; s/^0$/OFF/; s/^t$/TOGGLE/i; } } foreach my $out_value (sort @{cfg->{eventmap}->{$in_key}->{$in_value}}) { $output_handler_items_found = 1; my ($out_value_, @optlist); if (ref $out_value eq 'ARRAY' && defined $out_value->[1]) { $out_value_ = $out_value->[1]; foreach ($out_value_) { s/^1$/ON/; s/^0$/OFF/; s/^t$/TOGGLE/i; } } if (ref $out_value eq 'ARRAY' && defined $out_value->[2]) { my $opts = $out_value->[2]; if (ref $opts eq 'HASH') { foreach (sort keys %$opts) { push @optlist, sprintf "%s=%s", $_, $opts->{$_}; } } } my $out_key = ref $out_value eq 'ARRAY' && defined $out_value->[0] ? $out_value->[0] : (defined $out_value ? $out_value : 'n/a'); my $out_key_ = $out_key; if (defined $out_key_) { unless ($evmap_in || $evmap_out) { foreach ($out_key_) { s/^(r)emote(-(?:gpio|adc):)/$1$2/i; s/^(poe)-state(:)/$1$2/i; } } } my ($o_value_act, $o_time_act); if ($out_key =~ /^remote-gpio:/) { my ($o_type, $o_cn, $o_pin) = split ':', $out_key; if (defined $o_type && defined $o_cn && defined $o_pin) { $o_value_act = $heap->{eventsystem}->{output}->{$o_type}->{$o_cn}->{$o_pin}->{value}; $o_time_act = $heap->{eventsystem}->{output}->{$o_type}->{$o_cn}->{$o_pin}->{time}; } } elsif ($out_key =~ /^(?:latch|alarm)$/) { my ($i_type, $i_cn, $i_pin) = split ':', $in_key; if (defined $i_type && defined $i_cn && defined $i_pin) { $o_value_act = $heap->{eventsystem}->{$out_key}->{$i_type}->{$i_cn}->{$i_pin}->{value}; $o_time_act = $heap->{eventsystem}->{$out_key}->{$i_type}->{$i_cn}->{$i_pin}->{time}; } } if (defined $o_value_act) { foreach ($o_value_act) { s/^1$/ON/; s/^0$/OFF/; } } if (defined $o_time_act) { my $dur = concise_duration time_hires - $o_time_act; $o_time_act = sprintf "%s, %s", $dur, strftime("%F %T %z", localtime $o_time_act), } if ($times) { push @out, sprintf $line_format, $nm_in->{$in_key} || $in_key_, defined $i_value_act ? $i_value_act : '', $i_time_act || '', $nm_out->{$out_key} || $out_key_, defined $o_value_act ? $o_value_act : '', $o_time_act || ''; } elsif ($evmap_in && $evmap_out) { unless ($evmap_in_written_items->{$in_key}) { $evmap_in_written_items->{$in_key} = 1; push @lines_evmap_in, sprintf $line_format, $nm_in->{$in_key} || '', $in_key_, defined $i_value_act ? $i_value_act : '', $i_time_act || ''; } unless ($evmap_out_written_items->{$out_key}) { $evmap_out_written_items->{$out_key} = 1; push @lines_evmap_out, sprintf $line_format, $nm_out->{$out_key} || '', $out_key_, defined $o_value_act ? $o_value_act : '', $o_time_act || ''; } } elsif ($evmap_in) { unless ($evmap_in_written_items->{$in_key}) { $evmap_in_written_items->{$in_key} = 1; push @lines_evmap_in, sprintf $line_format, $nm_in->{$in_key} || '', $in_key_, defined $i_value_act ? $i_value_act : '', $i_time_act || ''; } } elsif ($evmap_out) { unless ($evmap_out_written_items->{$out_key}) { $evmap_out_written_items->{$out_key} = 1; push @lines_evmap_out, sprintf $line_format, $nm_out->{$out_key} || '', $out_key_, defined $o_value_act ? $o_value_act : '', $o_time_act || ''; } } else { push @out, sprintf $line_format, $nm_in->{$in_key} || $in_key_, defined $i_value_act ? $i_value_act : '', $in_value_, $nm_out->{$out_key} || $out_key_, defined $o_value_act ? $o_value_act : '', defined $out_value_ ? $out_value_ : '', @optlist ? join ',', @optlist : ''; } } unless ($output_handler_items_found) { if ($times) { push @out, sprintf $line_format, defined $in_key ? $nm_in->{$in_key} : (defined $in_key_ ? $in_key_ : 'n/a'), defined $i_value_act ? $i_value_act : '', defined $i_time_act ? $i_time_act : '', '', '', ''; } elsif ($evmap_in && $evmap_out) { if (defined $in_key && !$evmap_in_written_items->{$in_key}) { $evmap_in_written_items->{$in_key} = 1; push @lines_evmap_in, sprintf $line_format, defined $in_key ? $nm_in->{$in_key} || '' : '', defined $in_key_ ? $in_key_ : 'n/a', defined $i_value_act ? $i_value_act : '', $i_time_act || ''; } } elsif ($evmap_in) { if (defined $in_key && !$evmap_in_written_items->{$in_key}) { $evmap_in_written_items->{$in_key} = 1; push @lines_evmap_in, sprintf $line_format, defined $in_key ? $nm_in->{$in_key} || '' : '', defined $in_key_ ? $in_key_ : 'n/a', defined $i_value_act ? $i_value_act : '', $i_time_act || ''; } } elsif ($evmap_out) { } else { push @out, sprintf $line_format, defined $in_key ? $nm_in->{$in_key} || '' : (defined $in_key_ ? $in_key_ : 'n/a'), defined $i_value_act ? $i_value_act : '', defined $in_value_ ? $in_value_ : 'n/a', '', '', '', '', ''; } } } unless ($output_handlers_found) { if ($times) { push @out, sprintf $line_format, defined $in_key ? $nm_in->{$in_key} : (defined $in_key_ ? $in_key_ : 'n/a'), defined $i_value_act ? $i_value_act : '', defined $i_time_act ? $i_time_act : '', '', '', ''; } elsif ($evmap_in && $evmap_out) { if (defined $in_key && !$evmap_in_written_items->{$in_key}) { $evmap_in_written_items->{$in_key} = 1; push @lines_evmap_in, sprintf $line_format, defined $in_key ? $nm_in->{$in_key} || '' : '', defined $in_key_ ? $in_key_ : 'n/a', defined $i_value_act ? $i_value_act : '', $i_time_act || ''; } } elsif ($evmap_in) { if (defined $in_key && !$evmap_in_written_items->{$in_key}) { $evmap_in_written_items->{$in_key} = 1; push @lines_evmap_in, sprintf $line_format, defined $in_key ? $nm_in->{$in_key} || '' : '', defined $in_key_ ? $in_key_ : 'n/a', defined $i_value_act ? $i_value_act : '', $i_time_act || ''; } } elsif ($evmap_out) { } else { push @out, sprintf $line_format, defined $in_key ? $nm_in->{$in_key} || '' : (defined $in_key_ ? $in_key_ : 'n/a'), defined $i_value_act ? $i_value_act : '', '', '', '', '', '', ''; } } } if ($evmap_in && $evmap_out && !@out) { $output = sprintf "EVENTMAP%s\n%s\n%s\n%s\n%s\n%s", $args_fullname ? sprintf(" %s", $args_fullname) : '', '-' x 138, join("\n", @lines_evmap_in), '-' x 138, join("\n", @lines_evmap_out), '-' x 138; } else { $output = sprintf "EVENTMAP%s\n%s\n%s\n%s", $args_fullname ? sprintf(" %s", $args_fullname) : '', '-' x 138, join("\n", @out, @lines_evmap_in, @lines_evmap_out), '-' x 138; } } elsif ($input =~ /^\s*a(?:larm)?(?:\s+(.+))?\s*$/i) { my $args = $1 || ''; my $real_cmd = 'alarm'; my ($real_args, $reset); if ($args =~ /^\s*r(?:eset)?\s*$/i) { $real_args = 'reset'; $reset = 1; } my @out; foreach my $alarm_type (sort keys %{$heap->{eventsystem}->{alarm}}) { foreach my $alarm_cn (sort keys %{$heap->{eventsystem}->{alarm}->{$alarm_type}}) { foreach my $alarm_pin (sort keys %{$heap->{eventsystem}->{alarm}->{$alarm_type}->{$alarm_cn}}) { my $alarm_item = $heap->{eventsystem}->{alarm}->{$alarm_type}->{$alarm_cn}->{$alarm_pin}; next unless $alarm_item; my $alarm_time = $alarm_item->{time}; my $alarm_value = $alarm_item->{value}; next unless $alarm_time; my $map_key = join ':', $alarm_type, $alarm_cn, $alarm_pin; my $item_name = cfg->{event_namemap_reverse}->{input}->{$map_key}; push @out, sprintf "%s[%s %s] alarm, '%s' / '%s:%s:%s' = '%s'", ($reset ? 'delete ' : ''), strftime("%F %T %z", localtime $alarm_time), $alarm_time, $item_name || 'n/a', $alarm_type, $alarm_cn, $alarm_pin, $alarm_value; } } } foreach my $alarm_disconnect_cn (sort keys %{$heap->{eventsystem}->{alarm_disconnect}}) { my $alarm_disconnect_item = $heap->{eventsystem}->{alarm_disconnect}->{$alarm_disconnect_cn}; next unless $alarm_disconnect_item; my $alarm_disconnect_time = $alarm_disconnect_item->{time}; my $alarm_disconnect_wheel_error = $alarm_disconnect_item->{wheel_error}; my $alarm_disconnect_reason = $alarm_disconnect_item->{reason}; my $alarm_disconnect_shutdown_reason = $alarm_disconnect_item->{shutdown_reason}; next unless $alarm_disconnect_time; push @out, sprintf "%s[%s %s] alarm disconnect, '%s'%s%s%s", ($reset ? 'delete ' : ''), strftime("%F %T %z", localtime $alarm_disconnect_time), $alarm_disconnect_time, $alarm_disconnect_cn, (ref $alarm_disconnect_wheel_error eq 'ARRAY' ? sprintf ", '%s'", join ': ', @$alarm_disconnect_wheel_error : ''), (defined $alarm_disconnect_reason ? sprintf ", '%s'", $alarm_disconnect_reason : ''), (defined $alarm_disconnect_shutdown_reason ? sprintf ", '%s'", $alarm_disconnect_shutdown_reason : ''); } if ($reset) { $heap->{eventsystem}->{alarm} = {}; $heap->{eventsystem}->{alarm_disconnect} = {}; $kernel->yield('evsys_handler_alarm_deactivate'); } $output = sprintf "%s%s\n%s", uc $real_cmd, (defined $real_args ? sprintf " %s", $real_args : ''), join("\n", @out) || 'empty'; } elsif ($input =~ /^\s*(uc|cu|upgrade-client|rc|cr|reboot-client)\s+(.+)\s*$/i) { my $cmd = $1; my $upgrade_client_cn = $2; my $real_cmd = ''; my $poeserver_cmd; if ($cmd =~ /^\s*(?:uc|cu|upgrade-client)\s*$/i) { $real_cmd = 'upgrade-client'; $poeserver_cmd = 'systemupgrade'; } elsif ($cmd =~ /^\s*(?:rc|cr|reboot-client)\s*$/i) { $real_cmd = 'reboot-client'; $poeserver_cmd = 'sysreboot'; } my $upgrade_client = $heap->{tcpserver}->{connections_by_cn}->{$upgrade_client_cn}; if ($upgrade_client) { if ($upgrade_client->{wheel_id} && $upgrade_client->{poeclient}) { $upgrade_client->{sprintf "%s_requested", $poeserver_cmd} = 1; $kernel->yield(tcpserver_output => { wheel_id => $upgrade_client->{wheel_id}, output => sprintf("%s %s", POESERVER_CMD_PREFIX, $poeserver_cmd), }); $output = 'request sent'; } else { $output = 'invalid client'; } } else { $output = 'no such client'; } $output = sprintf "%s %s", uc $real_cmd, $output; } elsif ($input =~ /^\s*(?:dc|cd|disconnect-client)\s+(.+)\s*$/i) { my $disconnect_client = $1; my $real_cmd = 'disconnect-client'; if ($heap->{tcpserver}->{connections_by_cn}->{$disconnect_client}) { my $disconnect_client_wheel_id = $heap->{tcpserver}->{connections_by_cn}->{$disconnect_client}->{wheel_id}; my $reason = 'manual disconnect (using cn) by admin'; $kernel->call($session => tcpserver_client_kick => { wheel_id => $disconnect_client_wheel_id, reason => $reason, }); $output = 'success using cn'; } elsif ($heap->{tcpserver}->{connections}->{$disconnect_client}) { my $disconnect_client_wheel_id = $heap->{tcpserver}->{connections}->{$disconnect_client}->{wheel_id}; my $reason = 'manual disconnect (using id) by admin'; $kernel->call($session => tcpserver_client_kick => { wheel_id => $disconnect_client_wheel_id, reason => $reason, }); $output = 'success using id'; } else { $output = 'no such client'; } $output = sprintf "%s %s", uc $real_cmd, $output; } elsif ($input =~ /^\s*ws2801\s+(\S+)\s+(.+)\s*$/i) { my $ws2801_client_cn = $1; my $data = $2; my $real_cmd = 'ws2801'; my $ws2801_client = $heap->{tcpserver}->{connections_by_cn}->{$ws2801_client_cn}; my $outmsg; if ($ws2801_client) { if ($ws2801_client->{wheel_id} && $ws2801_client->{poeclient}) { $kernel->call($session => tcpserver_output => { wheel_id => $ws2801_client->{wheel_id}, output => sprintf("%s ws2801 %s", POESERVER_CMD_PREFIX, $data), use_flush => 1, }); #$outmsg = 'data sent'; } else { $outmsg = 'invalid client'; } } else { $outmsg = 'no such client'; } $output = sprintf "%s %s", uc $real_cmd, $outmsg if defined $outmsg; } elsif ($input =~ /^\s*m(?:odules)?\s*$/i) { my $real_cmd = 'modules'; my @out; foreach my $module (sort keys %{$heap->{poe_state_modules} || {}}) { push @out, sprintf "%s = %s", $module || 'n/a', join(' ', @{$heap->{poe_state_modules}->{$module} || []}) || 'n/a'; } $output = sprintf "%s\n\nconfigured (unsorted arrayref):\n\n%s\n\nloaded (sorted keys hashref = unsorted arrayref):\n\n%s\n", uc $real_cmd, join(' ', @{cfg->{modules} || []}) || 'n/a', join("\n\n", @out) || 'n/a'; } elsif ($input =~ /^\s*i(?:nfo)?\s*$/i) { my $cmd = 'info'; my @out; push @out, sprintf "\$^O = %s", $^O; push @out, sprintf "\$^V = %s", $^V; push @out, sprintf "\$] = %s", $]; push @out, sprintf "\$0 = %s", $0; push @out, sprintf "\$\$ = %s", $$; push @out, sprintf "\$< = %s", $<; push @out, sprintf "\$> = %s", $>; push @out, sprintf "\$( = %s", $(; push @out, sprintf "\$) = %s", $); push @out, sprintf "\$^T = %s", $^T; push @out, sprintf "\$^W = %s", $^W; push @out, sprintf "\$^X = %s", $^X; push @out, ''; my @modules = (); foreach my $module (sort keys %INC) { $module =~ s|\.p[ml]$||i; $module =~ s|/|::|g; push @modules, $module; } foreach my $module (sort @modules) { my $version; eval { $version = $module->VERSION; }; push @out, sprintf "%-50s %s", $module || 'n/a', (defined $version ? sprintf "v%s", $version : 'n/a'); } foreach my $module (sort keys %INC) { next unless $module =~ s|\.pm||i; $module =~ s|/|::|g; push @modules, $module; } push @out, ''; { local $Data::Dumper::Terse = 1; push @out, sprintf "\\\@INC =\n%s", Dumper(\@INC); push @out, sprintf "\\%%INC =\n%s", Dumper(\%INC); push @out, sprintf "\\%%ENV =\n%s", Dumper(\%ENV); } $output = sprintf "%s\n\n%s", uc $cmd, join("\n", @out) || 'n/a'; } elsif ($input =~ /^\s*(?:b(?:ans)?)(?:\s+(.+))?\s*$/i) { my $args = $1 || ''; my $real_cmd = 'bans'; my ($real_args, $reset); if ($args =~ /^\s*r(?:eset)?\s*$/i) { $real_args = 'reset'; $reset = 1; } my @out; foreach my $banned_ip (keys %{$heap->{tcpserver}->{client_banlist} || {}}) { my $item = $heap->{tcpserver}->{client_banlist}->{$banned_ip}; my $reason = $item->{reason} || ''; my $time = $item->{time} || 0; push @out, sprintf "%s[%s %s] tcpserver client ban addr %s, reason '%s'", ($reset ? 'delete ' : ''), strftime("%F %T %z", localtime $time), $time, $banned_ip, $reason; } $heap->{tcpserver}->{client_banlist} = {} if $reset; $output = sprintf "%s%s\n%s", uc $real_cmd, (defined $real_args ? sprintf " %s", $real_args : ''), join("\n", @out) || 'empty'; } elsif ($input =~ /^\s*p(?:eriodic)?\s*$/i) { my $real_cmd = 'periodic'; my @out; foreach my $state (keys %{$heap->{periodic} || {}}) { my $seconds = $heap->{periodic}->{$state}; push @out, sprintf "state %-45s runs every %s", $state || 'n/a', $seconds ? duration_exact $seconds : 'n/a'; } $output = sprintf "%s\n\n%s\n", uc $real_cmd, join("\n", @out) || 'n/a'; } elsif ($input =~ /^\s*ps\s*$/i) { my $real_cmd = 'ps'; $kernel->yield(ps => { source_wheel_id => $wheel_id, }); $output = sprintf "%s\n", uc $real_cmd; } elsif ($input =~ /^\s*q(?:uit)?\s*$/i) { my $reason = 'self-disconnect by admin command QUIT'; $kernel->call($session => tcpserver_client_kick => { wheel_id => $wheel_id, reason => $reason, }); } elsif ($input =~ /^\s*h(?:elp)?\s*$/i) { my @cmds = ( @{cfg->{change_gpio_pins} || []} ? 'GPIO p[=s]|all' : (), @{cfg->{valid_pwms} || []} ? 'PWM n[=v]|all' : (), cfg->{valid_adcs} ? 'ADC n|all' : (), 'PING [str]', cfg->{poll_gpio_pins} ? 'POLL start|stop' : (), 'KILL', 'RESTART', 'R|RELOAD [clear|force]', cfg->{tcpserver_command_sysreboot_enabled} ? 'SYSREBOOT' : (), 'LOAD', 'T|TIME', 'U|UPTIME', 'HEAP', 'CLIENT', 'C|CLIENTS [ml|sl|s|1]', 'S|SERVERS', 'PINS', 'W|WATCH', 'LOG', 'L|LATCH [r|reset]', 'RGPIO cn:p=s', 'E|EVENTMAP [t|time|times|i|input|o|output|io|inputoutput]', 'A|ALARM [r|reset]', 'UC|CU|UPGRADE-CLIENT cn', 'RC|CR|REBOOT-CLIENT cn', 'DC|CD|DISCONNECT-CLIENT cn', 'WS2801 cn r,g,b r,g,b r,g,b ...', 'M|MODULES', 'I|INFO', 'B|BANS [r|reset]', 'P|PERIODIC', 'PS', 'Q|QUIT', 'H|HELP', ); $output = sprintf "HELP %s", join(', ', @cmds); } elsif ($input =~ /^\s*$/) { return; } else { $output = 'ERR invalid command'; } return unless $output && $client->{wheel}; $kernel->call($session => tcpserver_output => { wheel_id => $wheel_id, output => $output, no_log => $no_output_log, }); } 1;