(file) Return to HTTPAcceptor.cpp CVS log (file) (dir) Up to [Pegasus] / pegasus / src / Pegasus / Common

Diff for /pegasus/src/Pegasus/Common/HTTPAcceptor.cpp between version 1.26 and 1.47

version 1.26, 2003/03/13 17:25:16 version 1.47, 2004/01/16 19:21:56
Line 1 
Line 1 
 //%/////////////////////////////////////////////////////////////////////////////  //%2003////////////////////////////////////////////////////////////////////////
 // //
 // Copyright (c) 2000, 2001, 2002 BMC Software, Hewlett-Packard Company, IBM,  // Copyright (c) 2000, 2001, 2002  BMC Software, Hewlett-Packard Development
 // The Open Group, Tivoli Systems  // Company, L. P., IBM Corp., The Open Group, Tivoli Systems.
   // Copyright (c) 2003 BMC Software; Hewlett-Packard Development Company, L. P.;
   // IBM Corp.; EMC Corporation, The Open Group.
 // //
 // Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to // of this software and associated documentation files (the "Software"), to
Line 26 
Line 28 
 // Modified By: // Modified By:
 //         Jenny Yu, Hewlett-Packard Company (jenny_yu@hp.com) //         Jenny Yu, Hewlett-Packard Company (jenny_yu@hp.com)
 //         Nag Boranna, Hewlett-Packard Company (nagaraja_boranna@hp.com) //         Nag Boranna, Hewlett-Packard Company (nagaraja_boranna@hp.com)
   //         Dave Rosckes (rosckes@us.ibm.com)
   //         Denise Eckstein (denise.eckstein@hp.com)
 // //
 //%///////////////////////////////////////////////////////////////////////////// //%/////////////////////////////////////////////////////////////////////////////
  
Line 56 
Line 60 
 #include "HTTPAcceptor.h" #include "HTTPAcceptor.h"
 #include "HTTPConnection.h" #include "HTTPConnection.h"
 #include "Tracer.h" #include "Tracer.h"
   #include <Pegasus/Common/MessageLoader.h> //l10n
   
   #ifdef PEGASUS_PLATFORM_OS400_ISERIES_IBM
   #include "OS400ConvertChar.h"
   #endif
  
 PEGASUS_USING_STD; PEGASUS_USING_STD;
  
Line 92 
Line 101 
  
 #if defined(PEGASUS_PLATFORM_ZOS_ZSERIES_IBM) #if defined(PEGASUS_PLATFORM_ZOS_ZSERIES_IBM)
    size_t address_size;    size_t address_size;
 #elif defined(PEGASUS_PLATFORM_AIX_RS_IBMCXX) || defined(PEGASUS_PLATFORM_LINUX_IX86_GNU) || defined(PEGASUS_PLATFORM_LINUX_GENERIC_GNU)  #elif defined(PEGASUS_PLATFORM_AIX_RS_IBMCXX) || defined(PEGASUS_PLATFORM_LINUX_GENERIC_GNU) || (defined(PEGASUS_PLATFORM_SOLARIS_SPARC_CC) && !defined(SUNOS_5_6))
    socklen_t address_size;    socklen_t address_size;
 #else #else
    int address_size;    int address_size;
 #endif #endif
         Mutex _connection_mut;
  
     Sint32 socket;     Sint32 socket;
     Array<HTTPConnection*> connections;     Array<HTTPConnection*> connections;
Line 153 
Line 163 
          else          else
          {          {
             // ATTN! this can't happen!             // ATTN! this can't happen!
               Tracer::trace(TRC_DISCARDED_DATA, Tracer::LEVEL2,
                 "HTTPAcceptor::handleEnqueue: Invalid SOCKET_MESSAGE received.");
          }          }
  
          break;          break;
Line 163 
Line 175 
          CloseConnectionMessage* closeConnectionMessage          CloseConnectionMessage* closeConnectionMessage
             = (CloseConnectionMessage*)message;             = (CloseConnectionMessage*)message;
  
            _rep->_connection_mut.lock(pegasus_thread_self());
   
          for (Uint32 i = 0, n = _rep->connections.size(); i < n; i++)          for (Uint32 i = 0, n = _rep->connections.size(); i < n; i++)
          {          {
             HTTPConnection* connection = _rep->connections[i];             HTTPConnection* connection = _rep->connections[i];
Line 176 
Line 190 
                break;                break;
             }             }
          }          }
            _rep->_connection_mut.unlock();
            break;
       }       }
  
       default:       default:
       // ATTN: need unexpected message error!       // ATTN: need unexpected message error!
         Tracer::trace(TRC_DISCARDED_DATA, Tracer::LEVEL2,
              "HTTPAcceptor::handleEnqueue: Invalid MESSAGE received.");
       break;       break;
    };    };
  
Line 192 
Line 210 
    Message* message = dequeue();    Message* message = dequeue();
  
    if (!message)    if (!message)
      {
         Tracer::trace(TRC_DISCARDED_DATA, Tracer::LEVEL2,
              "HTTPAcceptor::handleEnqueue(): No message on queue.");
       return;       return;
      }
  
    handleEnqueue(message);    handleEnqueue(message);
  
Line 200 
Line 222 
  
 void HTTPAcceptor::bind() void HTTPAcceptor::bind()
 { {
    if (_rep)     if (_rep){
       throw BindFailedException("HTTPAcceptor already bound");          //l10n
         //throw BindFailedException("HTTPAcceptor already bound");
   
         MessageLoaderParms parms("Common.HTTPAcceptor.ALREADY_BOUND",
                                  "HTTPAcceptor already bound");
   
         Tracer::trace(TRC_DISCARDED_DATA, Tracer::LEVEL2,
              "HTTPAcceptor::bind: HTTPAcceptor already bound.");
         throw BindFailedException(parms);
      }
  
    _rep = new HTTPAcceptorRep(_localConnection);    _rep = new HTTPAcceptorRep(_localConnection);
  
Line 230 
Line 261 
            AF_UNIX;            AF_UNIX;
        strcpy(reinterpret_cast<struct sockaddr_un*>(_rep->address)->sun_path,        strcpy(reinterpret_cast<struct sockaddr_un*>(_rep->address)->sun_path,
               PEGASUS_LOCAL_DOMAIN_SOCKET_PATH);               PEGASUS_LOCAL_DOMAIN_SOCKET_PATH);
   #ifdef PEGASUS_PLATFORM_OS400_ISERIES_IBM
          AtoE(reinterpret_cast<struct sockaddr_un*>(_rep->address)->sun_path);
   #endif
        ::unlink(reinterpret_cast<struct sockaddr_un*>(_rep->address)->sun_path);        ::unlink(reinterpret_cast<struct sockaddr_un*>(_rep->address)->sun_path);
 #else #else
        PEGASUS_ASSERT(false);        PEGASUS_ASSERT(false);
Line 260 
Line 294 
    {    {
       delete _rep;       delete _rep;
       _rep = 0;       _rep = 0;
       throw BindFailedException("Failed to create socket");        //l10n
         //throw BindFailedException("Failed to create socket");
         MessageLoaderParms parms("Common.HTTPAcceptor.FAILED_CREATE_SOCKET",
                                  "Failed to create socket");
         Tracer::trace(TRC_DISCARDED_DATA, Tracer::LEVEL2,
              "HTTPAcceptor::_bind _rep->socket < 0");
         throw BindFailedException(parms);
    }    }
  
   
   // set the close-on-exec bit for this file handle.
   // any unix that forks needs this bit set.
   #ifndef PEGASUS_PLATFORM_WIN32_IX86_MSVC
      int sock_flags;
    if( (sock_flags = fcntl(_rep->socket, F_GETFD, 0)) < 0)
      {
          PEG_TRACE_STRING(TRC_DISCARDED_DATA, Tracer::LEVEL2,
                     "HTTPAcceptor::_bind: fcntl(F_GETFD) failed");
      }
      else
      {
         sock_flags |= FD_CLOEXEC;
         if (fcntl(_rep->socket, F_SETFD, sock_flags) < 0)
         {
          PEG_TRACE_STRING(TRC_DISCARDED_DATA, Tracer::LEVEL2,
                     "HTTPAcceptor::_bind: fcntl(F_SETFD) failed");
         }
      }
   #endif
   
   
    //    //
    // Set the socket option SO_REUSEADDR to reuse the socket address so    // Set the socket option SO_REUSEADDR to reuse the socket address so
    // that we can rebind to a new socket using the same address when we    // that we can rebind to a new socket using the same address when we
Line 273 
Line 335 
    if (setsockopt(_rep->socket, SOL_SOCKET, SO_REUSEADDR,    if (setsockopt(_rep->socket, SOL_SOCKET, SO_REUSEADDR,
                   (char *)&opt, sizeof(opt)) < 0)                   (char *)&opt, sizeof(opt)) < 0)
    {    {
         Socket::close(_rep->socket);
       delete _rep;       delete _rep;
       _rep = 0;       _rep = 0;
       throw BindFailedException("Failed to set socket option");        //l10n
         //throw BindFailedException("Failed to set socket option");
         MessageLoaderParms parms("Common.HTTPAcceptor.FAILED_SET_SOCKET_OPTION",
                                  "Failed to set socket option");
         PEG_TRACE_STRING(TRC_DISCARDED_DATA, Tracer::LEVEL2,
                      "HTTPAcceptor::_bind: Failed to set socket option.");
         throw BindFailedException(parms);
    }    }
  
    // Bind socket to port:    // Bind socket to port:
Line 285 
Line 354 
       Socket::close(_rep->socket);       Socket::close(_rep->socket);
       delete _rep;       delete _rep;
       _rep = 0;       _rep = 0;
       throw BindFailedException("Failed to bind socket");        //l10n
         //throw BindFailedException("Failed to bind socket");
         MessageLoaderParms parms("Common.HTTPAcceptor.FAILED_BIND_SOCKET",
                                  "Failed to bind socket");
         PEG_TRACE_STRING(TRC_DISCARDED_DATA, Tracer::LEVEL2,
                           "HTTPAcceptor::_bind: Failed to bind socket.");
         throw BindFailedException(parms);
    }    }
  
    // Set up listening on the given socket:    // Set up listening on the given socket:
  
    int const MAX_CONNECTION_QUEUE_LENGTH = 5;     int const MAX_CONNECTION_QUEUE_LENGTH = 15;
  
    if (listen(_rep->socket, MAX_CONNECTION_QUEUE_LENGTH) < 0)    if (listen(_rep->socket, MAX_CONNECTION_QUEUE_LENGTH) < 0)
    {    {
       Socket::close(_rep->socket);       Socket::close(_rep->socket);
       delete _rep;       delete _rep;
       _rep = 0;       _rep = 0;
       throw BindFailedException("Failed to bind socket");        //l10n
         //throw BindFailedException("Failed to bind socket");
         MessageLoaderParms parms("Common.HTTPAcceptor.FAILED_BIND_SOCKET",
                                  "Failed to bind socket");
         PEG_TRACE_STRING(TRC_DISCARDED_DATA, Tracer::LEVEL2,
                     "HTTPAcceptor::_bind: Failed to bind socket(1).");
         throw BindFailedException(parms);
    }    }
  
    // Register to receive SocketMessages on this socket:    // Register to receive SocketMessages on this socket:
Line 311 
Line 392 
       Socket::close(_rep->socket);       Socket::close(_rep->socket);
       delete _rep;       delete _rep;
       _rep = 0;       _rep = 0;
       throw BindFailedException("Failed to solicit socket messaeges");        //l10n
         //throw BindFailedException("Failed to solicit socket messaeges");
         MessageLoaderParms parms("Common.HTTPAcceptor.FAILED_SOLICIT_SOCKET_MESSAGES",
                                  "Failed to solicit socket messaeges");
         PEG_TRACE_STRING(TRC_DISCARDED_DATA, Tracer::LEVEL2,
                     "HTTPAcceptor::_bind: Failed to solicit socket messages(2).");
         throw BindFailedException(parms);
    }    }
 } }
  
Line 324 
Line 411 
    if (_rep)    if (_rep)
    {    {
       // unregister the socket       // unregister the socket
       _monitor->unsolicitSocketMessages(_rep->socket);  
         // ATTN - comment out - see CIMServer::stopClientConnection()
         //_monitor->unsolicitSocketMessages(_rep->socket);
  
       // close the socket       // close the socket
       Socket::close(_rep->socket);       Socket::close(_rep->socket);
    }    }
      else
      {
         PEG_TRACE_STRING(TRC_DISCARDED_DATA, Tracer::LEVEL2,
                           "HTTPAcceptor::closeConnectionSocket failure _rep is null." );
      }
 } }
  
 /** /**
Line 340 
Line 434 
    {    {
       _bind();       _bind();
    }    }
      else
      {
         PEG_TRACE_STRING(TRC_DISCARDED_DATA, Tracer::LEVEL2,
                           "HTTPAcceptor::reopenConnectionSocket failure _rep is null." );
      }
 } }
  
 /** /**
Line 347 
Line 446 
 */ */
 Uint32 HTTPAcceptor::getOutstandingRequestCount() Uint32 HTTPAcceptor::getOutstandingRequestCount()
 { {
      Uint32 count = 0;
   
      _rep->_connection_mut.lock(pegasus_thread_self());
    if (_rep->connections.size() > 0)    if (_rep->connections.size() > 0)
    {    {
       HTTPConnection* connection = _rep->connections[0];       HTTPConnection* connection = _rep->connections[0];
       return(connection->getRequestCount());        count = connection->getRequestCount();
    }  
    else  
    {  
       return(0);  
    }    }
      _rep->_connection_mut.unlock();
      return count;
 } }
  
 void HTTPAcceptor::unbind() void HTTPAcceptor::unbind()
Line 377 
Line 477 
       delete _rep;       delete _rep;
       _rep = 0;       _rep = 0;
    }    }
      else
      {
         PEG_TRACE_STRING(TRC_DISCARDED_DATA, Tracer::LEVEL2,
                 "HTTPAcceptor::unbind failure _rep is null." );
      }
 } }
  
 void HTTPAcceptor::destroyConnections() void HTTPAcceptor::destroyConnections()
 { {
   
   
    // For each connection created by this object:    // For each connection created by this object:
  
      _rep->_connection_mut.lock(pegasus_thread_self());
    for (Uint32 i = 0, n = _rep->connections.size(); i < n; i++)    for (Uint32 i = 0, n = _rep->connections.size(); i < n; i++)
    {    {
       HTTPConnection* connection = _rep->connections[i];       HTTPConnection* connection = _rep->connections[i];
Line 399 
Line 507 
    }    }
  
    _rep->connections.clear();    _rep->connections.clear();
      _rep->_connection_mut.unlock();
 } }
  
 void HTTPAcceptor::_acceptConnection() void HTTPAcceptor::_acceptConnection()
Line 415 
Line 524 
    struct sockaddr* accept_address;    struct sockaddr* accept_address;
 #if defined(PEGASUS_PLATFORM_ZOS_ZSERIES_IBM) #if defined(PEGASUS_PLATFORM_ZOS_ZSERIES_IBM)
    size_t address_size;    size_t address_size;
 #elif defined(PEGASUS_PLATFORM_AIX_RS_IBMCXX) || defined(PEGASUS_PLATFORM_LINUX_IX86_GNU) || defined(PEGASUS_PLATFORM_LINUX_GENERIC_GNU)  #elif defined(PEGASUS_PLATFORM_AIX_RS_IBMCXX) || defined(PEGASUS_PLATFORM_LINUX_GENERIC_GNU) || (defined(PEGASUS_OS_SOLARIS) && !defined(SUNOS_5_6))
    socklen_t address_size;    socklen_t address_size;
 #else #else
    int address_size;    int address_size;
Line 442 
Line 551 
  
    if (socket < 0)    if (socket < 0)
    {    {
        PEG_TRACE_STRING(TRC_HTTP, Tracer::LEVEL2,  
          Logger::put(Logger::STANDARD_LOG, System::CIMSERVER, Logger::TRACE,
                      "HTTPAcceptor - accept() failure.  errno: $0"
                      ,errno);
   
          PEG_TRACE_STRING(TRC_DISCARDED_DATA, Tracer::LEVEL2,
                         "HTTPAcceptor: accept() failed");                         "HTTPAcceptor: accept() failed");
       return;       return;
    }    }
  
   // set the close on exec flag
   #ifndef PEGASUS_PLATFORM_WIN32_IX86_MSVC
      int sock_flags;
    if( (sock_flags = fcntl(socket, F_GETFD, 0)) < 0)
      {
          PEG_TRACE_STRING(TRC_DISCARDED_DATA, Tracer::LEVEL2,
                           "HTTPAcceptor: fcntl(F_GETFD) failed");
      }
      else
      {
         sock_flags |= FD_CLOEXEC;
         if (fcntl(socket, F_SETFD, sock_flags) < 0)
         {
          PEG_TRACE_STRING(TRC_DISCARDED_DATA, Tracer::LEVEL2,
                           "HTTPAcceptor: fcntl(F_SETFD) failed");
         }
      }
   #endif
   
   
      Logger::put(Logger::STANDARD_LOG, System::CIMSERVER, Logger::TRACE,
                  "HTTPAcceptor - accept() success.  Socket: $1"
                  ,socket);
   
    // Create a new conection and add it to the connection list:    // Create a new conection and add it to the connection list:
  
    MP_Socket * mp_socket = new MP_Socket(socket, _sslcontext);    MP_Socket * mp_socket = new MP_Socket(socket, _sslcontext);
    if (mp_socket->accept() < 0)    if (mp_socket->accept() < 0)
    {    {
        PEG_TRACE_STRING(TRC_HTTP, Tracer::LEVEL2,         PEG_TRACE_STRING(TRC_DISCARDED_DATA, Tracer::LEVEL2,
                         "HTTPAcceptor: SSL_accept() failed");                         "HTTPAcceptor: SSL_accept() failed");
       return;       return;
    }    }
Line 468 
Line 606 
           SocketMessage::READ | SocketMessage::EXCEPTION,           SocketMessage::READ | SocketMessage::EXCEPTION,
           connection->getQueueId(), Monitor::CONNECTION)) )           connection->getQueueId(), Monitor::CONNECTION)) )
    {    {
         // ATTN-DE-P2-2003100503::TODO::Need to enhance code to return
         // an error message to Client application.
         Tracer::trace(TRC_DISCARDED_DATA, Tracer::LEVEL2,
             "HTTPAcceptor::_acceptConnection: Attempt to allocate entry in _entries table failed.");
       delete connection;       delete connection;
       Socket::close(socket);       Socket::close(socket);
         return;
    }    }
  
    // Save the socket for cleanup later:    // Save the socket for cleanup later:
    connection->_entry_index = index;    connection->_entry_index = index;
      _rep->_connection_mut.lock(pegasus_thread_self());
    _rep->connections.append(connection);    _rep->connections.append(connection);
      _rep->_connection_mut.unlock();
   }
   
   AsyncDQueue<pegasus_acceptor> pegasus_acceptor::acceptors(true, 0);
   
   void pegasus_acceptor::close_all_acceptors(void)
   {
      try
      {
         pegasus_acceptor* temp = acceptors.remove_first();
         while(temp)
         {
            delete temp;
            temp = acceptors.remove_first();
         }
 } }
      catch(...)
      {
      }
   
   }
   
   
   
   pegasus_acceptor::pegasus_acceptor(monitor_2* monitor,
                                      MessageQueue* outputMessageQueue,
                                      Boolean localConnection,
                                      Uint32 portNumber,
                                      SSLContext* sslcontext)
     : _monitor(monitor), _outputMessageQueue(outputMessageQueue),
       _localConnection(localConnection), _portNumber(portNumber),
       _sslcontext(sslcontext), connections(true, 0)
   {
   
        Socket::initializeInterface();
        try {
          acceptors.insert_first(this);
        }
        catch(...){
        }
   
   }
   
   pegasus_acceptor::~pegasus_acceptor(void)
   {
      unbind();
      Socket::uninitializeInterface();
      try {
        acceptors.remove(this);
      }
      catch(...){
      }
   
   }
   
   
   void pegasus_acceptor::bind()
   {
   
     PEGASUS_SOCKLEN_SIZE addr_size;
     struct sockaddr *addr;
     struct sockaddr_in addr_in;
   # ifdef PEGASUS_LOCAL_DOMAIN_SOCKET
     struct sockaddr_un addr_un;
   #endif
   
     memset(&addr_in, 0, sizeof(addr_in));
     addr_in.sin_addr.s_addr = INADDR_ANY;
     addr_in.sin_family = AF_INET;
     addr_in.sin_port = htons(_portNumber);
     addr = (struct sockaddr*) &addr_in;
     addr_size = sizeof(addr_in);
   
     // first step: determine which kind of socket factory to initialize,
     // then create the socket and bind it to an address
     if(_localConnection == true){
   #ifdef PEGASUS_LOCAL_DOMAIN_SOCKET
       unix_socket_factory sf;
       pegasus_socket temp(&sf);
       _listener = temp;
   
       memset(&addr_un, 0, sizeof(addr_un));
       addr_un.sun_family = AF_UNIX;
       strcpy(addr_un.sun_path, PEGASUS_LOCAL_DOMAIN_SOCKET_PATH);
   #ifdef PEGASUS_PLATFORM_OS400_ISERIES_IBM
       AtoE(addr_un.sun_path);
   #endif
       addr = (struct sockaddr*) &addr_un;
       addr_size = sizeof(addr_un);
       _listener.socket(AF_UNIX, SOCK_STREAM, 0);
   #else
       bsd_socket_factory sf;
       pegasus_socket temp(&sf);
       _listener = temp;
       _listener.socket(AF_UNIX, SOCK_STREAM, 0);
   #endif
     }
     else if( _sslcontext != 0 ) {
   #ifdef PEGASUS_HAS_SSL
       ssl_socket_factory sf;
       pegasus_socket temp(&sf, _sslcontext);
   #else
       bsd_socket_factory sf;
       pegasus_socket temp(&sf);
   #endif
       _listener = temp;
       _listener.socket(PF_INET, SOCK_STREAM, 0);
     }
     else {
       bsd_socket_factory sf;
       pegasus_socket temp(&sf);
       _listener = temp;
       _listener.socket(PF_INET, SOCK_STREAM, 0);
     }
   
     _listener.bind((struct sockaddr*)addr, addr_size);
   
     // second step: listen on the socket
   
     _listener.listen(5);
   
     // third step: add this listening socket to the monitor
   
      _monitor->tickle();
      _monitor->add_entry(_listener, LISTEN, this, this);
   }
   
   
   /** Unbind from the given port.
    */
   void pegasus_acceptor::unbind()
   {
     // remove the socket from the monitor
     _monitor->remove_entry((Sint32)_listener);
   
     // close the socket
     _listener.close();
   }
   
   
         /** Close the connection socket.
          */
   void pegasus_acceptor::closeConnectionSocket()
   {
     unbind();
   }
   
   
         /** Reopen the connection socket.
          */
   void pegasus_acceptor::reopenConnectionSocket()
   {
     bind();
   }
   
   
     /** Returns the number of outstanding requests
      */
   Uint32 pegasus_acceptor::getOutstandingRequestCount()
   {
     return _monitor->getOutstandingRequestCount();
   }
   
   Boolean pegasus_acceptor::operator ==(const pegasus_acceptor& pa)
   {
     if(this == &pa)
       return true;
     return false;
   }
   
   Boolean pegasus_acceptor::operator ==(void* pa)
   {
     if((void*)this == pa)
       return true;
     return false;
   }
   
   
   pegasus_acceptor* pegasus_acceptor::find_acceptor(Boolean local, Uint32 port)
   {
     pegasus_acceptor* temp = 0;
   
     try {
       acceptors.try_lock(pegasus_thread_self());
       temp = acceptors.next(temp);
       while(temp){
         if( local == true ){
           if(temp->_localConnection){
             acceptors.unlock();
             return temp;
           }
         }
         if(temp->_localConnection == local && temp->_portNumber ==port){
           acceptors.unlock();
           return temp;
         }
         temp = acceptors.next(temp);
       }
       acceptors.unlock();
     }
     catch(...){
     }
     return temp;
   }
   
   class m2e_rep;
   
   void pegasus_acceptor::accept_dispatch(monitor_2_entry *entry)
   {
     pegasus_acceptor* myself = (pegasus_acceptor*)entry->get_accept();
   
     HTTPConnection2* connection = new HTTPConnection2(entry->_rep->psock, myself->_outputMessageQueue);
   
     // set the entry's dispatch parameter to point to the connection object
     entry->set_dispatch ((void*)connection);
   
     monitor_2::insert_connection(connection);
   
   }
   
  
 PEGASUS_NAMESPACE_END PEGASUS_NAMESPACE_END


Legend:
Removed from v.1.26  
changed lines
  Added in v.1.47

No CVS admin address has been configured
Powered by
ViewCVS 0.9.2