CGI::MxScreen::Config - configuration for CGI::MxScreen


CGI-MxScreen documentation Contained in the CGI-MxScreen distribution.

Index


Code Index:

NAME

Top

CGI::MxScreen::Config - configuration for CGI::MxScreen

SYNOPSIS

Top

 use CGI::MxScreen::Config "./filename.pl";
 use CGI::MxScreen::Config ({ -FILE => "./filename.pl" });

 use CGI::MxScreen::Config ({
     -FILE              => "./filename.pl",  # Common init
     -disable_upload    => 0,                # Supersede common init
     -fatals_to_browser => 0,
     -loglevel          => "debug",
 });

DESCRIPTION

Top

The CGI::MxScreen::Config module is meant to provide compile-time configuration for the CGI::MxScreen framework.

Configuration parameters may be set in a Perl file (which is loaded via require, so it must end-up being syntactically correct and return a true value). Inclusion happens in the CGI::MxScreen::cf namespace, and therefore you must not use any package declaration in that file.

Configuration parameters may also be specified as keys within a hash ref object, and may be prefixed by a - character to benefit from Perl's auto-quoting. That character is not part of the variable however, and case matters.

Note that you cannot say:

    use CGI::MxScreen::Config { -loglevel => "notice" };    # WRONG

because it is synctactically incorrect. You must enclose the hash ref within parenthesis, as in:

    use CGI::MxScreen::Config ({ -loglevel => "notice" });  # RIGHT

The reason we take a hash ref as first argument and not a list of values is to leave room for further extensions.

The special -FILE key points to a filename which is loaded before processing the remaining keys. Therefore, it is possible to override some variables on a per-script basis, yet share customized settings.

The CGI::MxScreen module uses Log::Agent for logging, with the File driver. Some options like logfile and loglevel may be used to customize its behaviour. By default, logging will be redirected to STDERR.

There's currently no option to choose another logging driver like Syslog, but this can be added easily if there's some demand.

INTERFACE

Top

The following configuration variables are available, in alphabetical order:

bindir

A preset variable, indicating the location of the CGI script.

cgi_carpout

When set to true and logfile is also defined, arrange for CGI::Carp to redirect errors to that logfile.

chdir

If set, attempt to chdir() to the specified directory very early during CGI::MxScreen::Config processing.

config

A read-only variable, set to the filename that was used to load configuration from, if any.

datum_config

When set to a non-null string, refers to the configuration file to load for Carp::Datum dynamic configuration. See Carp::Datum::Cfg for details.

datum_on

When true, activates Carp::Datum. Note that the datum_config variable is used to load the configuration even when this variable is set to false.

disable_upload

Sets $CGI::DISABLE_UPLOADS to true if set.

fatal_message

The fatal message to be used by CGI::Carp when fatals_to_browser is set. See CGI::Carp for details.

fatals_to_browser

When set to true, tells CGI::Carp to redirect fatal errors to the browser. This is useful during the debugging phase. It is true by default.

libdir

A set of directories to be added to @INC via "use lib", in the order given. The format is the same as the one used by the PERL5LIB variable.

logchannels

The hash ref to give to Log::Agent::Driver::File via -channels. Values are printf patterns where "%s" stands for the script's basename. This variable supersedes any setting in logfile.

When an empty logchannels hash ref is given, it is interpreted as if the following was given instead:

  {
      'error'     => "%s.err",
      'output'    => "%s.out",
      'debug'     => "%s.dbg",
  }

where "%s" stands for "$me", the basename of the script.

See Log::Agent::Driver::File for more information.

logdebug

Sets the value of the -debug flag for logconfig(). See Log::Agent for allowed values.

logdir

This variable is used to anchor logfiles to some directory when logfile does not specify a full path. If unset, it defaults to ".". Note that when chdir is used, "." is the directory where we chdir-ed to.

logfile

The path of the file where logging should go. Leaving all logdir, logfile and logchannels empty will cause logging to go to STDERR, which should end-up in the server's error log.

If left empty but logdir was set, the value of "$me.log" is assummed, where "$me" stands for the script's basename. You may use "%s" in the name to stand for "$me" (an sprintf() is called, so any % you wish to be part of the logfile name should be written as %%).

loglevel

Specify the loglevel for tracing (DTRACE) when the debug mode of Carp::Datum is turned off. Possible values are: emergency, alert, critical, error, warning, notice, info, debug

See Log::Agent for further details.

logstamp

The Log::Agent timestamping format to use. Defaults to "own" when a logfile is used, and is forced to "date" when logs are redirected to the server logs.

log_backlog

The amount of backlog you wish to keep after logfile rotation. Backlog is never compressed, because it is currently performed synchronously by Log::Agent::Rotate and that could cause huge delays when log_maxsize is big, impacting your script response time.

Defaults to 7.

log_maxsize

The maximum size in bytes for the logfile. See Log::Agent::Rotate.

log_maxtime

The maximum time a logfile can be kept open before being rotated. See Log::Agent::Rotate for the allowed syntax.

log_single_host

By default, CGI::MxScreen assumes nothing upon how logfiles can be accessed. When you know they are only accessed from ONE machine, you can set this variable to true, and it will speed up locking done via LockFile::Simple during log rotations.

mx_buffer_stdout

When true, all regular output to STDOUT is saved away, and the context will be emitted before any other form output (when saved within the form). This has a slight overhead cost since we have to copy things, but has two nice advantages: in case of fatal errors, the regular output is not mixed with the CGI::Carp output; and the widgets appear on their browser when the context is already emitted, meaning that even if they press a submit button before they have got the whole form, we'll be able to process it sanely.

Default is true.

mx_check_vars

Boolean flag, telling whether CGI::MxScreen should trap any access to unknown keys within the screen global context (accessed through $screen->vars) and make them fatal errors.

Default is true.

mx_logfile

The CGI::MxScreen application logfile, where the module logs what is happening.

mx_loglevel

The CGI::MxScreen application loglevel. Syslog priorities are used, and the specified level is the maximum priority logged.

Here is the list of the various things logged, and their level:

    Level   What
    ------  -------------------------------------------
    emerg   
    alert   Internal errors
    crit    Display errors
    err     Callback failures
    warn    Current state, session info
    notice  Button pressed, state changes
    info    User-agent (1st time), global time spent
    debug   Detailed statistics

mx_medium

Defines the medium used to save the application context. It takes an ARRAY ref defining the medium type (must inherit from CGI::MxScreen::Session::Medium) followed by the creation routine arguments.

The first array item (the medium type) is a string which can be shortened: the "CGI::MxScreen::Session::Medium::" leading part may be replaced by '+', which will be automatically replaced.

If not specified, it defaults to:

  [
      "+Browser",
      -key => "an unsecure key -- not this one",
  ]

meaning the session contexts will be saved within the browser. This is for convenience only, but when releasing for production, you should never rely on this default and specify a sensible encryption key, to prevent eavesdropping and tampering on the context.

See CGI::MxScreen::Session::Medium for the list of available media.

mx_serializer

Defines the serializer to use to create a frozen context image. It takes an ARRAY ref defining the medium type (must inherit from CGI::MxScreen::Serializer) followed by the creation routine arguments.

The first array item (the serializer type) is a string which can be shortened: the "CGI::MxScreen::Serializer::" leading part may be replaced by '+', which will be automatically replaced.

If not specified, this defaults to:

  ["+Storable"]

An alternative when using the browser to store sessions (see mx_medium) is to use:

  [
      "+Storable",
      -shared     => 0,
      -compress   => 1,
  ]

to create a more compact representation, and reduce network traffic at the cost of more CPU time spent on the server.

See CGI::MxScreen::Serializer for the list of supported serializers.

path

If set, its value is used to set the PATH environment variable. Remember that CGI scripts should always be run with taint checks enabled, and that explicitely setting the PATH is necessary to be able to run commands via system().

post_max

The value of this variable is used to set $CGI::POST_MAX. It defaults to 1 Mbyte when not set.

view_source

By default, the $\ variable is set to "\n" so that all print statements end-up being on their own line. This tends to produce view-able source code. Setting this to 0 avoids touching $\. You may of course add your own "\n" to each print statement, but this is not recommended.

EXAMPLE

Top

Here is a configuration file example:

    $disable_upload = 1;
    $post_max = 10 * 1024;

    $fatals_to_browser = 1;

    $datum_config = "debug.cf";
    $datum_on = 1;

    $logdir = "/home/ram/public_html/log";
    $logfile = "%s.log";
    $logstamp = "own";
    $loglevel = "warn";
    $logdebug = 0;
    $logchannels = {} if $datum_on;  # take defaults, supersedes $logfile

    $cgi_carpout = 1;
    $view_source = 1;

    $path = "/bin:/usr/bin:/home/ram/bin/scripts:/home/ram/bin/i386";
    $chdir = "/home/ram/public_html/scripts";
    $libpath = "$bindir/lib:/usr/local/cgilib";

	$mx_logfile = "mx.log";
	$mx_loglevel = "info";
	$mx_medium = ["+Browser", -key => "shush! it's a secret" ];
	$mx_serializer = ["+Storable", -compress => 1];

    1;

AUTHOR

Top

Raphael Manfredi <Raphael_Manfredi@pobox.com>

SEE ALSO

Top

CGI::Carp(3), CGI::MxScreen(3), CGI::MxScreen::Serializer(3), CGI::MxScreen::Session::Medium(3), Log::Agent(3).


CGI-MxScreen documentation Contained in the CGI-MxScreen distribution.

# -*- Mode: perl -*-
#
# $Id: Config.pm,v 0.1 2001/04/22 17:57:03 ram Exp $
#
#  Copyright (c) 1998-2001, Raphael Manfredi
#  Copyright (c) 2000-2001, Christophe Dehaudt
#  
#  You may redistribute only under the terms of the Artistic License,
#  as specified in the README file that comes with the distribution.
#
# HISTORY
# $Log: Config.pm,v $
# Revision 0.1  2001/04/22 17:57:03  ram
# Baseline for first Alpha release.
#
# $EndLog$
#

use strict;

#
# CGI::MxScreen run-time configuration, factorized.
#

package CGI::MxScreen::Config;

use Log::Agent;
use Carp::Datum;
require CGI;

use FindBin qw($Bin);
BEGIN {$Bin = $1 if $Bin =~ /^(\S+)$/;}	# untaint

#
# import
#
# Compile-time processing hook.
#
# The $config parameter may be either a "filename.pl" (naming scheme which
# properly intuits it has to be a syntactically correct perl file which can
# be safely required) or a HASH reference.
#
# It it's a file, it is required in the dynamic CGI::MxScreen::cf namepace.
#
# If it's a HASH reference, each key sets a configuration variable.
# Furthermore, if the special FILE key is set, its value is taken as
# a file to be required before setting. This allows one to refine a common
# configuration for specific purposes.
#
# NB: Any '-' in front of the keys is stripped.  This enables the use of
# Perl's automatic quoting of -words.
#
# Therefore, the following are strictly equivalent:
#
#	use CGI::MxScreen::Config "./filename.pl";
#	use CGI::MxScreen::Config ({ -FILE => "./filename.pl" });
#
sub import {
	DFEATURE my $f_;
	my ($module, $config) = @_;
	$CGI::MxScreen::cf::bindir = $Bin;
	set_defaults();
	if (ref $config eq 'HASH') {
		my $file = $config->{'-FILE'} || $config->{'FILE'};
		load_file($file) if $file;			# File guaranteed to load before
		load_hash($config);					# any hash configuration is made
	} else {
		load_file($config);
	}
	configure();							# Runtime configuration
	return DVOID;
}

#
# load_file
#
# Load specified file
#
sub load_file {
	DFEATURE my $f_;
	my ($file) = @_;
	$file = "$Bin/$file" unless $file =~ m|^/|;
	logdie "can't read config file $file: $!" unless -r $file;
	$CGI::MxScreen::cf::config = $file;
	eval {
		local $SIG{__DIE__};
		local $SIG{__WARN__};
		package CGI::MxScreen::cf;
		require $CGI::MxScreen::cf::config;	# Keep trace of which one it was
	};
	logdie "can't load config file $file: $@" if chop $@;
	$CGI::MxScreen::cf::_configured = 0;	# Signals: don't set defaults
	return DVOID;
}

#
# load_hash
#
# Load all keys as CGI::MxScreen::cf scalar variables.
# Leading '-' in keys are stripped and FILE is skipped.
#
sub load_hash {
	DFEATURE my $f_;
	my ($h) = @_;
	while (my ($var, $value) = each %$h) {
		$var =~ s/^-//;
		next if $var eq 'FILE';
		no strict 'refs';
		${"CGI::MxScreen::cf::$var"} = $value;
	}
	$CGI::MxScreen::cf::_configured = 0;	# Signals: don't set defaults
	return DVOID;
}

#
# set_defaults
#
# Set configuration defaults.
#
sub set_defaults {
	DFEATURE my $f_;
	{
		package CGI::MxScreen::cf;
		no strict 'vars';

		$fatals_to_browser = 1;
		$disable_upload = 1;
		$view_source = 1;
		$mx_check_vars = 1;
		$mx_buffer_stdout = 1;
	}
	return DVOID;
}

#
# is_configured
#
# Returns true when configuration was performed.
#
sub is_configured {
	DFEATURE my $f_;
	return DVAL
		defined $CGI::MxScreen::cf::_configured &&
		$CGI::MxScreen::cf::_configured;
}

#
# configure
#
# Perform runtime configuration.
# This routine is called by import(), i.e. is ususally run during compilation
# of the main script.
#
sub configure {
	DFEATURE my $f_;

	#
	# If they did not perform any customization already and called us
	# directly, set some defaults.
	#

	if (defined $CGI::MxScreen::cf::_configured) {
		return DVOID if $CGI::MxScreen::cf::_configured;
	} else {
		set_defaults();
	}

	#
	# "chdir" is the first thing we process
	#
	if (length $CGI::MxScreen::cf::chdir) {
		my $dir = $CGI::MxScreen::cf::chdir;
		logdie "invalid chdir directory $dir: $!" unless -d $dir;
		$dir = $1 if $dir =~ /^(.*)$/;		# untaint
		chdir($dir) || logdie "cannot chdir to '$dir': $!";
	}

	#
	# "path" -- recall we should be running in taint mode
	#

	$ENV{PATH} = $CGI::MxScreen::cf::path if defined $CGI::MxScreen::cf::path;

	#
	# "libpath" is handled via a manual "use lib" import.
	#

	if (length $CGI::MxScreen::cf::libpath) {
		my @libs = split(/:/, $CGI::MxScreen::cf::libpath);
		require lib;
		lib->import(@libs);
	}

	#
	# Log::Agent -- Application level dispatch
	#

	require Log::Agent::Driver::File;

	#
	# "logdir" and "logfile" (may use %s for $me)
	#

	my $me;
	($me = $0) =~ s|.*/(.*)|$1|;
	$me = $1 if $me =~ /^(.*)$/;			# untaint

	my ($logfile, $logchannels);
	my $logdir = $CGI::MxScreen::cf::logdir || ".";

	logdie "invalid logging directory $logdir: $!" unless -d $logdir;
	$logdir = $1 if $logdir =~ /^(.*)$/;	# untaint

	if (defined $CGI::MxScreen::cf::logfile) {
		my $fmt = $CGI::MxScreen::cf::logfile;
		$fmt = $1 if $fmt =~ /^(.*)$/;		# untaint
		$logfile = $fmt eq "" ? "$me.log" : sprintf($fmt, $me);
		$logfile = "$logdir/$logfile" unless $logfile =~ m|^/|;
	}

	#
	# "logchannels" (may use %s for $me)
	#

	if (ref $CGI::MxScreen::cf::logchannels) {
		my $channels = $CGI::MxScreen::cf::logchannels;
		$channels = {
			'error'		=> "%s.err",
			'output'	=> "%s.out",
			'debug'		=> "%s.dbg",
		} unless scalar keys %$channels;
		foreach my $chan (keys %$channels) {
			my $file = sprintf($channels->{$chan}, $me);
			$file = "$logdir/$file" unless $file =~ m|^/|;
			$channels->{$chan} = $file;
		}
		$logchannels = $channels;
	}

	#
	# "log_maxsize" and "log_maxtime"
	#

	my $rotate;

	if (
		defined $CGI::MxScreen::cf::log_maxsize ||
		defined $CGI::MxScreen::cf::log_maxtime
	) {
		require Log::Agent::Rotate;
		my $keep = 7;
		$keep = $CGI::MxScreen::cf::log_backlog
			if defined $CGI::MxScreen::cf::log_backlog &&
			$CGI::MxScreen::cf::log_backlog >= 0;

		$rotate = Log::Agent::Rotate->make(
			-backlog		=> $keep,
			-unzipped		=> $keep,	# don't gzip, could cause some delay
			-is_alone		=> 0,
			-max_size		=> $CGI::MxScreen::cf::log_maxsize,
			-max_time		=> $CGI::MxScreen::cf::log_maxtime,
			-single_host	=> $CGI::MxScreen::cf::log_single_host,
		);
	}

	#
	# Create driver
	#

	my $driver;

	if (defined $logfile || defined $logchannels) {
		my @dest = defined $logchannels ?
			(-channels	=> $logchannels) :
			(-file		=> $logfile);
		$driver = Log::Agent::Driver::File->make(
			-prefix		=> $me,
			-stampfmt	=> $CGI::MxScreen::cf::logstamp || "own",
			-showpid	=> 1,
			-duperr		=> 1,
			-rotate		=> $rotate,
			@dest,
		);
	} else {
		# Logs will go to the server, force the "date" format
		$driver = Log::Agent::Driver::File->make(
			-prefix     => $me,
			-stampfmt   => "date",
			-showpid    => 1,
			-file       => '>&STDERR',
			-magic_open => 1,
		);
	}

	#
	# "loglevel" and "logdebug"
	#
	logconfig(-prefix => $me, -driver => $driver,
		-trace => $CGI::MxScreen::cf::loglevel || 0,
		-debug => $CGI::MxScreen::cf::logdebug || 0,
	);

	#
	# CGI::MxScreen logging
	#

	if (defined $CGI::MxScreen::cf::mx_logfile) {
		require Log::Agent::Logger;
		require Log::Agent::Channel::File;

		my $fmt = $CGI::MxScreen::cf::mx_logfile;
		$fmt = $1 if $fmt =~ /^(.*)$/;		# untaint
		my $file = $fmt eq "" ? "$me.log" : sprintf($fmt, $me);
		$file = "$logdir/$file" unless $file =~ m|^/|;

		my $channel = Log::Agent::Channel::File->make(
			-prefix		=> $me,
			-stampfmt	=> "own",
			-filename	=> $file,
			-rotate		=> $rotate,
		);

		$CGI::MxScreen::Config::LOG = Log::Agent::Logger->make(
			-channel	=> $channel,
			-max_prio	=> $CGI::MxScreen::cf::mx_loglevel || "debug",
		);
	}

	#
	# CGI::MxScreen session handling
	#

	if (defined $CGI::MxScreen::cf::mx_serializer) {
		my $ary = $CGI::MxScreen::cf::mx_serializer;
		if (ref $ary eq 'ARRAY') {
			my ($type, @args) = @$ary;
			$type =~ s/^\+/CGI::MxScreen::Serializer::/;
			eval "require $type";
			if (chop $@) {
				logerr "can't load $type: $@";
			} else {
				my $s = $CGI::MxScreen::Config::SERIALIZER = $type->make(@args);
				logwarn "mx_serializer defined an unexpected type $s"
					unless $s->isa("CGI::MxScreen::Serializer");
			}
		} else {
			logerr "ignoring mx_serializer: not an arrray ref!";
		}
	}

	unless (ref $CGI::MxScreen::Config::SERIALIZER) {
		require CGI::MxScreen::Serializer::Storable;
		$CGI::MxScreen::Config::SERIALIZER =
			CGI::MxScreen::Serializer::Storable->make();
	}

	if (defined $CGI::MxScreen::cf::mx_medium) {
		my $ary = $CGI::MxScreen::cf::mx_medium;
		if (ref $ary eq 'ARRAY') {
			my ($type, @args) = @$ary;
			$type =~ s/^\+/CGI::MxScreen::Session::Medium::/;
			eval "require $type";
			if (chop $@) {
				logerr "can't load $type: $@";
			} else {
				my $s = $CGI::MxScreen::Config::MEDIUM = $type->make(@args);
				logwarn "mx_medium defined an unexpected type $s"
					unless $s->isa("CGI::MxScreen::Session::Medium");
			}
		} else {
			logerr "ignoring mx_medium: not an arrray ref!";
		}
	}

	unless (ref $CGI::MxScreen::Config::MEDIUM) {
		require CGI::MxScreen::Session::Medium::Browser;
		$CGI::MxScreen::Config::MEDIUM =
			CGI::MxScreen::Session::Medium::Browser->make(
				-key	=> "that's my unsecure _key_"
			);
	}

	#
	# Carp::Datum configuration
	#

	Carp::Datum->import($CGI::MxScreen::cf::datum_on ? "on" : "off");
	if (defined $CGI::MxScreen::cf::datum_config) {
		my $datum_config = $CGI::MxScreen::cf::datum_config;
		$datum_config = "$Bin/$datum_config" unless $datum_config =~ m|^/|;
		DLOAD_CONFIG(-file => $datum_config) if -f $datum_config;
	}

	#
	# "disable_upload" and "post_max"
	#

	$CGI::DISABLE_UPLOADS = 1
		if $CGI::MxScreen::cf::disable_upload;
	$CGI::POST_MAX = $CGI::MxScreen::cf::post_max || 1024 * 1024;

	#
	# "fatals_to_browser" and "fatal_message"
	#

	if ($CGI::MxScreen::cf::fatals_to_browser) {
		require CGI::Carp;
		CGI::Carp->import('fatalsToBrowser');
		CGI::Carp::set_message($CGI::MxScreen::cf::fatal_message)
			if $CGI::MxScreen::cf::fatal_message;
	}

	#
	# "cgi_carpout"
	#

	if (-t STDIN && -t STDOUT) {		# Debug mode anyway!
		require CGI::Carp;
		CGI::Carp::carpout(\*STDOUT);
	} elsif ($CGI::MxScreen::cf::cgi_carpout && defined $logfile) {
		local *FH;
		if (open(FH, ">> $logfile")) {
			select((select(FH), $| = 1)[0]);
			CGI::Carp::carpout(\*FH);
		} else {
			CGI::Carp::carp("can't append to $logfile: $!");
		}
	}

	#
	# "view_source" -- produce <VIEW SOURCE>-able HTML
	#

	$\ = "\n" unless
		defined($CGI::MxScreen::cf::view_source) &&
		!$CGI::MxScreen::cf::view_source;

	$CGI::MxScreen::cf::_configured = 1;		# Mark as "configured"

	return DVOID;
}

1;	# for require