[horde] Problems with a custom session handler

Rick Irvine irvine at purdue.edu
Wed Jul 10 16:33:17 PDT 2002


Systems:
  Cluster directors, nodes, and database backends all are 350mhz P2's running
  RedHat Linux 7.3

Software/RPMS:
  Horde 2.1 (via tarball)
  Imp 3.1 (via tarball)
  Turba 1.1 (via tarball)
  apache-1.3.23-11 (I know, I need to upgrade)
  php-ldap-4.1.2-7
  php-4.1.2-7
  php-devel-4.1.2-7
  php-imap-4.1.2-7
  php-mysql-4.1.2-7
  mysql-server-3.23.49-3
  mysql-3.23.49-3
  mysqlclient9-3.23.22-6

Additional/Reference Information:
I have pasted at the end of this message a replacement for the built in PHP
session handler.

Problem:

I am trying to run Horde/Imp/Turba on a load-balancing high availability
cluster, otherwise known as LVS (Linux Virtual Server).  This is mostly due to
scalability issues with the hardware (ie, linear cost to upgrade) and
maintenance issues.

I have met with some interesting challenges, mostly with the fact that Horde
now relies on the bulit in PHP session handler.  This handler currently only
supports storing the sessions in files on the local machine.  I have a
session handler written in PHP that redefines the handler functions to use
the MySQL database, and this actually does work (!!).  I can see the sessions
being stored in the table on the MySQL server and the two nodes both can
access them.

However, now I have an interesting problem and I have to admit, I have no
idea where to start looking.  I'm hoping someone who has more experience with
this version of Horde can help point me in the right direction.  (My actual
experiences with coding PHP and with Horde 2.1 are both very limited.)

Now when I click Logout, I get a blank white main window with the Horde bar
at the bottom.  If I click Logout again at the bottom I get the correct
behavior - another login screen.  This happens regardless of which logout
link I initially use.

I managed to figure out that there are three places to insert function
definitions for a replacement session handler, namely in login.php and
lib/Registery.php and imp/login.php.  (Basically the only places that
session_* functions are called.) I had to make a guess as to the "right way"
to insert this code.  It doesn't look like it should be a seperate library
object, as it's just a replacement for a few functions.  I ended up putting
the code into a small file in horde/config and #include'ing it into the two
relevant files.  This seems to work ok.  (If there is a better way I'm all
ears.)

My guess is that Horde doesn't like something in one of the return values from
the redefined session handler functions, but I don't see where.  It looks like
I'm logging out of Imp, but not out of Horde.  Either that or it's just not
redrawing the login page on the initial logout, but how would that be related
to changing the session handler? (And yes, I've boiled the problem down to the
session handler, my cluster currently only has 1 node and still has this
problem and if I rm the session handler everything works ok.)

I did turn on the error handling for creating the sessions (rm'd the "@" in
front of session_start()), in both horde/login.php and horde/lib/Registry.php
but didn't see anything reported.

If anyone could provide a clue as to what's going wrong exactly, I would
greatly appreciate it.  This is the only code I've modified in the Horde/Imp
setup.  Everything else is stock.

BTW, yes I know I could pull the db host/name/pword from the horde config
settings, but darned if I know how yet. :)  (I had all kinds of trouble
directly accessing the conf array, as I said I'm fairly new to this version
of Horde/Imp.

I must say, other than this one little snafu the rest of it runs great on a
cluster.  I'm really looking forward to rolling this out for our users this
fall.

- Rick Irvine

Here is a copy of the session handler I'm using.  Please note that I cut out
the header to keep this from getting too long, but it is from Ying Zhang and
can be found at http://www.phpbuilder.com/columns/ying20000602.php3 along
with an article on how to build custom session handlers, citing a DBM and
MySQL example.  (Including the header isn't required but I want to give credit
where it's due.)  This is currently included by horde/login.php,
horde/lib/Registry.php, and horde/imp/login.php.

---------- Begin Here ----------

<?php
(Snipped comments)

$SESS_DBHOST = "xxxxx";                 /* database server hostname */
$SESS_DBNAME = "xxxxx";                 /* database name */
$SESS_DBUSER = "xxxxx";                 /* database user */
$SESS_DBPASS = "xxxxx";                 /* database password */

$SESS_DBH = "";
$SESS_LIFE = get_cfg_var("session.gc_maxlifetime");

function sess_open($save_path, $session_name) {
        global $SESS_DBHOST, $SESS_DBNAME, $SESS_DBUSER, $SESS_DBPASS, $SESS_DBH
;

        if (! $SESS_DBH = mysql_pconnect($SESS_DBHOST, $SESS_DBUSER, $SESS_DBPAS
S)) {
                echo "<li>Can't connect to $SESS_DBHOST as $SESS_DBUSER";
                echo "<li>MySQL Error: ", mysql_error();
                die;
        }

        if (! mysql_select_db($SESS_DBNAME, $SESS_DBH)) {
                echo "<li>Unable to select database $SESS_DBNAME";
                die;
        }

        return true;
}

function sess_close() {
        return true;
}

function sess_read($key) {
        global $SESS_DBH, $SESS_LIFE;

        $qry = "SELECT value FROM sessions WHERE sesskey = '$key' AND expiry > "
 . time();
        $qid = mysql_query($qry, $SESS_DBH);

        if (list($value) = mysql_fetch_row($qid)) {
                return $value;
        }

        return "";
}

function sess_write($key, $val) {
        global $SESS_DBH, $SESS_LIFE;

        $expiry = time() + $SESS_LIFE;
        $value = addslashes($val);

        $qry = "INSERT INTO sessions VALUES ('$key', $expiry, '$value')";
        $qid = mysql_query($qry, $SESS_DBH);

        if (! $qid) {
                $qry = "UPDATE sessions SET expiry = $expiry, value = '$value' W
HERE sesskey = '$key' AND expiry > " . time();
                $qid = mysql_query($qry, $SESS_DBH);
        }

        return $qid;
}

function sess_destroy($key) {
        global $SESS_DBH;

        $qry = "DELETE FROM sessions WHERE sesskey = '$key'";
        $qid = mysql_query($qry, $SESS_DBH);

        return $qid;
}

function sess_gc($maxlifetime) {
        global $SESS_DBH;

        $qry = "DELETE FROM sessions WHERE expiry < " . time();
        $qid = mysql_query($qry, $SESS_DBH);

        return mysql_affected_rows($SESS_DBH);
}

session_set_save_handler(
        "sess_open",
        "sess_close",
        "sess_read",
        "sess_write",
        "sess_destroy",
        "sess_gc");
?>




More information about the horde mailing list