|
MythTV
0.26-pre
|
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 }
1.7.6.1