#!/usr/bin/perl # IBM_PROLOG_BEGIN_TAG # This is an automatically generated prolog. # # bos72X src/bos/usr/sbin/install/suma/lib/SUMA/GConfig.pm 1.11.1.4 # # Licensed Materials - Property of IBM # # Restricted Materials of IBM # # COPYRIGHT International Business Machines Corp. 2004,2022 # All Rights Reserved # # US Government Users Restricted Rights - Use, duplication or # disclosure restricted by GSA ADP Schedule Contract with IBM Corp. # # IBM_PROLOG_END_TAG # This module will actually die() on any serious failures. # This implementation will maintain a sparse database. package SUMA::GConfig; =head1 NAME SUMA::GConfig - Access to global configuration settings. =head1 SYNOPSIS use SUMA::GConfig; # imports getGlobal and LVL_ macros use SUMA::GConfig qw/getGlobal setGlobal saveGlobals listGlobals printGlobals/; use SUMA::GConfig qw/LVL_OFF LVL_ERROR LVL_WARNING LVL_INFO LVL_VERBOSE LVL_DEBUG/; use SUMA::GConfig qw/:lvl/; # same use SUMA::GConfig qw/:all/; $fixserver_protocol = getGlobal('FIXSERVER_PROTOCOL'); $screen_verb_mnem = getGlobal('SCREEN_VERBOSE', 1); $oldval = setGlobal(PATH => '/blah/foo:/usr/bin:.'; saveGlobals() or die "Couldn't save configuration data"; $h = &listGlobals(1); while(($k, $v) = each(%$h)) { ... } select STDOUT; printGlobals(); =head1 DESCRIPTION Provides functions for accessing SUMA global configuration settings. Maintains a sparse database at /var/suma/data/config.suma =cut use strict; use constant LVL_OFF => -1; use constant LVL_ERROR => 0; use constant LVL_WARNING => 1; use constant LVL_INFO => 2; use constant LVL_VERBOSE => 3; use constant LVL_DEBUG => 4; require Exporter; our @ISA = qw(Exporter); my @lvl_macros = qw( LVL_OFF LVL_ERROR LVL_WARNING LVL_INFO LVL_VERBOSE LVL_DEBUG ); our @EXPORT = (@lvl_macros, qw(getGlobal)); our @EXPORT_OK = qw(setGlobal saveGlobals listGlobals printGlobals); our %EXPORT_TAGS = ( all => [@EXPORT, @EXPORT_OK], lvl => [@lvl_macros], def => [@EXPORT]); use lib qw(/usr/suma/lib); $ENV{PATH} = "/usr/suma/bin:/usr/bin:/usr/sbin"; delete @ENV{qw/IFS CDPATH ENV BASH_ENV/}; my $lvl_re = join('|', LVL_OFF, LVL_ERROR, LVL_WARNING, LVL_INFO, LVL_VERBOSE, LVL_DEBUG, qw/ LVL_OFF LVL_ERROR LVL_WARNING LVL_INFO LVL_VERBOSE LVL_DEBUG/); # A hack: sub RE_VERBOSITY() { return qr/^($lvl_re)$/oi; } use constant RE_PATH => qr!^([]/\w"#$%'+,-.:@[\\^{}~]+)$!o; use constant RE_INTEGER => qr/^([1-9]\d*)$/o; use constant RE_BOOL => qr/^(y(es)?|no?|0|1)$/oi; use constant RE_NOMOD => qr!^This value cannot be changed$!; # Not ideal, but an okay double-check use constant GCONFIG_PATH => '/var/suma/data/config.suma'; use constant RE_COMMENT => qr/^\s*#/o; use constant RE_KVLINE => qr/^\s*(\w+)\s*=\s*(.*?)\s*$/o; use constant RE_PROXYURL => qr/^(.*)$/o; # Make general, since protocol needs to be optional my $validate = { FIXSERVER_PROTOCOL => { default => 'https', match => qr/^(https)$/oi, seq => "1_net_010", }, DOWNLOAD_PROTOCOL => { default => 'http', match => qr/^(https?)$/oi, seq => "1_net_020", }, DL_TIMEOUT_SEC => { default => 180, match => RE_INTEGER, seq => "1_net_110", }, DL_RETRY => { default => 1, match => RE_INTEGER, seq => "1_net_120", }, # MAX_CONCURRENT_DOWNLOADS => { # default => 5, # match => qr/^(-1|unlimited|[1-9]\d*)$/oi, # seq => "1_net_130", # }, HTTP_PROXY => { default => '', match => qr/^()$/oi, seq => "1_net_210", }, HTTPS_PROXY => { default => '', match => qr/^()$/oi, seq => "1_net_220", }, # FTP_PROXY => { # default => '', # match => RE_PROXYURL, # seq => "1_net_230", # }, REMOVE_CONFLICTING_UPDATES => { default => 'yes', match => RE_BOOL, seq => "5_lppmgr_010", }, REMOVE_DUP_BASE_LEVELS => { default => 'yes', match => RE_BOOL, seq => "5_lppmgr_020", }, REMOVE_SUPERSEDE => { default => 'yes', match => RE_BOOL, seq => "5_lppmgr_030", }, USE_FIPS_PROVIDER => { default => 'no', match => RE_BOOL, seq => "1_net_250", }, CHECK_CERTIFICATE_REVOCATION => { default => 'no', match => RE_BOOL, seq => "1_net_260", }, USE_CC_CIPHERS => { default => 'no', match => RE_BOOL, seq => "1_net_270", }, SCREEN_VERBOSE => { default => LVL_INFO, match => RE_VERBOSITY, seq => "3_output_010", }, NOTIFY_VERBOSE => { default => LVL_INFO, match => RE_VERBOSITY, seq => "3_output_020", }, LOGFILE_VERBOSE => { default => LVL_VERBOSE, match => RE_VERBOSITY, seq => "3_output_030", }, # PATH => { # default => $ENV{PATH}, # match => RE_PATH, # seq => "7_disk_010", # }, TMPDIR => { default => '/var/suma/tmp', match => RE_PATH, seq => "7_disk_020", }, MAXLOGSIZE_MB => { default => 1, match => RE_INTEGER, seq => "4_log_010", }, POLICY_DB => { default => '/var/suma/data/policy.suma', match => RE_NOMOD, seq => "9_nomod_010", }, NOTIFY_DB => { default => '/var/suma/data/notify.suma', match => RE_NOMOD, seq => "9_nomod_020", }, MSG_MAPFILE_PATH => { default => '/usr/suma/lib/msg.map', match => RE_NOMOD, seq => "9_nomod_030", }, BOOTSTRAP_URL => { default => 'http://www.ibm.com/servers/aix/os/sysman/suma_config.html', match => RE_NOMOD, seq => "9_nomod_035", }, # FIXSERVER_URL => { # default => 'www14.software.ibm.com/webapp/set2/fixget', # match => qr/^(.*)$/, # seq => "9_nomod_040", # }, LOGFILE => { default => '/var/adm/ras/suma.log', match => RE_NOMOD, seq => "9_nomod_050", }, DL_HIST_LOGFILE => { default => '/var/adm/ras/suma_dl.log', match => RE_NOMOD, seq => "9_nomod_060", }, WEB_IDENTITY_FILE => { default => '', match => RE_PATH, seq => "7_disk_030", }, }; # " Remove this comment my $cache = {}; my $loaded = 0; ## Method getGlobal ########################################## =head1 METHODS =over 4 =item getGlobal KEY_STR =item getGlobal KEY_STR MNEMONIC_BOOL Retrieves and returns the global value associated with the specified key, or C if the key is not valid. If the MNEMONIC_BOOL argument evaluates to true, the mnemonic (if applicable) is returned instead of the actual value. =cut sub getGlobal { shift if ref($_[0]) || $_[0]->isa(__PACKAGE__); my $tag = uc(shift); exists $validate->{$tag} or return undef; my $ret; if(exists($cache->{$tag})) { $ret = $cache->{$tag}; } else { # Not in the cache - retrieve from the DB _load(); $ret = exists($cache->{$tag}) ? $cache->{$tag} : $validate->{$tag}->{default}; } my $mnemonic = shift; if($mnemonic) { # mnemonic conversions if($tag =~ /_VERBOSE$/) { require SUMA::GConfig; SUMA::GConfig->import(qw/LVL_OFF LVL_ERROR LVL_WARNING LVL_INFO LVL_VERBOSE LVL_DEBUG/); SWITCH: { if($ret <= LVL_OFF() ) { $ret = 'LVL_OFF'; last SWITCH; } if($ret == LVL_ERROR() ) { $ret = 'LVL_ERROR'; last SWITCH; } if($ret == LVL_WARNING()) { $ret = 'LVL_WARNING'; last SWITCH; } if($ret == LVL_INFO() ) { $ret = 'LVL_INFO'; last SWITCH; } if($ret == LVL_VERBOSE()) { $ret = 'LVL_VERBOSE'; last SWITCH; } if($ret >= LVL_DEBUG() ) { $ret = 'LVL_DEBUG'; last SWITCH; } } } } return $ret; } ## Method setGlobal ########################################## =item setGlobal KEY_STR, VALUE_STR Sets the value of the specified key, returning the old value. Returns undef (and does not set the value) if the key or value is not valid. This function does NOT write the value back to the configuration file. Use the C function to make values set via C persistent across invocations. =cut sub setGlobal($$) { shift if ref($_[0]) || $_[0]->isa(__PACKAGE__); my $tag = uc(shift); my $val = shift; _validate($tag, \$val) or return undef; my $oldval = getGlobal($tag); $cache->{$tag} = $val; return $oldval; } ## Method saveGlobals ########################################## =item saveGlobals Saves configuration settings to permanent storage. Anything changed via C will retain its new value for the next invocation of this library. =cut sub saveGlobals() { _load(); open(FH, '>' . GCONFIG_PATH) or die "Couldn't open global configuration file " . GCONFIG_PATH . " for writing: $!"; chmod 0600, GCONFIG_PATH; # Failure not big deal with rw root umask. Only allow root rw access. while(my($k, $v) = each(%$cache)) { $v = '' unless defined $v; print FH "$k = $v\n"; } close(FH); } ## Method listGlobals ########################################## =item listGlobals =item listGlobals ALL_BOOL Returns a reference to a hash containing configuration key/value pairs. If C evaluates to C, even "private" keys will be listed. Otherwise, only modifiable values will be returned. Modifying values in the returned hash reference should NOT affect the settings themselves. =cut sub listGlobals { my $all = shift; my $ret = {}; for(keys(%$validate)) { if($all || ($validate->{$_}->{match} ne RE_NOMOD)) { $ret->{$_} = getGlobal($_); } } return $ret; } ## Method printGlobals ########################################## =item printGlobals =item printGlobals ALL_BOOL Prints the global configuration values in a sensible order to the currently-selected filehandle. If C evaluates to C, even "private" keys will be listed. Otherwise, only modifiable values will be printed. =cut sub printGlobals { my $h = listGlobals(@_); for my $key (sort( { $validate->{$a}->{seq} cmp $validate->{$b}->{seq} } keys(%$h) )) { print("\t" . $key . '=' . getGlobal($key, 1) . "\n"); } } # Pulls all the values from the DB, but does # NOT overwrite values already in the cache. sub _load { return 1 if $loaded; return 2 unless -e GCONFIG_PATH; # OK if the DB doesn't exist open(FH, GCONFIG_PATH) or die "Couldn't open global configuration file " . GCONFIG_PATH . " for reading: $!"; my($k, $v); while(my $line = ) { chomp $line; next if $line =~ RE_COMMENT; ($k, $v) = $line =~ RE_KVLINE; # Fix or blitz bogus values entered by hand unless(_validate($k, \$v)) { next unless exists $validate->{$k}; $v = $validate->{$k}->{default}; } next if defined $cache->{$k}; # Don't overwrite cached values $cache->{$k} = $v; } close(FH); $loaded = 1; } sub _validate { my($tag, $valref) = @_; $tag = uc($tag); return undef unless ( exists $validate->{$tag} && defined $$valref && (($$valref) = $$valref =~ $validate->{$tag}->{match}) # taint clean ); return undef if (($validate->{$tag}->{match} eq RE_NOMOD ) && ( $$valref ne $validate->{$tag}->{default})); # Wash the values as necessary. # Lenient in what we require, strict in what we provide. SWITCH: { if($validate->{$tag}->{match} eq RE_BOOL) { $$valref = ($$valref =~ /[y1]/oi) ? 'yes' : 'no'; last SWITCH; } if($validate->{$tag}->{match} eq RE_VERBOSITY) { last SWITCH if $$valref =~ /^[+-]?\d+$/o; # integer-numeric $$valref = uc($$valref); VERBOSITY: { if($$valref eq 'LVL_OFF' ) { $$valref = LVL_OFF ; last VERBOSITY } if($$valref eq 'LVL_ERROR' ) { $$valref = LVL_ERROR ; last VERBOSITY } if($$valref eq 'LVL_WARNING') { $$valref = LVL_WARNING; last VERBOSITY } if($$valref eq 'LVL_INFO' ) { $$valref = LVL_INFO ; last VERBOSITY } if($$valref eq 'LVL_VERBOSE') { $$valref = LVL_VERBOSE; last VERBOSITY } if($$valref eq 'LVL_DEBUG' ) { $$valref = LVL_DEBUG ; last VERBOSITY } # else die "$$valref unexpectedly matched " . RE_VERBOSITY . " but isn't recognized."; } last SWITCH; } # if($tag eq 'MAX_CONCURRENT_DOWNLOADS' && $$valref =~ /unlimited/oi) { # $$valref = -1; # last SWITCH; # } if($tag =~ /_PROTOCOL$/) { $$valref = lc $$valref; } } # SWITCH return 1; } 1;