MythTV  0.26-pre
zmserver.cpp
Go to the documentation of this file.
00001 /* Implementation of the ZMServer class.
00002  * ============================================================
00003  * This program is free software; you can redistribute it
00004  * and/or modify it under the terms of the GNU General
00005  * Public License as published bythe Free Software Foundation;
00006  * either version 2, or (at your option)
00007  * any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  *
00014  * ============================================================ */
00015 
00016 
00017 #include <iostream>
00018 #include <cstdlib>
00019 #include <cstring>
00020 #include <cstdio>
00021 #include <errno.h>
00022 #include <sys/socket.h>
00023 #include <fcntl.h>
00024 #include <netinet/in.h>
00025 #include <sys/stat.h>
00026 #include <sys/shm.h>
00027 #include <sys/mman.h>
00028 
00029 #ifdef linux
00030 #  include <sys/vfs.h>
00031 #  include <sys/statvfs.h>
00032 #  include <sys/sysinfo.h>
00033 #else
00034 #  include <sys/param.h>
00035 #  include <sys/mount.h>
00036 #  if CONFIG_CYGWIN
00037 #    include <sys/statfs.h>
00038 #  else // if !CONFIG_CYGWIN
00039 #    include <sys/sysctl.h>
00040 #  endif // !CONFIG_CYGWIN
00041 #endif
00042 
00043 #include "mythtv/mythconfig.h"
00044 
00045 #if CONFIG_DARWIN
00046 #define MSG_NOSIGNAL 0  // Apple also has SO_NOSIGPIPE?
00047 #endif
00048 
00049 #include "zmserver.h"
00050 
00051 // the version of the protocol we understand
00052 #define ZM_PROTOCOL_VERSION "7"
00053 
00054 // the maximum image size we are ever likely to get from ZM
00055 #define MAX_IMAGE_SIZE  (2048*1536*3)
00056 
00057 #define ADD_STR(list,s)  list += s; list += "[]:[]";
00058 
00059 // error messages
00060 #define ERROR_TOKEN_COUNT      "Invalid token count"
00061 #define ERROR_MYSQL_QUERY      "Mysql Query Error"
00062 #define ERROR_MYSQL_ROW        "Mysql Get Row Error"
00063 #define ERROR_FILE_OPEN        "Cannot open event file"
00064 #define ERROR_INVALID_MONITOR  "Invalid Monitor"
00065 #define ERROR_INVALID_POINTERS "Cannot get shared memory pointers"
00066 #define ERROR_INVALID_MONITOR_FUNCTION  "Invalid Monitor Function"
00067 #define ERROR_INVALID_MONITOR_ENABLE_VALUE "Invalid Monitor Enable Value"
00068 
00069 MYSQL   g_dbConn;
00070 string  g_zmversion = "";
00071 string  g_password = "";
00072 string  g_server = "";
00073 string  g_database = "";
00074 string  g_webPath = "";
00075 string  g_user = "";
00076 string  g_webUser = "";
00077 string  g_binPath = "";
00078 
00079 time_t  g_lastDBKick = 0;
00080 
00081 void loadZMConfig(const string &configfile)
00082 {
00083     cout << "loading zm config from " << configfile << endl;
00084     FILE *cfg;
00085     char line[512];
00086     char val[250];
00087 
00088     if ( (cfg = fopen(configfile.c_str(), "r")) == NULL )
00089     {
00090         fprintf(stderr,"Can't open %s\n", configfile.c_str());
00091         exit(1);
00092     }
00093 
00094     while ( fgets( line, sizeof(line), cfg ) != NULL )
00095     {
00096         char *line_ptr = line;
00097         // Trim off any cr/lf line endings
00098         size_t chomp_len = strcspn( line_ptr, "\r\n" );
00099         line_ptr[chomp_len] = '\0';
00100 
00101         // Remove leading white space
00102         size_t white_len = strspn( line_ptr, " \t" );
00103         line_ptr += white_len;
00104 
00105         // Check for comment or empty line
00106         if ( *line_ptr == '\0' || *line_ptr == '#' )
00107             continue;
00108 
00109         // Remove trailing white space
00110         char *temp_ptr = line_ptr+strlen(line_ptr)-1;
00111         while ( *temp_ptr == ' ' || *temp_ptr == '\t' )
00112         {
00113             *temp_ptr-- = '\0';
00114             temp_ptr--;
00115         }
00116 
00117         // Now look for the '=' in the middle of the line
00118         temp_ptr = strchr( line_ptr, '=' );
00119         if ( !temp_ptr )
00120         {
00121             fprintf(stderr,"Invalid data in %s: '%s'\n", configfile.c_str(), line );
00122             continue;
00123         }
00124 
00125         // Assign the name and value parts
00126         char *name_ptr = line_ptr;
00127         char *val_ptr = temp_ptr+1;
00128 
00129         // Trim trailing space from the name part
00130         do
00131         {
00132             *temp_ptr = '\0';
00133             temp_ptr--;
00134         }
00135         while ( *temp_ptr == ' ' || *temp_ptr == '\t' );
00136 
00137         // Remove leading white space from the value part
00138         white_len = strspn( val_ptr, " \t" );
00139         val_ptr += white_len;
00140 
00141         strncpy( val, val_ptr, strlen(val_ptr)+1 );
00142         if ( strcasecmp( name_ptr, "ZM_DB_HOST" ) == 0 )       g_server = val;
00143         else if ( strcasecmp( name_ptr, "ZM_DB_NAME" ) == 0 )  g_database = val;
00144         else if ( strcasecmp( name_ptr, "ZM_DB_USER" ) == 0 )  g_user = val;
00145         else if ( strcasecmp( name_ptr, "ZM_DB_PASS" ) == 0 )  g_password = val;
00146         else if ( strcasecmp( name_ptr, "ZM_PATH_WEB" ) == 0 ) g_webPath = val;
00147         else if ( strcasecmp( name_ptr, "ZM_PATH_BIN" ) == 0 ) g_binPath = val;
00148         else if ( strcasecmp( name_ptr, "ZM_WEB_USER" ) == 0 ) g_webUser = val;
00149         else if ( strcasecmp( name_ptr, "ZM_VERSION" ) == 0 ) g_zmversion = val;
00150     }
00151     fclose(cfg);
00152 }
00153 
00154 void connectToDatabase(void)
00155 {
00156     if (!mysql_init(&g_dbConn))
00157     {
00158         cout << "Error: Can't initialise structure: " <<  mysql_error(&g_dbConn) << endl;
00159         exit(mysql_errno(&g_dbConn));
00160     }
00161 
00162     if (!mysql_real_connect(&g_dbConn, g_server.c_str(), g_user.c_str(),
00163          g_password.c_str(), 0, 0, 0, 0))
00164     {
00165         cout << "Error: Can't connect to server: " <<  mysql_error(&g_dbConn) << endl;
00166         exit(mysql_errno( &g_dbConn));
00167     }
00168 
00169     if (mysql_select_db(&g_dbConn, g_database.c_str()))
00170     {
00171         cout << "Error: Can't select database: " << mysql_error(&g_dbConn) << endl;
00172         exit(mysql_errno(&g_dbConn));
00173     }
00174 }
00175 
00176 void kickDatabase(bool debug)
00177 {
00178     if (time(NULL) < g_lastDBKick + DB_CHECK_TIME)
00179         return;
00180 
00181     if (debug)
00182         cout << "Kicking database connection" << endl;
00183 
00184     g_lastDBKick = time(NULL);
00185 
00186     if (mysql_query(&g_dbConn, "SELECT NULL;") == 0)
00187     {
00188         MYSQL_RES *res = mysql_store_result(&g_dbConn);
00189         if (res)
00190             mysql_free_result(res);
00191         return;
00192     }
00193 
00194     cout << "Lost connection to DB - trying to reconnect" << endl;
00195 
00196     // failed so try to reconnect to the DB
00197     mysql_close(&g_dbConn);
00198     connectToDatabase();
00199 }
00200 
00201 ZMServer::ZMServer(int sock, bool debug)
00202 {
00203     if (debug)
00204         cout << "Using server protocol version '" << ZM_PROTOCOL_VERSION << "'\n";
00205 
00206     m_sock = sock;
00207     m_debug = debug;
00208 
00209     // get the shared memory key
00210     char buf[100];
00211     m_shmKey = 0x7a6d2000;
00212     string setting = getZMSetting("ZM_SHM_KEY");
00213 
00214     if (setting != "")
00215         sscanf(setting.c_str(), "%20x", (unsigned int *)&m_shmKey);
00216     if (m_debug)
00217     {
00218         snprintf(buf, sizeof(buf), "0x%x", (unsigned int)m_shmKey);
00219         cout << "Shared memory key is: " << buf << endl;
00220     }
00221 
00222     // get the MMAP path
00223     m_mmapPath = getZMSetting("ZM_PATH_MAP");
00224     if (m_debug)
00225     {
00226         cout << "Memory path directory is: " << m_mmapPath << endl;
00227     }
00228 
00229     // get the event filename format
00230     setting = getZMSetting("ZM_EVENT_IMAGE_DIGITS");
00231     int eventDigits = atoi(setting.c_str());
00232     snprintf(buf, sizeof(buf), "%%0%dd-capture.jpg", eventDigits);
00233     m_eventFileFormat = buf;
00234     if (m_debug)
00235         cout << "Event file format is: " << m_eventFileFormat << endl;
00236 
00237     // get the analyse filename format
00238     snprintf(buf, sizeof(buf), "%%0%dd-analyse.jpg", eventDigits);
00239     m_analyseFileFormat = buf;
00240     if (m_debug)
00241         cout << "Analyse file format is: " << m_analyseFileFormat << endl;
00242 
00243     // is ZM using the deep storage directory format?
00244     m_useDeepStorage = (getZMSetting("ZM_USE_DEEP_STORAGE") == "1");
00245     if (m_debug)
00246     {
00247         if (m_useDeepStorage)
00248             cout << "using deep storage directory structure" << endl;
00249         else
00250             cout << "using flat directory structure" << endl;
00251     }
00252 
00253     getMonitorList();
00254 }
00255 
00256 ZMServer::~ZMServer()
00257 {
00258     if (m_debug)
00259         cout << "ZMServer destroyed\n";
00260 }
00261 
00262 void ZMServer::tokenize(const string &command, vector<string> &tokens)
00263 {
00264     string token = "";
00265     tokens.clear();
00266     string::size_type startPos = 0;
00267     string::size_type endPos = 0;
00268 
00269     while((endPos = command.find("[]:[]", startPos)) != string::npos)
00270     {
00271         token = command.substr(startPos, endPos - startPos);
00272         tokens.push_back(token);
00273         startPos = endPos + 5;
00274     }
00275 
00276     // make sure we add the last token
00277     if (endPos != command.length())
00278     {
00279         token = command.substr(startPos);
00280         tokens.push_back(token);
00281     }
00282 }
00283 
00284 void ZMServer::processRequest(char* buf, int nbytes)
00285 {
00286 #if 0
00287     // first 8 bytes is the length of the following data
00288     char len[9];
00289     memcpy(len, buf, 8);
00290     len[8] = '\0';
00291     int dataLen = atoi(len);
00292 #endif
00293 
00294     buf[nbytes] = '\0';
00295     string s(buf+8);
00296     vector<string> tokens;
00297     tokenize(s, tokens);
00298 
00299     if (tokens.empty())
00300         return;
00301 
00302     if (m_debug)
00303         cout << "Processing: '" << tokens[0] << "'" << endl;
00304 
00305     if (tokens[0] == "HELLO")
00306         handleHello();
00307     else if (tokens[0] == "GET_SERVER_STATUS")
00308         handleGetServerStatus();
00309     else if (tokens[0] == "GET_MONITOR_STATUS")
00310         handleGetMonitorStatus();
00311     else if (tokens[0] == "GET_EVENT_LIST")
00312         handleGetEventList(tokens);
00313     else if (tokens[0] == "GET_EVENT_DATES")
00314         handleGetEventDates(tokens);
00315     else if (tokens[0] == "GET_EVENT_FRAME")
00316         handleGetEventFrame(tokens);
00317     else if (tokens[0] == "GET_ANALYSE_FRAME")
00318         handleGetAnalyseFrame(tokens);
00319     else if (tokens[0] == "GET_LIVE_FRAME")
00320         handleGetLiveFrame(tokens);
00321     else if (tokens[0] == "GET_FRAME_LIST")
00322         handleGetFrameList(tokens);
00323     else if (tokens[0] == "GET_CAMERA_LIST")
00324         handleGetCameraList();
00325     else if (tokens[0] == "GET_MONITOR_LIST")
00326         handleGetMonitorList();
00327     else if (tokens[0] == "DELETE_EVENT")
00328         handleDeleteEvent(tokens);
00329     else if (tokens[0] == "DELETE_EVENT_LIST")
00330         handleDeleteEventList(tokens);
00331     else if (tokens[0] == "RUN_ZMAUDIT")
00332         handleRunZMAudit();
00333     else if (tokens[0] == "SET_MONITOR_FUNCTION")
00334         handleSetMonitorFunction(tokens);
00335     else
00336         send("UNKNOWN_COMMAND");
00337 }
00338 
00339 bool ZMServer::send(const string s) const
00340 {
00341     // send length
00342     uint32_t len = s.size();
00343     char buf[9];
00344     sprintf(buf, "%8d", len);
00345     int status = ::send(m_sock, buf, 8, MSG_NOSIGNAL);
00346     if (status == -1)
00347         return false;
00348 
00349     // send message
00350     status = ::send(m_sock, s.c_str(), s.size(), MSG_NOSIGNAL);
00351     if ( status == -1 )
00352         return false;
00353     else
00354         return true;
00355 }
00356 
00357 bool ZMServer::send(const string s, const unsigned char *buffer, int dataLen) const
00358 {
00359     // send length
00360     uint32_t len = s.size();
00361     char buf[9];
00362     sprintf(buf, "%8d", len);
00363     int status = ::send(m_sock, buf, 8, MSG_NOSIGNAL);
00364     if (status == -1)
00365         return false;
00366 
00367     // send message
00368     status = ::send(m_sock, s.c_str(), s.size(), MSG_NOSIGNAL);
00369     if ( status == -1 )
00370         return false;
00371 
00372     // send data
00373     status = ::send(m_sock, buffer, dataLen, MSG_NOSIGNAL);
00374     if ( status == -1 )
00375         return false;
00376 
00377     return true;
00378 }
00379 
00380 void ZMServer::sendError(string error)
00381 {
00382     string outStr("");
00383     ADD_STR(outStr, string("ERROR - ") + error);
00384     send(outStr);
00385 }
00386 
00387 void ZMServer::handleHello()
00388 {
00389     // just send OK so the client knows all is well
00390     // followed by the protocol version we understand
00391     string outStr("");
00392     ADD_STR(outStr, "OK");
00393     ADD_STR(outStr, ZM_PROTOCOL_VERSION);
00394     send(outStr);
00395 }
00396 
00397 long long ZMServer::getDiskSpace(const string &filename, long long &total, long long &used)
00398 {
00399     struct statfs statbuf;
00400     memset(&statbuf, 0, sizeof(statbuf));
00401     long long freespace = -1;
00402 
00403     total = used = -1;
00404 
00405     // there are cases where statfs will return 0 (good), but f_blocks and
00406     // others are invalid and set to 0 (such as when an automounted directory
00407     // is not mounted but still visible because --ghost was used),
00408     // so check to make sure we can have a total size > 0
00409     if ((statfs(filename.c_str(), &statbuf) == 0) &&
00410          (statbuf.f_blocks > 0) &&
00411          (statbuf.f_bsize > 0))
00412     {
00413         total      = statbuf.f_blocks;
00414         total     *= statbuf.f_bsize;
00415         total      = total >> 10;
00416 
00417         freespace  = statbuf.f_bavail;
00418         freespace *= statbuf.f_bsize;
00419         freespace  = freespace >> 10;
00420 
00421         used       = total - freespace;
00422     }
00423 
00424     return freespace;
00425 }
00426 
00427 void ZMServer::handleGetServerStatus(void)
00428 {
00429     string outStr("");
00430     ADD_STR(outStr, "OK")
00431 
00432     // server status
00433     string status = runCommand(g_binPath + "/zmdc.pl check");
00434     ADD_STR(outStr, status)
00435 
00436     // get load averages
00437     double loads[3];
00438     if (getloadavg(loads, 3) == -1)
00439     {
00440         ADD_STR(outStr, "Unknown")
00441     }
00442     else
00443     {
00444         char buf[30];
00445         sprintf(buf, "%0.2lf", loads[0]);
00446         ADD_STR(outStr, buf)
00447     }
00448 
00449     // get free space on the disk where the events are stored
00450     char buf[15];
00451     long long total, used;
00452     string eventsDir = g_webPath + "/events/";
00453     getDiskSpace(eventsDir, total, used);
00454     sprintf(buf, "%d%%", (int) ((100.0 / ((float) total / used))));
00455     ADD_STR(outStr, buf)
00456 
00457     send(outStr);
00458 }
00459 
00460 void ZMServer::handleGetEventList(vector<string> tokens)
00461 {
00462     string outStr("");
00463 
00464     if (tokens.size() != 4)
00465     {
00466         sendError(ERROR_TOKEN_COUNT);
00467         return;
00468     }
00469 
00470     string monitor = tokens[1];
00471     bool oldestFirst = (tokens[2] == "1");
00472     string date = tokens[3];
00473 
00474     if (m_debug)
00475         cout << "Loading events for monitor: " << monitor << ", date: " << date << endl;
00476 
00477     ADD_STR(outStr, "OK")
00478 
00479     MYSQL_RES *res;
00480     MYSQL_ROW row;
00481 
00482     string sql("SELECT E.Id, E.Name, M.Id AS MonitorID, M.Name AS MonitorName, E.StartTime,  "
00483             "E.Length, M.Width, M.Height, M.DefaultRate, M.DefaultScale "
00484             "from Events as E inner join Monitors as M on E.MonitorId = M.Id ");
00485 
00486     if (monitor != "<ANY>")
00487     {
00488         sql += "WHERE M.Name = '" + monitor + "' ";
00489 
00490         if (date != "<ANY>")
00491             sql += "AND DATE(E.StartTime) = DATE('" + date + "') ";
00492     }
00493     else
00494     {
00495         if (date != "<ANY>")
00496             sql += "WHERE DATE(E.StartTime) = DATE('" + date + "') ";
00497     }
00498 
00499     if (oldestFirst)
00500         sql += "ORDER BY E.StartTime ASC";
00501     else
00502         sql += "ORDER BY E.StartTime DESC";
00503 
00504     if (mysql_query(&g_dbConn, sql.c_str()))
00505     {
00506         fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
00507         sendError(ERROR_MYSQL_QUERY);
00508         return;
00509     }
00510 
00511     res = mysql_store_result(&g_dbConn);
00512     int eventCount = mysql_num_rows(res);
00513 
00514     if (m_debug)
00515         cout << "Got " << eventCount << " events" << endl;
00516 
00517     char str[10];
00518     sprintf(str, "%d", eventCount);
00519     ADD_STR(outStr, str)
00520 
00521     for (int x = 0; x < eventCount; x++)
00522     {
00523         row = mysql_fetch_row(res);
00524         if (row)
00525         {
00526             ADD_STR(outStr, row[0]) // eventID
00527             ADD_STR(outStr, row[1]) // event name
00528             ADD_STR(outStr, row[2]) // monitorID
00529             ADD_STR(outStr, row[3]) // monitor name
00530             row[4][10] = 'T';
00531             ADD_STR(outStr, row[4]) // start time
00532             ADD_STR(outStr, row[5]) // length
00533         }
00534         else
00535         {
00536             cout << "Failed to get mysql row" << endl;
00537             sendError(ERROR_MYSQL_ROW);
00538             return;
00539         }
00540     }
00541 
00542     mysql_free_result(res);
00543 
00544     send(outStr);
00545 }
00546 
00547 void ZMServer::handleGetEventDates(vector<string> tokens)
00548 {
00549     string outStr("");
00550 
00551     if (tokens.size() != 3)
00552     {
00553         sendError(ERROR_TOKEN_COUNT);
00554         return;
00555     }
00556 
00557     string monitor = tokens[1];
00558     bool oldestFirst = (tokens[2] == "1");
00559 
00560     if (m_debug)
00561         cout << "Loading event dates for monitor: " << monitor << endl;
00562 
00563     ADD_STR(outStr, "OK")
00564 
00565     MYSQL_RES *res;
00566     MYSQL_ROW row;
00567 
00568     string sql("SELECT DISTINCT DATE(E.StartTime) "
00569             "from Events as E inner join Monitors as M on E.MonitorId = M.Id ");
00570 
00571     if (monitor != "<ANY>")
00572         sql += "WHERE M.Name = '" + monitor + "' ";
00573 
00574     if (oldestFirst)
00575         sql += "ORDER BY E.StartTime ASC";
00576     else
00577         sql += "ORDER BY E.StartTime DESC";
00578 
00579     if (mysql_query(&g_dbConn, sql.c_str()))
00580     {
00581         fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
00582         sendError(ERROR_MYSQL_QUERY);
00583         return;
00584     }
00585 
00586     res = mysql_store_result(&g_dbConn);
00587     int dateCount = mysql_num_rows(res);
00588 
00589     if (m_debug)
00590         cout << "Got " << dateCount << " dates" << endl;
00591 
00592     char str[10];
00593     sprintf(str, "%d", dateCount);
00594     ADD_STR(outStr, str)
00595 
00596     for (int x = 0; x < dateCount; x++)
00597     {
00598         row = mysql_fetch_row(res);
00599         if (row)
00600         {
00601             ADD_STR(outStr, row[0]) // event date
00602         }
00603         else
00604         {
00605             cout << "Failed to get mysql row" << endl;
00606             sendError(ERROR_MYSQL_ROW);
00607             return;
00608         }
00609     }
00610 
00611     mysql_free_result(res);
00612 
00613     send(outStr);
00614 }
00615 
00616 void ZMServer::handleGetMonitorStatus(void)
00617 {
00618     string outStr("");
00619     ADD_STR(outStr, "OK")
00620 
00621     // get monitor list
00622     MYSQL_RES *res;
00623     MYSQL_ROW row;
00624 
00625     string sql("SELECT Id, Name, Type, Device, Host, Channel, Function, Enabled "
00626                "FROM Monitors;");
00627     if (mysql_query(&g_dbConn, sql.c_str()))
00628     {
00629         fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
00630         sendError(ERROR_MYSQL_QUERY);
00631         return;
00632     }
00633 
00634     res = mysql_store_result(&g_dbConn);
00635 
00636      // add monitor count
00637     int monitorCount = mysql_num_rows(res);
00638 
00639     if (m_debug)
00640         cout << "Got " << monitorCount << " monitors" << endl;
00641 
00642     char str[10];
00643     sprintf(str, "%d", monitorCount);
00644     ADD_STR(outStr, str)
00645 
00646     for (int x = 0; x < monitorCount; x++)
00647     {
00648         row = mysql_fetch_row(res);
00649         if (row)
00650         {
00651             string id = row[0];
00652             string type = row[2];
00653             string device = row[3];
00654             string host = row[4];
00655             string channel = row[5];
00656             string function = row[6];
00657             string enabled = row[7];
00658             string name = row[1];
00659             string events = "";
00660             string zmcStatus = "";
00661             string zmaStatus = "";
00662             getMonitorStatus(id, type, device, host, channel, function,
00663                              zmcStatus, zmaStatus, enabled);
00664             MYSQL_RES *res2;
00665             MYSQL_ROW row2;
00666 
00667             string sql2("SELECT count(if(Archived=0,1,NULL)) AS EventCount "
00668                         "FROM Events AS E "
00669                         "WHERE MonitorId = " + id);
00670 
00671             if (mysql_query(&g_dbConn, sql2.c_str()))
00672             {
00673                 fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
00674                 sendError(ERROR_MYSQL_QUERY);
00675                 return;
00676             }
00677 
00678             res2 = mysql_store_result(&g_dbConn);
00679             if (mysql_num_rows(res2) > 0)
00680             {
00681                 row2 = mysql_fetch_row(res2);
00682                 if (row2)
00683                     events = row2[0];
00684                 else
00685                 {
00686                     cout << "Failed to get mysql row" << endl;
00687                     sendError(ERROR_MYSQL_ROW);
00688                     return;
00689                 }
00690             }
00691 
00692             ADD_STR(outStr, id)
00693             ADD_STR(outStr, name)
00694             ADD_STR(outStr, zmcStatus)
00695             ADD_STR(outStr, zmaStatus)
00696             ADD_STR(outStr, events)
00697             ADD_STR(outStr, function)
00698             ADD_STR(outStr, enabled)
00699 
00700             mysql_free_result(res2);
00701         }
00702         else
00703         {
00704             cout << "Failed to get mysql row" << endl;
00705             sendError(ERROR_MYSQL_ROW);
00706             return;
00707         }
00708     }
00709 
00710     mysql_free_result(res);
00711 
00712     send(outStr);
00713 }
00714 
00715 string ZMServer::runCommand(string command)
00716 {
00717     string outStr("");
00718     FILE *fd = popen(command.c_str(), "r");
00719     char buffer[100];
00720 
00721     while (fgets(buffer, sizeof(buffer), fd) != NULL)
00722     {
00723         outStr += buffer;
00724     }
00725     pclose(fd);
00726     return outStr;
00727 }
00728 
00729 void ZMServer::getMonitorStatus(string id, string type, string device, string host, string channel,
00730                                 string function, string &zmcStatus, string &zmaStatus,
00731                                 string enabled)
00732 {
00733     zmaStatus = "";
00734     zmcStatus = "";
00735 
00736     string command(g_binPath + "/zmdc.pl status");
00737     string status = runCommand(command);
00738 
00739     if (type == "Local")
00740     {
00741         if (enabled == "0")
00742             zmaStatus = device + "(" + channel + ") [-]";
00743         else if (status.find("'zma -m " + id + "' running") != string::npos)
00744             zmaStatus = device + "(" + channel + ") [R]";
00745         else
00746             zmaStatus = device + "(" + channel + ") [S]";
00747     }
00748     else
00749     {
00750         if (enabled == "0")
00751             zmaStatus = host + " [-]";
00752         else if (status.find("'zma -m " + id + "' running") != string::npos)
00753             zmaStatus = host + " [R]";
00754         else
00755             zmaStatus = host + " [S]";
00756     }
00757 
00758     if (type == "Local")
00759     {
00760         if (enabled == "0")
00761             zmcStatus = function + " [-]";
00762         else if (status.find("'zmc -d "+ device + "' running") != string::npos)
00763             zmcStatus = function + " [R]";
00764         else
00765             zmcStatus = function + " [S]";
00766     }
00767     else
00768     {
00769         if (enabled == "0")
00770             zmcStatus = function + " [-]";
00771         else if (status.find("'zmc -m " + id + "' running") != string::npos)
00772             zmcStatus = function + " [R]";
00773         else
00774             zmcStatus = function + " [S]";
00775     }
00776 }
00777 
00778 void ZMServer::handleGetEventFrame(vector<string> tokens)
00779 {
00780     static unsigned char buffer[MAX_IMAGE_SIZE];
00781 
00782     if (tokens.size() != 5)
00783     {
00784         sendError(ERROR_TOKEN_COUNT);
00785         return;
00786     }
00787 
00788     string monitorID(tokens[1]);
00789     string eventID(tokens[2]);
00790     int frameNo = atoi(tokens[3].c_str());
00791     string eventTime(tokens[4]);
00792 
00793     if (m_debug)
00794         cout << "Getting frame " << frameNo << " for event " << eventID
00795              << " on monitor " << monitorID  << " event time is " << eventTime << endl;
00796 
00797     string outStr("");
00798 
00799     ADD_STR(outStr, "OK")
00800 
00801     // try to find the frame file
00802     string filepath("");
00803     char str[100];
00804 
00805     if (m_useDeepStorage)
00806     {
00807         filepath = g_webPath + "/events/" + monitorID + "/" + eventTime + "/";
00808         sprintf(str, m_eventFileFormat.c_str(), frameNo);
00809         filepath += str;
00810     }
00811     else
00812     {
00813         filepath = g_webPath + "/events/" + monitorID + "/" + eventID + "/";
00814         sprintf(str, m_eventFileFormat.c_str(), frameNo);
00815         filepath += str;
00816     }
00817 
00818     FILE *fd;
00819     int fileSize = 0;
00820     if ((fd = fopen(filepath.c_str(), "r" )))
00821     {
00822         fileSize = fread(buffer, 1, sizeof(buffer), fd);
00823         fclose(fd);
00824     }
00825     else
00826     {
00827         cout << "Can't open " << filepath << ": " << strerror(errno) << endl;
00828         sendError(ERROR_FILE_OPEN + string(" - ") + filepath + " : " + strerror(errno));
00829         return;
00830     }
00831 
00832     if (m_debug)
00833         cout << "Frame size: " <<  fileSize << endl;
00834 
00835     // get the file size
00836     sprintf(str, "%d", fileSize);
00837     ADD_STR(outStr, str)
00838 
00839     // send the data
00840     send(outStr, buffer, fileSize);
00841 }
00842 
00843 void ZMServer::handleGetAnalyseFrame(vector<string> tokens)
00844 {
00845     static unsigned char buffer[MAX_IMAGE_SIZE];
00846     char str[100];
00847 
00848     if (tokens.size() != 5)
00849     {
00850         sendError(ERROR_TOKEN_COUNT);
00851         return;
00852     }
00853 
00854     string monitorID(tokens[1]);
00855     string eventID(tokens[2]);
00856     int frameNo = atoi(tokens[3].c_str());
00857     string eventTime(tokens[4]);
00858 
00859     if (m_debug)
00860         cout << "Getting anaylse frame " << frameNo << " for event " << eventID
00861              << " on monitor " << monitorID << " event time is " << eventTime << endl;
00862 
00863     // get the 'alarm' frames from the Frames table for this event
00864     MYSQL_RES *res;
00865     MYSQL_ROW row = NULL;
00866 
00867     string sql("");
00868     sql += "SELECT FrameId FROM Frames ";
00869     sql += "WHERE EventID = " + eventID + " ";
00870     sql += "AND Type = 'Alarm' ";
00871     sql += "ORDER BY FrameID";
00872 
00873     if (mysql_query(&g_dbConn, sql.c_str()))
00874     {
00875         fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
00876         sendError(ERROR_MYSQL_QUERY);
00877         return;
00878     }
00879 
00880     res = mysql_store_result(&g_dbConn);
00881     int frameCount = mysql_num_rows(res);
00882     int frameID;
00883 
00884     // if the required frame mumber is 0 or out of bounds then use the middle frame
00885     if (frameNo == 0 || frameNo < 0 || frameNo > frameCount)
00886         frameNo = (frameCount / 2) + 1;
00887 
00888     // move to the required frame in the table
00889     for (int x = 0; x < frameNo; x++)
00890     {
00891         row = mysql_fetch_row(res);
00892     }
00893 
00894     if (row)
00895     {
00896         frameID = atoi(row[0]);
00897     }
00898     else
00899     {
00900         cout << "handleGetAnalyseFrame: Failed to get mysql row for frameNo " << frameNo << endl;
00901         sendError(ERROR_MYSQL_ROW);
00902         return;
00903     }
00904 
00905     string outStr("");
00906 
00907     ADD_STR(outStr, "OK")
00908 
00909     // try to find the analyse frame file
00910     string filepath("");
00911     if (m_useDeepStorage)
00912     {
00913         filepath = g_webPath + "/events/" + monitorID + "/" + eventTime + "/";
00914         sprintf(str, m_analyseFileFormat.c_str(), frameID);
00915         filepath += str;
00916     }
00917     else
00918     {
00919         filepath = g_webPath + "/events/" + monitorID + "/" + eventID + "/";
00920         sprintf(str, m_analyseFileFormat.c_str(), frameID);
00921         filepath += str;
00922     }
00923 
00924     FILE *fd;
00925     int fileSize = 0;
00926     if ((fd = fopen(filepath.c_str(), "r" )))
00927     {
00928         fileSize = fread(buffer, 1, sizeof(buffer), fd);
00929         fclose(fd);
00930     }
00931     else
00932     {
00933         cout << "Can't open " << filepath << ": " << strerror(errno) << endl;
00934         sendError(ERROR_FILE_OPEN + string(" - ") + filepath + " : " + strerror(errno));
00935         return;
00936     }
00937 
00938     if (m_debug)
00939         cout << "Frame size: " <<  fileSize << endl;
00940 
00941     // get the file size
00942     sprintf(str, "%d", fileSize);
00943     ADD_STR(outStr, str)
00944 
00945     // send the data
00946     send(outStr, buffer, fileSize);
00947 }
00948 
00949 void ZMServer::handleGetLiveFrame(vector<string> tokens)
00950 {
00951     static unsigned char buffer[MAX_IMAGE_SIZE];
00952     char str[100];
00953 
00954     // we need to periodically kick the DB connection here to make sure it
00955     // stays alive because the user may have left the frontend on the live
00956     // view which doesn't query the DB at all and eventually the connection
00957     // will timeout
00958     kickDatabase(m_debug);
00959 
00960     if (tokens.size() != 2)
00961     {
00962         sendError(ERROR_TOKEN_COUNT);
00963         return;
00964     }
00965 
00966     int monitorID = atoi(tokens[1].c_str());
00967 
00968     if (m_debug)
00969         cout << "Getting live frame from monitor: " << monitorID << endl;
00970 
00971     string outStr("");
00972 
00973     ADD_STR(outStr, "OK")
00974 
00975     // echo the monitor id
00976     sprintf(str, "%d", monitorID);
00977     ADD_STR(outStr, str)
00978 
00979     // try to find the correct MONITOR
00980     MONITOR *monitor;
00981     if (m_monitors.find(monitorID) != m_monitors.end())
00982         monitor = m_monitors[monitorID];
00983     else
00984     {
00985         sendError(ERROR_INVALID_MONITOR);
00986         return;
00987     }
00988 
00989     // are the data pointers valid?
00990     if (monitor->shared_data == NULL ||  monitor->shared_images == NULL)
00991     {
00992         sendError(ERROR_INVALID_POINTERS);
00993         return;
00994     }
00995 
00996     // read a frame from the shared memory
00997     int dataSize = getFrame(buffer, sizeof(buffer), monitor);
00998 
00999     if (m_debug)
01000         cout << "Frame size: " <<  dataSize << endl;
01001 
01002     if (dataSize == 0)
01003     {
01004         // not really an error
01005         outStr = "";
01006         ADD_STR(outStr, "WARNING - No new frame available");
01007         send(outStr);
01008         return;
01009     }
01010 
01011     // add status
01012     ADD_STR(outStr, monitor->status)
01013 
01014     // send the data size
01015     sprintf(str, "%d", dataSize);
01016     ADD_STR(outStr, str)
01017 
01018     // send the data
01019     send(outStr, buffer, dataSize);
01020 }
01021 
01022 void ZMServer::handleGetFrameList(vector<string> tokens)
01023 {
01024     string eventID;
01025     string outStr("");
01026 
01027     if (tokens.size() != 2)
01028     {
01029         sendError(ERROR_TOKEN_COUNT);
01030         return;
01031     }
01032 
01033     eventID = tokens[1];
01034 
01035     if (m_debug)
01036         cout << "Loading frames for event: " << eventID << endl;
01037 
01038     ADD_STR(outStr, "OK")
01039 
01040     MYSQL_RES *res;
01041     MYSQL_ROW row;
01042 
01043     string sql("");
01044     sql += "SELECT Type, Delta FROM Frames ";
01045     sql += "WHERE EventID = " + eventID + " ";
01046     sql += "ORDER BY FrameID";
01047 
01048     if (mysql_query(&g_dbConn, sql.c_str()))
01049     {
01050         fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
01051         sendError(ERROR_MYSQL_QUERY);
01052         return;
01053     }
01054 
01055     res = mysql_store_result(&g_dbConn);
01056     int frameCount = mysql_num_rows(res);
01057 
01058     if (m_debug)
01059         cout << "Got " << frameCount << " frames" << endl;
01060 
01061     char str[10];
01062     sprintf(str, "%d\n", frameCount);
01063     ADD_STR(outStr, str)
01064 
01065     for (int x = 0; x < frameCount; x++)
01066     {
01067         row = mysql_fetch_row(res);
01068         if (row)
01069         {
01070             ADD_STR(outStr, row[0]) // Type
01071             ADD_STR(outStr, row[1]) // Delta
01072         }
01073         else
01074         {
01075             cout << "handleGetFrameList: Failed to get mysql row " << x << endl;
01076             sendError(ERROR_MYSQL_ROW);
01077             return;
01078         }
01079     }
01080 
01081     mysql_free_result(res);
01082 
01083     send(outStr);
01084 }
01085 
01086 void ZMServer::handleGetCameraList(void)
01087 {
01088     string outStr("");
01089 
01090     ADD_STR(outStr, "OK")
01091 
01092     MYSQL_RES *res;
01093     MYSQL_ROW row;
01094 
01095     string sql("");
01096     sql += "SELECT DISTINCT M.Name FROM Events AS E ";
01097     sql += "INNER JOIN Monitors AS M ON E.MonitorId = M.Id;";
01098 
01099     if (mysql_query(&g_dbConn, sql.c_str()))
01100     {
01101         fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
01102         sendError(ERROR_MYSQL_QUERY);
01103         return;
01104     }
01105 
01106     res = mysql_store_result(&g_dbConn);
01107     int monitorCount = mysql_num_rows(res);
01108     char str[10];
01109     sprintf(str, "%d", monitorCount);
01110     ADD_STR(outStr, str)
01111 
01112     for (int x = 0; x < monitorCount; x++)
01113     {
01114         row = mysql_fetch_row(res);
01115         if (row)
01116         {
01117             ADD_STR(outStr, row[0]) // Name
01118         }
01119         else
01120         {
01121             cout << "handleGetCameraList: Failed to get mysql row " << x << endl;
01122             sendError(ERROR_MYSQL_ROW);
01123             return;
01124         }
01125     }
01126 
01127     mysql_free_result(res);
01128 
01129     send(outStr);
01130 }
01131 
01132 void ZMServer::handleGetMonitorList(void)
01133 {
01134     string outStr("");
01135 
01136     ADD_STR(outStr, "OK")
01137 
01138     MYSQL_RES *res;
01139     MYSQL_ROW row;
01140 
01141     string sql("");
01142     sql += "SELECT Id, Name, Width, Height, Palette FROM Monitors ORDER BY Id";
01143 
01144     if (mysql_query(&g_dbConn, sql.c_str()))
01145     {
01146         fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
01147         sendError(ERROR_MYSQL_QUERY);
01148         return;
01149     }
01150 
01151     res = mysql_store_result(&g_dbConn);
01152     int monitorCount = mysql_num_rows(res);
01153 
01154     if (m_debug)
01155         cout << "Got " << monitorCount << " monitors" << endl;
01156 
01157     char str[10];
01158     sprintf(str, "%d", monitorCount);
01159     ADD_STR(outStr, str)
01160 
01161     for (int x = 0; x < monitorCount; x++)
01162     {
01163         row = mysql_fetch_row(res);
01164         if (row)
01165         {
01166             ADD_STR(outStr, row[0]) // Id
01167             ADD_STR(outStr, row[1]) // Name
01168             ADD_STR(outStr, row[2]) // Width
01169             ADD_STR(outStr, row[3]) // Height
01170             ADD_STR(outStr, row[4]) // Palette
01171 
01172             if (m_debug)
01173             {
01174                 cout << "id:      " << row[0] << endl;
01175                 cout << "name:    " << row[1] << endl;
01176                 cout << "width:   " << row[2] << endl;
01177                 cout << "height:  " << row[3] << endl;
01178                 cout << "palette: " << row[4] << endl;
01179                 cout << "-------------------" << endl;
01180             }
01181         }
01182         else
01183         {
01184             cout << "Failed to get mysql row" << endl;
01185             sendError(ERROR_MYSQL_ROW);
01186             return;
01187         }
01188     }
01189 
01190     mysql_free_result(res);
01191 
01192     send(outStr);
01193 }
01194 
01195 void ZMServer::handleDeleteEvent(vector<string> tokens)
01196 {
01197     string eventID;
01198     string outStr("");
01199 
01200     if (tokens.size() != 2)
01201     {
01202         sendError(ERROR_TOKEN_COUNT);
01203         return;
01204     }
01205 
01206     eventID = tokens[1];
01207 
01208     if (m_debug)
01209         cout << "Deleting event: " << eventID << endl;
01210 
01211     ADD_STR(outStr, "OK")
01212 
01213     string sql("");
01214     sql += "DELETE FROM Events WHERE Id = " + eventID;
01215 
01216     if (mysql_query(&g_dbConn, sql.c_str()))
01217     {
01218         fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
01219         sendError(ERROR_MYSQL_QUERY);
01220         return;
01221     }
01222 
01223     // run zmaudit.pl to clean everything up
01224     string command(g_binPath + "/zmaudit.pl &");
01225     errno = 0;
01226     if (system(command.c_str()) < 0 && errno)
01227         cerr << "Failed to run '" << command << "'" << endl;
01228 
01229     send(outStr);
01230 }
01231 
01232 void ZMServer::handleDeleteEventList(vector<string> tokens)
01233 {
01234     string eventList("");
01235     string outStr("");
01236 
01237     vector<string>::iterator it = tokens.begin();
01238     if (it != tokens.end())
01239         ++it;
01240     while (it != tokens.end())
01241     {
01242         if (eventList == "")
01243             eventList = (*it);
01244         else
01245             eventList += "," + (*it);
01246 
01247         ++it;
01248     }
01249 
01250     if (m_debug)
01251         cout << "Deleting events: " << eventList << endl;
01252 
01253     string sql("");
01254     sql += "DELETE FROM Events WHERE Id IN (" + eventList + ")";
01255 
01256     if (mysql_query(&g_dbConn, sql.c_str()))
01257     {
01258         fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
01259         sendError(ERROR_MYSQL_QUERY);
01260         return;
01261     }
01262 
01263     ADD_STR(outStr, "OK")
01264     send(outStr);
01265 }
01266 
01267 void ZMServer::handleRunZMAudit(void)
01268 {
01269     string outStr("");
01270 
01271     // run zmaudit.pl to clean up orphaned db entries etc
01272     string command(g_binPath + "/zmaudit.pl &");
01273 
01274     if (m_debug)
01275         cout << "Running command: " << command << endl;
01276 
01277     errno = 0;
01278     if (system(command.c_str()) < 0 && errno)
01279         cerr << "Failed to run '" << command << "'" << endl;
01280 
01281     ADD_STR(outStr, "OK")
01282     send(outStr);
01283 }
01284 
01285 void ZMServer::getMonitorList(void)
01286 {
01287     m_monitors.clear();
01288 
01289     string sql("SELECT Id, Name, Width, Height, ImageBufferCount, MaxFPS, Palette, ");
01290     sql += " Type, Function, Enabled, Device, Host, Controllable, TrackMotion ";
01291     sql += "FROM Monitors";
01292 
01293     MYSQL_RES *res;
01294     MYSQL_ROW row;
01295 
01296     if (mysql_query(&g_dbConn, sql.c_str()))
01297     {
01298         fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
01299         return;
01300     }
01301 
01302     res = mysql_store_result(&g_dbConn);
01303     int monitorCount = mysql_num_rows(res);
01304 
01305     if (m_debug)
01306         cout << "Got " << monitorCount << " monitors" << endl;
01307 
01308     for (int x = 0; x < monitorCount; x++)
01309     {
01310         row = mysql_fetch_row(res);
01311         if (row)
01312         {
01313             MONITOR *m = new MONITOR;
01314             m->mon_id = atoi(row[0]);
01315             m->name = row[1];
01316             m->width = atoi(row[2]);
01317             m->height = atoi(row[3]);
01318             m->image_buffer_count = atoi(row[4]);
01319             m->palette = atoi(row[6]);
01320             m->type = row[7];
01321             m->function = row[8];
01322             m->enabled = atoi(row[9]);
01323             m->device = row[10];
01324             m->host = row[11];
01325             m->controllable = atoi(row[12]);
01326             m->trackMotion = atoi(row[13]);
01327             m_monitors[m->mon_id] = m;
01328 
01329             initMonitor(m);
01330         }
01331         else
01332         {
01333             cout << "Failed to get mysql row" << endl;
01334             return;
01335         }
01336     }
01337 
01338     mysql_free_result(res);
01339 }
01340 
01341 void ZMServer::initMonitor(MONITOR *monitor)
01342 {
01343     void *shm_ptr = NULL;
01344 
01345     monitor->shared_data = NULL;
01346     monitor->shared_images = NULL;
01347 
01348     if (monitor->palette == 1)
01349         monitor->frame_size = monitor->width * monitor->height;
01350     else
01351         monitor->frame_size = monitor->width * monitor->height * 3;
01352 
01353     int shared_data_size;
01354 
01355     if (g_zmversion == "1.22.2")
01356         shared_data_size = sizeof(SharedData) +
01357             sizeof(TriggerData_old) +
01358             ((monitor->image_buffer_count) * (sizeof(struct timeval))) +
01359             ((monitor->image_buffer_count) * monitor->frame_size);
01360     else
01361         shared_data_size = sizeof(SharedData) +
01362             sizeof(TriggerData) +
01363             ((monitor->image_buffer_count) * (sizeof(struct timeval))) +
01364             ((monitor->image_buffer_count) * monitor->frame_size);
01365 
01366 
01367 #if _POSIX_MAPPED_FILES > 0L
01368     /*
01369      * Try to open the mmap file first if the architecture supports it.
01370      * Otherwise, legacy shared memory will be used below.
01371      */
01372     stringstream mmap_filename;
01373     mmap_filename << m_mmapPath << "/zm.mmap." << monitor->mon_id;
01374 
01375     int mapFile = open(mmap_filename.str().c_str(), O_RDONLY, 0x0);
01376     if (mapFile >= 0)
01377     {
01378         if (m_debug)
01379             cout << "Opened mmap file: " << mmap_filename << endl;
01380 
01381         shm_ptr = mmap(NULL, shared_data_size, PROT_READ,
01382                                             MAP_SHARED, mapFile, 0x0);
01383         if (shm_ptr == NULL)
01384         {
01385             cout << "Failed to map shared memory from file [" << 
01386                 mmap_filename << "] " <<
01387                 "for monitor: " << 
01388                 monitor->mon_id << 
01389                 endl;
01390             monitor->status = "Error";
01391             return;
01392         }
01393     }
01394 #endif
01395 
01396     if (shm_ptr == NULL)
01397     {
01398         // fail back to shmget() functionality if mapping memory above failed.
01399         int shmid;
01400 
01401         if ((shmid = shmget((m_shmKey & 0xffffff00) | monitor->mon_id,
01402              shared_data_size, SHM_R)) == -1)
01403         {
01404             cout << "Failed to shmget for monitor: " << monitor->mon_id << endl;
01405             monitor->status = "Error";
01406             switch(errno)
01407             {
01408                 case EACCES: cout << "EACCES - no rights to access segment\n"; break;
01409                 case EEXIST: cout << "EEXIST - segment already exists\n"; break;
01410                 case EINVAL: cout << "EINVAL - size < SHMMIN or size > SHMMAX\n"; break;
01411                 case ENFILE: cout << "ENFILE - limit on open files has been reached\n"; break;
01412                 case ENOENT: cout << "ENOENT - no segment exists for the given key\n"; break;
01413                 case ENOMEM: cout << "ENOMEM - couldn't reserve memory for segment\n"; break;
01414                 case ENOSPC: cout << "ENOSPC - shmmni or shmall limit reached\n"; break;
01415             }
01416 
01417             return;
01418         }
01419 
01420         shm_ptr = shmat(shmid, 0, SHM_RDONLY);
01421 
01422 
01423         if (shm_ptr == NULL)
01424         {
01425             cout << "Failed to shmat for monitor: " << monitor->mon_id << endl;
01426             monitor->status = "Error";
01427             return;
01428         }
01429     }
01430 
01431     monitor->shared_data = (SharedData*)shm_ptr;
01432 
01433     if (g_zmversion == "1.22.2")
01434         monitor->shared_images = (unsigned char*) shm_ptr +
01435             sizeof(SharedData) +
01436             sizeof(TriggerData_old) +
01437             ((monitor->image_buffer_count) * sizeof(struct timeval));
01438     else
01439         monitor->shared_images = (unsigned char*) shm_ptr +
01440             sizeof(SharedData) +
01441             sizeof(TriggerData) +
01442             ((monitor->image_buffer_count) * sizeof(struct timeval));
01443 }
01444 
01445 int ZMServer::getFrame(unsigned char *buffer, int bufferSize, MONITOR *monitor)
01446 {
01447     (void) bufferSize;
01448 
01449     // is there a new frame available?
01450     if (monitor->shared_data->last_write_index == monitor->last_read)
01451         return 0;
01452 
01453     // sanity check last_read
01454     if (monitor->shared_data->last_write_index < 0 ||
01455             monitor->shared_data->last_write_index >= monitor->image_buffer_count)
01456         return 0;
01457 
01458     monitor->last_read = monitor->shared_data->last_write_index;
01459 
01460     switch (monitor->shared_data->state)
01461     {
01462         case IDLE:
01463             monitor->status = "Idle";
01464             break;
01465         case PREALARM:
01466             monitor->status = "Pre Alarm";
01467             break;
01468         case ALARM:
01469             monitor->status = "Alarm";
01470             break;
01471         case ALERT:
01472             monitor->status = "Alert";
01473             break;
01474         case TAPE:
01475             monitor->status = "Tape";
01476             break;
01477         default:
01478             monitor->status = "Unknown";
01479             break;
01480     }
01481 
01482     unsigned char *data = monitor->shared_images +
01483             monitor->frame_size * monitor->last_read;
01484 
01485     // FIXME: should do some sort of compression JPEG??
01486     // just copy the data to our buffer for now
01487     memcpy(buffer, data, monitor->frame_size);
01488 
01489 
01490     return monitor->frame_size;
01491 }
01492 
01493 string ZMServer::getZMSetting(const string &setting)
01494 {
01495     string result;
01496     string sql("SELECT Name, Value FROM Config ");
01497     sql += "WHERE Name = '" + setting + "'";
01498 
01499     MYSQL_RES *res;
01500     MYSQL_ROW row;
01501 
01502     if (mysql_query(&g_dbConn, sql.c_str()))
01503     {
01504         fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
01505         return "";
01506     }
01507 
01508     res = mysql_store_result(&g_dbConn);
01509     row = mysql_fetch_row(res);
01510     if (row)
01511     {
01512         result = row[1];
01513     }
01514     else
01515     {
01516         cout << "Failed to get mysql row" << endl;
01517         result = "";
01518     }
01519 
01520     if (m_debug)
01521         cout << "getZMSetting: " << setting << " Result: " << result << endl;
01522 
01523     mysql_free_result(res);
01524 
01525     return result;
01526 }
01527 
01528 void ZMServer::handleSetMonitorFunction(vector<string> tokens)
01529 {
01530     string outStr("");
01531 
01532     if (tokens.size() != 4)
01533     {
01534         sendError(ERROR_TOKEN_COUNT);
01535         return;
01536     }
01537 
01538     string monitorID(tokens[1]);
01539     string function(tokens[2]);
01540     string enabled(tokens[3]);
01541 
01542     // Check validity of input passed to server. Does monitor exist && is function ok
01543     if (m_monitors.find(atoi(monitorID.c_str())) == m_monitors.end())
01544     {
01545         sendError(ERROR_INVALID_MONITOR);
01546         return;
01547     }
01548 
01549     if (function != FUNCTION_NONE && function != FUNCTION_MONITOR &&
01550         function != FUNCTION_MODECT && function != FUNCTION_NODECT &&
01551         function != FUNCTION_RECORD && function != FUNCTION_MOCORD)
01552     {
01553         sendError(ERROR_INVALID_MONITOR_FUNCTION);
01554         return;
01555     }
01556 
01557     if (enabled != "0" && enabled != "1")
01558     {
01559         sendError(ERROR_INVALID_MONITOR_ENABLE_VALUE);
01560         return;
01561     }
01562 
01563     if (m_debug)
01564         cout << "User input validated OK" << endl;
01565 
01566 
01567     // Now perform db update && (re)start/stop daemons as required.
01568     MONITOR *monitor = m_monitors[atoi(monitorID.c_str())];
01569     string oldFunction = monitor->function;
01570     string newFunction = function;
01571     int oldEnabled  = monitor->enabled;
01572     int newEnabled  = atoi(enabled.c_str());
01573     monitor->function = newFunction;
01574     monitor->enabled = newEnabled;
01575 
01576     if (m_debug)
01577         cout << "SetMonitorFunction MonitorId: " << monitorID << endl <<
01578                 "  oldEnabled: " << oldEnabled << endl <<
01579                 "  newEnabled: " << newEnabled << endl <<
01580                 " oldFunction: " << oldFunction << endl <<
01581                 " newFunction: " << newFunction << endl;
01582 
01583     if ( newFunction != oldFunction || newEnabled != oldEnabled)
01584     {
01585         string sql("UPDATE Monitors ");
01586         sql += "SET Function = '" + function + "', ";
01587         sql += "Enabled = '" + enabled + "' ";
01588         sql += "WHERE Id = '" + monitorID + "'";
01589 
01590         if (mysql_query(&g_dbConn, sql.c_str()))
01591         {
01592             fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
01593             sendError(ERROR_MYSQL_QUERY);
01594             return;
01595         }
01596 
01597         if (m_debug)
01598             cout << "Monitor function SQL update OK" << endl;
01599 
01600         string status = runCommand(g_binPath + "/zmdc.pl check");
01601 
01602         // Now refresh servers
01603         if (RUNNING.compare(0, RUNNING.size(), status, 0, RUNNING.size()) == 0)
01604         {
01605             if (m_debug)
01606                 cout << "Monitor function Refreshing daemons" << endl;
01607 
01608             bool restart = (oldFunction == FUNCTION_NONE) ||
01609                            (newFunction == FUNCTION_NONE) ||
01610                            (newEnabled != oldEnabled);
01611 
01612             if (restart)
01613                 zmcControl(monitor, RESTART);
01614             else
01615                 zmcControl(monitor, "");
01616             zmaControl(monitor, RELOAD);
01617         }
01618         else
01619             if (m_debug)
01620                 cout << "zm daemons are not running" << endl;
01621     }
01622     else
01623         cout << "Not updating monitor function as identical to existing configuration" << endl;
01624 
01625     ADD_STR(outStr, "OK")
01626     send(outStr);
01627 }
01628 
01629 void ZMServer::zmcControl(MONITOR *monitor, const string &mode)
01630 {
01631     string zmcArgs = "";
01632     string sql = "";
01633     sql += "SELECT count(if(Function!='None',1,NULL)) as ActiveCount ";
01634     sql += "FROM Monitors ";
01635 
01636     if (monitor->type == "Local" )
01637     {
01638         sql += "WHERE Device = '" + monitor->device + "'";
01639         zmcArgs = "-d " + monitor->device;
01640     }
01641     else
01642     {
01643         sql += "WHERE Id = '" + monitor->getIdStr() + "'";
01644         zmcArgs = "-m " + monitor->mon_id;
01645     }
01646 
01647     if (mysql_query(&g_dbConn, sql.c_str()))
01648     {
01649         fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
01650         sendError(ERROR_MYSQL_QUERY);
01651         return;
01652     }
01653 
01654     MYSQL_RES *res;
01655     MYSQL_ROW row;
01656     int activeCount;
01657 
01658     res = mysql_store_result(&g_dbConn);
01659     row = mysql_fetch_row(res);
01660 
01661     if (row)
01662         activeCount = atoi(row[0]);
01663     else
01664     {
01665         sendError(ERROR_MYSQL_QUERY);
01666         return;
01667     }
01668 
01669     if (!activeCount)
01670         runCommand(g_binPath + "/zmdc.pl stop zmc " + zmcArgs);
01671     else
01672     {
01673         if (mode == RESTART)
01674             runCommand(g_binPath + "/zmdc.pl stop zmc " + zmcArgs);
01675 
01676         runCommand(g_binPath + "/zmdc.pl start zmc " + zmcArgs);
01677     }
01678 }
01679 
01680 void ZMServer::zmaControl(MONITOR *monitor, const string &mode)
01681 {
01682     int zmOptControl = atoi(getZMSetting("ZM_OPT_CONTROL").c_str());
01683     int zmOptFrameServer = atoi(getZMSetting("ZM_OPT_FRAME_SERVER").c_str());
01684 
01685     if (monitor->function == FUNCTION_MODECT ||
01686         monitor->function == FUNCTION_RECORD ||
01687         monitor->function == FUNCTION_MOCORD ||
01688         monitor->function == FUNCTION_NODECT)
01689     {
01690         if (mode == RESTART)
01691         {
01692             if (zmOptControl)
01693                  runCommand(g_binPath + "/zmdc.pl stop zmtrack.pl -m " + monitor->getIdStr());
01694 
01695             runCommand(g_binPath + "/zmdc.pl stop zma -m " + monitor->getIdStr());
01696 
01697             if (zmOptFrameServer)
01698                 runCommand(g_binPath + "/zmdc.pl stop zmf -m " + monitor->getIdStr());
01699         }
01700 
01701         if (zmOptFrameServer)
01702             runCommand(g_binPath + "/zmdc.pl start zmf -m " + monitor->getIdStr());
01703 
01704         runCommand(g_binPath + "/zmdc.pl start zma -m " + monitor->getIdStr());
01705 
01706         if (zmOptControl && monitor->controllable && monitor->trackMotion &&
01707             ( monitor->function == FUNCTION_MODECT || monitor->function == FUNCTION_MOCORD) )
01708             runCommand(g_binPath + "/zmdc.pl start zmtrack.pl -m " + monitor->getIdStr());
01709 
01710         if (mode == RELOAD)
01711             runCommand(g_binPath + "/zmdc.pl reload zma -m " + monitor->getIdStr());
01712     }
01713     else
01714     {
01715         if (zmOptControl)
01716                  runCommand(g_binPath + "/zmdc.pl stop zmtrack.pl -m " + monitor->getIdStr());
01717 
01718         runCommand(g_binPath + "/zmdc.pl stop zma -m " + monitor->getIdStr());
01719 
01720         if (zmOptFrameServer)
01721                 runCommand(g_binPath + "/zmdc.pl stop zmf -m " + monitor->getIdStr());
01722     }
01723 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends