change code to cURL routine

remove all code to run http utils and insert support for cURL with
CoProcesses
This commit is contained in:
Marko Oldenburg 2020-06-18 10:43:44 +02:00
parent d9673cece4
commit 71941e0398
2 changed files with 245 additions and 101 deletions

View File

@ -1,2 +1,2 @@
UPD 2020-06-17_15:03:50 3368 FHEM/98_backupToStorage.pm UPD 2020-06-17_15:03:50 3368 FHEM/98_backupToStorage.pm
UPD 2020-06-17_16:56:54 11187 lib/FHEM/backupToStorage.pm UPD 2020-06-18_10:42:44 15143 lib/FHEM/backupToStorage.pm

View File

@ -35,10 +35,81 @@ use strict;
use warnings; use warnings;
use utf8; use utf8;
use GPUtils qw(GP_Import GP_Export); use GPUtils qw(GP_Import);
use Data::Dumper; #only for Debugging use Data::Dumper; #only for Debugging
# try to use JSON::MaybeXS wrapper
# for chance of better performance + open code
eval {
require JSON::MaybeXS;
import JSON::MaybeXS qw( decode_json encode_json );
1;
};
if ($@) {
$@ = undef;
# try to use JSON wrapper
# for chance of better performance
eval {
# JSON preference order
local $ENV{PERL_JSON_BACKEND} =
'Cpanel::JSON::XS,JSON::XS,JSON::PP,JSON::backportPP'
unless ( defined( $ENV{PERL_JSON_BACKEND} ) );
require JSON;
import JSON qw( decode_json encode_json );
1;
};
if ($@) {
$@ = undef;
# In rare cases, Cpanel::JSON::XS may
# be installed but JSON|JSON::MaybeXS not ...
eval {
require Cpanel::JSON::XS;
import Cpanel::JSON::XS qw(decode_json encode_json);
1;
};
if ($@) {
$@ = undef;
# In rare cases, JSON::XS may
# be installed but JSON not ...
eval {
require JSON::XS;
import JSON::XS qw(decode_json encode_json);
1;
};
if ($@) {
$@ = undef;
# Fallback to built-in JSON which SHOULD
# be available since 5.014 ...
eval {
require JSON::PP;
import JSON::PP qw(decode_json encode_json);
1;
};
if ($@) {
$@ = undef;
# Fallback to JSON::backportPP in really rare cases
require JSON::backportPP;
import JSON::backportPP qw(decode_json encode_json);
1;
}
}
}
}
}
## Import der FHEM Funktionen ## Import der FHEM Funktionen
#-- Run before package compilation #-- Run before package compilation
BEGIN { BEGIN {
@ -47,7 +118,12 @@ BEGIN {
GP_Import( GP_Import(
qw( qw(
readingsSingleUpdate readingsSingleUpdate
readingsBulkUpdate
readingsBeginUpdate
readingsEndUpdate
ReadingsVal ReadingsVal
gettimeofday
InternalTimer
defs defs
modules modules
setKeyValue setKeyValue
@ -137,8 +213,7 @@ sub Notify {
PushToStorage($hash) PushToStorage($hash)
if ( ( grep m{^backup.done(.+)?$}xms, @{$events} ) if ( ( grep m{^backup.done(.+)?$}xms, @{$events} )
&& $devname eq 'global' && $devname eq 'global'
&& $init_done && $init_done );
);
CheckAttributsForCredentials($hash) CheckAttributsForCredentials($hash)
if ( if (
@ -214,8 +289,13 @@ sub Set {
DeletePassword($hash); DeletePassword($hash);
} }
else { else {
return 'Unknown argument ' . $cmd my $list = (
. ', choose one of addpassword deletepassword:noArg'; defined( ReadPassword( $hash, $name ) )
? 'deletepassword:noArg'
: 'addpassword'
);
return 'Unknown argument ' . $cmd . ', choose one of ' . $list;
} }
return; return;
@ -240,91 +320,136 @@ sub PushToStorage {
Log3( $name, 4, "backupToStorage ($name) - push to storage function" ); Log3( $name, 4, "backupToStorage ($name) - push to storage function" );
if ( AttrVal( $name, 'bTSType', 'Nextcloud' ) eq 'Nextcloud' ) { require "SubProcess.pm";
my $subprocess = SubProcess->new( { onRun => \&FileUpload } );
my $backupFile = ReadingsVal( $name, 'fhemBackupFile', 'none' );
my @fileNameAtStorage_array = split( '/', $backupFile );
my $fileNameAtStorage = $fileNameAtStorage_array[$#fileNameAtStorage_array];
$subprocess->{type} = AttrVal( $name, 'bTSType', 'Nextcloud' );
$subprocess->{host} = AttrVal( $name, 'bTS_Host', '' );
$subprocess->{user} = AttrVal( $name, 'bTS_User', '' );
$subprocess->{pass} = ReadPassword( $hash, $name );
$subprocess->{path} = AttrVal( $name, 'bTS_Path', '' );
$subprocess->{backupfile} = $backupFile;
$subprocess->{fileNameAtStorage} = $fileNameAtStorage;
my $pid = $subprocess->run();
readingsSingleUpdate( $hash, 'state', ' file upload in progress', 1 );
if ( !defined($pid) ) {
Log3( $name, 1,
"backupToStorage ($name) - Cannot execute command asynchronously" );
CleanSubprocess($hash);
readingsSingleUpdate( $hash, 'state',
'Cannot execute command asynchronously', 1 );
return undef;
}
Log3( $name, 4, Log3( $name, 4,
"backupToStorage ($name) - push to storage function: Nextcloud detected"); "backupToStorage ($name) - execute command asynchronously (PID=$pid)"
ncUpload( $hash, ReadingsVal( $name, 'fhemBackupFile', 'none' ) ); );
}
$hash->{".fhem"}{subprocess} = $subprocess;
InternalTimer( gettimeofday() + 1,
"FHEM::backupToStorage::PollChild", $hash );
Log3( $hash, 4,
"backupToStorage ($name) - control passed back to main loop." );
return; return;
} }
sub ncUpload { sub PollChild {
my $hash = shift; my $hash = shift;
my $backupFile = shift;
my $name = $hash->{NAME}; my $name = $hash->{NAME};
Log3($name, 4, "backupToStorage ($name) - nextcloud upload function"); if ( defined( $hash->{".fhem"}{subprocess} ) ) {
my $subprocess = $hash->{".fhem"}{subprocess};
my $json = $subprocess->readFromChild();
open FD, '<', if ( !defined($json) ) {
"$backupFile" Log3( $name, 5,
or return Log3( $name, 1, "backupToStorage ($name) - still waiting ("
"backupToStorage ($name) - ncUpload: can\'t open backupfile for Nextcloud upload" . $subprocess->{lasterror}
. ")." );
InternalTimer( gettimeofday() + 1,
"FHEM::backupToStorage::PollChild", $hash );
return;
}
else {
Log3( $name, 4,
"backupToStorage ($name) - got result from asynchronous parsing."
); );
$subprocess->wait();
Log3( $name, 4,
"backupToStorage ($name) - asynchronous finished." );
binmode FD; CleanSubprocess($hash);
WriteReadings( $hash, $json );
}
}
}
local $/ = undef; # $/ is $INPUT_RECORD_SEPARATOR or $RS in English ######################################
my $cont = <FD>; # Begin Childprozess
######################################
sub FileUpload {
my $subprocess = shift;
my $response = {};
close FD; if ( $subprocess->{type} eq 'Nextcloud' ) {
$response->{ncUpload} = ExecuteNCupload($subprocess);
}
my $ncUser = AttrVal( $name, 'bTS_User', '' ); my $json = eval { encode_json($response) };
my $ncPass = ReadPassword( $hash, $name ); if ($@) {
my $ncHost = AttrVal( $name, 'bTS_Host', '' ); Log3( 'backupToStorage File Upload',
my $ncPath = AttrVal( $name, 'bTS_Path', '' ); 1, "backupToStorage - JSON error: $@" );
my @fhemBackupFiles = split( '/', $json = '{"jsonerror":"$@"}';
ReadingsVal( $name, 'fhemBackupFile', 'no-FHEM-backup-name.tar.gz' ) ); }
my $fhemBackupFile = $fhemBackupFiles[$#fhemBackupFiles];
my $param = { $subprocess->writeToParent($json);
url => 'https://'
. $ncHost
. '/remote.php/dav/files/'
. $ncUser . '/'
. $ncPath . '/'
. $fhemBackupFile,
timeout => AttrVal( $name, 'btS_UploadTimeout', 15 ),
method => 'PUT',
data => $cont,
hash => $hash,
user => $ncUser,
pwd => $ncPass,
callback => \&FHEM::backupToStorage::ncUploadCb,
};
HttpUtils_NonblockingGet($param);
$hash->{helper}->{HttpUtilsParam} = $param;
return; return;
} }
sub ncUploadCb { sub ExecuteNCupload {
my $param = shift; my $subprocess = shift;
my $err = shift;
my $data = shift; my $command = 'curl -u ';
$command .= $subprocess->{user} . ':' . $subprocess->{pass};
$command .= ' -T ' . $subprocess->{backupfile};
$command .= ' "https://';
$command .= $subprocess->{host};
$command .= '/remote.php/dav/files/';
$command .= $subprocess->{user};
$command .= $subprocess->{path};
$command .= '/';
$command .= $subprocess->{fileNameAtStorage};
$command .= '"';
print 'DEBUG!!! - Command: ' . $command . "\n";
return qx{$command};
}
######################################
# End Childprozess
######################################
sub CleanSubprocess {
my $hash = shift;
my $hash = $param->{hash};
my $name = $hash->{NAME}; my $name = $hash->{NAME};
Log3($name, 1, "backupToStorage ($name) - backup URL: $param->{url}"); delete( $hash->{".fhem"}{subprocess} );
Log3($name, 1, "backupToStorage ($name) - backup User: $param->{user}"); Log3( $name, 4, "backupToStorage ($name) - clean Subprocess" );
Log3($name, 1, "backupToStorage ($name) - backup Pass: $param->{pwd}");
Log3(
$name, 3,
"backupToStorage ($name) - callback: backup Nextcloud upload "
. (
( $data or $err )
? 'failed - Error: ' . $err . ' Data: ' . $data
: 'succesfully'
)
);
delete $hash->{helper}->{HttpUtilsParam};
return;
} }
sub StorePassword { sub StorePassword {
@ -372,7 +497,8 @@ sub ReadPassword {
if ( defined($err) ) { if ( defined($err) ) {
Log3( $name, 3, Log3( $name, 3,
"backupToStorage ($name) - unable to read password from file: $err"); "backupToStorage ($name) - unable to read password from file: $err"
);
return undef; return undef;
} }
@ -434,4 +560,22 @@ sub CheckAttributsForCredentials {
return readingsSingleUpdate( $hash, 'state', $status, 1 ); return readingsSingleUpdate( $hash, 'state', $status, 1 );
} }
sub WriteReadings {
my $hash = shift;
my $json = shift;
my $name = $hash->{NAME};
my $decode_json = eval { decode_json($json) };
if ($@) {
Log3( $name, 2, "backupToStorage ($name) - JSON error: $@" );
return;
}
readingsBeginUpdate($hash);
readingsBulkUpdate( $hash, 'state', 'ready' );
readingsBulkUpdate( $hash, 'uploadState', $decode_json->{ncUpload} );
readingsEndUpdate( $hash, 1 );
}
1; 1;