#include "gb-include.h" #define X_DISPLAY_MISSING 1 //#include //#include #include #include "CollectionRec.h" #include "Pages.h" #include "Statsdb.h" #include "Hostdb.h" #include "SafeBuf.h" #include "SafeList.h" #include "Threads.h" class StateStatsdb { public: TcpSocket *m_socket; HttpRequest m_request; SafeBuf m_sb2; // Original timestamp request data time_t m_startDate; time_t m_endDate; long m_datePeriod; long m_dateUnits; // Timestamp data modified for the request time_t m_startDateR; time_t m_endDateR; // For the auto-update AJAX script bool m_autoUpdate; long m_samples; // Misc. request data long m_hostId; // Request & build flags bool m_dateLimit; bool m_dateCustom; bool m_cacti; bool m_now; long m_niceness; }; static time_t genDate( char *date, long dateLen ) ; static void sendReply ( void *st ) ; // . returns false if blocked, otherwise true // . sets g_errno on error bool sendPageStatsdb ( TcpSocket *s, HttpRequest *r ) { char *cgi; long cgiLen; StateStatsdb *st; try { st = new StateStatsdb; } catch ( ... ) { g_errno = ENOMEM; log(LOG_INFO, "PageStatsdb: failed to allocate state memory."); return true; } mnew( st, sizeof(StateStatsdb), "PageStatsdb" ); st->m_niceness = MAX_NICENESS; st->m_socket = s; //st->m_request = *r; st->m_request.copy ( r ); // hostId must be one of the following: // 0-n - a valid hostId // -1 - a sample (subset) of the hosts // -2 - all hosts // -3 - this host st->m_hostId = r->getLong( "host", -3 ); if ( st->m_hostId == -3 ) st->m_hostId = g_hostdb.getMyHostId(); // If we are pulling from multiple hosts, are we merging // the data into a single graph? // TODO: // - Make sure this always happens. Now our only concern // is how many stats we will be drawing. //st->m_mergeResults = (bool )r->getLong( "merge_results" , 1 ); // get session parameters st->m_cacti = (bool )r->getLong( "cacti" , 0 ); // get date parameters cgi = r->getString( "sdate" , &cgiLen , NULL ); st->m_startDate = genDate( cgi, cgiLen ); cgi = r->getString( "edate" , &cgiLen , NULL ); st->m_endDate = genDate( cgi, cgiLen ); st->m_dateCustom = (bool)r->getLong( "custom", 0 ); // default to 10 hours, i would do 1 day except that there are // some bugs that mess up the display a lot when i do that st->m_datePeriod = r->getLong( "date_period" , 300 );//36000 ); st->m_dateUnits = r->getLong( "date_units" , 1 );//SECS_PER_MIN st->m_now = (bool)r->getLong( "date_now" , 1 ); st->m_autoUpdate = (bool)r->getLong( "auto_update" , 0 ); // # samples in moving average st->m_samples = r->getLong( "samples" , 300 ); //if ( st->m_columns < MIN_COLUMNS || st->m_columns > MAX_COLUMNS ) // st->m_columns = DEF_COLUMNS; if ( st->m_now ) st->m_startDate = (time_t)getTimeGlobalNoCore(); st->m_startDateR = st->m_startDate; st->m_endDateR = st->m_endDate; if ( ! st->m_dateCustom ) { st->m_endDateR = st->m_startDateR - ( st->m_datePeriod * st->m_dateUnits ); st->m_endDate = st->m_endDateR; } // // this is no longer a gif, but an html graph in g_statsdb.m_sb // if ( ! g_statsdb.makeGIF ( st->m_endDateR , st->m_startDateR , st->m_samples , &st->m_sb2 , st , sendReply ) ) return false; // if we didn't block call it ourselves directly sendReply ( st ); return true; } static void writeControls ( SafeBuf *buf, StateStatsdb *st ) ; void sendReply ( void *state ) { StateStatsdb *st = (StateStatsdb *)state; if ( g_errno ) { g_httpServer.sendErrorReply(st->m_socket, 500,mstrerror(g_errno)); return; } TcpSocket *s = st->m_socket; SafeBuf buf( 1024*32 , "tmpbuf0" ); SafeBuf tmpBuf( 1024 , "tmpbuf1" ); // // take these out until we need them! // /* // print the top of the page tmpBuf.safePrintf( //"\n" "\n" "" "\n" "\n" "\n" "\n" ); */ // make the query string char qs[1024]; sprintf(qs,"&date_period=%li&date_units=%li&samples=%li", st->m_datePeriod, st->m_dateUnits, st->m_samples); // print standard header g_pages.printAdminTop ( &buf , st->m_socket , &st->m_request , qs ); buf.cat ( tmpBuf ); //g_pages.printAdminTop2 ( &buf , st->m_socket , &st->m_request, NULL , // tmpBuf.getBufStart(), tmpBuf.length() ); // write the controls section of the page writeControls( &buf, st ); // Debug print of CGI parameters and errors char startTimeStr[30]; char endTimeStr[30]; strncpy( startTimeStr, ctime( &st->m_startDate ), 30 ); strncpy( endTimeStr, ctime( &st->m_endDate ), 30 ); buf.safePrintf("
\n"); if ( ! g_conf.m_useStatsdb ) buf.safePrintf("Statsdb disabled. " "Turn on in the master controls." "\n" ); buf.safePrintf("\n"); buf.safePrintf("\n"); // the map key buf.safePrintf("\n"); buf.safePrintf( "
" "
"); ///////////////////////// // // insert the div graph here // ///////////////////////// buf.cat ( g_statsdb.m_gw ); // purge it g_statsdb.m_gw.purge(); g_statsdb.m_dupTable.reset(); //"" //st->m_hostId, //g_statsdb.getImgHeight(), //g_statsdb.getImgWidth()); buf.safePrintf("
" //"class=\"statsdb_image\">" "
"); buf.cat ( st->m_sb2 ); buf.safePrintf("
\n" ); buf.safePrintf("
"); // print the bottom of the page g_pages.printAdminBottom2( &buf ); g_errno = 0; mdelete ( st, sizeof(StateStatsdb), "PageStatsdb" ); delete st; g_httpServer.sendDynamicPage ( s, buf.getBufStart(), buf.length() ); } void writeControls ( SafeBuf *buf, StateStatsdb *st ) { // Print the controls. struct tm *tmBuild; buf->safePrintf ( "
\n" "
\n" ); g_pages.printFormTop( buf, &st->m_request ); // Print the start date (most recent date) buf->safePrintf ( "
\n" //"
" //"Time Selection" //"" //"
\n" "\n" "" "" "" "" "\n" "\n" "\n" "\n" ); // "start at current time" buf->safePrintf("\n" "\n"); buf->safePrintf( "\n" "\n" "\n" "\n" ); buf->safePrintf( "\n" "\n" "\n" "\n" //"\n" //"\n" "\n" "\n" "\n" "\n"); */ // This checkbox pulls the current time from the server, // and uses it for the request. Can only use the time // period dialog when this is selected. buf->safePrintf ( "
Moving Average Samples" "" "
Start \n", st->m_samples ); tmBuild = localtime( &st->m_startDate ); buf->safePrintf ( "\n", tmBuild->tm_mon + 1, tmBuild->tm_mday, tmBuild->tm_year + 1900, tmBuild->tm_hour, tmBuild->tm_min ); buf->safePrintf ( "\n" "\n" "
"); buf->safePrintf("m_now ) buf->safePrintf( " checked" ); buf->safePrintf ( " /> Start at Current Time\n" "
Go back \n" "\n" "\n" "
Go back to \n"//, //st->m_datePeriod ); tmBuild = localtime( &st->m_endDate ); buf->safePrintf ( "\n", tmBuild->tm_mon + 1, tmBuild->tm_mday, tmBuild->tm_year + 1900, tmBuild->tm_hour, tmBuild->tm_min ); buf->safePrintf ( "\n" "\n" "
\n" //"m_dateCustom ) buf->safePrintf( " checked" ); buf->safePrintf ( " /> Custom End Time\n" "
\n" "m_now ) buf->safePrintf( " checked" ); buf->safePrintf ( " /> Start at Current Time\n" "
\n" ); buf->safePrintf ("
\n"); /* buf->safePrintf ( "
\n" "
" "Stat Selection" "
\n" "\n" "\n" "\n" "\n" "\n" //"\n" //"\n" //"\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "
Host(s) \n" "\n" "
Statistic \n" //"" "
\n" "m_multiStat ) buf->safePrintf( " checked" ); buf->safePrintf ( " /> Select Multiple Stats\n" "
\n" "" "
\n" "
\n" ); // Print the graph type selection, can select one of: // . Line - just a line drawn through each point // . Fill - like the above, but fills under the line // . Block - uses vertical bars to draw the graph buf->safePrintf ( "
\n" "
" "Display Properties" "
\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" ); */ //for ( uint32_t i=MIN_COLUMNS; i <= MAX_COLUMNS; i++ ) { // buf->safePrintf( "m_columns == i ) // buf->safePrintf ( " selected" ); // buf->safePrintf( ">%u\n", i ); //} buf->safePrintf( "\n" "\n" "\n" "
Graph Style\n" "m_linePlot ) buf->safePrintf( " checked" ); buf->safePrintf ( " />" "Line" " \n" "m_lineFill ) buf->safePrintf( " checked" ); buf->safePrintf ( " />" "Fill" " \n" "m_lineBlock ) buf->safePrintf( " checked" ); buf->safePrintf ( " />" "Block\n" "
GIF Size\n" "\n" "
Columns\n" "\n" "
\n" "m_autoUpdate ) buf->safePrintf( " checked" ); buf->safePrintf ( " /> Auto Update Stats\n" ); buf->safePrintf ( "
\n" "\n" ); g_pages.printFormData( buf, st->m_socket, &st->m_request ); buf->safePrintf ( "" "
\n\n
\n
\n" ); } time_t genDate( char *date, long dateLen ) { time_t result = -1; // the date string should always be the same length if ( ! date || dateLen != 16 ) return result; struct tm tmRef; struct tm tmBuild; //* memset( (char *)&tmRef, 0, sizeof( tmRef ) ); time_t now = (time_t)getTimeGlobal(); localtime_r( &now, &tmRef ); now = mktime( &tmRef ); // */ char tmp[18]; char *p = tmp; memcpy( p, date, dateLen ); p[2] = '\0'; p[5] = '\0'; p[10] = '\0'; p[13] = '\0'; p[16] = '\0'; memset( (char *)&tmBuild, 0, sizeof( tmBuild ) ); tmBuild.tm_mon = atoi( p ) - 1; p += 3; tmBuild.tm_mday = atoi( p ); p += 3; tmBuild.tm_year = atoi( p ) - 1900; p += 5; tmBuild.tm_hour = atoi( p ); p += 3; tmBuild.tm_min = atoi( p ); p += 3; tmBuild.tm_isdst = tmRef.tm_isdst; p += 3; // We must manually adjust for DST difference // if the current state of DST does not match // that of the date that was requested. /* struct tm nowDST; struct tm resultDST; localtime_r( &now, &nowDST ); localtime_r( &result, &resultDST ); if ( nowDST.tm_isdst && !resultDST.tm_isdst ) tmBuild.tm_hour++; else if ( !nowDST.tm_isdst && resultDST.tm_isdst ) tmBuild.tm_hour--; memcpy( p, date, dateLen ); p[16] = '\0'; log ( LOG_DEBUG, "stats: user string [%s]", p ); log ( LOG_DEBUG, "stats: user provided time [%s]", ctime( &result ) ); log ( LOG_DEBUG, "stats: our timestamp [%s]", ctime( &now ) ); // */ result = mktime( &tmBuild ); return result; }