package POEDaemon::TCPClient::Main; use strict; use warnings FATAL => 'all'; no warnings 'redefine'; use POE; use POEDaemon; sub states { return $_[0], [qw( tcpclient_onconnect tcpclient_ondisconnect tcpclient_input tcpclient_output tcpclient_idle tcpclient_graceful_disconnect )]; } sub tcpclient_onconnect { my ($kernel, $heap, $wheel_id) = @_[KERNEL, HEAP, ARG0]; my $client = $heap->{tcpclient}->{connections}->{$wheel_id}; return unless $client; return unless $client->{wheel}; my $output = sprintf "%s ready%s%s%s", POECLIENT_CMD_PREFIX, (ref cfg->{tcpclient_valid_pwms} eq 'ARRAY' ? sprintf " allowed_pwm=%s", join(',', sort @{cfg->{tcpclient_valid_pwms}}) : ''), (ref cfg->{tcpclient_valid_adcs} eq 'ARRAY' ? sprintf " allowed_adc=%s", join(',', sort {$a <=> $b} @{cfg->{tcpclient_valid_adcs}}) : ''), (ref cfg->{tcpclient_valid_gpios} eq 'ARRAY' ? sprintf " allowed_gpio=%s", join(',', sort {$a <=> $b} @{cfg->{tcpclient_valid_gpios}}) : ''); $kernel->yield(tcpclient_output => { wheel_id => $wheel_id, output => $output, }); } sub tcpclient_ondisconnect { my ($kernel, $heap) = @_[KERNEL, HEAP]; foreach my $pin (sort {$a <=> $b} (keys %{$heap->{tcpclient_dynamic_gpio_pins}->{out}}, cfg->{tcpclient_connected_gpio_pin} || ())) { $kernel->yield(gpiochange_set => { pin => $pin, value => GPIO_LOW, }); } if (exists $heap->{statelist}->{gpiows2801} && $heap->{statelist}->{gpiows2801}) { $kernel->yield(gpiows2801 => 'alloff'); } } sub tcpclient_input { my ($kernel, $session, $heap, $input, $wheel_id) = @_[KERNEL, SESSION, HEAP, ARG0, ARG1]; my $client = $heap->{tcpclient}->{connections}->{$wheel_id}; return unless $client; my $sock_wheel_id = $client->{sock_wheel_id}; my $ssl_filter = $client->{ssl_filter}; $client->{last_input_time} = time_hires; if (!$client->{ssl_ok} && $ssl_filter) { unless ($ssl_filter->handshakeDone) { log_enabled && logline "[tcpclient #%s conn #%s] ssl handshake not done yet", $sock_wheel_id, $wheel_id; return; } my $ssl_cipher = $ssl_filter->getCipher; if ($ssl_cipher =~ /none/i) { log_enabled && logline "[tcpclient #%s conn #%s] ssl server cipher = none", $sock_wheel_id, $wheel_id; delete $heap->{tcpclient}->{connections}->{$wheel_id}; if (cfg->{tcpclient_connected_gpio_pin} && !%{$heap->{tcpclient}->{connections}}) { $kernel->yield(gpiochange_set => { pin => cfg->{tcpclient_connected_gpio_pin}, value => GPIO_LOW }); } return; } log_enabled && logline "[tcpclient #%s conn #%s] ssl server cipher = %s", $sock_wheel_id, $wheel_id, $ssl_cipher || 'n/a'; $client->{cipher} = $ssl_cipher if $ssl_cipher; $client->{ssl_handshake_time} = time_hires; $client->{ssl_ok} = 1; } log_enabled && logline "[tcpclient #%s conn #%s] input = '%s'", $client->{sock_wheel_id}, $wheel_id, $input; return unless $client->{ssl_ok}; my @out; return unless $input =~ s/^\s*poeserver\s*//i; if ($input =~ /^\s*gpio\s+(\d{1,3})\s*=\s*([01])\s*$/i) { my $pin = $1; my $value = $2; if ($heap->{tcpclient_dynamic_gpio_pins}->{out}->{$pin}) { $kernel->call($session => gpiochange_set => { pin => $pin, value => $value, }); } } elsif ($input =~ /^\s*ws2801\s+(.+)\s*$/i) { my $data = $1; $kernel->call($session => gpiows2801 => $data); } elsif ($input =~ /^\s*ping(?:\s+([a-z0-9_.-]{1,255}))?\s*$/i) { my $ping_args = $1; if (defined $ping_args) { push @out, sprintf "pong %s", $ping_args; } else { push @out, 'pong'; } } elsif ($input =~ /^\s*pins\s+(.{1,512})\s*$/i) { my @args = split /\s+/, $1; my $valid_pins = { map { $_ => 1 } @{cfg->{tcpclient_valid_gpios} || []} }; my $pins_old; $pins_old->{$_} = join ',', sort {$a <=> $b} keys %{$heap->{tcpclient_dynamic_gpio_pins}->{$_} || {}} foreach qw(in in_interrupt out); $pins_old->{out_ws2801} = join ',', @{$heap->{tcpclient_dynamic_gpio_pins}->{out_ws2801} || []}; $heap->{tcpclient_dynamic_gpio_pins} = { in => {}, in_interrupt => {}, out => {}, out_ws2801 => [], }; foreach my $arg (@args) { if ($arg =~ /^\s*(in|in_interrupt|out|out_ws2801)=([\d,]{1,512})\s*$/i) { my $direction = lc $1; my $pins = $2; my $direction_inverted; if ($direction =~ /^in/) { $direction_inverted = 'out'; } elsif ($direction =~ /^out/) { $direction_inverted = 'in'; } foreach my $pin (split ',', $pins) { next unless $valid_pins->{$pin}; next if $direction_inverted && $heap->{tcpclient_dynamic_gpio_pins}->{$direction_inverted}->{$pin}; if ($direction eq 'out_ws2801') { push @{$heap->{tcpclient_dynamic_gpio_pins}->{$direction}}, $pin; } else { $heap->{tcpclient_dynamic_gpio_pins}->{$direction}->{$pin} = 1; } } } } my $pins_new; $pins_new->{$_} = join ',', sort {$a <=> $b} keys %{$heap->{tcpclient_dynamic_gpio_pins}->{$_} || {}} foreach qw(in in_interrupt out); $pins_new->{out_ws2801} = join ',', @{$heap->{tcpclient_dynamic_gpio_pins}->{out_ws2801} || []}; log_enabled && logline "dynamic GPIO configured: in(poll)=%s in_interrupt=%s out(set)=%s out_ws2801=%s", $pins_new->{in}, $pins_new->{in_interrupt}, $pins_new->{out}, $pins_new->{out_ws2801}; push @out, sprintf "pins in=%s in_interrupt=%s out=%s out_ws2801=%s", $pins_new->{in}, $pins_new->{in_interrupt}, $pins_new->{out}, $pins_new->{out_ws2801}; if ($pins_new->{in} eq $pins_old->{in}) { foreach my $gpio_poll_pin (sort {$a <=> $b} keys %{$heap->{gpiopoll}->{pinstatus} || {}}) { my $gpio_poll_pin_value = $heap->{gpiopoll}->{pinstatus}->{$gpio_poll_pin}; next unless defined $gpio_poll_pin_value; push @out, sprintf "gpio %s=%s", $gpio_poll_pin, $gpio_poll_pin_value; } push @out, 'gpiopoll readynochange'; } else { log_enabled && logline 'kernel->yield(gpiopoll_restart)'; $kernel->yield('gpiopoll_restart'); } if ($pins_new->{in_interrupt} eq $pins_old->{in_interrupt}) { push @out, 'gpiointerrupt readynochange'; } else { log_enabled && logline 'kernel->yield(gpiointerrupt_restart)'; $kernel->yield('gpiointerrupt_restart'); } if ($pins_new->{out} eq $pins_old->{out}) { push @out, 'gpiochange readynochange'; } else { log_enabled && logline 'kernel->yield(gpiochange_restart)'; $kernel->yield('gpiochange_restart'); } if ($pins_new->{out_ws2801} eq $pins_old->{out_ws2801}) { push @out, 'gpiows2801 readynochange'; } else { log_enabled && logline 'kernel->yield(gpiows2801_restart)'; $kernel->yield('gpiows2801_restart'); } } elsif ($input =~ /^\s*pwm\s+([12][ab]|all)(?:=(\d{1,4}))?\s*$/i) { my $pwm = $1; my $value = $2; my @valid_pwms = sort @{cfg->{tcpclient_valid_pwms} || []}; my $valid_pwms = { map { $_ => 1 } @valid_pwms }; my $outmsg; 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; } } $outmsg = 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, }); $outmsg = sprintf "%s=%s", $pwm, $value; } else { $outmsg = 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) { $outmsg = sprintf "%s=%s", $pwm, $new_value; } else { $outmsg = sprintf "value error %s", $pwm; } } } else { $outmsg = sprintf "name error %s", $pwm; } } else { $outmsg = 'invalid name'; } push @out, sprintf "pwm %s", $outmsg; } elsif ($input =~ /^\s*adc\s+([0-7]|all)\s*$/i) { my $adc = $1; my @adc_list = ($adc); @adc_list = sort {$a <=> $b} @{cfg->{tcpclient_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_wheel_id => $wheel_id, source_type => 'poeclient', }); } } elsif ($input =~ /^\s*connectedgpiopinon\s*$/i && cfg->{tcpclient_connected_gpio_pin}) { log_enabled && logline 'connectedgpiopinon'; $kernel->yield(gpiochange_set => { pin => cfg->{tcpclient_connected_gpio_pin}, value => GPIO_HIGH }); } elsif ($input =~ /^\s*loadavg\s*$/i) { $kernel->yield(sysctlget => { sysctl => 'vm.loadavg', source_wheel_id => $wheel_id, source_type => 'poeclient', }); } elsif ($input =~ /^\s*sysuptime\s*$/i) { if (cfg->{syslog_high_priority_debug} && !$client->{syslog_ssl_connection_established}) { $client->{syslog_ssl_connection_established} = 1; syslogline '#4, ssl connection to server fully established', { high_priority => 1 }; } $kernel->yield(sysctlget => { sysctl => 'kern.boottime', source_wheel_id => $wheel_id, source_type => 'poeclient', }); } elsif ($input =~ /^\s*sysversion\s*$/i) { $kernel->yield(sysctlget => { sysctl => 'kern.version', source_wheel_id => $wheel_id, source_type => 'poeclient', }); } elsif ($input =~ /^\s*ntpsyspeer\s*$/i) { if (exists $heap->{statelist}->{ntpdc} && $heap->{statelist}->{ntpdc}) { $kernel->yield(ntpdc => { cmd => 'sysinfo', source_wheel_id => $wheel_id, source_type => 'poeclient', }); } else { log_enabled && logline 'invalid state ntpdc'; } } elsif ($input =~ /^\s*systemupgrade(?:\s+(status))?\s*$/i) { my $args = $1 || ''; if ($args =~ /^\s*status\s*$/i) { push @out, sprintf "systemupgrade status active=%s exitcode=%s", defined $heap->{systemupgrade_active} ? $heap->{systemupgrade_active} : 'n/a', defined $heap->{systemupgrade_exitcode} ? $heap->{systemupgrade_exitcode} : 'n/a'; } else { $kernel->yield('systemupgrade_init'); } } elsif ($input =~ /^\s*rootfs\s*$/i) { push @out, sprintf "rootfs %s", $heap->{rootfs}; } elsif ($input =~ /^\s*sysreboot\s*$/i) { $heap->{sysreboot_active} = 1; $kernel->yield(wheelrun_exec => { prog => [ 'util/sysreboot-suidwrapper', ], }); } elsif ($input =~ /^\s*poeinittime\s*$/i) { push @out, sprintf "poeinittime %s", $heap->{poe_init_time} - $^T; } elsif ($input =~ /^\s*sysinittime\s*$/i) { if (exists $heap->{init_sysctls} && exists $heap->{init_sysctls}->{'kern.boottime'} && defined $heap->{init_sysctls}->{'kern.boottime'}) { push @out, sprintf "sysinittime %s", $^T - $heap->{init_sysctls}->{'kern.boottime'}; } } elsif ($input =~ /^\s*rtt\s+(\d{10}\.\d{6})\s*$/i) { push @out, sprintf "rtt %s", $1; } elsif ($input =~ /^\s*i2csysutil\s*$/i) { if (exists $heap->{statelist}->{i2csysutil} && $heap->{statelist}->{i2csysutil}) { $kernel->yield(i2csysutil => { source_wheel_id => $wheel_id, }); } else { log_enabled && logline 'invalid state i2csysutil'; } } elsif ($input =~ /^\s*ubootversion\s*$/i) { if (exists $heap->{init_kenvs} && exists $heap->{init_kenvs}->{'uboot.ver'} && defined $heap->{init_kenvs}->{'uboot.ver'}) { push @out, sprintf "ubootversion %s", $heap->{init_kenvs}->{'uboot.ver'}; } } elsif ($input =~ /^\s*ubootbootcount\s*$/i) { if (exists $heap->{init_kenvs} && exists $heap->{init_kenvs}->{'uboot.bootcount'} && defined $heap->{init_kenvs}->{'uboot.bootcount'}) { push @out, sprintf "ubootbootcount %s", $heap->{init_kenvs}->{'uboot.bootcount'}; } } elsif ($input =~ /^\s*compilerversion\s*$/i) { if (exists $heap->{init_sysctls} && exists $heap->{init_sysctls}->{'kern.compiler_version'} && defined $heap->{init_sysctls}->{'kern.compiler_version'}) { push @out, sprintf "compilerversion %s", $heap->{init_sysctls}->{'kern.compiler_version'}; } } return unless @out && $client->{wheel}; my $output = join "\n", map { sprintf "%s %s", POECLIENT_CMD_PREFIX, $_ } @out; $kernel->call($session => tcpclient_output => { wheel_id => $wheel_id, output => $output, }); } sub tcpclient_output { my ($kernel, $heap, $args) = @_[KERNEL, HEAP, ARG0]; my $wheel_id = $args->{wheel_id}; my $output = $args->{output}; my $use_flush = $args->{use_flush}; return unless $wheel_id && $output; my $client = $heap->{tcpclient}->{connections}->{$wheel_id}; return unless $client; my $wheel = $client->{wheel}; return unless $wheel; log_enabled && logline "[tcpclient #%s conn #%s]%s output = '%s'", $heap->{tcpclient}->{connections}->{$wheel_id}->{sock_wheel_id}, $wheel_id, ($use_flush ? ' flush' : ''), $output; $client->{last_output_time} = time_hires; $wheel->put($output); $wheel->flush if $use_flush; } sub tcpclient_idle { my ($kernel, $heap, $wheel_id) = @_[KERNEL, HEAP, ARG0]; return unless $heap->{tcpclient}->{connections}->{$wheel_id}; my $output = sprintf "%s idle", POECLIENT_CMD_PREFIX; $kernel->yield(tcpclient_output => { wheel_id => $wheel_id, output => $output, }); } sub tcpclient_graceful_disconnect { my ($kernel, $heap, $wheel_id) = @_[KERNEL, HEAP, ARG0]; my $wheel = $heap->{tcpclient}->{connections}->{$wheel_id}->{wheel}; my $output = sprintf "%s shutdown %s", POECLIENT_CMD_PREFIX, $heap->{shutdown_reason} || 'n/a'; $wheel->event(FlushedEvent => 'tcpclient_shutdown'); $kernel->yield(tcpclient_output => { wheel_id => $wheel_id, output => $output, }); } 1;