← Index
NYTProf Performance Profile   « line view »
For ./view
  Run on Fri Jul 31 18:42:36 2015
Reported on Fri Jul 31 18:48:13 2015

Filename/var/www/foswikidev/core/lib/Foswiki/Configure/Load.pm
StatementsExecuted 4581 statements in 20.2ms
Subroutines
Calls P F Exclusive
Time
Inclusive
Time
Subroutine
876118.95ms12.1msFoswiki::Configure::Load::::_expandValueFoswiki::Configure::Load::_expandValue (recurses: max depth 5, inclusive time 23.8ms)
1118.30ms20.7msFoswiki::Configure::Load::::readConfigFoswiki::Configure::Load::readConfig
1114.49ms32.8msFoswiki::Configure::Load::::BEGIN@26Foswiki::Configure::Load::BEGIN@26
1114.31ms7.29msFoswiki::Configure::Load::::BEGIN@24Foswiki::Configure::Load::BEGIN@24
1114.02ms8.75msFoswiki::Configure::Load::::BEGIN@21Foswiki::Configure::Load::BEGIN@21
876422.45ms12.2msFoswiki::Configure::Load::::expandValueFoswiki::Configure::Load::expandValue (recurses: max depth 5, inclusive time 26.2ms)
1111.78ms1.96msFoswiki::Configure::Load::::BEGIN@22Foswiki::Configure::Load::BEGIN@22
3411762µs762µsFoswiki::Configure::Load::::_handleExpandFoswiki::Configure::Load::_handleExpand
11119µs39µsFoswiki::Configure::Load::::BEGIN@17Foswiki::Configure::Load::BEGIN@17
11115µs42µsFoswiki::Configure::Load::::BEGIN@19Foswiki::Configure::Load::BEGIN@19
11113µs21µsFoswiki::Configure::Load::::BEGIN@18Foswiki::Configure::Load::BEGIN@18
11112µs46µsFoswiki::Configure::Load::::BEGIN@20Foswiki::Configure::Load::BEGIN@20
11112µs12µsFoswiki::Configure::Load::::_workOutOSFoswiki::Configure::Load::_workOutOS
11111µs48µsFoswiki::Configure::Load::::BEGIN@29Foswiki::Configure::Load::BEGIN@29
1118µs8µsFoswiki::Configure::Load::::BEGIN@23Foswiki::Configure::Load::BEGIN@23
0000s0sFoswiki::Configure::Load::::_bootstrapSiteSettingsFoswiki::Configure::Load::_bootstrapSiteSettings
0000s0sFoswiki::Configure::Load::::_bootstrapStoreSettingsFoswiki::Configure::Load::_bootstrapStoreSettings
0000s0sFoswiki::Configure::Load::::bootstrapConfigFoswiki::Configure::Load::bootstrapConfig
0000s0sFoswiki::Configure::Load::::bootstrapWebSettingsFoswiki::Configure::Load::bootstrapWebSettings
0000s0sFoswiki::Configure::Load::::expandedFoswiki::Configure::Load::expanded
0000s0sFoswiki::Configure::Load::::findDependenciesFoswiki::Configure::Load::findDependencies
0000s0sFoswiki::Configure::Load::::setBootstrapFoswiki::Configure::Load::setBootstrap
Call graph for these subroutines as a Graphviz dot language file.
Line State
ments
Time
on line
Calls Time
in subs
Code
1# See bottom of file for license and copyright information
2
3=begin TML
4
5---+ package Foswiki::Configure::Load
6
7Handling for loading configuration information (Foswiki.spec, Config.spec and
8LocalSite.cfg) as efficiently and flexibly as possible.
9
10This reads *values* from these files and does *not* parse the
11structured comments or build a spec database. For that, see LoadSpec.pm
12
13=cut
14
15package Foswiki::Configure::Load;
16
17240µs258µs
# spent 39µs (19+19) within Foswiki::Configure::Load::BEGIN@17 which was called: # once (19µs+19µs) by Foswiki::BEGIN@53 at line 17
use strict;
# spent 39µs making 1 call to Foswiki::Configure::Load::BEGIN@17 # spent 19µs making 1 call to strict::import
18241µs228µs
# spent 21µs (13+7) within Foswiki::Configure::Load::BEGIN@18 which was called: # once (13µs+7µs) by Foswiki::BEGIN@53 at line 18
use warnings;
# spent 21µs making 1 call to Foswiki::Configure::Load::BEGIN@18 # spent 7µs making 1 call to warnings::import
19237µs270µs
# spent 42µs (15+28) within Foswiki::Configure::Load::BEGIN@19 which was called: # once (15µs+28µs) by Foswiki::BEGIN@53 at line 19
use Cwd qw( abs_path );
# spent 42µs making 1 call to Foswiki::Configure::Load::BEGIN@19 # spent 28µs making 1 call to Exporter::import
20234µs279µs
# spent 46µs (12+33) within Foswiki::Configure::Load::BEGIN@20 which was called: # once (12µs+33µs) by Foswiki::BEGIN@53 at line 20
use Assert;
# spent 46µs making 1 call to Foswiki::Configure::Load::BEGIN@20 # spent 33µs making 1 call to Exporter::import
212132µs28.80ms
# spent 8.75ms (4.02+4.73) within Foswiki::Configure::Load::BEGIN@21 which was called: # once (4.02ms+4.73ms) by Foswiki::BEGIN@53 at line 21
use Encode;
# spent 8.75ms making 1 call to Foswiki::Configure::Load::BEGIN@21 # spent 52µs making 1 call to Exporter::import
222199µs21.99ms
# spent 1.96ms (1.78+173µs) within Foswiki::Configure::Load::BEGIN@22 which was called: # once (1.78ms+173µs) by Foswiki::BEGIN@53 at line 22
use File::Basename;
# spent 1.96ms making 1 call to Foswiki::Configure::Load::BEGIN@22 # spent 38µs making 1 call to Exporter::import
23234µs18µs
# spent 8µs within Foswiki::Configure::Load::BEGIN@23 which was called: # once (8µs+0s) by Foswiki::BEGIN@53 at line 23
use File::Spec;
# spent 8µs making 1 call to Foswiki::Configure::Load::BEGIN@23
242141µs28.47ms
# spent 7.29ms (4.31+2.98) within Foswiki::Configure::Load::BEGIN@24 which was called: # once (4.31ms+2.98ms) by Foswiki::BEGIN@53 at line 24
use POSIX qw(locale_h);
# spent 7.29ms making 1 call to Foswiki::Configure::Load::BEGIN@24 # spent 1.17ms making 1 call to POSIX::import
25
262141µs132.8ms
# spent 32.8ms (4.49+28.3) within Foswiki::Configure::Load::BEGIN@26 which was called: # once (4.49ms+28.3ms) by Foswiki::BEGIN@53 at line 26
use Foswiki::Configure::FileUtil;
# spent 32.8ms making 1 call to Foswiki::Configure::Load::BEGIN@26
27
28# Enable to trace auto-configuration (Bootstrap)
2923.58ms284µs
# spent 48µs (11+37) within Foswiki::Configure::Load::BEGIN@29 which was called: # once (11µs+37µs) by Foswiki::BEGIN@53 at line 29
use constant TRAUTO => 1;
# spent 48µs making 1 call to Foswiki::Configure::Load::BEGIN@29 # spent 36µs making 1 call to constant::import
30
31# This should be the one place in Foswiki that knows the syntax of valid
32# configuration item keys. Only simple scalar hash keys are supported.
33#
3417µsour $ITEMREGEX = qr/(?:\{(?:'(?:\\.|[^'])+'|"(?:\\.|[^"])+"|[A-Za-z0-9_]+)\})+/;
35
36# Generic booleans, used in some older LSC's
371400nsour $TRUE = 1;
381200nsour $FALSE = 0;
39
40# Configuration items that have been deprecated and must be mapped to
41# new configuration items. The value is mapped unchanged.
4216µsour %remap = (
43 '{StoreImpl}' => '{Store}{Implementation}',
44 '{AutoAttachPubFiles}' => '{RCS}{AutoAttachPubFiles}',
45 '{QueryAlgorithm}' => '{Store}{QueryAlgorithm}',
46 '{SearchAlgorithm}' => '{Store}{SearchAlgorithm}',
47 '{Site}{CharSet}' => '{Store}{Encoding}',
48 '{RCS}{FgrepCmd}' => '{Store}{FgrepCmd}',
49 '{RCS}{EgrepCmd}' => '{Store}{EgrepCmd}',
50 '{RCS}{overrideUmask}' => '{Store}{overrideUmask}',
51 '{RCS}{dirPermission}' => '{Store}{dirPermission}',
52 '{RCS}{filePermission}' => '{Store}{filePermission}',
53 '{RCS}{WorkAreaDir}' => '{Store}{WorkAreaDir}'
54);
55
56
# spent 12µs within Foswiki::Configure::Load::_workOutOS which was called: # once (12µs+0s) by Foswiki::Configure::Load::readConfig at line 148
sub _workOutOS {
571600ns unless ( $Foswiki::cfg{DetailedOS} ) {
5815µs $Foswiki::cfg{DetailedOS} = $^O;
591500ns unless ( $Foswiki::cfg{DetailedOS} ) {
60
61 # SMELL: the perlvar doc for $^O says "The value is identical
62 # to $Config{'osname'}" so this would appear redundant.
63 require Config;
64 $Foswiki::cfg{DetailedOS} = $Config::Config{'osname'};
65
66 # SMELL: is it really worth continuing if we still can't
67 # work it out? Proceed with a null string unless someone knows
68 # better.
69 }
70 }
711300ns return if $Foswiki::cfg{OS};
7219µs if ( $Foswiki::cfg{DetailedOS} =~ m/darwin/i ) { # MacOS X
73 $Foswiki::cfg{OS} = 'UNIX';
74 }
75 elsif ( $Foswiki::cfg{DetailedOS} =~ m/Win/i ) {
76 $Foswiki::cfg{OS} = 'WINDOWS';
77 }
78 elsif ( $Foswiki::cfg{DetailedOS} =~ m/vms/i ) {
79 $Foswiki::cfg{OS} = 'VMS';
80 }
81 elsif ( $Foswiki::cfg{DetailedOS} =~ m/bsdos/i ) {
82 $Foswiki::cfg{OS} = 'UNIX';
83 }
84 elsif ( $Foswiki::cfg{DetailedOS} =~ m/solaris/i ) {
85 $Foswiki::cfg{OS} = 'UNIX';
86 }
87 elsif ( $Foswiki::cfg{DetailedOS} =~ m/dos/i ) {
88 $Foswiki::cfg{OS} = 'DOS';
89 }
90 elsif ( $Foswiki::cfg{DetailedOS} =~ m/^MacOS$/i ) {
91
92 # MacOS 9 or earlier
93 $Foswiki::cfg{OS} = 'MACINTOSH';
94 }
95 elsif ( $Foswiki::cfg{DetailedOS} =~ m/os2/i ) {
96 $Foswiki::cfg{OS} = 'OS2';
97 }
98 else {
99
100 # Erm.....
1011800ns $Foswiki::cfg{OS} = 'UNIX';
102 }
103}
104
105=begin TML
106
107---++ StaticMethod readConfig([$noexpand][,$nospec][,$config_spec][,$noLocal)
108
109In normal Foswiki operations as a web server this method is called by the
110=BEGIN= block of =Foswiki.pm=. However, when benchmarking/debugging it can be
111replaced by custom code which sets the configuration hash. To prevent us from
112overriding the custom code again, we use an "unconfigurable" key
113=$cfg{ConfigurationFinished}= as an indicator.
114
115Note that this method is called by Foswiki and configure, and normally reads
116=Foswiki.spec= to get defaults. Other spec files (those for extensions) are
117*not* read unless the $config_spec flag is set.
118
119The assumption is that =configure= will be run when an extension is installed,
120and that will add the config values to LocalSite.cfg, so no defaults are
121needed. Foswiki.spec is still read because so much of the core code doesn't
122provide defaults, and it would be silly to have them in two places anyway.
123
124 * =$noexpand= - suppress expansion of $Foswiki vars embedded in
125 values.
126 * =$nospec= - can be set when the caller knows that Foswiki.spec
127 has already been read.
128 * =$config_spec= - if set, will also read Config.spec files located
129 using the standard methods (iff !$nospec). Slow.
130 * =$noLocal= - if set, Load will not re-read an existing LocalSite.cfg.
131 this is needed when testing the bootstrap. If it rereads an existing
132 config, it overlays all the bootstrapped settings.
133=cut
134
135
# spent 20.7ms (8.30+12.4) within Foswiki::Configure::Load::readConfig which was called: # once (8.30ms+12.4ms) by Foswiki::BEGIN@176 at line 355 of /var/www/foswikidev/core/lib/Foswiki.pm
sub readConfig {
1361900ns my ( $noexpand, $nospec, $config_spec, $noLocal ) = @_;
137
138 # To prevent us from overriding the custom code in test mode
1391600ns return 1 if $Foswiki::cfg{ConfigurationFinished};
140
141 # Assume LocalSite.cfg is valid - will be set false if errors detected.
1421200ns my $validLSC = 1;
143
144 # Read Foswiki.spec and LocalSite.cfg
145 # (Suppress Foswiki.spec if already read)
146
147 # Old configs might not bootstrap the OS settings, so set if needed.
14812µs112µs _workOutOS() unless ( $Foswiki::cfg{OS} && $Foswiki::cfg{DetailedOS} );
# spent 12µs making 1 call to Foswiki::Configure::Load::_workOutOS
149
1501200ns my @files;
1511800ns unless ($nospec) {
152 push @files, 'Foswiki.spec';
153 }
1541400ns if ( !$nospec && $config_spec ) {
155 foreach my $dir (@INC) {
156 foreach my $subdir ( 'Foswiki/Plugins', 'Foswiki/Contrib' ) {
157 my $d;
158 next unless opendir( $d, "$dir/$subdir" );
159 my %read;
160 foreach
161 my $extension ( grep { !/^\./ && !/^Empty/ } readdir $d )
162 {
163 next if $read{$extension};
164 $extension =~ m/(.*)/; # untaint
165 my $file = "$dir/$subdir/$1/Config.spec";
166 next unless -e $file;
167 push( @files, $file );
168 $read{$extension} = 1;
169 }
170 closedir($d);
171 }
172 }
173 }
1741500ns unless ($noLocal) {
175 push @files, 'LocalSite.cfg';
176 }
177
17811µs for my $file (@files) {
17923.30ms my $return = do $file;
180
18125µs unless ( defined $return && $return eq '1' ) {
182
183 my $errorMessage;
184 if ($@) {
185 $errorMessage = "Failed to parse $file: $@";
186 warn "couldn't parse $file: $@" if $@;
187 }
188 next if ( !DEBUG && ( $file =~ m/Config\.spec$/ ) );
189 if ( not defined $return ) {
190 unless ( $! == 2 && $file eq 'LocalSite.cfg' ) {
191
192 # LocalSite.cfg doesn't exist, which is OK
193 warn "couldn't do $file: $!";
194 $errorMessage = "Could not do $file: $!";
195 }
196 $validLSC = 0;
197 }
198
199 # Pointless (says CDot), Config.spec does not need 1; at the end
200 #elsif ( not $return eq '1' ) {
201 # print STDERR
202 # "Running file $file returned unexpected results: $return \n";
203 #}
204 if ($errorMessage) {
205 die <<GOLLYGOSH;
206Content-type: text/plain
207
208$errorMessage
209Please inform the site admin.
210GOLLYGOSH
211 exit 1;
212 }
213 }
214 }
215
216 # Patch deprecated config settings
217 # TODO: remove this in version 2.0
2181500ns if ( exists $Foswiki::cfg{StoreImpl} ) {
219 $Foswiki::cfg{Store}{Implementation} =
220 'Foswiki::Store::' . $Foswiki::cfg{StoreImpl};
221 delete $Foswiki::cfg{StoreImpl};
222 }
22314µs foreach my $el ( keys %remap ) {
224
225 # Only remap if the old key extsts, and the new key does NOT exist
22611323µs if ( ( eval("exists \$Foswiki::cfg$el") ) ) {
# spent 3µs executing statements in string eval # spent 3µs executing statements in string eval # spent 2µs executing statements in string eval # spent 2µs executing statements in string eval # spent 2µs executing statements in string eval # spent 2µs executing statements in string eval # spent 2µs executing statements in string eval # spent 2µs executing statements in string eval # spent 2µs executing statements in string eval # spent 2µs executing statements in string eval # spent 2µs executing statements in string eval
227138µs eval( <<CODE );
# spent 5µs executing statements in string eval
228\$Foswiki::cfg$remap{$el}=\$Foswiki::cfg$el unless ( exists \$Foswiki::cfg$remap{$el} );
229delete \$Foswiki::cfg$el;
230CODE
2311300ns print STDERR "REMAP failed $@" if ($@);
232 }
233 }
234
235 # Expand references to $Foswiki::cfg vars embedded in the values of
236 # other $Foswiki::cfg vars.
23713µs112.1ms expandValue( \%Foswiki::cfg ) unless $noexpand;
# spent 12.1ms making 1 call to Foswiki::Configure::Load::expandValue
238
2391800ns $Foswiki::cfg{ConfigurationFinished} = 1;
240
24111µs if ( $^O eq 'MSWin32' ) {
242
243 #force paths to use '/'
244 $Foswiki::cfg{PubDir} =~ s|\\|/|g;
245 $Foswiki::cfg{DataDir} =~ s|\\|/|g;
246 $Foswiki::cfg{ToolsDir} =~ s|\\|/|g;
247 $Foswiki::cfg{ScriptDir} =~ s|\\|/|g;
248 $Foswiki::cfg{TemplateDir} =~ s|\\|/|g;
249 $Foswiki::cfg{LocalesDir} =~ s|\\|/|g;
250 $Foswiki::cfg{WorkingDir} =~ s|\\|/|g;
251 }
252
253 # Alias TWiki cfg to Foswiki cfg for plugins and contribs
25411µs *TWiki::cfg = \%Foswiki::cfg;
255
256 # Add explicit {Site}{CharSet} for older extensions. Default to utf-8.
257 # Explanation is in http://foswiki.org/Tasks/Item13435
2581800ns $Foswiki::cfg{Site}{CharSet} = 'utf-8';
259
260 # Explicit return true if we've completed the load
26114µs return $validLSC;
262}
263
264=begin TML
265
266---++ StaticMethod expanded($value) -> $expanded
267
268Given a value of a configuration item, expand references to
269$Foswiki::cfg configuration items within strings in the value.
270
271If an embedded $Foswiki::cfg reference is not defined, it will
272be expanded as 'undef'.
273
274=cut
275
276sub expanded {
277 my $val = shift;
278 return undef unless defined $val;
279 expandValue($val);
280 return $val;
281}
282
283=begin TML
284
285---++ StaticMethod expandValue($datum [, $mode])
286
287Expands references to Foswiki configuration items which occur in the
288values configuration items contained within the datum, which may be a
289hash or array reference, or a scalar value. The replacement is done in-place.
290
291$mode - How to handle undefined values:
292 * false: 'undef' (string) is returned when an undefined value is
293 encountered.
294 * 1 : return undef if any undefined value is encountered.
295 * 2 : return '' for any undefined value (including embedded)
296 * 3 : die if an undefined value is encountered.
297
298=cut
299
300
# spent 12.2ms (2.45+9.71) within Foswiki::Configure::Load::expandValue which was called 876 times, avg 14µs/call: # 788 times (2.20ms+-2.20ms) by Foswiki::Configure::Load::_expandValue at line 312, avg 0s/call # 86 times (234µs+-234µs) by Foswiki::Configure::Load::_expandValue at line 315, avg 0s/call # once (4µs+12.1ms) by Foswiki::Configure::Load::readConfig at line 237 # once (7µs+7µs) by Foswiki::Logger::PlainFile::_getLogsForLevel at line 270 of /var/www/foswikidev/core/lib/Foswiki/Logger/PlainFile.pm
sub expandValue {
30187674µs my $undef;
302876646µs87612.1ms _expandValue( $_[0], ( $_[1] || 0 ), $undef );
# spent 35.9ms making 876 calls to Foswiki::Configure::Load::_expandValue, avg 41µs/call, recursion: max depth 5, sum of overlapping time 23.8ms
303
3048766.72ms $_[0] = undef if ($undef);
305}
306
307# $_[0] - value being expanded
308# $_[1] - $mode
309# $_[2] - $undef (return)
310
# spent 12.1ms (8.95+3.20) within Foswiki::Configure::Load::_expandValue which was called 876 times, avg 14µs/call: # 876 times (8.95ms+3.20ms) by Foswiki::Configure::Load::expandValue at line 302, avg 14µs/call
sub _expandValue {
3118761.69ms if ( ref( $_[0] ) eq 'HASH' ) {
3121961.18ms7880s expandValue( $_, $_[1] ) foreach ( values %{ $_[0] } );
# spent 25.4ms making 788 calls to Foswiki::Configure::Load::expandValue, avg 32µs/call, recursion: max depth 5, sum of overlapping time 25.4ms
313 }
314 elsif ( ref( $_[0] ) eq 'ARRAY' ) {
3156102µs860s expandValue( $_, $_[1] ) foreach ( @{ $_[0] } );
# spent 813µs making 86 calls to Foswiki::Configure::Load::expandValue, avg 9µs/call, recursion: max depth 5, sum of overlapping time 813µs
316
317 # Can't do this, because Windows uses an object (Regexp) for regular
318 # expressions.
319 # } elsif (ref($_[0])) {
320 # die("Can't handle a ".ref($_[0]));
321 }
322 else {
3233450µs34762µs 1 while ( defined( $_[0] )
# spent 762µs making 34 calls to Foswiki::Configure::Load::_handleExpand, avg 22µs/call
324 && $_[0] =~
325674874µs s/(\$Foswiki::cfg$ITEMREGEX)/_handleExpand($1, @_[1,2])/ges );
326 }
327}
328
329# Used to expand the $Foswiki::cfg variable in the expand* routines.
330# $_[0] - $item
331# $_[1] - $mode
332# $_[2] - $undef
333
# spent 762µs within Foswiki::Configure::Load::_handleExpand which was called 34 times, avg 22µs/call: # 34 times (762µs+0s) by Foswiki::Configure::Load::_expandValue at line 323, avg 22µs/call
sub _handleExpand {
33434631µs my $val = eval( $_[0] );
# spent 34µs executing statements in 14 string evals (merged) # spent 12µs executing statements in 4 string evals (merged) # spent 10µs executing statements in 4 string evals (merged) # spent 9µs executing statements in 4 string evals (merged) # spent 8µs executing statements in 4 string evals (merged) # spent 5µs executing statements in 2 string evals (merged) # spent 3µs executing statements in string eval # spent 2µs executing statements in string eval
335345µs die "Error expanding $_[0]: $@" if ($@);
336
33734105µs return $val if ( defined $val );
338 return 'undef' if ( !$_[1] );
339 return '' if ( $_[1] == 2 );
340 die "Undefined value in expanded string $_[0]\n" if ( $_[1] == 3 );
341 $_[2] = 1;
342 return '';
343}
344
345=begin TML
346
347---++ StaticMethod setBootstrap()
348
349This routine is called to initialize the bootstrap process. It sets the list of
350configuration parameters that will need to be set and "protected" during bootstrap.
351
352If any keys will be set during bootstrap / initial creation of LocalSite.cfg, they
353should be added here so that they are preserved when the %Foswiki::cfg hash is
354wiped and re-initialized from the Foswiki spec.
355
356=cut
357
358sub setBootstrap {
359
360 # Bootstrap works out the correct values of these keys
361 my @BOOTSTRAP =
362 qw( {DataDir} {DefaultUrlHost} {DetailedOS} {OS} {PubUrlPath} {ToolsDir} {WorkingDir}
363 {PubDir} {TemplateDir} {ScriptDir} {ScriptUrlPath} {ScriptUrlPaths}{view}
364 {ScriptSuffix} {LocalesDir} {Store}{Implementation}
365 {Store}{SearchAlgorithm} {Site}{Locale} );
366
367 $Foswiki::cfg{isBOOTSTRAPPING} = 1;
368 push( @{ $Foswiki::cfg{BOOTSTRAP} }, @BOOTSTRAP );
369}
370
371=begin TML
372
373---++ StaticMethod bootstrapConfig()
374
375This routine is called from Foswiki.pm BEGIN block to discover the mandatory
376settings for operation when a LocalSite.cfg could not be found.
377
378=cut
379
380sub bootstrapConfig {
381
382 print STDERR "AUTOCONFIG: Bootstrap Phase 1: "
383 . Data::Dumper::Dumper( \%ENV )
384 if (TRAUTO);
385
386 # Failed to read LocalSite.cfg
387 # Clear out $Foswiki::cfg to allow variable expansion to work
388 # when reloading Foswiki.spec et al.
389 # SMELL: have to keep {Engine} as this is defined by the
390 # script (smells of a hack).
391 %Foswiki::cfg = ( Engine => $Foswiki::cfg{Engine} );
392
393 # Try to create $Foswiki::cfg in a minimal configuration,
394 # using paths and URLs relative to this request. If URL
395 # rewriting is happening in the web server this is likely
396 # to go down in flames, but it gives us the best chance of
397 # recovering. We need to guess values for all the vars that
398
399 # would trigger "undefined" errors
400 my $bin;
401 my $script = '';
402 if ( defined $ENV{FOSWIKI_SCRIPTS} ) {
403 $bin = $ENV{FOSWIKI_SCRIPTS};
404 }
405 else {
406 eval('require FindBin');
407 die "Could not load FindBin to support configuration recovery: $@"
408 if $@;
409 FindBin::again(); # in case we are under mod_perl or similar
410 $FindBin::Bin =~ m/^(.*)$/;
411 $bin = $1;
412 $FindBin::Script =~ m/^(.*)$/;
413 $script = $1;
414 }
415
416 print STDERR "AUTOCONFIG: Found Bin dir: "
417 . Encode::decode_utf8($bin)
418 . ", Script name: $script using FindBin\n"
419 if (TRAUTO);
420
421 $Foswiki::cfg{ScriptSuffix} = ( fileparse( $script, qr/\.[^.]*/ ) )[2];
422 $Foswiki::cfg{ScriptSuffix} = ''
423 if ( $Foswiki::cfg{ScriptSuffix} eq '.fcgi' );
424 print STDERR
425 "AUTOCONFIG: Found SCRIPT SUFFIX $Foswiki::cfg{ScriptSuffix} \n"
426 if ( TRAUTO && $Foswiki::cfg{ScriptSuffix} );
427
428 my %rel_to_root = (
429 DataDir => { dir => 'data', required => 0 },
430 LocalesDir => { dir => 'locale', required => 0 },
431 PubDir => { dir => 'pub', required => 0 },
432 ToolsDir => { dir => 'tools', required => 0 },
433 WorkingDir => {
434 dir => 'working',
435 required => 1,
436 validate_file => 'README'
437 },
438 TemplateDir => {
439 dir => 'templates',
440 required => 1,
441 validate_file => 'foswiki.tmpl'
442 },
443 ScriptDir => {
444 dir => 'bin',
445 required => 1,
446 validate_file => 'setlib.cfg'
447 }
448 );
449
450 # Note that we don't resolve x/../y to y, as this might
451 # confuse soft links
452 my $root = File::Spec->catdir( $bin, File::Spec->updir() );
453 $root =~ s{\\}{/}g;
454 my $fatal = '';
455 my $warn = '';
456 while ( my ( $key, $def ) = each %rel_to_root ) {
457 $Foswiki::cfg{$key} = File::Spec->rel2abs( $def->{dir}, $root );
458 $Foswiki::cfg{$key} = abs_path( $Foswiki::cfg{$key} );
459 ( $Foswiki::cfg{$key} ) = $Foswiki::cfg{$key} =~ m/^(.*)$/; # untaint
460
461 # Need to decode utf8 back to perl characters. The file path operations
462 # all worked with bytes, but Foswiki needs characters.
463 $Foswiki::cfg{$key} = Encode::decode_utf8( $Foswiki::cfg{$key} );
464
465 print STDERR "AUTOCONFIG: $key = $Foswiki::cfg{$key} \n"
466 if (TRAUTO);
467
468 if ( -d $Foswiki::cfg{$key} ) {
469 if ( $def->{validate_file}
470 && !-e "$Foswiki::cfg{$key}/$def->{validate_file}" )
471 {
472 $fatal .=
473"\n{$key} (guessed $Foswiki::cfg{$key}) $Foswiki::cfg{$key}/$def->{validate_file} not found";
474 }
475 }
476 elsif ( $def->{required} ) {
477 $fatal .= "\n{$key} (guessed $Foswiki::cfg{$key})";
478 }
479 else {
480 $warn .=
481 "\n * Note: {$key} could not be guessed. Set it manually!";
482 }
483 }
484
485 # Bootstrap the Site Locale and CharSet
486 _bootstrapSiteSettings();
487
488 # Bootstrap the store related settings.
489 _bootstrapStoreSettings();
490
491 if ($fatal) {
492 die <<EPITAPH;
493Unable to bootstrap configuration. LocalSite.cfg could not be loaded,
494and Foswiki was unable to guess the locations of the following critical
495directories: $fatal
496EPITAPH
497 }
498
499# Re-read Foswiki.spec *and Config.spec*. We need the Config.spec's
500# to get a true picture of our defaults (notably those from
501# JQueryPlugin. Without the Config.spec, no plugins get registered)
502# Don't load LocalSite.cfg if it exists (should normally not exist when bootstrapping)
503 Foswiki::Configure::Load::readConfig( 0, 0, 1, 1 );
504
505 _workOutOS();
506 print STDERR
507"AUTOCONFIG: Detected OS $Foswiki::cfg{OS}: DetailedOS: $Foswiki::cfg{DetailedOS} \n"
508 if (TRAUTO);
509
510 $Foswiki::cfg{isVALID} = 1;
511 Foswiki::Configure::Load::setBootstrap();
512
513 # Note: message is not I18N'd because there is no point; there
514 # is no localisation in a default cfg derived from Foswiki.spec
515 my $system_message = <<BOOTS;
516*WARNING !LocalSite.cfg could not be found* (This is normal for a new installation) %BR%
517This Foswiki is running using a bootstrap configuration worked
518out by detecting the layout of the installation.
519BOOTS
520
521 if ($warn) {
522 chomp $system_message;
523 $system_message .= $warn . "\n";
524 }
525 return ( $system_message || '' );
526
527}
528
529=begin TML
530
531---++ StaticMethod _bootstrapSiteSettings()
532
533Called by bootstrapConfig. This handles the {Site} settings.
534
535=cut
536
537sub _bootstrapSiteSettings {
538
539# Guess a locale first. This isn't necessarily used, but helps guess a CharSet, which is always used.
540
541 require locale;
542 $Foswiki::cfg{Site}{Locale} = setlocale(LC_CTYPE);
543
544 print STDERR
545"AUTOCONFIG: Set initial {Site}{Locale} to $Foswiki::cfg{Site}{Locale}\n";
546}
547
548=begin TML
549
550---++ StaticMethod _bootstrapStoreSettings()
551
552Called by bootstrapConfig. This handles the store specific settings. This in turn
553tests each Store Contib to determine if it's capable of bootstrapping.
554
555=cut
556
557sub _bootstrapStoreSettings {
558
559 # Ask each installed store to bootstrap itself.
560
561 my @stores = Foswiki::Configure::FileUtil::findPackages(
562 'Foswiki::Contrib::*StoreContrib');
563
564 foreach my $store (@stores) {
565 eval("require $store");
566 print STDERR $@ if ($@);
567 unless ($@) {
568 my $ok;
569 eval('$ok = $store->can(\'bootstrapStore\')');
570 if ($@) {
571 print STDERR $@;
572 }
573 else {
574 $store->bootstrapStore() if ($ok);
575 }
576 }
577 }
578
579 # Handle the common store settings managed by Core. Important ones
580 # guessed/checked here include:
581 # - $Foswiki::cfg{Store}{SearchAlgorithm}
582
583 # Set PurePerl search on Windows, or FastCGI systems.
584 if (
585 (
586 $Foswiki::cfg{Engine}
587 && $Foswiki::cfg{Engine} =~ m/(FastCGI|Apache)/
588 )
589 || $^O eq 'MSWin32'
590 )
591 {
592 $Foswiki::cfg{Store}{SearchAlgorithm} =
593 'Foswiki::Store::SearchAlgorithms::PurePerl';
594 print STDERR
595"AUTOCONFIG: Detected FastCGI, mod_perl or MS Windows. {Store}{SearchAlgorithm} set to PurePerl\n"
596 if (TRAUTO);
597 }
598 else {
599
600 # SMELL: The fork to `grep goes into a loop in the unit tests
601 # Not sure why, for now just default to pure perl bootstrapping
602 # in the unit tests.
603 if ( !$Foswiki::inUnitTestMode ) {
604
605 # Untaint PATH so we can check for grep on the path
606 my $x = $ENV{PATH} || '';
607 $x =~ m/^(.*)$/;
608 $ENV{PATH} = $1;
609 `grep -V 2>&1`;
610 if ($!) {
611 print STDERR
612"AUTOCONFIG: Unable to find a valid 'grep' on the path. Forcing PurePerl search\n"
613 if (TRAUTO);
614 $Foswiki::cfg{Store}{SearchAlgorithm} =
615 'Foswiki::Store::SearchAlgorithms::PurePerl';
616 }
617 else {
618 $Foswiki::cfg{Store}{SearchAlgorithm} =
619 'Foswiki::Store::SearchAlgorithms::Forking';
620 print STDERR
621 "AUTOCONFIG: {Store}{SearchAlgorithm} set to Forking\n"
622 if (TRAUTO);
623 }
624 $ENV{PATH} = $x; # re-taint
625 }
626 else {
627 $Foswiki::cfg{Store}{SearchAlgorithm} =
628 'Foswiki::Store::SearchAlgorithms::PurePerl';
629 }
630 }
631}
632
633=begin TML
634
635---++ StaticMethod bootstrapWebSettings($script)
636
637Called by bootstrapConfig. This handles the web environment specific settings only:
638
639 * ={DefaultUrlHost}=
640 * ={ScriptUrlPath}=
641 * ={ScriptUrlPaths}{view}=
642 * ={PubUrlPath}=
643
644=cut
645
646sub bootstrapWebSettings {
647 my $script = shift;
648
649 print STDERR "AUTOCONFIG: Bootstrap Phase 2: "
650 . Data::Dumper::Dumper( \%ENV )
651 if (TRAUTO);
652
653 # Cannot bootstrap the web side from CLI environments
654 if ( $Foswiki::cfg{Engine} eq 'Foswiki::Engine::CLI' ) {
655 $Foswiki::cfg{DefaultUrlHost} = 'http://localhost';
656 $Foswiki::cfg{ScriptUrlPath} = '/bin';
657 $Foswiki::cfg{PubUrlPath} = '/pub';
658 print STDERR
659 "AUTOCONFIG: Bootstrap Phase 2 bypassed! n/a in the CLI Environment\n"
660 if (TRAUTO);
661 return 'Phase 2 boostrap bypassed - n/a in CLI environment\n';
662 }
663
664 my $protocol = $ENV{HTTPS} ? 'https' : 'http';
665
666 # Figure out the DefaultUrlHost
667 if ( $ENV{HTTP_HOST} ) {
668 $Foswiki::cfg{DefaultUrlHost} = "$protocol://$ENV{HTTP_HOST}";
669 print STDERR
670"AUTOCONFIG: Set DefaultUrlHost $Foswiki::cfg{DefaultUrlHost} from HTTP_HOST $ENV{HTTP_HOST} \n"
671 if (TRAUTO);
672 }
673 elsif ( $ENV{SERVER_NAME} ) {
674 $Foswiki::cfg{DefaultUrlHost} = "$protocol://$ENV{SERVER_NAME}";
675 print STDERR
676"AUTOCONFIG: Set DefaultUrlHost $Foswiki::cfg{DefaultUrlHost} from SERVER_NAME $ENV{SERVER_NAME} \n"
677 if (TRAUTO);
678 }
679 elsif ( $ENV{SCRIPT_URI} ) {
680 ( $Foswiki::cfg{DefaultUrlHost} ) =
681 $ENV{SCRIPT_URI} =~ m#^(https?://[^/]+)/#;
682 print STDERR
683"AUTOCONFIG: Set DefaultUrlHost $Foswiki::cfg{DefaultUrlHost} from SCRIPT_URI $ENV{SCRIPT_URI} \n"
684 if (TRAUTO);
685 }
686 else {
687
688 # OK, so this is barfilicious. Think of something better.
689 $Foswiki::cfg{DefaultUrlHost} = "$protocol://localhost";
690 print STDERR
691"AUTOCONFIG: barfilicious: Set DefaultUrlHost $Foswiki::cfg{DefaultUrlHost} \n"
692 if (TRAUTO);
693 }
694
695# Examine the CGI path. The 'view' script it typically removed from the
696# URL when using "Short URLs. If this BEGIN block is being run by
697# 'view', then $Foswiki::cfg{ScriptUrlPaths}{view} will be correctly
698# bootstrapped. If run for any other script, it will be set to a
699# reasonable though probably incorrect default.
700#
701# In order to recover the correct view path when the script is 'configure',
702# the ConfigurePlugin stashes the path to the view script into a session variable.
703# and then recovers it. When the jsonrpc script is called to save the configuration
704# it then has the VIEWPATH parameter available. If "view" was never called during
705# configuration, then it will not be set correctly.
706 my $path_info = $ENV{'PATH_INFO'}
707 || ''; #SMELL Sometimes PATH_INFO appears to be undefined.
708 print STDERR "AUTOCONFIG: REQUEST_URI is "
709 . ( $ENV{REQUEST_URI} || '(undef)' ) . "\n"
710 if (TRAUTO);
711 print STDERR "AUTOCONFIG: SCRIPT_URI is "
712 . ( $ENV{SCRIPT_URI} || '(undef)' ) . " \n"
713 if (TRAUTO);
714 print STDERR "AUTOCONFIG: PATH_INFO is $path_info \n" if (TRAUTO);
715 print STDERR "AUTOCONFIG: ENGINE is $Foswiki::cfg{Engine}\n"
716 if (TRAUTO);
717
718# This code tries to break the url up into <prefix><script><path> ... The script may or may not
719# be present. Short URLs will omit the script from view operations, and *may* omit the
720# <prefix> for all operations. Examples of URLs and shortening.
721#
722# Full: /foswiki/bin/view/Main/WebHome /foswiki/bin/edit/Main/WebHome
723# Full: /bin/view/Main/WebHome /bin/edit/Main/WebHome omitting prefix
724# Short: /foswiki/Main/WebHome /foswiki/bin/edit/Main/WebHome omitting bin/view
725# Short: /Main/WebHome /bin/edit/Main/WebHome omitting prefix and bin/view
726# Shorter: /Main/WebHome /edit/Main/WebHome omitting prefix and bin in all cases.
727#
728# Note that some of this can't be done as part of the view script. The only way to know if "bin" is omitted in
729# all cases is when a script other than view runs, like jsonrpc.
730
731 my $pfx;
732
733 my $suffix =
734 ( defined $ENV{SCRIPT_URL}
735 && length( $ENV{SCRIPT_URL} ) < length($path_info) )
736 ? $ENV{SCRIPT_URL}
737 : $path_info;
738
739 # Try to Determine the prefix of the script part of the URI.
740 if ( $ENV{SCRIPT_URI} && $ENV{SCRIPT_URL} ) {
741 if ( index( $ENV{SCRIPT_URI}, $Foswiki::cfg{DefaultUrlHost} ) eq 0 ) {
742 $pfx =
743 substr( $ENV{SCRIPT_URI},
744 length( $Foswiki::cfg{DefaultUrlHost} ) );
745 $pfx =~ s#$suffix$##;
746 print STDERR
747"AUTOCONFIG: Calculated prefix $pfx from SCRIPT_URI and SCRIPT_URL\n"
748 if (TRAUTO);
749 }
750 }
751
752 unless ( defined $pfx ) {
753 if ( my $idx = index( $ENV{REQUEST_URI}, $path_info ) ) {
754 $pfx = substr( $ENV{REQUEST_URI}, 0, $idx + 1 );
755 }
756 $pfx = '' unless ( defined $pfx );
757 print STDERR "AUTOCONFIG: URI Prefix is $pfx\n" if (TRAUTO);
758 }
759
760 # Work out the URL path for Short and standard URLs
761 if ( $ENV{REQUEST_URI} =~ m{^(.*?)/$script(\b|$)} ) {
762 print STDERR
763"AUTOCONFIG: SCRIPT $script fully contained in REQUEST_URI $ENV{REQUEST_URI}, Not short URLs\n"
764 if (TRAUTO);
765
766 # Conventional URLs with path and script
767 $Foswiki::cfg{ScriptUrlPath} = $1;
768 $Foswiki::cfg{ScriptUrlPaths}{view} =
769 $1 . '/view' . $Foswiki::cfg{ScriptSuffix};
770
771 # This might not work, depending on the websrver config,
772 # but it's the best we can do
773 $Foswiki::cfg{PubUrlPath} = "$1/../pub";
774 }
775 else {
776 print STDERR "AUTOCONFIG: Building Short URL paths using prefix $pfx \n"
777 if (TRAUTO);
778 $Foswiki::cfg{ScriptUrlPath} = $pfx . '/bin';
779 $Foswiki::cfg{ScriptUrlPaths}{view} = $pfx;
780 $Foswiki::cfg{PubUrlPath} = $pfx . '/pub';
781 }
782
783 if (TRAUTO) {
784 print STDERR
785 "AUTOCONFIG: Using ScriptUrlPath $Foswiki::cfg{ScriptUrlPath} \n";
786 print STDERR "AUTOCONFIG: Using {ScriptUrlPaths}{view} "
787 . (
788 ( defined $Foswiki::cfg{ScriptUrlPaths}{view} )
789 ? $Foswiki::cfg{ScriptUrlPaths}{view}
790 : 'undef'
791 ) . "\n";
792 print STDERR
793 "AUTOCONFIG: Using PubUrlPath: $Foswiki::cfg{PubUrlPath} \n";
794 }
795
796 # Note: message is not I18N'd because there is no point; there
797 # is no localisation in a default cfg derived from Foswiki.spec
798 my $vp = '';
799 $vp = '?VIEWPATH=' . $Foswiki::cfg{ScriptUrlPaths}{view}
800 if ( defined $Foswiki::cfg{ScriptUrlPaths}{view} );
801 my $system_message = <<BOOTS;
802*WARNING !LocalSite.cfg could not be found* (This is normal for a new installation) %BR%
803This Foswiki is running using a bootstrap configuration worked
804out by detecting the layout of the installation.
805To complete the bootstrap process you should either:
806 * Restore the missing !LocalSite.cfg from a backup, *or*
807 * Complete the new Foswiki installation:
808 * visit [[%SCRIPTURL{configure}%$vp][configure]] and save a new configuration.
809 * Register a user and add it to the %USERSWEB%.AdminGroup
810%BR% *You have been logged in as a temporary administrator.*
811Any requests made to this Foswiki will be treated as requests made by an administrator with full rights
812Your temporary administrator rights will "stick" until you've logged out from this session.
813BOOTS
814
815 return ( $system_message || '' );
816}
817
818=begin TML
819
820---++ StaticMethod findDependencies(\%cfg) -> \%deps
821
822 * =\%cfg= configuration hash to scan; defaults to %Foswiki::cfg
823
824Recursively locate references to other keys in the values of keys.
825Returns a hash containing two keys:
826 * =forward= => a hash mapping keys to a list of the keys that depend
827 on their value
828 * =reverse= => a hash mapping keys to a list of keys whose value they
829 depend on.
830
831=cut
832
833sub findDependencies {
834 my ( $fwcfg, $deps, $extend_keypath, $keypath ) = @_;
835
836 unless ( defined $fwcfg ) {
837 ( $fwcfg, $extend_keypath, $keypath ) = ( \%Foswiki::cfg, 1, '' );
838 }
839
840 $deps ||= { forward => {}, reverse => {} };
841
842 if ( ref($fwcfg) eq 'HASH' ) {
843 while ( my ( $k, $v ) = each %$fwcfg ) {
844 if ( defined $v ) {
845 my $subkey = $extend_keypath ? "$keypath\{$k\}" : $keypath;
846 findDependencies( $v, $deps, $extend_keypath, $subkey );
847 }
848 }
849 }
850 elsif ( ref($fwcfg) eq 'ARRAY' ) {
851 foreach my $v (@$fwcfg) {
852 if ( defined $v ) {
853 findDependencies( $v, $deps, 0, $keypath );
854 }
855 }
856 }
857 else {
858 while ( $fwcfg =~ m/\$Foswiki::cfg(({[^}]*})+)/g ) {
859 push( @{ $deps->{forward}->{$1} }, $keypath );
860 push( @{ $deps->{reverse}->{$keypath} }, $1 );
861 }
862 }
863 return $deps;
864}
865
86616µs1;
867__END__