X-Git-Url: http://git.freeside.biz/gitweb/?p=freeside.git;a=blobdiff_plain;f=rt%2Flib%2FRT%2FInterface%2FWeb%2FSession.pm;h=3ee4cc6800740fc0ae9399d039a8e9e72d285c2a;hp=1e0e6d5f0aead79082a28904b7f6d63dce3bfbf1;hb=44dd00a3ff974a17999e86e64488e996edc71e3c;hpb=e70abd21bab68b23488f7ef1ee2e693a3b365691 diff --git a/rt/lib/RT/Interface/Web/Session.pm b/rt/lib/RT/Interface/Web/Session.pm index 1e0e6d5f0..3ee4cc680 100644 --- a/rt/lib/RT/Interface/Web/Session.pm +++ b/rt/lib/RT/Interface/Web/Session.pm @@ -1,40 +1,40 @@ # BEGIN BPS TAGGED BLOCK {{{ -# +# # COPYRIGHT: -# -# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC -# -# +# +# This software is Copyright (c) 1996-2019 Best Practical Solutions, LLC +# +# # (Except where explicitly superseded by other copyright notices) -# -# +# +# # LICENSE: -# +# # This work is made available to you under the terms of Version 2 of # the GNU General Public License. A copy of that license should have # been provided with this software, but in any event can be snarfed # from www.gnu.org. -# +# # This work is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 or visit their web page on the internet at # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. -# -# +# +# # CONTRIBUTION SUBMISSION POLICY: -# +# # (The following paragraph is not intended to limit the rights granted # to you to modify and distribute this software under the terms of # the GNU General Public License and is only of importance to you if # you choose to contribute your changes and enhancements to the # community by submitting them to Best Practical Solutions, LLC.) -# +# # By intentionally submitting any modifications, corrections or # derivatives to this work, or any other work intended for use with # Request Tracker, to Best Practical Solutions, LLC, you confirm that @@ -43,7 +43,7 @@ # royalty-free, perpetual, license to use, copy, create derivative # works based on those contributions, and sublicense and distribute # those contributions and any derivatives thereof. -# +# # END BPS TAGGED BLOCK }}} package RT::Interface::Web::Session; @@ -84,8 +84,7 @@ sub Class { my $class = RT->Config->Get('WebSessionClass') || $self->Backends->{RT->Config->Get('DatabaseType')} || 'Apache::Session::File'; - eval "require $class"; - die $@ if $@; + $class->require or die "Can't load $class: $@"; return $class; } @@ -98,8 +97,9 @@ sessions class names as values. sub Backends { return { - mysql => 'Apache::Session::MySQL', - Pg => 'Apache::Session::Postgres', + mysql => 'Apache::Session::MySQL', + Pg => 'Apache::Session::Postgres', + Oracle => 'Apache::Session::Oracle', }; } @@ -112,15 +112,27 @@ new session objects. sub Attributes { my $class = $_[0]->Class; - return !$class->isa('Apache::Session::File') ? { - Handle => $RT::Handle->dbh, - LockHandle => $RT::Handle->dbh, - Transaction => 1, - } : { + my $res; + if ( my %props = RT->Config->Get('WebSessionProperties') ) { + $res = \%props; + } + elsif ( $class->isa('Apache::Session::File') ) { + $res = { Directory => $RT::MasonSessionDir, LockDirectory => $RT::MasonSessionDir, Transaction => 1, }; + } + else { + $res = { + Handle => $RT::Handle->dbh, + LockHandle => $RT::Handle->dbh, + Transaction => 1, + }; + } + $res->{LongReadLen} = RT->Config->Get('MaxAttachmentSize') + if $class->isa('Apache::Session::Oracle'); + return $res; } =head3 Ids @@ -170,7 +182,7 @@ sub ClearOld { my $class = shift || __PACKAGE__; my $attributes = $class->Attributes; if( $attributes->{Directory} ) { - return $class->_CleariOldDir( $attributes->{Directory}, @_ ); + return $class->_ClearOldDir( $attributes->{Directory}, @_ ); } else { return $class->_ClearOldDB( $RT::Handle->dbh, @_ ); } @@ -192,7 +204,7 @@ sub _ClearOldDB { die "couldn't execute query: ". $dbh->errstr unless defined $rows; } - $RT::Logger->info("successfuly deleted $rows sessions"); + $RT::Logger->info("successfully deleted $rows sessions"); return; } @@ -207,8 +219,8 @@ sub _ClearOldDir { foreach my $id( @{ $self->Ids } ) { if( int $older_than ) { - my $ctime = (stat(File::Spec->catfile($dir,$id)))[9]; - if( $ctime > $now - $older_than ) { + my $mtime = (stat(File::Spec->catfile($dir,$id)))[9]; + if( $mtime > $now - $older_than ) { $RT::Logger->debug("skipped session '$id', isn't old"); next; } @@ -222,11 +234,53 @@ sub _ClearOldDir { next; } tied(%session)->delete; - $RT::Logger->info("successfuly deleted session '$id'"); + $RT::Logger->info("successfully deleted session '$id'"); } + + # Apache::Session::Lock::File will clean out locks older than X, but it + # leaves around bogus locks if they're too new, even though they're + # guaranteed dead. On even just largeish installs, the accumulated number + # of them may bump into ext3/4 filesystem limits since Apache::Session + # doesn't use a fan-out tree. + my $lock = Apache::Session::Lock::File->new; + $lock->clean( $dir, $older_than ); + + # Take matters into our own hands and clear bogus locks hanging around + # regardless of how recent they are. + $self->ClearOrphanLockFiles($dir); + return; } +=head3 ClearOrphanLockFiles + +Takes a directory in which to look for L locks +which no longer have a corresponding session file. If not provided, the +directory is taken from the session configuration data. + +=cut + +sub ClearOrphanLockFiles { + my $class = shift; + my $dir = shift || $class->Attributes->{Directory} + or return; + + if (opendir my $dh, $dir) { + for (readdir $dh) { + next unless /^Apache-Session-([0-9a-f]{32})\.lock$/; + next if -e "$dir/$1"; + + RT->Logger->debug("deleting orphaned session lockfile '$_'"); + + unlink "$dir/$_" + or warn "Failed to unlink session lockfile $dir/$_: $!"; + } + closedir $dh; + } else { + warn "Unable to open directory '$dir' for reading: $!"; + } +} + =head3 ClearByUser Checks all sessions and if user has more then one session @@ -239,6 +293,7 @@ sub ClearByUser { my $class = $self->Class; my $attrs = $self->Attributes; + my $deleted; my %seen = (); foreach my $id( @{ $self->Ids } ) { my %session; @@ -255,8 +310,10 @@ sub ClearByUser { } } tied(%session)->delete; - $RT::Logger->info("successfuly deleted session '$id'"); + $RT::Logger->info("successfully deleted session '$id'"); + $deleted++; } + $self->ClearOrphanLockFiles if $deleted; } sub TIEHASH { @@ -272,10 +329,8 @@ sub TIEHASH { eval { tie %session, $class, $id, $attrs }; eval { tie %session, $class, undef, $attrs } if $@; if ( $@ ) { - die loc("RT couldn't store your session.") . "\n" - . loc("This may mean that that the directory '[_1]' isn't writable or a database table is missing or corrupt.", - $RT::MasonSessionDir) - . "\n\n" + die "RT couldn't store your session. " + . "This may mean that that the directory '$RT::MasonSessionDir' isn't writable or a database table is missing or corrupt.\n\n" . $@; }