| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343 |
- # $Id: SubProcess.pm 14334 2017-05-20 23:11:06Z neubert $
- ##############################################################################
- #
- # SubProcess.pm
- # Copyright by Dr. Boris Neubert
- # e-mail: omega at online dot de
- #
- # This file is part of fhem.
- #
- # Fhem is free software: you can redistribute it and/or modify
- # it under the terms of the GNU General Public License as published by
- # the Free Software Foundation, either version 2 of the License, or
- # (at your option) any later version.
- #
- # Fhem is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- #
- # You should have received a copy of the GNU General Public License
- # along with fhem. If not, see <http://www.gnu.org/licenses/>.
- #
- ##############################################################################
- package SubProcess;
- use warnings;
- use strict;
- use POSIX ":sys_wait_h";
- use Socket;
- use IO::Handle;
- #
- # creates a new subprocess
- #
- sub new() {
- my ($class, $args)= @_;
- my ($child, $parent);
- # http://perldoc.perl.org/functions/socketpair.html
- # man 2 socket
- # AF_UNIX Local communication
- # SOCK_STREAM Provides sequenced, reliable, two-way, connection-based
- # byte streams. An out-of-band data transmission mechanism
- # may be supported
- #
- socketpair($child, $parent, AF_UNIX, SOCK_STREAM || SOCK_NONBLOCK, PF_UNSPEC) ||
- return undef; # die "socketpair: $!";
- $child->autoflush(1);
- $parent->autoflush(1);
- # Buffers are not used in this version of SubProcess.pm
- # Revision 8393 had it
- my %childBuffer= ();
- my %parentBuffer= ();
-
- my $self= {
-
- onRun => $args->{onRun},
- onExit => $args->{onExit},
- timeout => $args->{timeout},
- timeoutread => $args->{timeoutread},
- timeoutwrite => $args->{timeoutwrite},
- child => $child,
- parent => $parent,
- pid => undef,
- childBufferRef => \%childBuffer,
- parentBufferRef => \%parentBuffer,
- lasterror => ''
-
- }; # we are a hash reference
- # Timeout must be defined and > 0
- # 0 = Polling, undef = Block until data available
- if(defined($self->{timeout})) {
- $self->{timeout} = 0.001 if ($self->{timeout} <= 0.0);
- }
- else {
- $self->{timeout} = 0.001;
- }
- if (!defined ($self->{timeoutread})) {
- $self->{timeoutread} = $self->{timeout};
- }
- if (!defined ($self->{timeoutwrite})) {
- $self->{timeoutwrite} = $self->{timeout};
- }
-
- return bless($self, $class); # make $self an object of class $class
-
- }
- sub lasterror() {
- my $self = shift;
- return exists ($self->{lasterror}) ? $self->{lasterror} : '';
- }
- #
- # returns the pid of the subprocess
- # undef if subprocess not available
- #
- sub pid() {
-
- my $self= shift;
- return $self->{pid};
- }
-
- #
- # return 1 if subprocess is still running, else 0
- #
- sub running() {
- my $self= shift;
- my $pid= $self->{pid};
- return waitpid($pid, WNOHANG) > 0 ? 1 : 0;
- }
- #
- # waits for the subprocess to terminate
- #
- sub wait() {
- my $self= shift;
- my $pid= $self->{pid};
- if(defined($pid)) {
- main::Log3 $pid, 5, "Waiting for SubProcess $pid...";
- waitpid($pid, 0);
- main::Log3 $pid, 5, "SubProcess $pid terminated.";
- }
- }
- #
- # send a POSIX signal to the subproess
- #
- sub signal() {
- my ($self, $signal)= @_;
- my $pid= $self->{pid};
- main::Log3 $pid, 5, "Sending signal $signal to SubProcess $pid...";
- return kill $signal, $pid;
- }
- #
- # terminates thr subprocess (HUP)
- #
- sub terminate() {
- my $self= shift;
- return $self->signal('HUP');
- }
- #
- # kills the subprocess (KILL)
- #
- sub kill() {
- my $self= shift;
- return $self->signal('KILL');
- }
- #
- # the socket used by the parent to communicate with the subprocess
- #
- sub child() {
- my $self= shift;
- return $self->{child};
- }
- #
- # the socket used by the subprocess to communicate with the parent
- #
- sub parent() {
- my $self= shift;
- return $self->{parent};
- }
- # this is a helper function for reading
- # returns 1 datagram or undef on error
- sub readFrom() {
- my ($self, $fh) = @_;
- my $header;
- my $data;
- # Check if data is available
- my $rin= '';
- vec($rin, fileno($fh), 1) = 1;
- my $nfound = select($rin, undef, undef, $self->{timeoutread});
- if ($nfound < 0) {
- $self->{lasterror} = $!;
- return undef;
- }
- elsif ($nfound == 0) {
- $self->{lasterror} = "read: no data";
- return undef;
- }
-
- # Read datagram size
- my $sbytes = sysread ($fh, $header, 4);
- if (!defined ($sbytes)) {
- $self->{lasterror} = $!;
- return undef;
- }
- elsif ($sbytes != 4) {
- $self->{lasterror} = "read: short header";
- return undef;
- }
- # Read datagram
- my $size = unpack ('N', $header);
- my $buffer;
- while($size> 0) {
- my $bytes = sysread ($fh, $buffer, $size);
- if (!defined ($bytes)) {
- $self->{lasterror} = $!;
- return undef;
- }
- $data.= $buffer;
- $size-= $bytes;
- }
- return $data;
- }
- # this is a helper function for writing
- # writes 4 byte datagram size + datagram
- sub writeTo() {
- my ($self, $fh, $msg) = @_;
- my $win= '';
- vec($win, fileno($fh), 1)= 1;
- my $nfound = select (undef, $win, undef, $self->{timeoutwrite});
- if ($nfound < 0) {
- $self->{lasterror} = $!;
- return undef;
- }
- elsif ($nfound == 0) {
- $self->{lasterror} = "write: no reader";
- return undef;
- }
- my $size= pack("N", length($msg));
- my $bytes= syswrite ($fh, $size . $msg);
- if (!defined ($bytes)) {
- $self->{lasterror} = $!;
- return undef;
- }
- elsif ($bytes != length ($size.$msg)) {
- $self->{lasterror} = "write: incomplete data";
- return undef;
- }
-
- return $bytes;
- }
-
- # this function is called from the parent to read from the subprocess
- # returns undef on error or if nothing was read
- sub readFromChild() {
- my $self= shift;
-
- return $self->readFrom($self->child());
- }
- # this function is called from the parent to write to the subprocess
- # returns 0 on error, else 1
- sub writeToChild() {
- my ($self, $msg)= @_;
- return $self->writeTo($self->child(), $msg);
- }
- # this function is called from the subprocess to read from the parent
- # returns undef on error or if nothing was read
- sub readFromParent() {
- my $self= shift;
- return $self->readFrom($self->parent());
- }
- # this function is called from the subprocess to write to the parent
- # returns 0 on error, else 1
- sub writeToParent() {
- my ($self, $msg)= @_;
- return $self->writeTo($self->parent(), $msg);
- }
- #
- # starts the subprocess
- #
- sub run() {
- my $self= shift;
- my $pid= fork;
- if(!defined($pid)) {
- main::Log3 undef, 2, "SubProcess: Cannot fork: $!";
- return undef;
- }
- $self->{pid}= $pid;
-
- if(!$pid) {
- # CHILD
-
- # run
- main::Log3 undef, 5, "SubProcess $$ started.";
- my $onRun= $self->{onRun};
- if(defined($onRun)) {
- eval { &$onRun($self) };
- main::Log3 undef, 2, "SubProcess: onRun returned error: $@" if($@);
- }
-
- # exit
- my $onExit= $self->{onExit};
- if(defined($onExit)) {
- eval { &$onExit($self) };
- main::Log3 undef, 2, "SubProcess: onExit returned error: $@" if($@);
- }
-
- main::Log3 undef, 5, "SubProcess $$ ended.";
- POSIX::_exit(0);
-
- } else {
- # PARENT
- main::Log3 $pid, 5, "SubProcess $pid created.";
-
- return $pid;
- }
- }
- 1;
|