2009-10-14 16:29:32 -04:00
/* dbwebserver.cpp
This is the administrative web page displayed on port 28017.
*/
2008-11-29 20:01:58 -05:00
/**
* Copyright ( C ) 2008 10 gen Inc .
2008-12-28 20:28:49 -05:00
*
2008-11-29 20:01:58 -05:00
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU Affero General Public License , version 3 ,
* as published by the Free Software Foundation .
2008-12-28 20:28:49 -05:00
*
2008-11-29 20:01:58 -05:00
* This program 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 Affero General Public License for more details .
2008-12-28 20:28:49 -05:00
*
2008-11-29 20:01:58 -05:00
* You should have received a copy of the GNU Affero General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
2010-04-27 15:27:52 -04:00
# include "pch.h"
2008-11-29 20:01:58 -05:00
# include "../util/miniwebserver.h"
2010-05-07 16:42:55 -04:00
# include "../util/mongoutils/html.h"
2009-01-30 15:06:33 -05:00
# include "../util/md5.hpp"
2008-11-29 20:01:58 -05:00
# include "db.h"
2008-11-30 21:00:54 -05:00
# include "repl.h"
2010-04-21 18:46:31 -04:00
# include "replpair.h"
2009-01-02 16:41:13 -05:00
# include "instance.h"
2009-01-20 11:05:53 -05:00
# include "security.h"
2010-02-02 17:16:25 -05:00
# include "stats/snapshots.h"
2010-02-10 15:34:41 -05:00
# include "background.h"
2010-03-18 17:11:02 -04:00
# include "commands.h"
2009-01-30 15:06:33 -05:00
# include <pcrecpp.h>
2009-07-24 10:53:46 -04:00
# include <boost/date_time/posix_time/posix_time.hpp>
2009-10-15 16:47:35 -04:00
# undef assert
2010-04-23 20:09:32 -04:00
# define assert MONGO_assert
2009-07-24 10:53:46 -04:00
2009-01-14 17:09:51 -05:00
namespace mongo {
2010-04-27 12:51:49 -04:00
using namespace mongoutils : : html ;
2010-05-05 14:00:25 -04:00
using namespace bson ;
2010-04-27 12:51:49 -04:00
2010-05-07 16:42:55 -04:00
extern void fillRsLog ( stringstream & ) ;
2009-04-29 14:14:51 -04:00
extern string bind_ip ;
2009-01-15 10:17:11 -05:00
extern const char * replInfo ;
bool getInitialSyncCompleted ( ) ;
time_t started = time ( 0 ) ;
/*
string toString ( ) {
stringstream ss ;
unsigned long long dt = last - start ;
ss < < dt / 1000 ;
ss < < ' \t ' ;
ss < < timeLocked / 1000 < < ' \t ' ;
if ( dt )
ss < < ( timeLocked * 100 ) / dt < < ' % ' ;
return ss . str ( ) ;
2008-11-30 21:00:54 -05:00
}
2009-01-15 10:17:11 -05:00
*/
struct Timing {
Timing ( ) {
start = timeLocked = 0 ;
2008-11-30 21:00:54 -05:00
}
2009-01-15 10:17:11 -05:00
unsigned long long start , timeLocked ;
} ;
2008-11-30 21:00:54 -05:00
2009-01-15 10:17:11 -05:00
bool _bold ;
string bold ( bool x ) {
_bold = x ;
return x ? " <b> " : " " ;
}
string bold ( ) {
return _bold ? " </b> " : " " ;
2008-11-30 21:00:54 -05:00
}
2008-11-29 20:01:58 -05:00
2010-04-19 21:05:46 -04:00
bool execCommand ( Command * c ,
Client & client , int queryOptions ,
const char * ns , BSONObj & cmdObj ,
BSONObjBuilder & result ,
bool fromRepl ) ;
2009-01-15 10:17:11 -05:00
class DbWebServer : public MiniWebServer {
public :
2010-05-06 15:38:58 -04:00
DbWebServer ( const string & ip , int port ) : MiniWebServer ( ip , port ) { }
2010-04-05 08:57:44 -04:00
2010-05-06 15:38:58 -04:00
private :
2009-01-15 10:17:11 -05:00
// caller locks
void doLockedStuff ( stringstream & ss ) {
2010-01-02 01:25:53 -05:00
ss < < " # databases: " < < dbHolder . size ( ) < < ' \n ' ;
2010-02-02 17:16:25 -05:00
2010-04-22 21:41:51 -04:00
if ( ClientCursor : : byLocSize ( ) > 500 )
ss < < bold ( ClientCursor : : byLocSize ( ) > 10000 ) < < " Cursors byLoc.size(): " < < ClientCursor : : byLocSize ( ) < < bold ( ) < < ' \n ' ;
2010-04-23 16:55:31 -04:00
ss < < " \n replication: " ;
2010-04-22 21:41:51 -04:00
if ( * replInfo )
ss < < " \n replInfo: " < < replInfo < < " \n \n " ;
2010-04-22 13:07:36 -04:00
if ( replSet ) {
2010-04-27 12:51:49 -04:00
ss < < a ( " " , " see replSetGetStatus link top of page " ) < < " --replSet </a> " < < cmdLine . replSet < < ' \n ' ;
2010-04-22 13:07:36 -04:00
}
else {
2010-04-23 16:55:31 -04:00
ss < < " \n master: " < < replSettings . master < < ' \n ' ;
2010-04-22 13:07:36 -04:00
ss < < " slave: " < < replSettings . slave < < ' \n ' ;
if ( replPair ) {
ss < < " replpair: \n " ;
ss < < replPair - > getInfo ( ) ;
}
bool seemCaughtUp = getInitialSyncCompleted ( ) ;
if ( ! seemCaughtUp ) ss < < " <b> " ;
ss < < " initialSyncCompleted: " < < seemCaughtUp ;
if ( ! seemCaughtUp ) ss < < " </b> " ;
ss < < ' \n ' ;
2009-01-15 10:17:11 -05:00
}
2010-02-02 17:16:25 -05:00
auto_ptr < SnapshotDelta > delta = statsSnapshots . computeDelta ( ) ;
if ( delta . get ( ) ) {
2010-04-22 13:07:36 -04:00
ss < < " \n <b>dbtop</b> (occurences|percent of elapsed) \n " ;
ss < < " <table border=1 cellpadding=2 cellspacing=0> " ;
2010-04-27 12:51:49 -04:00
ss < < " <tr align='left'><th> " ;
ss < < a ( " http://www.mongodb.org/display/DOCS/Developer+FAQ#DeveloperFAQ-What%27sa%22namespace%22%3F " , " namespace " ) < <
" NS</a></th> "
2010-04-22 18:43:37 -04:00
" <th colspan=2>total</th> "
" <th colspan=2>Reads</th> "
" <th colspan=2>Writes</th> "
" <th colspan=2>Queries</th> "
" <th colspan=2>GetMores</th> "
" <th colspan=2>Inserts</th> "
" <th colspan=2>Updates</th> "
" <th colspan=2>Removes</th> " ;
2010-04-24 16:43:09 -04:00
ss < < " </tr> \n " ;
2010-02-02 17:16:25 -05:00
2010-02-04 14:25:49 -05:00
display ( ss , ( double ) delta - > elapsed ( ) , " GLOBAL " , delta - > globalUsageDiff ( ) ) ;
2010-02-02 17:16:25 -05:00
Top : : UsageMap usage = delta - > collectionUsageDiff ( ) ;
for ( Top : : UsageMap : : iterator i = usage . begin ( ) ; i ! = usage . end ( ) ; i + + ) {
2010-02-04 14:25:49 -05:00
display ( ss , ( double ) delta - > elapsed ( ) , i - > first , i - > second ) ;
2010-02-02 17:16:25 -05:00
}
ss < < " </table> " ;
2009-01-15 10:17:11 -05:00
}
2010-02-02 17:16:25 -05:00
statsSnapshots . outputLockInfoHTML ( ss ) ;
2010-02-10 15:34:41 -05:00
BackgroundOperation : : dump ( ss ) ;
2010-02-02 17:16:25 -05:00
}
void display ( stringstream & ss , double elapsed , const Top : : UsageData & usage ) {
ss < < " <td> " ;
ss < < usage . count ;
2010-03-16 16:01:59 -04:00
ss < < " </td><td> " ;
2010-02-02 17:16:25 -05:00
double per = 100 * ( ( double ) usage . time ) / elapsed ;
ss < < setprecision ( 2 ) < < fixed < < per < < " % " ;
ss < < " </td> " ;
}
void display ( stringstream & ss , double elapsed , const string & ns , const Top : : CollectionData & data ) {
if ( ns ! = " GLOBAL " & & data . total . count = = 0 )
return ;
ss < < " <tr><th> " < < ns < < " </th> " ;
display ( ss , elapsed , data . total ) ;
display ( ss , elapsed , data . readLock ) ;
display ( ss , elapsed , data . writeLock ) ;
display ( ss , elapsed , data . queries ) ;
display ( ss , elapsed , data . getmore ) ;
display ( ss , elapsed , data . insert ) ;
display ( ss , elapsed , data . update ) ;
display ( ss , elapsed , data . remove ) ;
2010-04-24 16:43:09 -04:00
ss < < " </tr> \n " ;
2008-12-31 09:17:30 -05:00
}
2009-01-14 17:17:24 -05:00
2010-02-09 10:35:31 -05:00
void tablecell ( stringstream & ss , bool b ) {
ss < < " <td> " < < ( b ? " <b>X</b> " : " " ) < < " </td> " ;
}
template < typename T >
void tablecell ( stringstream & ss , const T & t ) {
ss < < " <td> " < < t < < " </td> " ;
}
2009-01-15 10:17:11 -05:00
void doUnlockedStuff ( stringstream & ss ) {
2009-03-25 17:30:30 -04:00
/* this is in the header already ss << "port: " << port << '\n'; */
2010-04-20 12:29:00 -04:00
ss < < mongodVersion ( ) < < ' \n ' ;
ss < < " git hash: " < < gitVersion ( ) < < ' \n ' ;
ss < < " sys info: " < < sysInfo ( ) < < ' \n ' ;
2010-04-23 15:50:49 -04:00
ss < < " uptime: " < < time ( 0 ) - started < < " seconds \n " ;
2009-02-04 13:22:02 -05:00
if ( replAllDead )
ss < < " <b>replication replAllDead= " < < replAllDead < < " </b> \n " ;
2010-04-27 12:51:49 -04:00
ss < < a ( " " , " information on caught assertion exceptions " ) ;
2010-04-22 13:07:36 -04:00
ss < < " assertions:</a> \n " ;
2009-01-15 10:17:11 -05:00
for ( int i = 0 ; i < 4 ; i + + ) {
if ( lastAssert [ i ] . isSet ( ) ) {
2010-04-22 18:43:37 -04:00
if ( i = = 3 ) ss < < " uassert " ;
else if ( i = = 2 ) ss < < " massert " ;
else if ( i = = 0 ) ss < < " assert " ;
else if ( i = = 1 ) ss < < " warnassert " ;
2009-01-15 10:17:11 -05:00
else ss < < i ;
2010-04-22 13:07:36 -04:00
ss < < ' ' < < lastAssert [ i ] . toString ( ) ;
2009-01-15 10:17:11 -05:00
}
}
2009-01-14 17:17:24 -05:00
2010-05-02 17:31:50 -04:00
ss < < " \n <table border=1 cellpadding=2 cellspacing=0> " ;
2010-02-09 10:35:31 -05:00
ss < < " <tr align='left'> "
2010-05-02 17:31:50 -04:00
< < " <th>Client</th> "
2010-02-09 10:35:31 -05:00
< < " <th>OpId</th> "
< < " <th>Active</th> "
< < " <th>LockType</th> "
< < " <th>Waiting</th> "
< < " <th>SecsRunning</th> "
< < " <th>Op</th> "
< < " <th>NameSpace</th> "
< < " <th>Query</th> "
< < " <th>client</th> "
2010-03-15 11:18:08 -04:00
< < " <th>msg</th> "
< < " <th>progress</th> "
2010-02-09 10:35:31 -05:00
< < " </tr> \n " ;
2009-10-16 15:36:34 -04:00
{
2010-03-15 09:42:01 -07:00
scoped_lock bl ( Client : : clientsMutex ) ;
2009-10-16 15:36:34 -04:00
for ( set < Client * > : : iterator i = Client : : clients . begin ( ) ; i ! = Client : : clients . end ( ) ; i + + ) {
Client * c = * i ;
CurOp & co = * ( c - > curop ( ) ) ;
2010-02-09 10:35:31 -05:00
ss < < " <tr><td> " < < c - > desc ( ) < < " </td> " ;
tablecell ( ss , co . opNum ( ) ) ;
tablecell ( ss , co . active ( ) ) ;
tablecell ( ss , co . getLockType ( ) ) ;
tablecell ( ss , co . isWaitingForLock ( ) ) ;
if ( co . active ( ) )
tablecell ( ss , co . elapsedSeconds ( ) ) ;
else
tablecell ( ss , " " ) ;
tablecell ( ss , co . getOp ( ) ) ;
tablecell ( ss , co . getNS ( ) ) ;
if ( co . haveQuery ( ) )
tablecell ( ss , co . query ( ) ) ;
else
tablecell ( ss , " " ) ;
tablecell ( ss , co . getRemoteString ( ) ) ;
2010-03-15 11:18:08 -04:00
tablecell ( ss , co . getMessage ( ) ) ;
tablecell ( ss , co . getProgressMeter ( ) . toString ( ) ) ;
2010-04-24 16:43:09 -04:00
ss < < " </tr> \n " ;
2009-10-16 15:36:34 -04:00
}
}
2009-12-07 15:42:26 -05:00
ss < < " </table> \n " ;
2009-01-15 10:17:11 -05:00
}
2009-01-30 15:06:33 -05:00
2010-05-06 15:38:58 -04:00
private :
2010-05-14 14:45:54 -04:00
string hostname ( ) {
stringstream s ;
s < < getHostName ( ) ;
if ( mongo : : cmdLine . port ! = CmdLine : : DefaultDBPort )
s < < ' : ' < < mongo : : cmdLine . port ;
return s . str ( ) ;
}
2010-05-05 14:00:25 -04:00
/* /_replSet show replica set status in html format */
2010-05-03 13:33:49 -04:00
string _replSet ( ) {
stringstream s ;
2010-05-14 14:45:54 -04:00
s < < start ( " Replica Set Status " + hostname ( ) ) ;
2010-05-11 13:33:55 -04:00
s < < p ( a ( " / " , " back " , " Home " ) + " | " +
2010-05-14 16:32:24 -04:00
a ( " /local/system.replset/?html=1 " , " " , " View Replset Config " ) + " | " +
2010-05-12 17:43:21 -04:00
a ( " /replSetGetStatus?text " , " " , " replSetGetStatus " ) + " | " +
a ( " http://www.mongodb.org/display/DOCS/Replica+Sets " , " " , " Docs " )
) ;
2010-05-03 13:33:49 -04:00
if ( theReplSet = = 0 ) {
2010-05-07 22:39:33 -04:00
if ( cmdLine . replSet . empty ( ) )
s < < p ( " Not using --replSet " ) ;
2010-05-13 17:18:17 -04:00
else {
s < < p ( " Still starting up, or else set is not yet " + a ( " http://www.mongodb.org/display/DOCS/Replica+Set+Configuration#InitialSetup " , " " , " initiated " )
+ " .<br> " + ReplSet : : startupStatusMsg ) ;
}
2010-05-03 13:33:49 -04:00
}
else {
2010-05-05 16:33:35 -04:00
try {
theReplSet - > summarizeAsHtml ( s ) ;
2010-05-03 13:33:49 -04:00
}
2010-05-07 16:42:55 -04:00
catch ( . . . ) { s < < " error summarizing replset status \n " ; }
2010-05-03 13:33:49 -04:00
}
2010-05-09 15:16:14 -04:00
s < < p ( " Recent replset log activity: " ) ;
2010-05-07 16:42:55 -04:00
fillRsLog ( s ) ;
2010-05-05 16:33:35 -04:00
s < < _end ( ) ;
2010-05-03 13:33:49 -04:00
return s . str ( ) ;
}
2010-05-06 15:38:58 -04:00
bool allowed ( const char * rq , vector < string > & headers , const SockAddr & from ) {
2010-04-02 13:01:32 -04:00
if ( from . isLocalHost ( ) )
2009-05-13 12:28:59 -04:00
return true ;
2010-05-06 15:38:58 -04:00
{
readlocktryassert rl ( " admin.system.users " , 10000 ) ;
if ( Helpers : : isEmpty ( " admin.system.users " ) )
return true ;
}
2010-03-26 16:33:12 -04:00
Client : : GodScope gs ;
2009-01-30 15:06:33 -05:00
string auth = getHeader ( rq , " Authorization " ) ;
if ( auth . size ( ) > 0 & & auth . find ( " Digest " ) = = 0 ) {
auth = auth . substr ( 7 ) + " , " ;
map < string , string > parms ;
pcrecpp : : StringPiece input ( auth ) ;
string name , val ;
pcrecpp : : RE re ( " ( \\ w+)= \" ?(.*?) \" ?, " ) ;
while ( re . Consume ( & input , & name , & val ) ) {
parms [ name ] = val ;
}
BSONObj user = db . findOne ( " admin.system.users " , BSON ( " user " < < parms [ " username " ] ) ) ;
if ( ! user . isEmpty ( ) ) {
string ha1 = user [ " pwd " ] . str ( ) ;
string ha2 = md5simpledigest ( ( string ) " GET " + " : " + parms [ " uri " ] ) ;
2010-05-06 15:46:11 -04:00
stringstream r ;
r < < ha1 < < ' : ' < < parms [ " nonce " ] ;
2009-01-30 15:06:33 -05:00
if ( parms [ " nc " ] . size ( ) & & parms [ " cnonce " ] . size ( ) & & parms [ " qop " ] . size ( ) ) {
2010-05-06 15:46:11 -04:00
r < < ' : ' ;
r < < parms [ " nc " ] ;
r < < ' : ' ;
r < < parms [ " cnonce " ] ;
r < < ' : ' ;
r < < parms [ " qop " ] ;
2009-01-30 15:06:33 -05:00
}
2010-05-06 15:46:11 -04:00
r < < ' : ' ;
r < < ha2 ;
string r1 = md5simpledigest ( r . str ( ) ) ;
2009-01-30 15:06:33 -05:00
2010-05-06 15:46:11 -04:00
if ( r1 = = parms [ " response " ] )
2009-01-30 15:06:33 -05:00
return true ;
}
}
stringstream authHeader ;
authHeader
< < " WWW-Authenticate: "
< < " Digest realm= \" mongo \" , "
< < " nonce= \" abc \" , "
< < " algorithm=MD5, qop= \" auth \" "
;
headers . push_back ( authHeader . str ( ) ) ;
return 0 ;
}
2008-12-04 14:33:18 -05:00
2010-05-06 15:38:58 -04:00
string _commands ( ) {
stringstream ss ;
ss < < start ( " Commands List " ) ;
ss < < p ( a ( " / " , " back " , " Home " ) ) ;
ss < < p ( " <b>MongoDB List of <a href= \" http://www.mongodb.org/display/DOCS/Commands \" >Commands</a></b> \n " ) ;
const map < string , Command * > * m = Command : : commandsByBestName ( ) ;
ss < < " S:slave-only N:no-lock R:read-lock W:write-lock A:admin-only<br> \n " ;
ss < < table ( ) ;
ss < < " <tr><th>Command</th><th>Attributes</th><th>Help</th></tr> \n " ;
for ( map < string , Command * > : : const_iterator i = m - > begin ( ) ; i ! = m - > end ( ) ; i + + )
i - > second - > htmlHelp ( ss ) ;
ss < < _table ( ) < < _end ( ) ;
return ss . str ( ) ;
}
2009-01-15 10:17:11 -05:00
virtual void doRequest (
const char * rq , // the full request
string url ,
// set these and return them:
string & responseMsg ,
int & responseCode ,
2009-05-13 12:28:59 -04:00
vector < string > & headers , // if completely empty, content-type: text/html will be added
const SockAddr & from
2009-01-15 10:17:11 -05:00
)
2008-12-04 14:33:18 -05:00
{
2009-01-15 10:17:11 -05:00
if ( url . size ( ) > 1 ) {
2010-05-06 15:38:58 -04:00
if ( ! allowed ( rq , headers , from ) ) {
responseCode = 401 ;
headers . push_back ( " Content-Type: text/plain " ) ;
responseMsg = " not allowed \n " ;
return ;
}
2010-04-19 22:24:37 -04:00
if ( url . find ( " /_status " ) = = 0 ) {
headers . push_back ( " Content-Type: application/json " ) ;
generateServerStatus ( url , responseMsg ) ;
responseCode = 200 ;
return ;
}
if ( ! cmdLine . rest ) {
responseCode = 403 ;
stringstream ss ;
ss < < " REST is not enabled. use --rest to turn on. \n " ;
ss < < " check that port " < < _port < < " is secured for the network too. \n " ;
responseMsg = ss . str ( ) ;
headers . push_back ( " Content-Type: text/plain " ) ;
return ;
}
2010-05-03 13:33:49 -04:00
if ( startsWith ( url , " /_replSet " ) ) {
responseMsg = _replSet ( ) ;
responseCode = 200 ;
return ;
}
if ( startsWith ( url , " /_commands " ) ) {
2010-05-06 15:38:58 -04:00
responseMsg = _commands ( ) ;
2010-04-23 15:50:49 -04:00
responseCode = 200 ;
return ;
}
2010-04-19 21:05:46 -04:00
/* run a command from the web ui */
const char * p = url . c_str ( ) ;
if ( * p = = ' / ' ) {
const char * h = strstr ( p , " ?text " ) ;
string cmd = p + 1 ;
if ( h & & h > p + 1 )
cmd = string ( p + 1 , h - p - 1 ) ;
const map < string , Command * > * m = Command : : webCommands ( ) ;
if ( m & & m - > count ( cmd ) ) {
Command * c = m - > find ( cmd ) - > second ;
Client & client = cc ( ) ;
BSONObjBuilder result ;
BSONObjBuilder b ;
b . append ( c - > name , 1 ) ;
BSONObj cmdObj = b . obj ( ) ;
2010-04-25 22:11:16 -04:00
execCommand ( c , client , 0 , " admin. " , cmdObj , result , false ) ;
responseCode = 200 ;
2010-04-19 21:05:46 -04:00
string j = result . done ( ) . jsonString ( JS , h ! = 0 ? 1 : 0 ) ;
if ( h = = 0 ) {
headers . push_back ( " Content-Type: application/json " ) ;
}
else {
headers . push_back ( " Content-Type: text/plain " ) ;
}
responseMsg = j ;
2010-04-20 16:24:06 -04:00
if ( h )
responseMsg + = ' \n ' ;
2010-04-19 21:05:46 -04:00
return ;
}
}
2009-01-15 10:17:11 -05:00
handleRESTRequest ( rq , url , responseMsg , responseCode , headers ) ;
return ;
}
2008-11-29 20:01:58 -05:00
2009-01-15 10:17:11 -05:00
responseCode = 200 ;
stringstream ss ;
string dbname ;
{
stringstream z ;
2010-05-14 14:45:54 -04:00
z < < " mongod " < < hostname ( ) ;
2009-01-15 10:17:11 -05:00
dbname = z . str ( ) ;
2008-11-29 20:01:58 -05:00
}
2010-05-06 10:05:48 -04:00
ss < < start ( dbname ) < < h2 ( dbname ) ;
ss < < " <a href= \" /_commands \" >List all commands</a> | \n " ;
2010-05-05 14:00:25 -04:00
ss < < " <a href= \" /_replSet \" >Replica set status</a> \n " ;
2010-04-19 12:43:45 -04:00
ss < < " <pre> " ;
2010-04-19 21:05:46 -04:00
//ss << "<a href=\"/_status\">_status</a>";
{
const map < string , Command * > * m = Command : : webCommands ( ) ;
if ( m ) {
2010-05-07 22:39:33 -04:00
ss < < a ( " " , " These read-only context-less commands can be executed from the web interface. Results are json format, unless ?text is appended in which case the result is output as text for easier human viewing " , " Commands " ) < < " : " ;
2010-04-19 21:05:46 -04:00
for ( map < string , Command * > : : const_iterator i = m - > begin ( ) ; i ! = m - > end ( ) ; i + + ) {
2010-04-23 15:50:49 -04:00
stringstream h ;
i - > second - > help ( h ) ;
string help = h . str ( ) ;
ss < < " <a href= \" / " < < i - > first < < " ?text \" " ;
if ( help ! = " no help defined " )
ss < < " title= \" " < < help < < ' " ' ;
ss < < " > " < < i - > first < < " </a> " ;
2010-04-19 21:05:46 -04:00
}
2010-04-20 12:29:00 -04:00
ss < < ' \n ' ;
2010-04-19 21:05:46 -04:00
}
}
2010-04-20 12:29:00 -04:00
ss < < ' \n ' ;
2010-04-27 12:51:49 -04:00
ss < < " HTTP <a "
2010-04-22 18:43:37 -04:00
" title= \" click for documentation on this http interface \" "
2010-04-27 12:51:49 -04:00
" href= \" http://www.mongodb.org/display/DOCS/Http+Interface \" >admin port</a>: " < < _port < < " \n " ;
2010-04-23 16:55:31 -04:00
2009-01-15 10:17:11 -05:00
doUnlockedStuff ( ss ) ;
2010-04-27 12:51:49 -04:00
ss < < " <a "
" href= \" http://www.mongodb.org/pages/viewpage.action?pageId=7209296 \" "
" title= \" snapshot: was the db in the write lock when this page was generated? \" > " ;
2010-04-23 16:55:31 -04:00
ss < < " write locked:</a> " < < ( dbMutex . info ( ) . isLocked ( ) ? " true " : " false " ) < < " \n " ;
2010-02-09 12:35:26 -05:00
{
Timer t ;
readlocktry lk ( " " , 2000 ) ;
if ( lk . got ( ) ) {
2010-04-23 16:55:31 -04:00
ss < < " time to get readlock: " < < t . millis ( ) < < " ms \n " ;
2010-02-09 12:35:26 -05:00
doLockedStuff ( ss ) ;
2009-01-15 10:17:11 -05:00
}
2010-02-09 12:35:26 -05:00
else {
2009-01-15 10:17:11 -05:00
ss < < " \n <b>timed out getting dblock</b> \n " ;
}
2008-11-29 20:01:58 -05:00
}
2010-02-09 12:35:26 -05:00
2009-01-15 10:17:11 -05:00
2010-04-24 16:43:09 -04:00
ss < < " </pre> \n </body></html> \n " ;
2009-01-15 10:17:11 -05:00
responseMsg = ss . str ( ) ;
2009-05-13 11:58:24 -04:00
2009-10-09 14:59:44 -04:00
// we want to return SavedContext from before the authentication was performed
2009-05-13 12:28:59 -04:00
if ( ! allowed ( rq , headers , from ) ) {
2009-05-13 11:58:24 -04:00
responseCode = 401 ;
responseMsg = " not allowed \n " ;
return ;
}
2008-11-29 20:01:58 -05:00
}
2010-03-19 16:31:02 -04:00
void generateServerStatus ( string url , string & responseMsg ) {
2010-03-18 17:11:02 -04:00
static vector < string > commands ;
if ( commands . size ( ) = = 0 ) {
commands . push_back ( " serverStatus " ) ;
commands . push_back ( " buildinfo " ) ;
}
2010-03-19 16:31:02 -04:00
BSONObj params ;
if ( url . find ( " ? " ) ! = string : : npos ) {
parseParams ( params , url . substr ( url . find ( " ? " ) + 1 ) ) ;
}
2010-03-18 17:11:02 -04:00
BSONObjBuilder buf ( 1024 ) ;
for ( unsigned i = 0 ; i < commands . size ( ) ; i + + ) {
string cmd = commands [ i ] ;
Command * c = Command : : findCommand ( cmd ) ;
assert ( c ) ;
assert ( c - > locktype ( ) = = 0 ) ;
2010-03-19 16:31:02 -04:00
BSONObj co ;
{
BSONObjBuilder b ;
b . append ( cmd . c_str ( ) , 1 ) ;
if ( cmd = = " serverStatus " & & params [ " repl " ] . type ( ) ) {
b . append ( " repl " , atoi ( params [ " repl " ] . valuestr ( ) ) ) ;
}
co = b . obj ( ) ;
}
2010-03-18 17:11:02 -04:00
string errmsg ;
BSONObjBuilder sub ;
if ( ! c - > run ( " admin.$cmd " , co , errmsg , sub , false ) )
buf . append ( cmd . c_str ( ) , errmsg ) ;
else
buf . append ( cmd . c_str ( ) , sub . obj ( ) ) ;
}
responseMsg = buf . obj ( ) . jsonString ( ) ;
}
2009-01-15 10:17:11 -05:00
void handleRESTRequest ( const char * rq , // the full request
string url ,
string & responseMsg ,
int & responseCode ,
vector < string > & headers // if completely empty, content-type: text/html will be added
) {
string : : size_type first = url . find ( " / " , 1 ) ;
if ( first = = string : : npos ) {
responseCode = 400 ;
return ;
}
2009-01-14 17:17:24 -05:00
2009-01-15 10:17:11 -05:00
string method = parseMethod ( rq ) ;
string dbname = url . substr ( 1 , first - 1 ) ;
string coll = url . substr ( first + 1 ) ;
string action = " " ;
2009-01-14 17:17:24 -05:00
2010-03-12 15:00:01 -05:00
BSONObj params ;
2009-01-15 10:17:11 -05:00
if ( coll . find ( " ? " ) ! = string : : npos ) {
parseParams ( params , coll . substr ( coll . find ( " ? " ) + 1 ) ) ;
coll = coll . substr ( 0 , coll . find ( " ? " ) ) ;
}
2009-01-14 17:17:24 -05:00
2009-01-15 10:17:11 -05:00
string : : size_type last = coll . find_last_of ( " / " ) ;
if ( last = = string : : npos ) {
action = coll ;
coll = " _defaultCollection " ;
}
else {
action = coll . substr ( last + 1 ) ;
coll = coll . substr ( 0 , last ) ;
}
2009-01-14 17:17:24 -05:00
2009-01-15 10:17:11 -05:00
for ( string : : size_type i = 0 ; i < coll . size ( ) ; i + + )
if ( coll [ i ] = = ' / ' )
coll [ i ] = ' . ' ;
2009-01-14 17:17:24 -05:00
2010-03-03 22:06:03 -05:00
string fullns = urlDecode ( dbname + " . " + coll ) ;
2009-01-14 17:17:24 -05:00
2009-01-15 10:17:11 -05:00
headers . push_back ( ( string ) " x-action: " + action ) ;
headers . push_back ( ( string ) " x-ns: " + fullns ) ;
2010-05-11 13:33:55 -04:00
bool html = false ;
2009-01-14 17:17:24 -05:00
2009-01-15 10:17:11 -05:00
stringstream ss ;
2009-01-14 17:17:24 -05:00
2009-01-15 10:17:11 -05:00
if ( method = = " GET " ) {
responseCode = 200 ;
2010-05-11 13:33:55 -04:00
html = handleRESTQuery ( fullns , action , params , responseCode , ss ) ;
2009-01-15 10:17:11 -05:00
}
else if ( method = = " POST " ) {
responseCode = 201 ;
handlePost ( fullns , body ( rq ) , params , responseCode , ss ) ;
}
else {
responseCode = 400 ;
headers . push_back ( " X_err: bad request " ) ;
ss < < " don't know how to handle a [ " < < method < < " ] " ;
2009-01-15 11:26:38 -05:00
out ( ) < < " don't know how to handle a [ " < < method < < " ] " < < endl ;
2009-01-15 10:17:11 -05:00
}
2009-01-14 17:17:24 -05:00
2010-05-11 13:33:55 -04:00
if ( html )
headers . push_back ( " Content-Type: text/html;charset=utf-8 " ) ;
else
headers . push_back ( " Content-Type: text/plain;charset=utf-8 " ) ;
2009-01-15 10:17:11 -05:00
responseMsg = ss . str ( ) ;
2008-12-31 15:07:01 -05:00
}
2009-01-14 17:17:24 -05:00
2010-05-11 13:33:55 -04:00
bool handleRESTQuery ( string ns , string action , BSONObj & params , int & responseCode , stringstream & out ) {
2009-01-15 10:17:11 -05:00
Timer t ;
2008-12-31 15:07:01 -05:00
2010-05-11 13:33:55 -04:00
int html = _getOption ( params [ " html " ] , 0 ) ;
2009-01-15 10:17:11 -05:00
int skip = _getOption ( params [ " skip " ] , 0 ) ;
2010-05-11 13:33:55 -04:00
int num = _getOption ( params [ " limit " ] , _getOption ( params [ " count " ] , 1000 ) ) ; // count is old, limit is new
2009-01-14 17:17:24 -05:00
2009-01-15 10:17:11 -05:00
int one = 0 ;
2010-03-12 15:00:01 -05:00
if ( params [ " one " ] . type ( ) = = String & & tolower ( params [ " one " ] . valuestr ( ) [ 0 ] ) = = ' t ' ) {
2009-01-15 10:17:11 -05:00
num = 1 ;
one = 1 ;
}
2009-01-14 17:17:24 -05:00
2009-01-15 10:17:11 -05:00
BSONObjBuilder queryBuilder ;
2009-01-14 17:17:24 -05:00
2010-03-12 15:00:01 -05:00
BSONObjIterator i ( params ) ;
while ( i . more ( ) ) {
BSONElement e = i . next ( ) ;
string name = e . fieldName ( ) ;
if ( ! name . find ( " filter_ " ) = = 0 )
2009-01-15 10:17:11 -05:00
continue ;
2009-01-14 17:17:24 -05:00
2010-05-16 10:57:52 -04:00
string field = name . substr ( 7 ) ;
2010-03-12 15:00:01 -05:00
const char * val = e . valuestr ( ) ;
2009-01-05 16:12:44 -05:00
2009-01-15 10:17:11 -05:00
char * temp ;
2009-01-14 17:17:24 -05:00
2009-01-15 10:17:11 -05:00
// TODO: this is how i guess if something is a number. pretty lame right now
double number = strtod ( val , & temp ) ;
if ( temp ! = val )
2010-05-16 10:57:52 -04:00
queryBuilder . append ( field . c_str ( ) , number ) ;
2009-01-15 10:17:11 -05:00
else
2010-05-16 10:57:52 -04:00
queryBuilder . append ( field . c_str ( ) , val ) ;
2009-01-15 10:17:11 -05:00
}
2009-01-05 16:12:44 -05:00
2009-02-09 13:04:32 -05:00
BSONObj query = queryBuilder . obj ( ) ;
2009-01-15 10:17:11 -05:00
auto_ptr < DBClientCursor > cursor = db . query ( ns . c_str ( ) , query , num , skip ) ;
2010-04-09 14:32:24 -04:00
uassert ( 13085 , " query failed for dbwebserver " , cursor . get ( ) ) ;
2010-05-11 13:33:55 -04:00
2009-01-15 10:17:11 -05:00
if ( one ) {
if ( cursor - > more ( ) ) {
BSONObj obj = cursor - > next ( ) ;
2010-05-11 13:33:55 -04:00
out < < obj . jsonString ( Strict , html ? 1 : 0 ) < < ' \n ' ;
2009-01-15 10:17:11 -05:00
}
else {
responseCode = 404 ;
}
2010-05-11 13:33:55 -04:00
return html ! = 0 ;
2008-12-31 15:07:01 -05:00
}
2010-05-11 13:33:55 -04:00
if ( html ) {
2010-05-14 16:32:24 -04:00
string title = string ( " query " ) + ns ;
2010-05-11 13:33:55 -04:00
out < < start ( title )
2010-05-14 17:00:42 -04:00
< < p ( title )
2010-05-11 13:33:55 -04:00
< < " <pre> " ;
} else {
out < < " { \n " ;
out < < " \" offset \" : " < < skip < < " , \n " ;
out < < " \" rows \" : [ \n " ;
}
2009-01-15 10:17:11 -05:00
int howMany = 0 ;
while ( cursor - > more ( ) ) {
2010-05-14 16:32:24 -04:00
if ( howMany + + & & html = = 0 )
2009-01-15 10:17:11 -05:00
out < < " , \n " ;
BSONObj obj = cursor - > next ( ) ;
2010-05-11 13:33:55 -04:00
if ( html ) {
if ( out . tellp ( ) > 4 * 1024 * 1024 ) {
out < < " Stopping output: more than 4MB returned and in html mode \n " ;
break ;
}
out < < obj . jsonString ( Strict , html ? 1 : 0 ) < < " \n \n " ;
}
else {
if ( out . tellp ( ) > 50 * 1024 * 1024 ) // 50MB limit - we are using ram
break ;
out < < " " < < obj . jsonString ( ) ;
}
}
2009-01-14 17:17:24 -05:00
2010-05-11 13:33:55 -04:00
if ( html ) {
out < < " </pre> \n " ;
if ( howMany = = 0 ) out < < p ( " Collection is empty " ) ;
out < < _end ( ) ;
}
else {
out < < " \n ], \n \n " ;
out < < " \" total_rows \" : " < < howMany < < " , \n " ;
out < < " \" query \" : " < < query . jsonString ( ) < < " , \n " ;
out < < " \" millis \" : " < < t . millis ( ) < < ' \n ' ;
out < < " } \n " ;
2009-01-15 10:17:11 -05:00
}
2009-01-14 17:17:24 -05:00
2010-05-11 13:33:55 -04:00
return html ! = 0 ;
2008-12-31 11:59:41 -05:00
}
2009-01-14 17:17:24 -05:00
2009-01-15 10:17:11 -05:00
// TODO Generate id and revision per couch POST spec
2010-03-12 15:00:01 -05:00
void handlePost ( string ns , const char * body , BSONObj & params , int & responseCode , stringstream & out ) {
2009-01-15 10:17:11 -05:00
try {
BSONObj obj = fromjson ( body ) ;
db . insert ( ns . c_str ( ) , obj ) ;
} catch ( . . . ) {
responseCode = 400 ; // Bad Request. Seems reasonable for now.
out < < " { \" ok \" : false } " ;
return ;
}
2009-01-07 10:08:12 -05:00
2009-01-15 10:17:11 -05:00
responseCode = 201 ;
out < < " { \" ok \" : true } " ;
2009-01-07 10:08:12 -05:00
}
2009-01-14 17:17:24 -05:00
2010-03-12 15:00:01 -05:00
int _getOption ( BSONElement e , int def ) {
if ( e . isNumber ( ) )
return e . numberInt ( ) ;
if ( e . type ( ) = = String )
return atoi ( e . valuestr ( ) ) ;
return def ;
2009-01-15 10:17:11 -05:00
}
2009-01-14 17:17:24 -05:00
2009-01-15 10:17:11 -05:00
private :
static DBDirectClient db ;
} ;
2008-11-29 20:01:58 -05:00
2009-01-15 10:17:11 -05:00
DBDirectClient DbWebServer : : db ;
2009-01-07 10:08:12 -05:00
2009-01-15 10:17:11 -05:00
void webServerThread ( ) {
2009-10-16 15:36:34 -04:00
Client : : initThread ( " websvr " ) ;
2010-04-05 08:57:44 -04:00
const int p = cmdLine . port + 1000 ;
DbWebServer mini ( bind_ip , p ) ;
2010-04-05 10:51:33 -04:00
log ( ) < < " web admin interface listening on port " < < p < < endl ;
mini . initAndListen ( ) ;
2009-10-16 15:36:34 -04:00
cc ( ) . shutdown ( ) ;
2009-01-15 10:17:11 -05:00
}
2009-01-14 17:09:51 -05:00
} // namespace mongo