1 # BEGIN BPS TAGGED BLOCK {{{
5 # This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
6 # <jesse@bestpractical.com>
8 # (Except where explicitly superseded by other copyright notices)
13 # This work is made available to you under the terms of Version 2 of
14 # the GNU General Public License. A copy of that license should have
15 # been provided with this software, but in any event can be snarfed
18 # This work is distributed in the hope that it will be useful, but
19 # WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 # General Public License for more details.
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26 # 02110-1301 or visit their web page on the internet at
27 # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
30 # CONTRIBUTION SUBMISSION POLICY:
32 # (The following paragraph is not intended to limit the rights granted
33 # to you to modify and distribute this software under the terms of
34 # the GNU General Public License and is only of importance to you if
35 # you choose to contribute your changes and enhancements to the
36 # community by submitting them to Best Practical Solutions, LLC.)
38 # By intentionally submitting any modifications, corrections or
39 # derivatives to this work, or any other work intended for use with
40 # Request Tracker, to Best Practical Solutions, LLC, you confirm that
41 # you are the copyright holder for those contributions and you grant
42 # Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
43 # royalty-free, perpetual, license to use, copy, create derivative
44 # works based on those contributions, and sublicense and distribute
45 # those contributions and any derivatives thereof.
47 # END BPS TAGGED BLOCK }}}
54 use vars qw($VERSION $System $SystemUser $Nobody $Handle $Logger
66 $MasonLocalComponentRoot
71 $VERSION = '@RT_VERSION_MAJOR@.@RT_VERSION_MINOR@.@RT_VERSION_PATCH@';
72 $CORE_CONFIG_FILE = "@CONFIG_FILE_PATH@/RT_Config.pm";
73 $SITE_CONFIG_FILE = "@CONFIG_FILE_PATH@/RT_SiteConfig.pm";
77 $BasePath = '@RT_PATH@';
79 $EtcPath = '@RT_ETC_PATH@';
80 $BinPath = '@RT_BIN_PATH@';
81 $VarPath = '@RT_VAR_PATH@';
82 $LocalPath = '@RT_LOCAL_PATH@';
83 $LocalEtcPath = '@LOCAL_ETC_PATH@';
84 $LocalLexiconPath = '@LOCAL_LEXICON_PATH@';
86 # $MasonComponentRoot is where your rt instance keeps its mason html files
88 $MasonComponentRoot = '@MASON_HTML_PATH@';
90 # $MasonLocalComponentRoot is where your rt instance keeps its site-local
93 $MasonLocalComponentRoot = '@MASON_LOCAL_HTML_PATH@';
95 # $MasonDataDir Where mason keeps its datafiles
97 $MasonDataDir = '@MASON_DATA_PATH@';
99 # RT needs to put session data (for preserving state between connections
100 # via the web interface)
101 $MasonSessionDir = '@MASON_SESSION_PATH@';
111 A fully featured request tracker package
117 Load RT's config file. First, the site configuration file
118 (C<RT_SiteConfig.pm>) is loaded, in order to establish overall site
119 settings like hostname and name of RT instance. Then, the core
120 configuration file (C<RT_Config.pm>) is loaded to set fallback values
121 for all settings; it bases some values on settings from the site
124 In order for the core configuration to not override the site's
125 settings, the function C<Set> is used; it only sets values if they
126 have not been set already.
131 local *Set = sub { $_[0] = $_[1] unless defined $_[0] };
133 my $username = getpwuid($>);
134 my $group = getgrgid($();
137 RT couldn't load RT config file %s as:
141 The file is owned by user %s and group %s.
143 This usually means that the user/group your webserver is running
144 as cannot read the file. Be careful not to make the permissions
145 on this file too liberal, because it contains database passwords.
146 You may need to put the webserver user in the appropriate group
147 (%s) or change permissions be able to run succesfully.
151 if ( -f "$SITE_CONFIG_FILE" ) {
152 eval { require $SITE_CONFIG_FILE };
154 my ($fileuid,$filegid) = (stat($SITE_CONFIG_FILE))[4,5];
155 my $fileusername = getpwuid($fileuid);
156 my $filegroup = getgrgid($filegid);
157 my $errormessage = sprintf($message, $SITE_CONFIG_FILE,
158 $fileusername, $filegroup, $filegroup);
159 die ("$errormessage\n$@");
162 eval { require $CORE_CONFIG_FILE };
164 my ($fileuid,$filegid) = (stat($CORE_CONFIG_FILE))[4,5];
165 my $fileusername = getpwuid($fileuid);
166 my $filegroup = getgrgid($filegid);
167 my $errormessage = sprintf($message, $CORE_CONFIG_FILE,
168 $fileusername, $filegroup, $filegroup);
169 die ("$errormessage\n$@")
172 # RT::Essentials mistakenly recommends that WebPath be set to '/'.
173 # If the user does that, do what they mean.
174 $RT::WebPath = '' if ($RT::WebPath eq '/');
176 $ENV{'TZ'} = $RT::Timezone if ($RT::Timezone);
183 Conenct to the database, set up logging.
191 CheckPerlRequirements();
193 #Get a database connection
196 #RT's system user is a genuine database user. its id lives here
197 $SystemUser = new RT::CurrentUser();
198 $SystemUser->LoadByName('RT_System');
200 #RT's "nobody user" is a genuine database user. its ID lives here.
201 $Nobody = new RT::CurrentUser();
202 $Nobody->LoadByName('Nobody');
204 $System = RT::System->new();
211 =head2 ConnectToDatabase
213 Get a database connection
217 sub ConnectToDatabase {
219 unless ($Handle && $Handle->dbh && $Handle->dbh->ping) {
220 $Handle = RT::Handle->new();
227 Create the RT::Logger object.
235 # We have to set the record separator ($, man perlvar)
236 # or Log::Dispatch starts getting
237 # really pissy, as some other module we use unsets it.
240 use Log::Dispatch 1.6;
242 unless ($RT::Logger) {
244 $RT::Logger = Log::Dispatch->new();
246 my $simple_cb = sub {
247 # if this code throw any warning we can get segfault
252 my $frame = 0; # stack frame index
253 # skip Log::* stack frames
254 $frame++ while( caller($frame) && caller($frame) =~ /^Log::/ );
256 my ($package, $filename, $line) = caller($frame);
257 $p{message} =~ s/(?:\r*\n)+$//;
258 my $str = "[".gmtime(time)."] [".$p{level}."]: $p{message} ($filename:$line)\n";
260 if( $RT::LogStackTraces ) {
261 $str .= "\nStack trace:\n";
262 # skip calling of the Log::* subroutins
263 $frame++ while( caller($frame) && (caller($frame))[3] =~ /^Log::/ );
264 while( my ($package, $filename, $line, $sub) = caller($frame++) ) {
265 $str .= "\t". $sub ."() called at $filename:$line\n";
271 my $syslog_cb = sub {
274 my $frame = 0; # stack frame index
275 # skip Log::* stack frames
276 $frame++ while( caller($frame) && caller($frame) =~ /^Log::/ );
277 my ($package, $filename, $line) = caller($frame);
279 # syswrite() cannot take utf8; turn it off here.
280 Encode::_utf8_off($p{message});
282 $p{message} =~ s/(?:\r*\n)+$//;
283 if ($p{level} eq 'debug') {
284 return "$p{message}\n"
286 return "$p{message} ($filename:$line)\n"
290 if ($RT::LogToFile) {
291 my ($filename, $logdir);
292 if ($RT::LogToFileNamed =~ m![/\\]!) {
293 # looks like an absolute path.
294 $filename = $RT::LogToFileNamed;
295 ($logdir) = $RT::LogToFileNamed =~ m!^(.*[/\\])!;
298 $filename = "$RT::LogDir/$RT::LogToFileNamed";
299 $logdir = $RT::LogDir;
302 unless ( -d $logdir && ( ( -f $filename && -w $filename ) || -w $logdir ) ) {
303 # localizing here would be hard when we don't have a current user yet
304 die "Log file $filename couldn't be written or created.\n RT can't run.";
307 package Log::Dispatch::File;
308 require Log::Dispatch::File;
309 $RT::Logger->add(Log::Dispatch::File->new
311 min_level=> $RT::LogToFile,
312 filename=> $filename,
314 callbacks => $simple_cb,
317 if ($RT::LogToScreen) {
318 package Log::Dispatch::Screen;
319 require Log::Dispatch::Screen;
320 $RT::Logger->add(Log::Dispatch::Screen->new
322 min_level => $RT::LogToScreen,
323 callbacks => $simple_cb,
327 if ($RT::LogToSyslog) {
328 package Log::Dispatch::Syslog;
329 require Log::Dispatch::Syslog;
330 $RT::Logger->add(Log::Dispatch::Syslog->new
333 min_level => $RT::LogToSyslog,
334 callbacks => $syslog_cb,
342 # {{{ Signal handlers
344 ## This is the default handling of warnings and die'ings in the code
345 ## (including other used modules - maybe except for errors catched by
346 ## Mason). It will log all problems through the standard logging
347 ## mechanism (see above).
349 unless ( $arg{'NoSignalHandlers'} ) {
351 $SIG{__WARN__} = sub {
352 # The 'wide character' warnings has to be silenced for now, at least
353 # until HTML::Mason offers a sane way to process both raw output and
355 # use 'goto &foo' syntax to hide ANON sub from stack
356 if( index($_[0], 'Wide character in ') != 0 ) {
357 unshift @_, $RT::Logger, qw(level warning message);
358 goto &Log::Dispatch::log;
362 #When we call die, trap it and log->crit with the value of the die.
364 $SIG{__DIE__} = sub {
365 unless ($^S || !defined $^S ) {
366 $RT::Handle->Rollback();
367 $RT::Logger->crit("$_[0]");
379 sub CheckPerlRequirements {
380 if ($^V < 5.008003) {
381 die sprintf "RT requires Perl v5.8.3 or newer. Your current Perl is v%vd\n", $^V;
388 require Scalar::Util; Scalar::Util::weaken($y);
393 RT requires the Scalar::Util module be built with support for the 'weaken'
396 It is sometimes the case that operating system upgrades will replace
397 a working Scalar::Util with a non-working one. If your system was working
398 correctly up until now, this is likely the cause of the problem.
400 Please reinstall Scalar::Util, being careful to let it build with your C
401 compiler. Ususally this is as simple as running the following command as
404 perl -MCPAN -e'install Scalar::Util'
414 Load all modules that define base classes
420 require RT::Transactions;
421 require RT::Attachments;
423 require RT::Principals;
424 require RT::CurrentUser;
425 require RT::Templates;
427 require RT::ScripActions;
428 require RT::ScripConditions;
431 require RT::GroupMembers;
432 require RT::CustomFields;
433 require RT::CustomFieldValues;
434 require RT::ObjectCustomFields;
435 require RT::ObjectCustomFieldValues;
436 require RT::Attributes;
438 # on a cold server (just after restart) people could have an object
439 # in the session, as we deserialize it so we never call constructor
440 # of the class, so the list of accessible fields is empty and we die
441 # with "Method xxx is not implemented in RT::SomeClass"
442 $_->_BuildTableAttributes foreach qw(
457 RT::ObjectCustomField
458 RT::ObjectCustomFieldValue
476 Please report them to rt-bugs@fsck.com, if you know what's broken and have at least
477 some idea of what needs to be fixed.
479 If you're not sure what's going on, report them rt-devel@lists.bestpractical.com.
484 L<DBIx::SearchBuilder>
488 ok ($RT::Nobody->Name() eq 'Nobody', "Nobody is nobody");
489 ok ($RT::Nobody->Name() ne 'root', "Nobody isn't named root");
490 ok ($RT::SystemUser->Name() eq 'RT_System', "The system user is RT_System");
491 ok ($RT::SystemUser->Name() ne 'noname', "The system user isn't noname");
497 eval "require RT_Vendor";
498 die $@ if ($@ && $@ !~ qr{^Can't locate RT_Vendor.pm});
499 eval "require RT_Local";
500 die $@ if ($@ && $@ !~ qr{^Can't locate RT_Local.pm});