2015-10-14 09:16:30 +00:00
|
|
|
# $Id$
|
2014-12-01 12:43:24 +00:00
|
|
|
|
2017-02-07 16:10:16 +00:00
|
|
|
=for comment
|
|
|
|
|
2014-03-02 18:49:49 +00:00
|
|
|
##############################################################################
|
|
|
|
#
|
|
|
|
# configDB.pm
|
|
|
|
#
|
|
|
|
# A fhem library to enable configuration from sql database
|
|
|
|
# instead of plain text file, e.g. fhem.cfg
|
|
|
|
#
|
|
|
|
# READ COMMANDREF DOCUMENTATION FOR CORRECT USE!
|
|
|
|
#
|
|
|
|
# Copyright: betateilchen ®
|
|
|
|
#
|
|
|
|
# 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/>.
|
|
|
|
#
|
|
|
|
##############################################################################
|
|
|
|
#
|
|
|
|
# ChangeLog
|
|
|
|
#
|
|
|
|
# 2014-03-01 - SVN 5080 - initial release of interface inside fhem.pl
|
|
|
|
# - initial release of configDB.pm
|
|
|
|
#
|
|
|
|
# 2014-03-02 - added template files for sqlite in contrib/configDB
|
|
|
|
# - updated commandref (EN) documentation
|
|
|
|
# - added commandref (DE) documentation
|
|
|
|
#
|
2014-03-03 15:59:14 +00:00
|
|
|
# 2014-03-03 - changed performance optimized by using version uuid table
|
|
|
|
# - updated commandref docu for migration
|
|
|
|
# - added cfgDB_svnId for fhem.pl CommandVersion
|
|
|
|
# - added cfgDB_List to show device info from database
|
|
|
|
# - updated commandref docu for cfgDB_List
|
|
|
|
#
|
2014-03-07 11:06:45 +00:00
|
|
|
# 2014-03-06 - added cfgDB_Diff to compare device in two versions
|
|
|
|
#
|
|
|
|
# 2014-03-07 - changed optimized cfgDB_Diff
|
|
|
|
# restructured libraray internally
|
|
|
|
# improved source code documentation
|
|
|
|
#
|
2014-03-20 19:43:52 +00:00
|
|
|
# 2014-03-20 - added export/import
|
2014-04-03 15:43:17 +00:00
|
|
|
# 2014-04-01 - removed export/import due to not working properly
|
|
|
|
#
|
|
|
|
# 2014-04-03 - fixed global attributes not read from version 0
|
2014-03-20 19:43:52 +00:00
|
|
|
#
|
2014-04-20 21:11:19 +00:00
|
|
|
# 2014-04-18 - added commands fileimport, fileexport
|
2014-04-21 11:32:49 +00:00
|
|
|
# 2014-04-19 - added commands filelist, filedelete
|
2014-04-20 21:11:19 +00:00
|
|
|
# interface cfgDB_Readfile for interaction
|
|
|
|
# with other modules
|
|
|
|
#
|
2014-04-21 11:32:49 +00:00
|
|
|
# 2014-04-21 - added interface functions for FHEMWEB and fhem.pl
|
|
|
|
# to show files in "Edit files" and use them
|
|
|
|
# with CommandReload() mechanism
|
|
|
|
#
|
2014-04-21 12:24:31 +00:00
|
|
|
# modified _cfgDB_Info to show number of files in db
|
|
|
|
#
|
2014-04-25 14:02:06 +00:00
|
|
|
# 2014-04-23 - added command fileshow, filemove
|
|
|
|
#
|
2014-04-26 10:47:09 +00:00
|
|
|
# 2014-04-26 - added migration to generic file handling
|
2014-04-26 16:22:52 +00:00
|
|
|
# fixed problem on migration of multiline DEFs
|
2014-04-26 10:47:09 +00:00
|
|
|
#
|
2014-04-27 18:01:14 +00:00
|
|
|
# 2014-04-27 - added new functions for binfile handling
|
|
|
|
#
|
2014-05-20 18:45:54 +00:00
|
|
|
# 2014-05-11 - removed command binfileimport
|
2014-05-12 21:21:58 +00:00
|
|
|
# changed store all files as binary
|
2014-05-13 14:18:01 +00:00
|
|
|
# added _cfgDB_Move to move all files from text
|
2014-05-12 21:21:58 +00:00
|
|
|
# to binary filesave on first load of configDB
|
|
|
|
#
|
2014-05-13 14:18:01 +00:00
|
|
|
# 2014-05-12 - added sorted write & read for config data
|
|
|
|
#
|
2014-05-19 09:23:52 +00:00
|
|
|
# 2014-05-15 - fixed handling of multiline defs
|
|
|
|
#
|
2014-05-20 16:25:07 +00:00
|
|
|
# 2014-05-20 - removed no longer needed functions for file handling
|
2014-05-20 18:45:54 +00:00
|
|
|
# changed code improvement; use strict; use warnings;
|
2014-05-20 16:25:07 +00:00
|
|
|
#
|
2014-08-22 11:52:04 +00:00
|
|
|
# 2014-08-22 - added automatic fileimport during migration
|
|
|
|
#
|
2014-09-30 15:33:13 +00:00
|
|
|
# 2014-09-30 - added support for device based userattr
|
|
|
|
#
|
2015-01-15 09:50:15 +00:00
|
|
|
# 2015-01-12 - changed use fhem function createUniqueId()
|
|
|
|
# instead of database calls
|
|
|
|
#
|
|
|
|
# 2015-01-15 - changed remove 99_Utils.pm from filelist
|
|
|
|
#
|
2015-01-17 20:44:19 +00:00
|
|
|
# 2015-01-17 - added configdb diff all current
|
|
|
|
# shows diff table between version 0
|
|
|
|
# and currently running version (in memory)
|
|
|
|
#
|
2015-01-23 18:21:29 +00:00
|
|
|
# 2015-01-23 - changed attribute handling for internal configDB attrs
|
|
|
|
#
|
2015-01-23 18:41:10 +00:00
|
|
|
# 2015-01-23 - added FileRead() caching - experimental
|
|
|
|
#
|
2015-10-14 09:16:30 +00:00
|
|
|
# 2015-10-14 - changed search conditions use ESCAPE, forum #42190
|
|
|
|
#
|
2016-03-26 13:13:40 +00:00
|
|
|
# 2016-03-19 - changed use modpath, forum #51036
|
|
|
|
#
|
|
|
|
# 2016-03-26 - added log entry for search (verbose=5)
|
|
|
|
#
|
2016-05-28 16:54:59 +00:00
|
|
|
# 2016-05-22 - added configdb dump (for sqlite)
|
|
|
|
#
|
|
|
|
# 2016-05-28 - added configdb dump (for mysql)
|
2016-05-22 16:21:33 +00:00
|
|
|
#
|
2016-05-29 19:46:13 +00:00
|
|
|
# 2016-05-29 - changed improve support for postgresql (tnx to Matze)
|
|
|
|
# added configdb dump (for postgresql)
|
|
|
|
#
|
2016-07-03 17:17:08 +00:00
|
|
|
# 2016-07-03 - added support for multiple hosts (experimental)
|
2016-07-04 20:02:31 +00:00
|
|
|
# 2016-07-04 - fixed improve config file read
|
2016-07-07 16:32:14 +00:00
|
|
|
# 2016-07-07 - bugfix select configuration
|
2016-07-04 20:02:31 +00:00
|
|
|
#
|
2017-03-24 16:13:32 +00:00
|
|
|
# 2017-03-24 - added use index on fhemconfig (only sqlite)
|
|
|
|
#
|
2017-08-04 13:15:20 +00:00
|
|
|
# 2017-07-17 - changed store files base64 encoded
|
|
|
|
#
|
2017-08-31 06:39:23 +00:00
|
|
|
# 2017-08-31 - changed improve table_info for migration check
|
|
|
|
#
|
2018-02-18 19:23:23 +00:00
|
|
|
# 2018-02-17 - changed remove experimenatal cache functions
|
|
|
|
# 2018-02-18 - changed move dump processing to backend
|
2018-02-17 19:07:37 +00:00
|
|
|
#
|
2018-03-25 18:41:15 +00:00
|
|
|
# 2018-03-24 - changed set privacy as default for username and password
|
|
|
|
# 2018-03-25 - changed move rescue modes from ENV to config file
|
|
|
|
#
|
2018-06-17 19:29:56 +00:00
|
|
|
# 2018-06-17 - changed remove migration on FHEM start by default
|
|
|
|
# check migration only if parameter migrate => 1
|
|
|
|
# is set in configDB.conf
|
|
|
|
#
|
2018-07-07 11:07:56 +00:00
|
|
|
# 2018-07-04 - bugfix change rescue mode persistence
|
|
|
|
#
|
|
|
|
# 2018-07-07 - change lastReorg added to info output
|
|
|
|
#
|
2018-09-08 11:01:04 +00:00
|
|
|
# 2018-09-08 - change remove base64 migration functions
|
|
|
|
#
|
2019-01-17 15:41:38 +00:00
|
|
|
# 2019-01-17 - added support for device specific uuid (setuuid)
|
2019-01-18 18:06:55 +00:00
|
|
|
# 2019-01-18 - changed use GetDefAndAttr()
|
2019-01-17 15:41:38 +00:00
|
|
|
#
|
2014-03-02 18:49:49 +00:00
|
|
|
##############################################################################
|
2016-07-07 16:32:14 +00:00
|
|
|
=cut
|
2014-03-02 18:49:49 +00:00
|
|
|
|
2014-05-20 16:25:07 +00:00
|
|
|
use strict;
|
|
|
|
use warnings;
|
2015-01-17 20:44:19 +00:00
|
|
|
use Text::Diff;
|
2014-03-01 11:59:56 +00:00
|
|
|
use DBI;
|
2016-07-03 17:17:08 +00:00
|
|
|
use Sys::Hostname;
|
2017-08-04 13:15:20 +00:00
|
|
|
use MIME::Base64;
|
2014-04-26 10:47:09 +00:00
|
|
|
|
2014-03-01 11:59:56 +00:00
|
|
|
##################################################
|
|
|
|
# Forward declarations for functions in fhem.pl
|
|
|
|
#
|
|
|
|
sub AnalyzeCommandChain($$;$);
|
2019-01-18 18:06:55 +00:00
|
|
|
sub GetDefAndAttr($;$);
|
2016-01-01 18:33:43 +00:00
|
|
|
sub Log($$);
|
2014-03-01 11:59:56 +00:00
|
|
|
sub Log3($$$);
|
2015-01-13 11:37:50 +00:00
|
|
|
sub createUniqueId();
|
2014-03-01 11:59:56 +00:00
|
|
|
|
|
|
|
##################################################
|
2014-03-07 11:06:45 +00:00
|
|
|
# Forward declarations inside this library
|
|
|
|
#
|
2014-05-13 14:18:01 +00:00
|
|
|
sub cfgDB_AttrRead($);
|
|
|
|
sub cfgDB_Init();
|
|
|
|
sub cfgDB_FileRead($);
|
|
|
|
sub cfgDB_FileUpdate($);
|
|
|
|
sub cfgDB_Fileversion($$);
|
|
|
|
sub cfgDB_FileWrite($@);
|
2014-05-20 16:25:07 +00:00
|
|
|
sub cfgDB_FW_fileList($$@);
|
2014-05-13 14:18:01 +00:00
|
|
|
sub cfgDB_Read99();
|
|
|
|
sub cfgDB_ReadAll($);
|
2015-01-17 20:44:19 +00:00
|
|
|
sub cfgDB_SaveCfg(;$);
|
2014-05-13 14:18:01 +00:00
|
|
|
sub cfgDB_SaveState();
|
|
|
|
sub cfgDB_svnId();
|
|
|
|
|
|
|
|
sub _cfgDB_binFileimport($$;$);
|
|
|
|
sub _cfgDB_Connect();
|
2015-01-17 20:44:19 +00:00
|
|
|
sub _cfgDB_DeleteTemp();
|
2014-05-13 14:18:01 +00:00
|
|
|
sub _cfgDB_Diff($$);
|
2015-01-17 20:44:19 +00:00
|
|
|
sub __cfgDB_Diff($$$$);
|
2014-05-12 21:21:58 +00:00
|
|
|
sub _cfgDB_InsertLine($$$$);
|
2014-03-07 11:06:45 +00:00
|
|
|
sub _cfgDB_Execute($@);
|
2014-05-13 14:18:01 +00:00
|
|
|
sub _cfgDB_Filedelete($);
|
2015-02-01 13:36:06 +00:00
|
|
|
sub _cfgDB_Fileexport($;$);
|
2014-05-13 14:18:01 +00:00
|
|
|
sub _cfgDB_Filelist(;$);
|
2018-01-05 11:52:04 +00:00
|
|
|
sub _cfgDB_Info($);
|
2014-05-13 14:18:01 +00:00
|
|
|
sub _cfgDB_Migrate();
|
2014-03-07 11:06:45 +00:00
|
|
|
sub _cfgDB_ReadCfg(@);
|
|
|
|
sub _cfgDB_ReadState(@);
|
2014-05-13 14:18:01 +00:00
|
|
|
sub _cfgDB_Recover($);
|
2014-04-29 17:23:30 +00:00
|
|
|
sub _cfgDB_Reorg(;$$);
|
2015-01-17 20:44:19 +00:00
|
|
|
sub _cfgDB_Rotate($$);
|
2014-05-13 14:18:01 +00:00
|
|
|
sub _cfgDB_Search($$;$);
|
|
|
|
sub _cfgDB_Uuid();
|
2017-09-05 17:35:14 +00:00
|
|
|
sub _cfgDB_table_exists($$);
|
2018-02-18 19:23:23 +00:00
|
|
|
sub _cfgDB_dump($);
|
2014-05-13 14:18:01 +00:00
|
|
|
|
2014-03-07 11:06:45 +00:00
|
|
|
##################################################
|
|
|
|
# Read configuration file for DB connection
|
2014-03-01 11:59:56 +00:00
|
|
|
#
|
2014-03-07 11:06:45 +00:00
|
|
|
|
2019-01-17 15:41:38 +00:00
|
|
|
|
|
|
|
my ($err,@c) = FileRead({FileName => 'configDB.conf',
|
|
|
|
ForceType => "file"});
|
|
|
|
return 0 if ($err);
|
2016-07-04 20:02:31 +00:00
|
|
|
|
|
|
|
my @config;
|
2019-01-17 15:41:38 +00:00
|
|
|
|
|
|
|
foreach my $line (@c) {
|
2016-07-07 16:32:14 +00:00
|
|
|
$line =~ s/^\s+|\s+$//g; # remove whitespaces etc.
|
|
|
|
$line =~ s/;$/;;/; # duplicate ; at end-of-line
|
2016-07-04 20:02:31 +00:00
|
|
|
push (@config,$line) if($line !~ m/^#/ && length($line) > 0);
|
|
|
|
}
|
2019-01-17 15:41:38 +00:00
|
|
|
|
|
|
|
|
|
|
|
#while (<CONFIG>){
|
|
|
|
# my $line = $_;
|
|
|
|
# $line =~ s/^\s+|\s+$//g; # remove whitespaces etc.
|
|
|
|
# $line =~ s/;$/;;/; # duplicate ; at end-of-line
|
|
|
|
# push (@config,$line) if($line !~ m/^#/ && length($line) > 0);
|
|
|
|
#}
|
|
|
|
#close CONFIG;
|
2014-03-01 11:59:56 +00:00
|
|
|
|
2015-01-23 18:21:29 +00:00
|
|
|
use vars qw(%configDB);
|
|
|
|
|
2014-03-01 11:59:56 +00:00
|
|
|
my %dbconfig;
|
2016-07-03 17:17:08 +00:00
|
|
|
|
2016-07-07 16:32:14 +00:00
|
|
|
my $configs = join("",@config);
|
|
|
|
my @configs = split(/;;/,$configs);
|
|
|
|
my $count = @configs;
|
|
|
|
my $fhemhost = hostname;
|
2016-07-03 17:17:08 +00:00
|
|
|
|
|
|
|
if ($count > 1) {
|
|
|
|
foreach my $c (@configs) {
|
2016-07-04 20:02:31 +00:00
|
|
|
next unless $c =~ m/^%dbconfig.*/;
|
2016-07-07 16:32:14 +00:00
|
|
|
$dbconfig{fhemhost} = "";
|
2016-07-03 17:17:08 +00:00
|
|
|
eval $c;
|
|
|
|
last if ($dbconfig{fhemhost} eq $fhemhost);
|
|
|
|
}
|
2016-07-07 16:32:14 +00:00
|
|
|
eval $configs[0] if ($dbconfig{fhemhost} eq "");
|
2016-07-03 17:17:08 +00:00
|
|
|
} else {
|
|
|
|
eval $configs[0];
|
|
|
|
}
|
2014-03-01 11:59:56 +00:00
|
|
|
|
2018-07-04 07:03:37 +00:00
|
|
|
my $cfgDB_dbconn = $dbconfig{connection};
|
|
|
|
my $cfgDB_dbuser = $dbconfig{user};
|
|
|
|
my $cfgDB_dbpass = $dbconfig{password};
|
2014-03-01 11:59:56 +00:00
|
|
|
my $cfgDB_dbtype;
|
2017-02-10 20:48:14 +00:00
|
|
|
my $cfgDB_filename;
|
2014-03-01 11:59:56 +00:00
|
|
|
|
|
|
|
|
|
|
|
if($cfgDB_dbconn =~ m/pg:/i) {
|
2018-06-17 19:29:56 +00:00
|
|
|
$cfgDB_dbtype ="POSTGRESQL";
|
|
|
|
} elsif ($cfgDB_dbconn =~ m/mysql:/i) {
|
|
|
|
$cfgDB_dbtype = "MYSQL";
|
|
|
|
} elsif ($cfgDB_dbconn =~ m/sqlite:/i) {
|
|
|
|
$cfgDB_dbtype = "SQLITE";
|
|
|
|
(undef,$cfgDB_filename) = split(/=/,$cfgDB_dbconn);
|
|
|
|
} else {
|
|
|
|
$cfgDB_dbtype = "unknown";
|
2014-03-01 11:59:56 +00:00
|
|
|
}
|
|
|
|
|
2018-03-25 18:11:00 +00:00
|
|
|
$configDB{attr}{nostate} = defined($dbconfig{nostate}) ? $dbconfig{nostate} : 0;
|
|
|
|
$configDB{attr}{rescue} = defined($dbconfig{rescue}) ? $dbconfig{rescue} : 0;
|
|
|
|
$configDB{attr}{loadversion} = defined($dbconfig{loadversion}) ? $dbconfig{loadversion} : 0;
|
2014-05-21 15:34:59 +00:00
|
|
|
|
2018-03-25 18:31:03 +00:00
|
|
|
%dbconfig = ();
|
|
|
|
@config = ();
|
|
|
|
$configs = undef;
|
|
|
|
$count = undef;
|
2018-03-25 18:24:17 +00:00
|
|
|
|
2014-03-07 11:06:45 +00:00
|
|
|
##################################################
|
|
|
|
# Basic functions needed for DB configuration
|
|
|
|
# directly called from fhem.pl
|
|
|
|
#
|
2014-03-03 12:34:54 +00:00
|
|
|
|
2014-03-07 11:06:45 +00:00
|
|
|
# initialize database, create tables if necessary
|
2014-05-13 14:18:01 +00:00
|
|
|
sub cfgDB_Init() {
|
2014-03-01 11:59:56 +00:00
|
|
|
##################################################
|
2018-07-04 07:03:37 +00:00
|
|
|
# Create non-existing database tables
|
|
|
|
# Create default config entries if necessary
|
2014-03-01 11:59:56 +00:00
|
|
|
#
|
2014-03-07 11:06:45 +00:00
|
|
|
my $fhem_dbh = _cfgDB_Connect;
|
2014-03-01 11:59:56 +00:00
|
|
|
|
2014-03-03 13:13:52 +00:00
|
|
|
# create TABLE fhemversions ifnonexistent
|
|
|
|
$fhem_dbh->do("CREATE TABLE IF NOT EXISTS fhemversions(VERSION INT, VERSIONUUID CHAR(50))");
|
|
|
|
|
2014-03-01 11:59:56 +00:00
|
|
|
# create TABLE fhemconfig if nonexistent
|
2016-09-05 19:06:04 +00:00
|
|
|
$fhem_dbh->do("CREATE TABLE IF NOT EXISTS fhemconfig(COMMAND VARCHAR(32), DEVICE VARCHAR(64), P1 VARCHAR(50), P2 TEXT, VERSION INT, VERSIONUUID CHAR(50))");
|
2017-03-24 16:13:32 +00:00
|
|
|
|
|
|
|
# create INDEX on fhemconfig if nonexistent (only if SQLITE)
|
|
|
|
$fhem_dbh->do("CREATE INDEX IF NOT EXISTS config_idx on 'fhemconfig' (versionuuid,version)")
|
|
|
|
if($cfgDB_dbtype eq "SQLITE");
|
|
|
|
|
2014-03-01 11:59:56 +00:00
|
|
|
# check TABLE fhemconfig already populated
|
|
|
|
my $count = $fhem_dbh->selectrow_array('SELECT count(*) FROM fhemconfig');
|
|
|
|
if($count < 1) {
|
|
|
|
# insert default entries to get fhem running
|
2014-05-03 12:00:31 +00:00
|
|
|
$fhem_dbh->commit();
|
2014-03-07 11:06:45 +00:00
|
|
|
my $uuid = _cfgDB_Uuid;
|
2014-03-03 12:34:54 +00:00
|
|
|
$fhem_dbh->do("INSERT INTO fhemversions values (0, '$uuid')");
|
2014-05-12 21:21:58 +00:00
|
|
|
_cfgDB_InsertLine($fhem_dbh, $uuid, '#created by cfgDB_Init',0);
|
2016-07-10 10:11:28 +00:00
|
|
|
_cfgDB_InsertLine($fhem_dbh, $uuid, 'attr global logdir ./log',1);
|
2018-03-25 18:24:17 +00:00
|
|
|
_cfgDB_InsertLine($fhem_dbh, $uuid, 'attr global logfile %L/fhem-%Y-%m-%d.log',2);
|
|
|
|
_cfgDB_InsertLine($fhem_dbh, $uuid, 'attr global modpath .',3);
|
|
|
|
_cfgDB_InsertLine($fhem_dbh, $uuid, 'attr global userattr devStateIcon devStateStyle icon sortby webCmd',4);
|
|
|
|
_cfgDB_InsertLine($fhem_dbh, $uuid, 'attr global verbose 3',5);
|
|
|
|
_cfgDB_InsertLine($fhem_dbh, $uuid, 'define telnetPort telnet 7072 global',6);
|
|
|
|
_cfgDB_InsertLine($fhem_dbh, $uuid, 'define web FHEMWEB 8083 global',7);
|
|
|
|
_cfgDB_InsertLine($fhem_dbh, $uuid, 'attr web allowfrom .*',8);
|
|
|
|
_cfgDB_InsertLine($fhem_dbh, $uuid, 'define Logfile FileLog %L/fhem-%Y-%m-%d.log fakelog',9);
|
2014-03-01 11:59:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
# create TABLE fhemstate if nonexistent
|
|
|
|
$fhem_dbh->do("CREATE TABLE IF NOT EXISTS fhemstate(stateString TEXT)");
|
2014-03-03 12:34:54 +00:00
|
|
|
|
2017-08-04 13:15:20 +00:00
|
|
|
# create TABLE fhemb64filesave if nonexistent
|
2014-06-17 18:49:32 +00:00
|
|
|
if($cfgDB_dbtype eq "MYSQL") {
|
2017-08-04 13:15:20 +00:00
|
|
|
$fhem_dbh->do("CREATE TABLE IF NOT EXISTS fhemb64filesave(filename TEXT, content MEDIUMBLOB)");
|
2016-05-29 19:46:13 +00:00
|
|
|
} elsif ($cfgDB_dbtype eq "POSTGRESQL") {
|
2017-08-04 13:15:20 +00:00
|
|
|
$fhem_dbh->do("CREATE TABLE IF NOT EXISTS fhemb64filesave(filename TEXT, content bytea)");
|
2014-06-17 18:49:32 +00:00
|
|
|
} else {
|
2017-08-04 13:15:20 +00:00
|
|
|
$fhem_dbh->do("CREATE TABLE IF NOT EXISTS fhemb64filesave(filename TEXT, content BLOB)");
|
2014-06-17 18:49:32 +00:00
|
|
|
}
|
2014-04-27 17:42:08 +00:00
|
|
|
|
2016-01-01 18:33:43 +00:00
|
|
|
# close database connection
|
2014-03-01 11:59:56 +00:00
|
|
|
$fhem_dbh->commit();
|
|
|
|
$fhem_dbh->disconnect();
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-04-26 10:47:09 +00:00
|
|
|
# read attributes
|
2014-05-13 14:18:01 +00:00
|
|
|
sub cfgDB_AttrRead($) {
|
2014-04-26 10:01:52 +00:00
|
|
|
my ($readSpec) = @_;
|
2014-04-26 10:47:09 +00:00
|
|
|
my ($row, $sql, @line, @rets);
|
2014-04-26 09:50:56 +00:00
|
|
|
my $fhem_dbh = _cfgDB_Connect;
|
|
|
|
my $uuid = $fhem_dbh->selectrow_array('SELECT versionuuid FROM fhemversions WHERE version = 0');
|
2014-04-28 17:53:37 +00:00
|
|
|
$sql = "SELECT * FROM fhemconfig WHERE COMMAND = 'attr' AND DEVICE = '$readSpec' AND VERSIONUUID = '$uuid'";
|
|
|
|
$sql = "SELECT * FROM fhemconfig WHERE COMMAND = 'attr' AND (DEVICE = 'global' OR DEVICE = 'configdb') and VERSIONUUID = '$uuid'"
|
|
|
|
if($readSpec eq 'global');
|
2014-04-26 10:47:09 +00:00
|
|
|
my $sth = $fhem_dbh->prepare( $sql );
|
2014-04-26 09:50:56 +00:00
|
|
|
$sth->execute();
|
|
|
|
while (@line = $sth->fetchrow_array()) {
|
2014-04-26 16:22:52 +00:00
|
|
|
if($line[1] eq 'configdb') {
|
2015-01-23 18:21:29 +00:00
|
|
|
$configDB{attr}{$line[2]} = $line[3];
|
2014-04-26 16:22:52 +00:00
|
|
|
} else {
|
2014-05-02 17:16:55 +00:00
|
|
|
push @rets, "attr $line[1] $line[2] $line[3]";
|
2014-04-26 16:22:52 +00:00
|
|
|
}
|
2014-04-26 09:50:56 +00:00
|
|
|
}
|
|
|
|
$fhem_dbh->disconnect();
|
|
|
|
return @rets;
|
|
|
|
}
|
|
|
|
|
2014-05-13 14:18:01 +00:00
|
|
|
# generic file functions called from fhem.pl
|
|
|
|
sub cfgDB_FileRead($) {
|
2014-04-26 10:47:09 +00:00
|
|
|
my ($filename) = @_;
|
2015-01-23 18:21:29 +00:00
|
|
|
|
2014-06-09 12:19:13 +00:00
|
|
|
Log3(undef, 4, "configDB reading file: $filename");
|
2014-05-20 11:10:05 +00:00
|
|
|
my ($err, @ret, $counter);
|
2014-03-07 11:06:45 +00:00
|
|
|
my $fhem_dbh = _cfgDB_Connect;
|
2017-08-04 13:15:20 +00:00
|
|
|
my $sth = $fhem_dbh->prepare( "SELECT content FROM fhemb64filesave WHERE filename LIKE '$filename'" );
|
2014-04-26 10:47:09 +00:00
|
|
|
$sth->execute();
|
2014-05-12 21:21:58 +00:00
|
|
|
my $blobContent = $sth->fetchrow_array();
|
2014-04-26 10:47:09 +00:00
|
|
|
$sth->finish();
|
|
|
|
$fhem_dbh->disconnect();
|
2017-08-04 13:15:20 +00:00
|
|
|
$blobContent = decode_base64($blobContent) if ($blobContent);
|
2014-05-12 21:21:58 +00:00
|
|
|
$counter = length($blobContent);
|
|
|
|
if($counter) {
|
|
|
|
@ret = split(/\n/,$blobContent);
|
|
|
|
$err = "";
|
|
|
|
} else {
|
|
|
|
@ret = undef;
|
|
|
|
$err = "Error on reading $filename from database!";
|
|
|
|
}
|
|
|
|
return ($err, @ret);
|
2014-04-26 10:47:09 +00:00
|
|
|
}
|
2018-03-25 18:41:15 +00:00
|
|
|
|
2014-05-13 14:18:01 +00:00
|
|
|
sub cfgDB_FileWrite($@) {
|
2014-04-26 10:47:09 +00:00
|
|
|
my ($filename,@content) = @_;
|
2014-06-09 12:19:13 +00:00
|
|
|
Log3(undef, 4, "configDB writing file: $filename");
|
2014-04-26 10:47:09 +00:00
|
|
|
my $fhem_dbh = _cfgDB_Connect;
|
2017-08-04 13:15:20 +00:00
|
|
|
$fhem_dbh->do("delete from fhemb64filesave where filename = '$filename'");
|
|
|
|
my $sth = $fhem_dbh->prepare('INSERT INTO fhemb64filesave values (?, ?)');
|
|
|
|
$sth->execute($filename,encode_base64(join("\n", @content)));
|
2014-04-26 10:47:09 +00:00
|
|
|
$sth->finish();
|
|
|
|
$fhem_dbh->commit();
|
2014-03-01 11:59:56 +00:00
|
|
|
$fhem_dbh->disconnect();
|
|
|
|
return;
|
|
|
|
}
|
2018-03-25 18:41:15 +00:00
|
|
|
|
2014-05-13 14:18:01 +00:00
|
|
|
sub cfgDB_FileUpdate($) {
|
2014-04-26 10:47:09 +00:00
|
|
|
my ($filename) = @_;
|
|
|
|
my $fhem_dbh = _cfgDB_Connect;
|
2017-08-04 13:15:20 +00:00
|
|
|
my $id = $fhem_dbh->selectrow_array("SELECT filename from fhemb64filesave where filename = '$filename'");
|
2014-04-26 10:47:09 +00:00
|
|
|
$fhem_dbh->disconnect();
|
|
|
|
if($id) {
|
2014-05-13 10:16:05 +00:00
|
|
|
my $filesize = -s $filename;
|
2014-12-01 12:48:59 +00:00
|
|
|
_cfgDB_binFileimport($filename,$filesize,1) if ($id) ;
|
|
|
|
Log(5, "file $filename updated in configDB");
|
2014-04-26 10:47:09 +00:00
|
|
|
}
|
2014-08-24 16:47:11 +00:00
|
|
|
return;
|
2014-04-26 10:47:09 +00:00
|
|
|
}
|
|
|
|
|
2014-05-13 14:18:01 +00:00
|
|
|
# read and execute fhemconfig and fhemstate
|
|
|
|
sub cfgDB_ReadAll($) {
|
2014-03-07 11:06:45 +00:00
|
|
|
my ($cl) = @_;
|
2014-05-20 16:25:07 +00:00
|
|
|
my ($ret, @dbconfig);
|
2016-01-01 18:33:43 +00:00
|
|
|
|
2018-03-25 18:11:00 +00:00
|
|
|
if ($configDB{attr}{rescue} == 1) {
|
2016-01-01 18:33:43 +00:00
|
|
|
Log (0, 'configDB starting in rescue mode!');
|
|
|
|
push (@dbconfig, 'attr global modpath .');
|
|
|
|
push (@dbconfig, 'attr global verbose 3');
|
|
|
|
push (@dbconfig, 'define telnetPort telnet 7072 global');
|
2018-03-25 18:24:17 +00:00
|
|
|
push (@dbconfig, 'define web FHEMWEB 8083 global');
|
|
|
|
push (@dbconfig, 'attr web allowfrom .*');
|
2016-01-01 18:33:43 +00:00
|
|
|
push (@dbconfig, 'define Logfile FileLog ./log/fhem-%Y-%m-%d.log fakelog');
|
|
|
|
} else {
|
|
|
|
# add Config Rows to commandfile
|
|
|
|
@dbconfig = _cfgDB_ReadCfg(@dbconfig);
|
|
|
|
# add State Rows to commandfile
|
2018-03-25 18:11:00 +00:00
|
|
|
@dbconfig = _cfgDB_ReadState(@dbconfig) unless $configDB{attr}{nostate} == 1;
|
2016-01-01 18:33:43 +00:00
|
|
|
}
|
|
|
|
|
2014-03-07 11:06:45 +00:00
|
|
|
# AnalyzeCommandChain for all entries
|
2014-05-20 16:25:07 +00:00
|
|
|
$ret = _cfgDB_Execute($cl, @dbconfig);
|
2014-03-03 18:12:27 +00:00
|
|
|
return $ret if($ret);
|
|
|
|
return undef;
|
2014-03-01 11:59:56 +00:00
|
|
|
}
|
|
|
|
|
2014-03-07 11:06:45 +00:00
|
|
|
# save running configuration to version 0
|
2015-01-17 20:44:19 +00:00
|
|
|
sub cfgDB_SaveCfg(;$) {
|
|
|
|
|
|
|
|
my ($internal) = shift;
|
|
|
|
$internal = defined($internal) ? $internal : 0;
|
2018-07-04 07:03:37 +00:00
|
|
|
my @dontSave = qw(configdb:rescue configdb:nostate configdb:loadversion
|
|
|
|
global:configfile global:version);
|
2014-05-20 16:25:07 +00:00
|
|
|
my (%devByNr, @rowList, %comments, $t, $out);
|
2014-03-01 11:59:56 +00:00
|
|
|
|
|
|
|
map { $devByNr{$defs{$_}{NR}} = $_ } keys %defs;
|
|
|
|
|
|
|
|
for(my $i = 0; $i < $devcount; $i++) {
|
|
|
|
|
|
|
|
my ($h, $d);
|
|
|
|
if($comments{$i}) {
|
|
|
|
$h = $comments{$i};
|
|
|
|
} else {
|
|
|
|
$d = $devByNr{$i};
|
|
|
|
next if(!defined($d) ||
|
|
|
|
$defs{$d}{TEMPORARY} || # e.g. WEBPGM connections
|
|
|
|
$defs{$d}{VOLATILE}); # e.g at, will be saved to the statefile
|
|
|
|
$h = $defs{$d};
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!defined($d)) {
|
|
|
|
push @rowList, $h->{TEXT};
|
|
|
|
next;
|
|
|
|
}
|
|
|
|
|
2019-01-18 18:06:55 +00:00
|
|
|
push (@rowList, GetDefAndAttr($d,1));
|
2014-09-30 15:33:13 +00:00
|
|
|
|
2014-03-01 11:59:56 +00:00
|
|
|
}
|
2014-03-13 17:40:56 +00:00
|
|
|
|
2015-01-23 18:21:29 +00:00
|
|
|
foreach my $a (sort keys %{$configDB{attr}}) {
|
|
|
|
my $val = $configDB{attr}{$a};
|
2015-01-24 18:16:54 +00:00
|
|
|
next unless $val;
|
2014-03-13 17:40:56 +00:00
|
|
|
$val =~ s/;/;;/g;
|
|
|
|
push @rowList, "attr configdb $a $val";
|
|
|
|
}
|
|
|
|
|
2014-03-01 11:59:56 +00:00
|
|
|
# Insert @rowList into database table
|
2014-03-07 11:06:45 +00:00
|
|
|
my $fhem_dbh = _cfgDB_Connect;
|
2015-01-17 20:44:19 +00:00
|
|
|
my $uuid = _cfgDB_Rotate($fhem_dbh,$internal);
|
2014-03-01 11:59:56 +00:00
|
|
|
$t = localtime;
|
|
|
|
$out = "#created $t";
|
|
|
|
push @rowList, $out;
|
2014-05-12 21:21:58 +00:00
|
|
|
my $counter = 0;
|
|
|
|
foreach (@rowList) {
|
|
|
|
_cfgDB_InsertLine($fhem_dbh, $uuid, $_, $counter);
|
|
|
|
$counter++;
|
|
|
|
}
|
2014-03-01 11:59:56 +00:00
|
|
|
$fhem_dbh->commit();
|
|
|
|
$fhem_dbh->disconnect();
|
2015-01-23 18:21:29 +00:00
|
|
|
my $maxVersions = $configDB{attr}{maxversions};
|
2014-05-13 14:18:01 +00:00
|
|
|
$maxVersions = ($maxVersions) ? $maxVersions : 0;
|
2015-01-17 20:44:19 +00:00
|
|
|
_cfgDB_Reorg($maxVersions,1) if($maxVersions && $internal != -1);
|
2014-03-01 11:59:56 +00:00
|
|
|
return 'configDB saved.';
|
|
|
|
}
|
|
|
|
|
2014-03-07 11:06:45 +00:00
|
|
|
# save statefile
|
2014-05-13 14:18:01 +00:00
|
|
|
sub cfgDB_SaveState() {
|
2014-03-01 11:59:56 +00:00
|
|
|
my ($out,$val,$r,$rd,$t,@rowList);
|
|
|
|
|
|
|
|
$t = localtime;
|
|
|
|
$out = "#$t";
|
|
|
|
push @rowList, $out;
|
|
|
|
|
|
|
|
foreach my $d (sort keys %defs) {
|
|
|
|
next if($defs{$d}{TEMPORARY});
|
|
|
|
if($defs{$d}{VOLATILE}) {
|
|
|
|
$out = "define $d $defs{$d}{TYPE} $defs{$d}{DEF}";
|
|
|
|
push @rowList, $out;
|
|
|
|
}
|
|
|
|
$val = $defs{$d}{STATE};
|
|
|
|
if(defined($val) &&
|
|
|
|
$val ne "unknown" &&
|
|
|
|
$val ne "Initialized" &&
|
2018-02-20 21:53:26 +00:00
|
|
|
$val ne "" &&
|
2014-03-01 11:59:56 +00:00
|
|
|
$val ne "???") {
|
|
|
|
$val =~ s/;/;;/g;
|
|
|
|
$val =~ s/\n/\\\n/g;
|
|
|
|
$out = "setstate $d $val";
|
|
|
|
push @rowList, $out;
|
|
|
|
}
|
|
|
|
$r = $defs{$d}{READINGS};
|
|
|
|
if($r) {
|
|
|
|
foreach my $c (sort keys %{$r}) {
|
|
|
|
$rd = $r->{$c};
|
|
|
|
if(!defined($rd->{TIME})) {
|
|
|
|
Log3(undef, 4, "WriteStatefile $d $c: Missing TIME, using current time");
|
|
|
|
$rd->{TIME} = TimeNow();
|
|
|
|
}
|
|
|
|
if(!defined($rd->{VAL})) {
|
|
|
|
Log3(undef, 4, "WriteStatefile $d $c: Missing VAL, setting it to 0");
|
|
|
|
$rd->{VAL} = 0;
|
|
|
|
}
|
|
|
|
$val = $rd->{VAL};
|
|
|
|
$val =~ s/;/;;/g;
|
|
|
|
$val =~ s/\n/\\\n/g;
|
|
|
|
$out = "setstate $d $rd->{TIME} $c $val";
|
|
|
|
push @rowList, $out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-07 11:06:45 +00:00
|
|
|
my $fhem_dbh = _cfgDB_Connect;
|
2014-03-01 11:59:56 +00:00
|
|
|
$fhem_dbh->do("DELETE FROM fhemstate");
|
|
|
|
my $sth = $fhem_dbh->prepare('INSERT INTO fhemstate values ( ? )');
|
|
|
|
foreach (@rowList) { $sth->execute( $_ ); }
|
|
|
|
$fhem_dbh->commit();
|
|
|
|
$fhem_dbh->disconnect();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-08-22 11:52:04 +00:00
|
|
|
# import existing files during migration
|
|
|
|
sub cfgDB_MigrationImport() {
|
|
|
|
|
|
|
|
my ($ret, $filename, @files, @def);
|
2016-03-19 17:27:36 +00:00
|
|
|
my $modpath = AttrVal("global","modpath",".");
|
2014-08-22 11:52:04 +00:00
|
|
|
|
|
|
|
# find eventTypes file
|
|
|
|
$filename = '';
|
|
|
|
@def = '';
|
|
|
|
@def = _cfgDB_findDef('TYPE=eventTypes');
|
|
|
|
foreach $filename (@def) {
|
|
|
|
next unless $filename;
|
|
|
|
push @files, $filename;
|
|
|
|
}
|
|
|
|
|
|
|
|
# import templateDB.gplot
|
2016-03-19 17:27:36 +00:00
|
|
|
$filename = "$modpath/www/gplot/template.gplot";
|
2015-02-07 13:59:43 +00:00
|
|
|
push @files, $filename;
|
2016-03-19 17:27:36 +00:00
|
|
|
$filename = "$modpath/www/gplot/templateDB.gplot";
|
2014-08-22 11:52:04 +00:00
|
|
|
push @files, $filename;
|
|
|
|
|
2015-02-12 22:04:09 +00:00
|
|
|
# import template.layout
|
2016-03-19 17:27:36 +00:00
|
|
|
$filename = "$modpath/FHEM/template.layout";
|
2015-02-12 22:04:09 +00:00
|
|
|
push @files, $filename;
|
|
|
|
|
2014-08-22 11:52:04 +00:00
|
|
|
# find used gplot files
|
|
|
|
$filename ='';
|
|
|
|
@def = '';
|
|
|
|
@def = _cfgDB_findDef('TYPE=SVG','GPLOTFILE');
|
|
|
|
foreach $filename (@def) {
|
|
|
|
next unless $filename;
|
2016-03-19 17:27:36 +00:00
|
|
|
push @files, "$modpath/www/gplot/".$filename.".gplot";
|
2014-08-22 11:52:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
# find DbLog configs
|
|
|
|
$filename ='';
|
|
|
|
@def = '';
|
|
|
|
@def = _cfgDB_findDef('TYPE=DbLog','CONFIGURATION');
|
|
|
|
foreach $filename (@def) {
|
|
|
|
next unless $filename;
|
|
|
|
push @files, $filename;
|
|
|
|
}
|
|
|
|
|
|
|
|
# find RSS layouts
|
|
|
|
$filename ='';
|
|
|
|
@def = '';
|
|
|
|
@def = _cfgDB_findDef('TYPE=RSS','LAYOUTFILE');
|
|
|
|
foreach $filename (@def) {
|
|
|
|
next unless $filename;
|
|
|
|
push @files, $filename;
|
|
|
|
}
|
|
|
|
|
2015-02-07 13:59:43 +00:00
|
|
|
# find InfoPanel layouts
|
|
|
|
$filename ='';
|
|
|
|
@def = '';
|
|
|
|
@def = _cfgDB_findDef('TYPE=InfoPanel','LAYOUTFILE');
|
|
|
|
foreach $filename (@def) {
|
|
|
|
next unless $filename;
|
|
|
|
push @files, $filename;
|
|
|
|
}
|
|
|
|
|
2014-08-22 11:52:04 +00:00
|
|
|
# find holiday files
|
|
|
|
$filename ='';
|
|
|
|
@def = '';
|
|
|
|
@def = _cfgDB_findDef('TYPE=holiday','NAME');
|
|
|
|
foreach $filename (@def) {
|
|
|
|
next unless $filename;
|
2017-05-15 18:56:49 +00:00
|
|
|
if(defined($defs{$filename}{HOLIDAYFILE})) {
|
|
|
|
push @files, $defs{$filename}{HOLIDAYFILE};
|
|
|
|
} else {
|
|
|
|
push @files, "$modpath/FHEM/".$filename.".holiday";
|
|
|
|
}
|
2014-08-22 11:52:04 +00:00
|
|
|
}
|
|
|
|
|
2016-03-19 17:27:36 +00:00
|
|
|
# import uniqueID file
|
|
|
|
$filename = "$modpath/FHEM/FhemUtils/uniqueID";
|
|
|
|
push @files,$filename if (-e $filename);
|
|
|
|
|
|
|
|
|
2014-08-22 11:52:04 +00:00
|
|
|
# do the import
|
|
|
|
$filename = '';
|
|
|
|
foreach $filename (@files) {
|
|
|
|
if ( -r $filename ) {
|
|
|
|
my $filesize = -s $filename;
|
|
|
|
_cfgDB_binFileimport($filename,$filesize);
|
|
|
|
$ret .= "importing: $filename\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return $ret;
|
|
|
|
}
|
|
|
|
|
2014-03-07 11:06:45 +00:00
|
|
|
# return SVN Id, called by fhem's CommandVersion
|
2014-05-13 14:18:01 +00:00
|
|
|
sub cfgDB_svnId() {
|
2015-10-14 09:16:30 +00:00
|
|
|
return "# ".'$Id$'
|
2014-03-07 11:06:45 +00:00
|
|
|
}
|
|
|
|
|
2014-04-26 16:22:52 +00:00
|
|
|
# return filelist depending on directory and regexp
|
2014-05-20 16:25:07 +00:00
|
|
|
sub cfgDB_FW_fileList($$@) {
|
2014-04-20 21:11:19 +00:00
|
|
|
my ($dir,$re,@ret) = @_;
|
|
|
|
my @files = split(/\n/, _cfgDB_Filelist('notitle'));
|
|
|
|
foreach my $f (@files) {
|
|
|
|
next if( $f !~ m/^$dir/ );
|
|
|
|
$f =~ s,$dir\/,,;
|
2015-01-15 09:50:15 +00:00
|
|
|
next if($f !~ m,^$re$, || $f eq '99_Utils.pm');
|
|
|
|
push @ret, "$f.configDB";
|
2014-04-20 21:11:19 +00:00
|
|
|
}
|
|
|
|
return @ret;
|
|
|
|
}
|
|
|
|
|
2014-04-26 16:22:52 +00:00
|
|
|
# read filelist containing 99_ files in database
|
2014-05-13 14:18:01 +00:00
|
|
|
sub cfgDB_Read99() {
|
2014-06-16 17:54:00 +00:00
|
|
|
my $ret = "";
|
2014-04-26 09:50:56 +00:00
|
|
|
my $fhem_dbh = _cfgDB_Connect;
|
2017-08-04 13:15:20 +00:00
|
|
|
my $sth = $fhem_dbh->prepare( "SELECT filename FROM fhemb64filesave WHERE filename like '%/99_%.pm' group by filename" );
|
2014-04-26 09:50:56 +00:00
|
|
|
$sth->execute();
|
|
|
|
while (my $line = $sth->fetchrow_array()) {
|
|
|
|
$line =~ m,^(.*)/([^/]*)$,; # Split into dir and file
|
|
|
|
$ret .= "$2,"; #
|
|
|
|
}
|
|
|
|
$sth->finish();
|
|
|
|
$fhem_dbh->disconnect();
|
|
|
|
$ret =~ s/,$//;
|
|
|
|
return $ret;
|
|
|
|
}
|
|
|
|
|
2014-04-26 16:22:52 +00:00
|
|
|
# return SVN Id from file stored in database
|
2014-05-13 14:18:01 +00:00
|
|
|
sub cfgDB_Fileversion($$) {
|
2014-04-26 09:50:56 +00:00
|
|
|
my ($file,$ret) = @_;
|
2014-06-02 17:19:54 +00:00
|
|
|
$ret = "No Id found for $file";
|
2014-05-12 21:21:58 +00:00
|
|
|
my ($err,@in) = cfgDB_FileRead($file);
|
|
|
|
foreach(@in){ $ret = $_ if($_ =~ m/# \$Id:/); }
|
2014-04-26 09:50:56 +00:00
|
|
|
return $ret;
|
|
|
|
}
|
|
|
|
|
2014-03-07 11:06:45 +00:00
|
|
|
##################################################
|
|
|
|
# Basic functions needed for DB configuration
|
|
|
|
# but not called from fhem.pl directly
|
|
|
|
#
|
|
|
|
|
|
|
|
# connect do database
|
2014-05-13 14:18:01 +00:00
|
|
|
sub _cfgDB_Connect() {
|
2014-03-07 11:06:45 +00:00
|
|
|
my $fhem_dbh = DBI->connect(
|
|
|
|
"dbi:$cfgDB_dbconn",
|
|
|
|
$cfgDB_dbuser,
|
|
|
|
$cfgDB_dbpass,
|
|
|
|
{ AutoCommit => 0, RaiseError => 1 },
|
|
|
|
) or die $DBI::errstr;
|
|
|
|
return $fhem_dbh;
|
|
|
|
}
|
|
|
|
|
|
|
|
# add configuration entry into fhemconfig
|
2014-05-12 21:21:58 +00:00
|
|
|
sub _cfgDB_InsertLine($$$$) {
|
|
|
|
my ($fhem_dbh, $uuid, $line, $counter) = @_;
|
2014-03-07 11:06:45 +00:00
|
|
|
my ($c,$d,$p1,$p2) = split(/ /, $line, 4);
|
|
|
|
my $sth = $fhem_dbh->prepare('INSERT INTO fhemconfig values (?, ?, ?, ?, ?, ?)');
|
2014-05-12 21:21:58 +00:00
|
|
|
$sth->execute($c, $d, $p1, $p2, $counter, $uuid);
|
2014-03-07 11:06:45 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
# pass command table to AnalyzeCommandChain
|
|
|
|
sub _cfgDB_Execute($@) {
|
|
|
|
my ($cl, @dbconfig) = @_;
|
2014-05-14 18:54:58 +00:00
|
|
|
my (@ret);
|
|
|
|
|
|
|
|
foreach my $l (@dbconfig) {
|
2014-05-15 15:14:14 +00:00
|
|
|
$l =~ s/[\r\n]/\n/g;
|
|
|
|
$l =~ s/\\\n/\n/g;
|
2014-05-14 18:54:58 +00:00
|
|
|
my $tret = AnalyzeCommandChain($cl, $l);
|
|
|
|
push @ret, $tret if(defined($tret));
|
2014-03-07 11:06:45 +00:00
|
|
|
}
|
2014-05-14 18:54:58 +00:00
|
|
|
return join("\n", @ret) if(@ret);
|
2014-03-07 11:06:45 +00:00
|
|
|
return undef;
|
|
|
|
}
|
|
|
|
|
|
|
|
# read all entries from fhemconfig
|
|
|
|
# and add them to command table for execution
|
|
|
|
sub _cfgDB_ReadCfg(@) {
|
2014-03-01 11:59:56 +00:00
|
|
|
my (@dbconfig) = @_;
|
2014-03-07 11:06:45 +00:00
|
|
|
my $fhem_dbh = _cfgDB_Connect;
|
2014-03-01 11:59:56 +00:00
|
|
|
my ($sth, @line, $row);
|
|
|
|
|
2017-03-10 23:24:07 +00:00
|
|
|
my $version = $configDB{attr}{loadversion};
|
|
|
|
delete $configDB{attr}{loadversion};
|
|
|
|
if ($version > 0) {
|
|
|
|
my $count = $fhem_dbh->selectrow_array('SELECT count(*) FROM fhemversions');
|
|
|
|
$count--;
|
|
|
|
$version = $version > $count ? $count : $version;
|
2018-07-07 11:07:56 +00:00
|
|
|
Log 0, "configDB loading version $version on user request.";
|
2017-03-10 23:24:07 +00:00
|
|
|
}
|
|
|
|
|
2014-05-14 18:54:58 +00:00
|
|
|
# maybe this will be done with join later
|
2017-03-10 23:24:07 +00:00
|
|
|
my $uuid = $fhem_dbh->selectrow_array("SELECT versionuuid FROM fhemversions WHERE version = '$version'");
|
2014-05-12 21:21:58 +00:00
|
|
|
$sth = $fhem_dbh->prepare( "SELECT * FROM fhemconfig WHERE versionuuid = '$uuid' and device <>'configdb' order by version" );
|
2014-03-03 15:59:14 +00:00
|
|
|
|
2014-03-01 11:59:56 +00:00
|
|
|
$sth->execute();
|
|
|
|
while (@line = $sth->fetchrow_array()) {
|
2014-11-13 21:09:39 +00:00
|
|
|
$row = "$line[0] $line[1] $line[2]";
|
|
|
|
$row .= " $line[3]" if defined($line[3]);
|
2014-03-01 11:59:56 +00:00
|
|
|
push @dbconfig, $row;
|
|
|
|
}
|
|
|
|
$fhem_dbh->disconnect();
|
|
|
|
return @dbconfig;
|
|
|
|
}
|
|
|
|
|
2014-03-07 11:06:45 +00:00
|
|
|
# read all entries from fhemstate
|
|
|
|
# and add them to command table for execution
|
|
|
|
sub _cfgDB_ReadState(@) {
|
2014-03-01 11:59:56 +00:00
|
|
|
my (@dbconfig) = @_;
|
2014-03-07 11:06:45 +00:00
|
|
|
my $fhem_dbh = _cfgDB_Connect;
|
2014-03-01 11:59:56 +00:00
|
|
|
my ($sth, $row);
|
|
|
|
|
|
|
|
$sth = $fhem_dbh->prepare( "SELECT * FROM fhemstate" );
|
|
|
|
$sth->execute();
|
|
|
|
while ($row = $sth->fetchrow_array()) {
|
|
|
|
push @dbconfig, $row;
|
|
|
|
}
|
|
|
|
$fhem_dbh->disconnect();
|
|
|
|
return @dbconfig;
|
|
|
|
}
|
|
|
|
|
2014-03-07 11:06:45 +00:00
|
|
|
# rotate all versions to versionnum + 1
|
|
|
|
# return uuid for new version 0
|
2015-01-17 20:44:19 +00:00
|
|
|
sub _cfgDB_Rotate($$) {
|
|
|
|
my ($fhem_dbh,$newversion) = @_;
|
2014-03-07 11:06:45 +00:00
|
|
|
my $uuid = _cfgDB_Uuid;
|
2015-01-17 20:44:19 +00:00
|
|
|
$fhem_dbh->do("UPDATE fhemversions SET VERSION = VERSION+1 where VERSION >= 0") if $newversion == 0;
|
|
|
|
$fhem_dbh->do("INSERT INTO fhemversions values ('$newversion', '$uuid')");
|
2014-03-03 12:34:54 +00:00
|
|
|
return $uuid;
|
2014-03-01 11:59:56 +00:00
|
|
|
}
|
|
|
|
|
2015-01-13 11:37:50 +00:00
|
|
|
# 2015-01-12 use the fhem default function
|
2014-05-13 14:18:01 +00:00
|
|
|
sub _cfgDB_Uuid() {
|
2015-01-13 11:37:50 +00:00
|
|
|
return createUniqueId();
|
2014-03-01 11:59:56 +00:00
|
|
|
}
|
|
|
|
|
2017-02-10 20:48:14 +00:00
|
|
|
sub _cfgDB_filesize_str($) {
|
|
|
|
my ($size) = @_;
|
|
|
|
|
|
|
|
if ($size > 1099511627776) # TiB: 1024 GiB
|
|
|
|
{
|
|
|
|
return sprintf("%.2f TB", $size / 1099511627776);
|
|
|
|
}
|
|
|
|
elsif ($size > 1073741824) # GiB: 1024 MiB
|
|
|
|
{
|
|
|
|
return sprintf("%.2f GB", $size / 1073741824);
|
|
|
|
}
|
|
|
|
elsif ($size > 1048576) # MiB: 1024 KiB
|
|
|
|
{
|
|
|
|
return sprintf("%.2f MB", $size / 1048576);
|
|
|
|
}
|
|
|
|
elsif ($size > 1024) # KiB: 1024 B
|
|
|
|
{
|
|
|
|
return sprintf("%.2f KB", $size / 1024);
|
|
|
|
}
|
|
|
|
else # bytes
|
|
|
|
{
|
|
|
|
return "$size byte" . ($size == 1 ? "" : "s");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-08 12:08:19 +00:00
|
|
|
##################################################
|
2014-03-13 17:40:56 +00:00
|
|
|
# Additional backend functions
|
2014-03-08 12:08:19 +00:00
|
|
|
# not called from fhem.pl directly
|
|
|
|
#
|
|
|
|
|
2014-05-13 14:18:01 +00:00
|
|
|
# migrate existing fhem config into database
|
|
|
|
sub _cfgDB_Migrate() {
|
2014-03-13 17:40:56 +00:00
|
|
|
my $ret;
|
|
|
|
$ret = "Starting migration...\n";
|
2014-08-22 11:52:04 +00:00
|
|
|
Log3('configDB',4,'Starting migration');
|
|
|
|
$ret .= "Processing: database initialization\n";
|
|
|
|
Log3('configDB',4,'Processing: cfgDB_Init');
|
2014-03-01 11:59:56 +00:00
|
|
|
cfgDB_Init;
|
2014-08-22 11:52:04 +00:00
|
|
|
$ret .= "Processing: save config\n";
|
|
|
|
Log3('configDB',4,'Processing: cfgDB_SaveCfg');
|
2014-03-01 11:59:56 +00:00
|
|
|
cfgDB_SaveCfg;
|
2014-08-22 11:52:04 +00:00
|
|
|
$ret .= "Processing: save state\n";
|
|
|
|
Log3('configDB',4,'Processing: cfgDB_SaveState');
|
2014-03-01 11:59:56 +00:00
|
|
|
cfgDB_SaveState;
|
2014-08-22 11:52:04 +00:00
|
|
|
$ret .= "Processing: fileimport\n";
|
|
|
|
Log3('configDB',4,'Processing: cfgDB_MigrationImport');
|
|
|
|
$ret .= cfgDB_MigrationImport;
|
|
|
|
$ret .= "Migration completed\n\n";
|
|
|
|
Log3('configDB',4,'Migration completed.');
|
2018-01-05 16:55:57 +00:00
|
|
|
$ret .= _cfgDB_Info(undef);
|
2014-03-13 17:40:56 +00:00
|
|
|
return $ret;
|
2014-03-01 11:59:56 +00:00
|
|
|
}
|
|
|
|
|
2014-05-13 14:18:01 +00:00
|
|
|
# show database statistics
|
2018-01-05 11:52:04 +00:00
|
|
|
sub _cfgDB_Info($) {
|
2018-01-05 12:00:32 +00:00
|
|
|
my ($info2) = @_;
|
|
|
|
$info2 //= 'unknown';
|
2014-05-20 18:45:54 +00:00
|
|
|
my ($l, @r, $f);
|
2014-03-08 12:08:19 +00:00
|
|
|
for my $i (1..65){ $l .= '-';}
|
2014-05-20 18:45:54 +00:00
|
|
|
|
2018-03-24 17:39:11 +00:00
|
|
|
$configDB{attr}{private} //= 1;
|
|
|
|
|
2014-03-08 12:08:19 +00:00
|
|
|
push @r, $l;
|
|
|
|
push @r, " configDB Database Information";
|
|
|
|
push @r, $l;
|
2018-01-05 12:00:32 +00:00
|
|
|
my $info1 = cfgDB_svnId;
|
|
|
|
$info1 =~ s/# //;
|
2018-01-05 16:55:57 +00:00
|
|
|
push @r, " d:$info1";
|
|
|
|
push @r, " c:$info2";
|
2014-03-08 12:08:19 +00:00
|
|
|
push @r, $l;
|
|
|
|
push @r, " dbconn: $cfgDB_dbconn";
|
2015-01-23 18:21:29 +00:00
|
|
|
push @r, " dbuser: $cfgDB_dbuser" if !$configDB{attr}{private};
|
|
|
|
push @r, " dbpass: $cfgDB_dbpass" if !$configDB{attr}{private};
|
2014-03-08 12:08:19 +00:00
|
|
|
push @r, " dbtype: $cfgDB_dbtype";
|
2014-05-20 16:25:07 +00:00
|
|
|
push @r, " Unknown dbmodel type in configuration file." if $cfgDB_dbtype eq 'unknown';
|
|
|
|
push @r, " Only Mysql, Postgresql, SQLite are fully supported." if $cfgDB_dbtype eq 'unknown';
|
2017-02-10 20:48:14 +00:00
|
|
|
if ($cfgDB_dbtype eq "SQLITE") {
|
|
|
|
my $size = -s $cfgDB_filename;
|
|
|
|
$size = _cfgDB_filesize_str($size);
|
|
|
|
push @r, " dbsize: $size";
|
|
|
|
}
|
2014-03-08 12:08:19 +00:00
|
|
|
push @r, $l;
|
2014-03-07 11:06:45 +00:00
|
|
|
my $fhem_dbh = _cfgDB_Connect;
|
|
|
|
my ($sql, $sth, @line, $row);
|
|
|
|
|
2014-03-08 12:08:19 +00:00
|
|
|
# read versions table statistics
|
2015-01-23 18:21:29 +00:00
|
|
|
my $maxVersions = $configDB{attr}{maxversions};
|
2014-05-20 11:10:05 +00:00
|
|
|
$maxVersions = ($maxVersions) ? $maxVersions : 0;
|
|
|
|
push @r, " max Versions: $maxVersions" if($maxVersions);
|
2018-07-07 11:07:56 +00:00
|
|
|
push @r, " lastReorg: ".$configDB{attr}{'lastReorg'};
|
2014-03-07 11:06:45 +00:00
|
|
|
my $count;
|
|
|
|
$count = $fhem_dbh->selectrow_array('SELECT count(*) FROM fhemconfig');
|
2018-07-07 11:07:56 +00:00
|
|
|
push @r, " config: $count entries";
|
2014-05-20 11:10:05 +00:00
|
|
|
push @r, "";
|
2014-03-07 11:06:45 +00:00
|
|
|
|
2014-03-08 12:08:19 +00:00
|
|
|
# read versions creation time
|
2014-03-07 11:06:45 +00:00
|
|
|
$sql = "SELECT * FROM fhemconfig as c join fhemversions as v on v.versionuuid=c.versionuuid ".
|
|
|
|
"WHERE COMMAND like '#created%' ORDER by v.VERSION";
|
|
|
|
$sth = $fhem_dbh->prepare( $sql );
|
|
|
|
$sth->execute();
|
|
|
|
while (@line = $sth->fetchrow_array()) {
|
2014-06-22 15:35:23 +00:00
|
|
|
$line[3] = "" unless defined $line[3];
|
2014-03-07 11:06:45 +00:00
|
|
|
$row = " Ver $line[6] saved: $line[1] $line[2] $line[3] def: ".
|
|
|
|
$fhem_dbh->selectrow_array("SELECT COUNT(*) from fhemconfig where COMMAND = 'define' and VERSIONUUID = '$line[5]'");
|
|
|
|
$row .= " attr: ".
|
|
|
|
$fhem_dbh->selectrow_array("SELECT COUNT(*) from fhemconfig where COMMAND = 'attr' and VERSIONUUID = '$line[5]'");
|
2014-03-08 12:08:19 +00:00
|
|
|
push @r, $row;
|
2014-03-07 11:06:45 +00:00
|
|
|
}
|
2014-03-08 12:08:19 +00:00
|
|
|
push @r, $l;
|
2014-03-07 11:06:45 +00:00
|
|
|
|
2014-03-08 12:08:19 +00:00
|
|
|
# read state table statistics
|
2014-03-07 11:06:45 +00:00
|
|
|
$count = $fhem_dbh->selectrow_array('SELECT count(*) FROM fhemstate');
|
2014-04-21 12:24:31 +00:00
|
|
|
$f = ($count>1) ? "s" : "";
|
2014-03-08 12:08:19 +00:00
|
|
|
# read state table creation time
|
2014-03-07 11:06:45 +00:00
|
|
|
$sth = $fhem_dbh->prepare( "SELECT * FROM fhemstate WHERE STATESTRING like '#%'" );
|
|
|
|
$sth->execute();
|
|
|
|
while ($row = $sth->fetchrow_array()) {
|
|
|
|
(undef,$row) = split(/#/,$row);
|
2014-04-27 18:51:51 +00:00
|
|
|
$row = " state: $count entrie$f saved: $row";
|
2014-03-08 12:08:19 +00:00
|
|
|
push @r, $row;
|
2014-03-07 11:06:45 +00:00
|
|
|
}
|
2014-03-08 12:08:19 +00:00
|
|
|
push @r, $l;
|
2014-03-07 11:06:45 +00:00
|
|
|
|
2017-08-04 13:15:20 +00:00
|
|
|
$row = $fhem_dbh->selectall_arrayref("SELECT filename from fhemb64filesave group by filename");
|
2014-05-12 21:21:58 +00:00
|
|
|
$count = @$row;
|
2014-04-21 15:09:27 +00:00
|
|
|
$count = ($count)?$count:'No';
|
|
|
|
$f = ("$count" ne '1') ? "s" : "";
|
2014-04-27 18:51:51 +00:00
|
|
|
$row = " filesave: $count file$f stored in database";
|
2014-04-21 12:24:31 +00:00
|
|
|
push @r, $row;
|
|
|
|
push @r, $l;
|
|
|
|
|
2014-03-07 11:06:45 +00:00
|
|
|
$fhem_dbh->disconnect();
|
|
|
|
|
2014-03-08 12:08:19 +00:00
|
|
|
return join("\n", @r);
|
2014-03-07 11:06:45 +00:00
|
|
|
}
|
|
|
|
|
2014-05-13 14:18:01 +00:00
|
|
|
# recover former config from database archive
|
2014-03-13 17:40:56 +00:00
|
|
|
sub _cfgDB_Recover($) {
|
2014-03-07 11:06:45 +00:00
|
|
|
my ($version) = @_;
|
|
|
|
my ($cmd, $count, $ret);
|
|
|
|
|
|
|
|
if($version > 0) {
|
|
|
|
my $fhem_dbh = _cfgDB_Connect;
|
|
|
|
$cmd = "SELECT count(*) FROM fhemconfig WHERE VERSIONUUID in (select versionuuid from fhemversions where version = $version)";
|
|
|
|
$count = $fhem_dbh->selectrow_array($cmd);
|
|
|
|
|
|
|
|
if($count > 0) {
|
|
|
|
my $fromuuid = $fhem_dbh->selectrow_array("select versionuuid from fhemversions where version = $version");
|
|
|
|
my $touuid = _cfgDB_Uuid;
|
|
|
|
# Delete current version 0
|
|
|
|
$fhem_dbh->do("DELETE FROM fhemconfig WHERE VERSIONUUID in (select versionuuid from fhemversions where version = 0)");
|
|
|
|
$fhem_dbh->do("update fhemversions set versionuuid = '$touuid' where version = 0");
|
|
|
|
|
|
|
|
# Copy selected version to version 0
|
|
|
|
my ($sth, $sth2, @line);
|
|
|
|
$cmd = "SELECT * FROM fhemconfig WHERE VERSIONUUID = '$fromuuid'";
|
|
|
|
$sth = $fhem_dbh->prepare($cmd);
|
|
|
|
$sth->execute();
|
|
|
|
$sth2 = $fhem_dbh->prepare('INSERT INTO fhemconfig values (?, ?, ?, ?, ?, ?)');
|
|
|
|
while (@line = $sth->fetchrow_array()) {
|
2015-01-09 12:39:08 +00:00
|
|
|
$sth2->execute($line[0], $line[1], $line[2], $line[3], $line[4], $touuid);
|
2014-03-07 11:06:45 +00:00
|
|
|
}
|
|
|
|
$fhem_dbh->commit();
|
|
|
|
$fhem_dbh->disconnect();
|
|
|
|
|
|
|
|
# Inform user about restart or rereadcfg needed
|
|
|
|
$ret = "Version 0 deleted.\n";
|
|
|
|
$ret .= "Version $version copied to version 0\n\n";
|
|
|
|
$ret .= "Please use rereadcfg or restart to activate configuration.";
|
|
|
|
} else {
|
|
|
|
$fhem_dbh->disconnect();
|
|
|
|
$ret = "No entries found in version $version.\nNo changes committed to database.";
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$ret = 'Please select version 1..n for recovery.';
|
|
|
|
}
|
|
|
|
return $ret;
|
|
|
|
}
|
|
|
|
|
2014-05-13 14:18:01 +00:00
|
|
|
# delete old configurations
|
2014-04-29 17:23:30 +00:00
|
|
|
sub _cfgDB_Reorg(;$$) {
|
|
|
|
my ($lastversion,$quiet) = @_;
|
2014-05-22 11:42:51 +00:00
|
|
|
$lastversion = (defined($lastversion)) ? $lastversion : 3;
|
2014-03-07 11:06:45 +00:00
|
|
|
Log3('configDB', 4, "DB Reorg started, keeping last $lastversion versions.");
|
|
|
|
my $fhem_dbh = _cfgDB_Connect;
|
2018-07-07 11:07:56 +00:00
|
|
|
my $uuid = $fhem_dbh->selectrow_array("select versionuuid from fhemversions where version = 0");
|
2014-03-07 11:06:45 +00:00
|
|
|
$fhem_dbh->do("delete FROM fhemconfig where versionuuid in (select versionuuid from fhemversions where version > $lastversion)");
|
|
|
|
$fhem_dbh->do("delete from fhemversions where version > $lastversion");
|
2015-01-17 20:44:19 +00:00
|
|
|
$fhem_dbh->do("delete FROM fhemconfig where versionuuid in (select versionuuid from fhemversions where version = -1)");
|
|
|
|
$fhem_dbh->do("delete from fhemversions where version = -1");
|
2018-07-07 11:07:56 +00:00
|
|
|
my $ts = localtime(time);
|
|
|
|
$configDB{attr}{'lastReorg'} = $ts;
|
|
|
|
_cfgDB_InsertLine($fhem_dbh,$uuid,"attr configdb lastReorg $ts",-1);
|
2014-03-07 11:06:45 +00:00
|
|
|
$fhem_dbh->commit();
|
|
|
|
$fhem_dbh->disconnect();
|
2017-02-10 20:48:14 +00:00
|
|
|
eval qx(sqlite3 $cfgDB_filename vacuum) if($cfgDB_dbtype eq "SQLITE");
|
2014-04-29 17:23:30 +00:00
|
|
|
return if(defined($quiet));
|
2018-01-05 16:55:57 +00:00
|
|
|
return " Result after database reorg:\n"._cfgDB_Info(undef);
|
2014-03-07 11:06:45 +00:00
|
|
|
}
|
|
|
|
|
2015-01-17 20:44:19 +00:00
|
|
|
# delete temporary version
|
|
|
|
sub _cfgDB_DeleteTemp() {
|
|
|
|
Log3('configDB', 4, "configDB: delete temporary Version -1");
|
|
|
|
my $fhem_dbh = _cfgDB_Connect;
|
|
|
|
$fhem_dbh->do("delete FROM fhemconfig where versionuuid in (select versionuuid from fhemversions where version = -1)");
|
|
|
|
$fhem_dbh->do("delete from fhemversions where version = -1");
|
|
|
|
$fhem_dbh->commit();
|
|
|
|
$fhem_dbh->disconnect();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-05-13 14:18:01 +00:00
|
|
|
# search for device or fulltext in db
|
|
|
|
sub _cfgDB_Search($$;$) {
|
|
|
|
my ($search,$searchversion,$dsearch) = @_;
|
2014-04-29 18:06:04 +00:00
|
|
|
return 'Syntax error.' if(!(defined($search)));
|
|
|
|
my $fhem_dbh = _cfgDB_Connect;
|
2014-05-13 14:18:01 +00:00
|
|
|
my ($sql, $sth, @line, $row, @result, $ret, $text);
|
|
|
|
$sql = "SELECT command, device, p1, p2 FROM fhemconfig as c join fhemversions as v ON v.versionuuid=c.versionuuid ";
|
|
|
|
$sql .= "WHERE v.version = '$searchversion' AND command not like '#create%' ";
|
2015-10-25 08:51:21 +00:00
|
|
|
# 2015-10-24 - changed, forum #42190
|
|
|
|
if($cfgDB_dbtype eq 'SQLITE') {;
|
|
|
|
$sql .= "AND device like '$search%' ESCAPE '\\' " if($dsearch);
|
|
|
|
$sql .= "AND (device like '$search%' ESCAPE '\\' OR P1 like '$search%' ESCAPE '\\' OR P2 like '$search%' ESCAPE '\\') " if(!$dsearch);
|
|
|
|
} else {
|
|
|
|
$sql .= "AND device like '$search%' " if($dsearch);
|
|
|
|
$sql .= "AND (device like '$search%' OR P1 like '$search%' OR P2 like '$search%') " if(!$dsearch);
|
|
|
|
}
|
2014-05-13 14:18:01 +00:00
|
|
|
$sql .= "ORDER BY lower(device),command DESC";
|
2014-04-29 18:06:04 +00:00
|
|
|
$sth = $fhem_dbh->prepare( $sql);
|
2016-03-26 13:13:40 +00:00
|
|
|
Log 5,"configDB: $sql";
|
2014-04-29 18:06:04 +00:00
|
|
|
$sth->execute();
|
2014-05-13 14:18:01 +00:00
|
|
|
$text = " device" if($dsearch);
|
|
|
|
push @result, "search result for$text: $search in version: $searchversion";
|
2014-04-29 18:06:04 +00:00
|
|
|
push @result, "--------------------------------------------------------------------------------";
|
|
|
|
while (@line = $sth->fetchrow_array()) {
|
|
|
|
$row = "$line[0] $line[1] $line[2] $line[3]";
|
2019-01-17 15:41:38 +00:00
|
|
|
Log 5,"configDB: $row";
|
|
|
|
push @result, "$row" unless ($line[0] eq 'setuuid');
|
2014-04-29 18:06:04 +00:00
|
|
|
}
|
|
|
|
$fhem_dbh->disconnect();
|
|
|
|
$ret = join("\n", @result);
|
|
|
|
return $ret;
|
|
|
|
}
|
|
|
|
|
2014-05-13 14:18:01 +00:00
|
|
|
# called from cfgDB_Diff
|
2015-01-17 20:44:19 +00:00
|
|
|
sub __cfgDB_Diff($$$$) {
|
|
|
|
my ($fhem_dbh,$search,$searchversion,$svinternal) = @_;
|
2014-03-07 11:06:45 +00:00
|
|
|
my ($sql, $sth, @line, $ret);
|
2015-01-17 20:44:19 +00:00
|
|
|
if($svinternal != -1) {
|
2014-03-07 11:06:45 +00:00
|
|
|
$sql = "SELECT command, device, p1, p2 FROM fhemconfig as c join fhemversions as v ON v.versionuuid=c.versionuuid ".
|
|
|
|
"WHERE v.version = '$searchversion' AND device = '$search' ORDER BY command DESC";
|
2015-01-17 20:44:19 +00:00
|
|
|
} else {
|
|
|
|
$sql = "SELECT command, device, p1, p2 FROM fhemconfig as c join fhemversions as v ON v.versionuuid=c.versionuuid ".
|
|
|
|
"WHERE v.version = '$searchversion' ORDER BY command DESC";
|
|
|
|
}
|
2014-03-06 13:46:01 +00:00
|
|
|
$sth = $fhem_dbh->prepare( $sql);
|
|
|
|
$sth->execute();
|
|
|
|
while (@line = $sth->fetchrow_array()) {
|
2014-03-07 11:06:45 +00:00
|
|
|
$ret .= "$line[0] $line[1] $line[2] $line[3]\n";
|
2014-03-06 13:46:01 +00:00
|
|
|
}
|
2014-03-07 11:06:45 +00:00
|
|
|
return $ret;
|
|
|
|
}
|
2014-03-06 13:46:01 +00:00
|
|
|
|
2014-05-13 14:18:01 +00:00
|
|
|
# compare device configurations from 2 versions
|
2014-03-13 17:40:56 +00:00
|
|
|
sub _cfgDB_Diff($$) {
|
2014-03-07 11:06:45 +00:00
|
|
|
my ($search,$searchversion) = @_;
|
|
|
|
my ($ret, $v0, $v1);
|
2015-01-17 20:44:19 +00:00
|
|
|
|
|
|
|
if ($search eq 'all' && $searchversion eq 'current') {
|
|
|
|
_cfgDB_DeleteTemp();
|
|
|
|
cfgDB_SaveCfg(-1);
|
|
|
|
$searchversion = -1;
|
|
|
|
}
|
|
|
|
|
2014-03-07 11:06:45 +00:00
|
|
|
my $fhem_dbh = _cfgDB_Connect;
|
2015-01-17 20:44:19 +00:00
|
|
|
$v0 = __cfgDB_Diff($fhem_dbh,$search,0,$searchversion);
|
|
|
|
$v1 = __cfgDB_Diff($fhem_dbh,$search,$searchversion,$searchversion);
|
2014-03-07 11:06:45 +00:00
|
|
|
$fhem_dbh->disconnect();
|
|
|
|
$ret = diff \$v0, \$v1, { STYLE => "Table" };
|
2015-01-17 20:44:19 +00:00
|
|
|
if($searchversion == -1) {
|
|
|
|
_cfgDB_DeleteTemp();
|
|
|
|
$searchversion = "UNSAVED";
|
|
|
|
}
|
2014-03-07 11:06:45 +00:00
|
|
|
$ret = "\nNo differences found!" if !$ret;
|
|
|
|
$ret = "compare device: $search in current version 0 (left) to version: $searchversion (right)\n$ret\n";
|
2014-03-06 13:46:01 +00:00
|
|
|
return $ret;
|
|
|
|
}
|
2014-03-03 12:34:54 +00:00
|
|
|
|
2014-08-22 11:52:04 +00:00
|
|
|
# find DEF, input supports devspec definitions
|
|
|
|
sub _cfgDB_findDef($;$) {
|
|
|
|
my ($search,$internal) = @_;
|
|
|
|
$internal = 'DEF' unless defined($internal);
|
|
|
|
|
|
|
|
my @ret;
|
|
|
|
my @etDev = devspec2array($search);
|
|
|
|
foreach my $d (@etDev) {
|
|
|
|
next unless $d;
|
|
|
|
push @ret, $defs{$d}{$internal};
|
|
|
|
}
|
|
|
|
|
|
|
|
return @ret;
|
|
|
|
}
|
|
|
|
|
2017-07-01 10:32:07 +00:00
|
|
|
sub _cfgDB_type() {
|
2017-08-04 13:15:20 +00:00
|
|
|
return "$cfgDB_dbtype (b64)";
|
2017-07-01 10:32:07 +00:00
|
|
|
}
|
|
|
|
|
2018-02-18 19:23:23 +00:00
|
|
|
sub _cfgDB_dump($) {
|
|
|
|
my ($param1) = @_;
|
|
|
|
$param1 //= '';
|
|
|
|
|
|
|
|
my ($dbconn,$dbuser,$dbpass,$dbtype) = _cfgDB_readConfig();
|
|
|
|
my ($dbname,$dbhostname,$dbport,$gzip,$mp,$ret,$size,$source,$target,$ts);
|
|
|
|
$ts = strftime('%Y-%m-%d_%H-%M-%S',localtime);
|
|
|
|
$mp = $configDB{attr}{'dumpPath'};
|
|
|
|
$mp //= AttrVal('global','modpath','.').'/log';
|
|
|
|
$target = "$mp/configDB_$ts.dump";
|
|
|
|
|
|
|
|
if (lc($param1) eq 'unzipped') {
|
|
|
|
$gzip = '';
|
|
|
|
} else {
|
|
|
|
$gzip = '| gzip -c';
|
|
|
|
$target .= '.gz';
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($dbtype eq 'SQLITE') {
|
|
|
|
(undef,$source) = split (/=/, $dbconn);
|
|
|
|
my $dumpcmd = "echo '.dump fhem%' | sqlite3 $source $gzip > $target";
|
|
|
|
Log 4,"configDB: $dumpcmd";
|
|
|
|
$ret = qx($dumpcmd);
|
|
|
|
return $ret if $ret; # return error message if available
|
|
|
|
|
|
|
|
} elsif ($dbtype eq 'MYSQL') {
|
|
|
|
($dbname,$dbhostname,$dbport) = split (/;/,$dbconn);
|
|
|
|
$dbport //= '=3306';
|
|
|
|
(undef,$dbname) = split (/=/,$dbname);
|
|
|
|
(undef,$dbhostname) = split (/=/,$dbhostname);
|
|
|
|
(undef,$dbport) = split (/=/,$dbport);
|
|
|
|
my $dbtables = "fhemversions fhemconfig fhemstate fhemb64filesave";
|
|
|
|
my $dumpcmd = "mysqldump --user=$dbuser --password=$dbpass --host=$dbhostname --port=$dbport -Q $dbname $dbtables $gzip > $target";
|
|
|
|
Log 4,"configDB: $dumpcmd";
|
|
|
|
$ret = qx($dumpcmd);
|
|
|
|
return $ret if $ret;
|
|
|
|
$source = $dbname;
|
|
|
|
|
|
|
|
} elsif ($dbtype eq 'POSTGRESQL') {
|
|
|
|
($dbname,$dbhostname,$dbport) = split (/;/,$dbconn);
|
|
|
|
$dbport //= '=5432';
|
|
|
|
(undef,$dbname) = split (/=/,$dbname);
|
|
|
|
(undef,$dbhostname) = split (/=/,$dbhostname);
|
|
|
|
(undef,$dbport) = split (/=/,$dbport);
|
|
|
|
my $dbtables = "-t fhemversions -t fhemconfig -t fhemstate -t fhemb64filesave";
|
|
|
|
my $dumpcmd = "PGPASSWORD=$dbpass pg_dump -U $dbuser -h $dbhostname -p $dbport $dbname $dbtables $gzip > $target";
|
|
|
|
Log 4,"configDB: $dumpcmd";
|
|
|
|
$ret = qx($dumpcmd);
|
|
|
|
return $ret if $ret;
|
|
|
|
$source = $dbname;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
return "configdb dump not supported for $dbtype!";
|
|
|
|
}
|
|
|
|
|
|
|
|
$size = -s $target;
|
|
|
|
$size //= 0;
|
|
|
|
$ret = "configDB dumped $size bytes\nfrom: $source\n to: $target";
|
|
|
|
return $ret;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2014-04-26 10:47:09 +00:00
|
|
|
##################################################
|
2014-04-20 21:11:19 +00:00
|
|
|
# functions used for file handling
|
2014-05-13 14:18:01 +00:00
|
|
|
# called by 98_configdb.pm
|
2014-04-20 21:11:19 +00:00
|
|
|
#
|
2014-04-27 17:42:08 +00:00
|
|
|
|
2014-05-13 14:18:01 +00:00
|
|
|
# delete file from database
|
2014-04-19 19:42:54 +00:00
|
|
|
sub _cfgDB_Filedelete($) {
|
|
|
|
my ($filename) = @_;
|
|
|
|
my $fhem_dbh = _cfgDB_Connect;
|
2017-08-04 13:15:20 +00:00
|
|
|
my $ret = $fhem_dbh->do("delete from fhemb64filesave where filename = '$filename'");
|
2014-04-19 19:42:54 +00:00
|
|
|
$fhem_dbh->commit();
|
|
|
|
$fhem_dbh->disconnect();
|
2017-09-19 12:55:19 +00:00
|
|
|
$ret = ($ret > 0) ? 1 : undef;
|
2014-04-19 19:42:54 +00:00
|
|
|
return $ret;
|
|
|
|
}
|
|
|
|
|
2014-05-13 14:18:01 +00:00
|
|
|
# export file from database to filesystem
|
2015-02-01 13:36:06 +00:00
|
|
|
sub _cfgDB_Fileexport($;$) {
|
|
|
|
my ($filename,$raw) = @_;
|
2014-04-19 19:42:54 +00:00
|
|
|
my $fhem_dbh = _cfgDB_Connect;
|
2017-08-04 13:15:20 +00:00
|
|
|
my $sth = $fhem_dbh->prepare( "SELECT content FROM fhemb64filesave WHERE filename = '$filename'" );
|
2014-04-19 19:42:54 +00:00
|
|
|
$sth->execute();
|
2014-05-13 14:18:01 +00:00
|
|
|
my $blobContent = $sth->fetchrow_array();
|
2017-08-04 13:15:20 +00:00
|
|
|
$blobContent = decode_base64($blobContent);
|
2014-05-13 14:18:01 +00:00
|
|
|
my $counter = length($blobContent);
|
2015-02-05 16:22:02 +00:00
|
|
|
$sth->finish();
|
|
|
|
$fhem_dbh->disconnect();
|
2014-05-13 14:18:01 +00:00
|
|
|
return "No data found for file $filename" unless $counter;
|
2015-02-05 16:22:02 +00:00
|
|
|
return ($blobContent,$counter) if $raw;
|
|
|
|
|
2014-05-13 14:18:01 +00:00
|
|
|
open( FILE,">$filename" );
|
|
|
|
binmode(FILE);
|
|
|
|
print FILE $blobContent;
|
|
|
|
close( FILE );
|
2015-02-05 16:22:02 +00:00
|
|
|
return "$counter bytes written from database into file $filename";
|
2014-04-19 19:42:54 +00:00
|
|
|
}
|
|
|
|
|
2014-05-13 14:18:01 +00:00
|
|
|
# import file into database
|
2014-05-12 21:21:58 +00:00
|
|
|
sub _cfgDB_binFileimport($$;$) {
|
2014-04-27 17:42:08 +00:00
|
|
|
my ($filename,$filesize,$doDelete) = @_;
|
|
|
|
$doDelete = (defined($doDelete)) ? 1 : 0;
|
|
|
|
|
2014-05-20 16:25:07 +00:00
|
|
|
open (inFile,"<$filename") || die $!;
|
2014-04-27 17:42:08 +00:00
|
|
|
my $blobContent;
|
2014-05-20 16:25:07 +00:00
|
|
|
binmode(inFile);
|
|
|
|
my $readBytes = read(inFile, $blobContent, $filesize);
|
|
|
|
close(inFile);
|
2017-08-04 13:15:20 +00:00
|
|
|
$blobContent = encode_base64($blobContent);
|
2014-04-27 17:42:08 +00:00
|
|
|
my $fhem_dbh = _cfgDB_Connect;
|
2017-08-04 13:15:20 +00:00
|
|
|
$fhem_dbh->do("delete from fhemb64filesave where filename = '$filename'");
|
|
|
|
my $sth = $fhem_dbh->prepare('INSERT INTO fhemb64filesave values (?, ?)');
|
2016-05-29 19:46:13 +00:00
|
|
|
|
|
|
|
# add support for postgresql by Matze
|
|
|
|
$sth->bind_param( 1, $filename );
|
|
|
|
if ($cfgDB_dbtype eq "POSTGRESQL") {
|
|
|
|
$sth->bind_param( 2, $blobContent, { pg_type => DBD::Pg::PG_BYTEA() } );
|
|
|
|
} else {
|
|
|
|
$sth->bind_param( 2, $blobContent );
|
|
|
|
}
|
|
|
|
|
2014-04-27 17:42:08 +00:00
|
|
|
$sth->execute($filename, $blobContent);
|
|
|
|
$sth->finish();
|
|
|
|
$fhem_dbh->commit();
|
|
|
|
$fhem_dbh->disconnect();
|
|
|
|
|
2015-01-23 18:21:29 +00:00
|
|
|
unlink($filename) if(($configDB{attr}{deleteimported} || $doDelete) && $readBytes);
|
2014-04-27 17:42:08 +00:00
|
|
|
return "$readBytes bytes written from file $filename to database";
|
|
|
|
}
|
|
|
|
|
2014-05-13 14:18:01 +00:00
|
|
|
# list all files stored in database
|
2014-04-20 21:11:19 +00:00
|
|
|
sub _cfgDB_Filelist(;$) {
|
|
|
|
my ($notitle) = @_;
|
2014-04-19 19:42:54 +00:00
|
|
|
my $ret = "Files found in database:\n".
|
2016-03-19 17:13:07 +00:00
|
|
|
"------------------------------------------------------------\n";
|
2014-04-20 21:11:19 +00:00
|
|
|
$ret = "" if $notitle;
|
2014-04-18 20:53:42 +00:00
|
|
|
my $fhem_dbh = _cfgDB_Connect;
|
2017-08-04 13:15:20 +00:00
|
|
|
my $sql = "SELECT filename FROM fhemb64filesave group by filename order by filename";
|
2016-03-19 17:13:07 +00:00
|
|
|
my $content = $fhem_dbh->selectall_arrayref($sql);
|
|
|
|
foreach my $row (@$content) {
|
|
|
|
$ret .= "@$row[0]\n" if(defined(@$row[0]));
|
2014-04-18 20:53:42 +00:00
|
|
|
}
|
|
|
|
$fhem_dbh->disconnect();
|
2014-04-19 19:42:54 +00:00
|
|
|
return $ret;
|
2014-04-18 20:53:42 +00:00
|
|
|
}
|
2014-03-20 15:53:39 +00:00
|
|
|
|
2014-03-01 11:59:56 +00:00
|
|
|
1;
|
|
|
|
|
|
|
|
=pod
|
2016-01-16 20:00:25 +00:00
|
|
|
=item helper
|
2016-08-19 18:18:00 +00:00
|
|
|
=item summary configDB backend
|
|
|
|
=item summary_DE configDB backend
|
2014-03-01 11:59:56 +00:00
|
|
|
=begin html
|
|
|
|
|
|
|
|
<a name="configDB"></a>
|
|
|
|
<h3>configDB</h3>
|
|
|
|
<ul>
|
2017-02-09 10:00:51 +00:00
|
|
|
<a href="https://forum.fhem.de/index.php?board=46.0">Link to FHEM forum</a><br/><br/>
|
2014-03-13 17:40:56 +00:00
|
|
|
This is the core backend library for configuration from SQL database.<br/>
|
2014-03-10 19:37:50 +00:00
|
|
|
See <a href="#configdb">configdb command documentation</a> for detailed info.<br/>
|
2014-03-01 11:59:56 +00:00
|
|
|
</ul>
|
|
|
|
|
|
|
|
=end html
|
2014-03-02 18:49:49 +00:00
|
|
|
|
|
|
|
=begin html_DE
|
|
|
|
|
|
|
|
<a name="configDB"></a>
|
|
|
|
<h3>configDB</h3>
|
|
|
|
<ul>
|
2014-03-10 19:37:50 +00:00
|
|
|
configDB ist die Funktionsbibliothek für die Konfiguration aus einer SQL Datenbank.<br/>
|
|
|
|
Die ausführliche Dokumentation findet sich in der <a href="#configdb">configdb Befehlsbeschreibung</a>.
|
2014-03-02 18:49:49 +00:00
|
|
|
</ul>
|
|
|
|
|
|
|
|
=end html_DE
|
|
|
|
|
|
|
|
=cut
|