Perl Modul LoxBerry::System::General

The class LoxBerry::System::General is intended to be used from LoxBery-Core only and is processing the general.json basic configuration file.

The class is derived from LoxBerry::JSON, therefore all methods and parameters from LoxBerry::JSON can be used with LoxBerry::System::General.

Compared to LoxBerry::JSON, LoxBerry::System::General fulfills this major functions:

  • It automatically loads general.json without the filename parameter
  • It calculates additional values to be stored in the json, without explicitly setting them.
  • It automatically generates a new legacy general.cfg on changes to the general.json.
  • It is used via ajax-config-handler.cgi from PHP widgets, to trigger the general.cfg creation, when general.json was changed.
LoxBerry Compatibility

This object class is in available from V2.2 and from there, is the primary and only class to change general.json.. Until LB2.2, the base configuration file was general.cfg.

Inclusions

This library automatically includes:

  • use LoxBerry::JSON;
  • use JSON;

To consider, LoxBerry::System::General neither uses LoxBerry::System nor Config::Simple. The general.json path is evaluated by OS environment variable $ENV{'LBSCONFIG'}, but with fallback to require LoxBerry::System. The general.cfg file is generated as plain text file without Config::Simple library.

Abstract

use LoxBerry::System::General;
my $jsonobj = LoxBerry::System::General->new();
my $cfg = $jsonobj->open();
 
## This is used like LoxBerry::JSON
# Read values
print "Firstname: " . $cfg->{Firstname};
# Change values
$cfg->{Firstname} = "Christian";
 
## All the features and "special" features (like parse, find, encode, jsblock...) are available, see LoxBerry::JSON
 
# Write 
$jsonobj->write();
# On every write, the class checks for changes and omits writing if nothing has changed
my $saved = $jsonobj->write();
# Returns 1 if the config was changed

Open Parameter

Parameter Optional Default Description
writeonclosex 0 If 0, you need to explicitely call write. write can also be called multiple times.

If 1, the file is written automatically on descruction of the object.
readonly x 0 If 1, every writing to the file is omitted.

Open general.json and change - Example

#!/usr/bin/perl
use LoxBerry::System::General;
 
$LoxBerry::System::General::DEBUG = 1;
 
my $jsonobj = LoxBerry::System::General->new();
my $cfg = $cfgobj->open();
 
# Error handling
if (!$cfg) {
    print "Error loading general.json\n";
} else {
    print "File loaded\n";
}
 
# Accessing values
print "Timeserver: $cfg->{Timeserver}->{NTPServer}\n";
 
# Simple values
$cfg->{Timeserver}->{NTPServer} = "ntp.time.com";
 
$jsonobj->write();

write()

The write method will check if the content has changed, and only really writes the new content on changes.

write never writes a file:

  • if it has not changed,
  • if readonly ⇒ 1 is set

 From LB 2.2+: write returns 1, if the file was written, and undef if nothing has changed. Before LB2.2,write always returns undef.

Generation of legacy general.cfg

The legacy general.cfg file is created:

  • with write, if the content has changed and not opened with readonly ⇒ 1
  • if the object is descructed, if the content has changed and not opened with readonly ⇒1. Destruction means,
    • Object goes out of scope (opened in a sub, returned from sub)
    • Script exits (this may also happen on an exception!)
  • if $jsonobj->_json2cfg() is explicitely called (thought to be called from LoxBerry-Core PHP code)

Concurrency issue

LoxBerry::JSON can detect concurrency within the same script (e.g. opening two times and changing values on two different code parts).

It can not detect concurrency with two different processes changing the file.

Opening will read the json into memory.

Changing values will write the new data to memory.

write does not re-read the file first, but compares the old data in memory with the changed data in memory. Therefore, if any other process changes the json after you have opened it, and the json really is written (because you have changed values), the changes from the other process(es) will be lost. 

Usage examples

Open and change a value

use LoxBerry::System::General;
my $jsonobj = LoxBerry::System::General->new();
my $cfg = $jsonobj->open();
$cfg->{Webserver}->{Port} = 81;
$jsonobj->write();

Less "hand writing" by defining a var for a subkey

my $jsonobj = LoxBerry::System::General->new();
my $cfg = $jsonobj->open();
my $netip4 = $cfg->{Network}->{Ipv4};
$netip4->{Mask} = '255.255.255.0';

Accessing Miniservers

Loop through Miniservers

my $jsonobj = LoxBerry::System::General->new();
my $cfg = $jsonobj->open();
foreach my $msno ( sort keys %{$cfg->{Miniserver}} ) {
    my $curms = $cfg->{Miniserver}->{$msno};
    print "Miniserver $msno has name $curms->{Name}\n";
}

Miniserver count != Miniserver keys

The number of Miniserver objects in $obj→{Miniserver} may not match to the highest string value in the keys of the Miniserver objects. What?!? See the picture:

LEFT picture: The Miniserver object holds four named elements, their names are "1", "2", "3" and "4". These names are strings.

RIGHT picture: For all programming, it should not be a problem for the program, if one element is named "7" (with only 4 elements in the list).

To be stable against such situation, it is required to distinguish between number of elements in the list (4 elements), and highest element key in the list (highest is 7). In any case that LoxBerry provides to e.g. arrange elements, or delete MS in the middle, this is important.

Getting number of elements in the Miniserver object

my $count = keys %{$cfg->{Miniserver}};
print "$count Miniservers in the list\n";
# possibly parenthesis required (e.g. in a if condition)
if ( (keys %{$cfg->{Miniserver}}) < 1 ) {
    print "Error: No miniservers defined";
}

Getting the highest name of Miniservers

my $max = 0;
$_ > $max and $max = $_ for keys %{$cfg->{Miniserver}};
print "Highest MS key is $max\n";

Direct access to Miniserver with known number

my $msno = 3;
print "$msno's Name is " . $cfg->{Miniserver}->{$msno}->{Name};
 
# Less to write:
$curms =  $cfg->{Miniserver}->{$msno};
print "$msno's Name is " . $curms->{Name};

Recreation of the legacy general.cfg from other languages

Using LoxBerry::System::General automatically recreates the legacy general.cfg automatically.

If you need to change the json config from another language (PHP, Bash), you need to trigger the update of general.cfg:

$LBHOMEDIR/webfrontend/htmlauth/system/ajax/ajax-config-handler.cgi action=recreate-generalcfg

See further documentation