MythTV  0.28pre
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Groups Pages
zmserver.cpp
Go to the documentation of this file.
1 /* Implementation of the ZMServer class.
2  * ============================================================
3  * This program is free software; you can redistribute it
4  * and/or modify it under the terms of the GNU General
5  * Public License as published bythe Free Software Foundation;
6  * either version 2, or (at your option)
7  * any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * ============================================================ */
15 
16 
17 #include <iostream>
18 #include <cstdlib>
19 #include <cstring>
20 #include <cstdio>
21 #include <errno.h>
22 #include <sys/socket.h>
23 #include <fcntl.h>
24 #include <netinet/in.h>
25 #include <sys/stat.h>
26 #include <sys/shm.h>
27 #include <sys/mman.h>
28 
29 #ifdef linux
30 # include <sys/vfs.h>
31 # include <sys/statvfs.h>
32 # include <sys/sysinfo.h>
33 #else
34 # include <sys/param.h>
35 # include <sys/mount.h>
36 # if CONFIG_CYGWIN
37 # include <sys/statfs.h>
38 # else // if !CONFIG_CYGWIN
39 # include <sys/sysctl.h>
40 # endif // !CONFIG_CYGWIN
41 #endif
42 
43 #include "mythtv/mythconfig.h"
44 
45 #if CONFIG_DARWIN
46 #define MSG_NOSIGNAL 0 // Apple also has SO_NOSIGPIPE?
47 #endif
48 
49 #include "zmserver.h"
50 
51 // the version of the protocol we understand
52 #define ZM_PROTOCOL_VERSION "11"
53 
54 // the maximum image size we are ever likely to get from ZM
55 #define MAX_IMAGE_SIZE (2048*1536*3)
56 
57 #define ADD_STR(list,s) list += s; list += "[]:[]";
58 // TODO rewrite after we require C++11, see http://en.cppreference.com/w/cpp/string/basic_string/to_string
59 #define ADD_INT(list,n) sprintf(m_buf, "%d", (int)n); list += m_buf; list += "[]:[]";
60 
61 // error messages
62 #define ERROR_TOKEN_COUNT "Invalid token count"
63 #define ERROR_MYSQL_QUERY "Mysql Query Error"
64 #define ERROR_MYSQL_ROW "Mysql Get Row Error"
65 #define ERROR_FILE_OPEN "Cannot open event file"
66 #define ERROR_INVALID_MONITOR "Invalid Monitor"
67 #define ERROR_INVALID_POINTERS "Cannot get shared memory pointers"
68 #define ERROR_INVALID_MONITOR_FUNCTION "Invalid Monitor Function"
69 #define ERROR_INVALID_MONITOR_ENABLE_VALUE "Invalid Monitor Enable Value"
70 #define ERROR_NO_FRAMES "No frames found for event"
71 
72 // Subpixel ordering (from zm_rgb.h)
73 // Based on byte order naming. For example, for ARGB (on both little endian or big endian)
74 // byte+0 should be alpha, byte+1 should be red, and so on.
75 #define ZM_SUBPIX_ORDER_NONE 2
76 #define ZM_SUBPIX_ORDER_RGB 6
77 #define ZM_SUBPIX_ORDER_BGR 5
78 #define ZM_SUBPIX_ORDER_BGRA 7
79 #define ZM_SUBPIX_ORDER_RGBA 8
80 #define ZM_SUBPIX_ORDER_ABGR 9
81 #define ZM_SUBPIX_ORDER_ARGB 10
82 
83 MYSQL g_dbConn;
84 string g_zmversion = "";
85 string g_password = "";
86 string g_server = "";
87 string g_database = "";
88 string g_webPath = "";
89 string g_user = "";
90 string g_webUser = "";
91 string g_binPath = "";
95 
96 time_t g_lastDBKick = 0;
97 
98 // returns true if the ZM version >= the requested version
99 bool checkVersion(int major, int minor, int revision)
100 {
101  if (g_majorVersion >= major && g_minorVersion >= minor && g_revisionVersion >= revision)
102  return true;
103 
104  return false;
105 }
106 
107 void loadZMConfig(const string &configfile)
108 {
109  cout << "loading zm config from " << configfile << endl;
110  FILE *cfg;
111  char line[512];
112  char val[250];
113 
114  if ( (cfg = fopen(configfile.c_str(), "r")) == NULL )
115  {
116  fprintf(stderr,"Can't open %s\n", configfile.c_str());
117  exit(1);
118  }
119 
120  while ( fgets( line, sizeof(line), cfg ) != NULL )
121  {
122  char *line_ptr = line;
123  // Trim off any cr/lf line endings
124  size_t chomp_len = strcspn( line_ptr, "\r\n" );
125  line_ptr[chomp_len] = '\0';
126 
127  // Remove leading white space
128  size_t white_len = strspn( line_ptr, " \t" );
129  line_ptr += white_len;
130 
131  // Check for comment or empty line
132  if ( *line_ptr == '\0' || *line_ptr == '#' )
133  continue;
134 
135  // Remove trailing white space
136  char *temp_ptr = line_ptr+strlen(line_ptr)-1;
137  while ( *temp_ptr == ' ' || *temp_ptr == '\t' )
138  {
139  *temp_ptr-- = '\0';
140  temp_ptr--;
141  }
142 
143  // Now look for the '=' in the middle of the line
144  temp_ptr = strchr( line_ptr, '=' );
145  if ( !temp_ptr )
146  {
147  fprintf(stderr,"Invalid data in %s: '%s'\n", configfile.c_str(), line );
148  continue;
149  }
150 
151  // Assign the name and value parts
152  char *name_ptr = line_ptr;
153  char *val_ptr = temp_ptr+1;
154 
155  // Trim trailing space from the name part
156  do
157  {
158  *temp_ptr = '\0';
159  temp_ptr--;
160  }
161  while ( *temp_ptr == ' ' || *temp_ptr == '\t' );
162 
163  // Remove leading white space from the value part
164  white_len = strspn( val_ptr, " \t" );
165  val_ptr += white_len;
166 
167  strncpy( val, val_ptr, strlen(val_ptr)+1 );
168  if ( strcasecmp( name_ptr, "ZM_DB_HOST" ) == 0 ) g_server = val;
169  else if ( strcasecmp( name_ptr, "ZM_DB_NAME" ) == 0 ) g_database = val;
170  else if ( strcasecmp( name_ptr, "ZM_DB_USER" ) == 0 ) g_user = val;
171  else if ( strcasecmp( name_ptr, "ZM_DB_PASS" ) == 0 ) g_password = val;
172  else if ( strcasecmp( name_ptr, "ZM_PATH_WEB" ) == 0 ) g_webPath = val;
173  else if ( strcasecmp( name_ptr, "ZM_PATH_BIN" ) == 0 ) g_binPath = val;
174  else if ( strcasecmp( name_ptr, "ZM_WEB_USER" ) == 0 ) g_webUser = val;
175  else if ( strcasecmp( name_ptr, "ZM_VERSION" ) == 0 ) g_zmversion = val;
176  }
177  fclose(cfg);
178 }
179 
181 {
182  if (!mysql_init(&g_dbConn))
183  {
184  cout << "Error: Can't initialise structure: " << mysql_error(&g_dbConn) << endl;
185  exit(mysql_errno(&g_dbConn));
186  }
187 
188  if (!mysql_real_connect(&g_dbConn, g_server.c_str(), g_user.c_str(),
189  g_password.c_str(), 0, 0, 0, 0))
190  {
191  cout << "Error: Can't connect to server: " << mysql_error(&g_dbConn) << endl;
192  exit(mysql_errno( &g_dbConn));
193  }
194 
195  if (mysql_select_db(&g_dbConn, g_database.c_str()))
196  {
197  cout << "Error: Can't select database: " << mysql_error(&g_dbConn) << endl;
198  exit(mysql_errno(&g_dbConn));
199  }
200 }
201 
202 void kickDatabase(bool debug)
203 {
204  if (time(NULL) < g_lastDBKick + DB_CHECK_TIME)
205  return;
206 
207  if (debug)
208  cout << "Kicking database connection" << endl;
209 
210  g_lastDBKick = time(NULL);
211 
212  if (mysql_query(&g_dbConn, "SELECT NULL;") == 0)
213  {
214  MYSQL_RES *res = mysql_store_result(&g_dbConn);
215  if (res)
216  mysql_free_result(res);
217  return;
218  }
219 
220  cout << "Lost connection to DB - trying to reconnect" << endl;
221 
222  // failed so try to reconnect to the DB
223  mysql_close(&g_dbConn);
225 }
226 
228 
230  name(""), type(""), function(""), enabled(0), device(""), host(""),
231  image_buffer_count(0), width(0), height(0), bytes_per_pixel(3), mon_id(0),
232  shared_images(NULL), last_read(0), status(""), palette(0),
233  controllable(0), trackMotion(0), mapFile(-1), shm_ptr(NULL),
234  shared_data(NULL), shared_data26(NULL), id("")
235 {
236 }
237 
238 void MONITOR::initMonitor(bool debug, string mmapPath, int shmKey)
239 {
240  int shared_data_size;
241  int frame_size = width * height * bytes_per_pixel;
242 
243  if (checkVersion(1, 26, 0))
244  {
245  shared_data_size = sizeof(SharedData26) +
246  sizeof(TriggerData26) +
247  ((image_buffer_count) * (sizeof(struct timeval))) +
248  ((image_buffer_count) * frame_size) + 64;
249  }
250  else
251  {
252  shared_data_size = sizeof(SharedData) +
253  sizeof(TriggerData) +
254  ((image_buffer_count) * (sizeof(struct timeval))) +
255  ((image_buffer_count) * frame_size);
256  }
257 
258 #if _POSIX_MAPPED_FILES > 0L
259  /*
260  * Try to open the mmap file first if the architecture supports it.
261  * Otherwise, legacy shared memory will be used below.
262  */
263  stringstream mmap_filename;
264  mmap_filename << mmapPath << "/zm.mmap." << mon_id;
265 
266  mapFile = open(mmap_filename.str().c_str(), O_RDONLY, 0x0);
267  if (mapFile >= 0)
268  {
269  if (debug)
270  cout << "Opened mmap file: " << mmap_filename.str() << endl;
271 
272  shm_ptr = mmap(NULL, shared_data_size, PROT_READ,
273  MAP_SHARED, mapFile, 0x0);
274  if (shm_ptr == MAP_FAILED)
275  {
276  cout << "Failed to map shared memory from file ["
277  << mmap_filename << "] " << "for monitor: "
278  << mon_id << endl;
279  status = "Error";
280 
281  if (close(mapFile) == -1)
282  cout << "Failed to close mmap file" << endl;
283 
284  mapFile = -1;
285  shm_ptr = NULL;
286 
287  return;
288  }
289  }
290  else
291  {
292  // this is not necessarily a problem, maybe the user is still
293  // using the legacy shared memory support
294  if (debug)
295  {
296  cout << "Failed to open mmap file [" << mmap_filename << "] "
297  << "for monitor: " << mon_id
298  << " : " << strerror(errno) << endl;
299  cout << "Falling back to the legacy shared memory method" << endl;
300  }
301  }
302 #endif
303 
304  if (shm_ptr == NULL)
305  {
306  // fail back to shmget() functionality if mapping memory above failed.
307  int shmid;
308 
309  if ((shmid = shmget((shmKey & 0xffffff00) | mon_id,
310  shared_data_size, SHM_R)) == -1)
311  {
312  cout << "Failed to shmget for monitor: " << mon_id << endl;
313  status = "Error";
314  switch(errno)
315  {
316  case EACCES: cout << "EACCES - no rights to access segment\n"; break;
317  case EEXIST: cout << "EEXIST - segment already exists\n"; break;
318  case EINVAL: cout << "EINVAL - size < SHMMIN or size > SHMMAX\n"; break;
319  case ENFILE: cout << "ENFILE - limit on open files has been reached\n"; break;
320  case ENOENT: cout << "ENOENT - no segment exists for the given key\n"; break;
321  case ENOMEM: cout << "ENOMEM - couldn't reserve memory for segment\n"; break;
322  case ENOSPC: cout << "ENOSPC - shmmni or shmall limit reached\n"; break;
323  }
324 
325  return;
326  }
327 
328  shm_ptr = shmat(shmid, 0, SHM_RDONLY);
329 
330 
331  if (shm_ptr == NULL)
332  {
333  cout << "Failed to shmat for monitor: " << mon_id << endl;
334  status = "Error";
335  return;
336  }
337  }
338 
339  if (checkVersion(1, 26, 0))
340  {
341  shared_data = NULL;
343 
344  shared_images = (unsigned char*) shm_ptr +
345  sizeof(SharedData26) + sizeof(TriggerData26) +
346  ((image_buffer_count) * sizeof(struct timeval));
347 
348  if (((unsigned long)shared_images % 16) != 0)
349  {
350  // align images buffer to nearest 16 byte boundary
351  shared_images = (unsigned char*)((unsigned long)shared_images + (16 - ((unsigned long)shared_images % 16)));
352  }
353  }
354  else
355  {
356  shared_data26 = NULL;
358 
359  shared_images = (unsigned char*) shm_ptr +
360  sizeof(SharedData) + sizeof(TriggerData) +
361  ((image_buffer_count) * sizeof(struct timeval));
362  }
363 }
364 
366 {
367  if (checkVersion(1, 26, 0))
368  return shared_data26 != NULL && shared_images != NULL;
369 
370  // must be version >= 1.24.0 and < 1.26.0
371  return shared_data != NULL && shared_images != NULL;
372 }
373 
374 
375 string MONITOR::getIdStr(void)
376 {
377  if (id == "")
378  {
379  std::stringstream out;
380  out << mon_id;
381  id = out.str();
382  }
383  return id;
384 }
385 
387 {
388  if (shared_data)
390 
392 }
393 
395 {
396  if (shared_data)
397  return shared_data->state;
398 
399  return shared_data26->state;
400 }
401 
403 {
404  if (shared_data)
405  {
406  if (bytes_per_pixel == 1)
407  return ZM_SUBPIX_ORDER_NONE;
408  else
409  return ZM_SUBPIX_ORDER_RGB;
410  }
411 
412  return shared_data26->format;
413 }
414 
416 {
417  if (shared_data)
418  return width * height * bytes_per_pixel;
419 
420  return shared_data26->imagesize;
421 }
422 
424 
425 ZMServer::ZMServer(int sock, bool debug)
426 {
427  if (debug)
428  cout << "Using server protocol version '" << ZM_PROTOCOL_VERSION << "'\n";
429 
430  m_sock = sock;
431  m_debug = debug;
432 
433  // get the shared memory key
434  char buf[100];
435  m_shmKey = 0x7a6d2000;
436  string setting = getZMSetting("ZM_SHM_KEY");
437 
438  if (setting != "")
439  {
440  unsigned long long tmp = m_shmKey;
441  sscanf(setting.c_str(), "%20llx", &tmp);
442  m_shmKey = tmp;
443  }
444 
445  if (m_debug)
446  {
447  snprintf(buf, sizeof(buf), "0x%x", (unsigned int)m_shmKey);
448  cout << "Shared memory key is: " << buf << endl;
449  }
450 
451  // get the MMAP path
452  m_mmapPath = getZMSetting("ZM_PATH_MAP");
453  if (m_debug)
454  {
455  cout << "Memory path directory is: " << m_mmapPath << endl;
456  }
457 
458  // get the event filename format
459  setting = getZMSetting("ZM_EVENT_IMAGE_DIGITS");
460  int eventDigits = atoi(setting.c_str());
461  snprintf(buf, sizeof(buf), "%%0%dd-capture.jpg", eventDigits);
463  if (m_debug)
464  cout << "Event file format is: " << m_eventFileFormat << endl;
465 
466  // get the analysis filename format
467  snprintf(buf, sizeof(buf), "%%0%dd-analyse.jpg", eventDigits);
469  if (m_debug)
470  cout << "Analysis file format is: " << m_analysisFileFormat << endl;
471 
472  // is ZM using the deep storage directory format?
473  m_useDeepStorage = (getZMSetting("ZM_USE_DEEP_STORAGE") == "1");
474  if (m_debug)
475  {
476  if (m_useDeepStorage)
477  cout << "using deep storage directory structure" << endl;
478  else
479  cout << "using flat directory structure" << endl;
480  }
481 
482  // is ZM creating analysis images?
483  m_useAnalysisImages = (getZMSetting("ZM_CREATE_ANALYSIS_IMAGES") == "1");
484  if (m_debug)
485  {
487  cout << "using analysis images" << endl;
488  else
489  cout << "not using analysis images" << endl;
490  }
491 
492  getMonitorList();
493 
494  // zero buffer for conversion of integer to string in ADD_INT
495  memset (m_buf, '\0', sizeof(m_buf));
496 }
497 
499 {
500  for (uint x = 0; x < m_monitors.size(); x++)
501  {
502  MONITOR *mon = m_monitors.at(x);
503  if (mon->mapFile != -1)
504  {
505  if (close(mon->mapFile) == -1)
506  cout << "Failed to close mapFile" << endl;
507  else
508  if (m_debug)
509  cout << "Closed mapFile for monitor: " << mon->name << endl;
510  }
511 
512  delete mon;
513  }
514 
515  m_monitors.clear();
516  m_monitorMap.clear();
517 
518  if (m_debug)
519  cout << "ZMServer destroyed\n";
520 }
521 
522 void ZMServer::tokenize(const string &command, vector<string> &tokens)
523 {
524  string token = "";
525  tokens.clear();
526  string::size_type startPos = 0;
527  string::size_type endPos = 0;
528 
529  while((endPos = command.find("[]:[]", startPos)) != string::npos)
530  {
531  token = command.substr(startPos, endPos - startPos);
532  tokens.push_back(token);
533  startPos = endPos + 5;
534  }
535 
536  // make sure we add the last token
537  if (endPos != command.length())
538  {
539  token = command.substr(startPos);
540  tokens.push_back(token);
541  }
542 }
543 
544 // returns true if we get a QUIT command from the client
545 bool ZMServer::processRequest(char* buf, int nbytes)
546 {
547 #if 0
548  // first 8 bytes is the length of the following data
549  char len[9];
550  memcpy(len, buf, 8);
551  len[8] = '\0';
552  int dataLen = atoi(len);
553 #endif
554 
555  buf[nbytes] = '\0';
556  string s(buf+8);
557  vector<string> tokens;
558  tokenize(s, tokens);
559 
560  if (tokens.empty())
561  return false;
562 
563  if (m_debug)
564  cout << "Processing: '" << tokens[0] << "'" << endl;
565 
566  if (tokens[0] == "HELLO")
567  handleHello();
568  else if (tokens[0] == "QUIT")
569  return true;
570  else if (tokens[0] == "GET_SERVER_STATUS")
572  else if (tokens[0] == "GET_MONITOR_STATUS")
574  else if (tokens[0] == "GET_ALARM_STATES")
576  else if (tokens[0] == "GET_EVENT_LIST")
577  handleGetEventList(tokens);
578  else if (tokens[0] == "GET_EVENT_DATES")
579  handleGetEventDates(tokens);
580  else if (tokens[0] == "GET_EVENT_FRAME")
581  handleGetEventFrame(tokens);
582  else if (tokens[0] == "GET_ANALYSE_FRAME")
583  handleGetAnalysisFrame(tokens);
584  else if (tokens[0] == "GET_LIVE_FRAME")
585  handleGetLiveFrame(tokens);
586  else if (tokens[0] == "GET_FRAME_LIST")
587  handleGetFrameList(tokens);
588  else if (tokens[0] == "GET_CAMERA_LIST")
590  else if (tokens[0] == "GET_MONITOR_LIST")
592  else if (tokens[0] == "DELETE_EVENT")
593  handleDeleteEvent(tokens);
594  else if (tokens[0] == "DELETE_EVENT_LIST")
595  handleDeleteEventList(tokens);
596  else if (tokens[0] == "RUN_ZMAUDIT")
598  else if (tokens[0] == "SET_MONITOR_FUNCTION")
599  handleSetMonitorFunction(tokens);
600  else
601  send("UNKNOWN_COMMAND");
602 
603  return false;
604 }
605 
606 bool ZMServer::send(const string &s) const
607 {
608  // send length
609  size_t len = s.size();
610  char buf[9];
611  sprintf(buf, "%8u", (unsigned int) len);
612  int status = ::send(m_sock, buf, 8, MSG_NOSIGNAL);
613  if (status == -1)
614  return false;
615 
616  // send message
617  status = ::send(m_sock, s.c_str(), s.size(), MSG_NOSIGNAL);
618  if ( status == -1 )
619  return false;
620  else
621  return true;
622 }
623 
624 bool ZMServer::send(const string &s, const unsigned char *buffer, int dataLen) const
625 {
626  // send length
627  size_t len = s.size();
628  char buf[9];
629  sprintf(buf, "%8u", (unsigned int) len);
630  int status = ::send(m_sock, buf, 8, MSG_NOSIGNAL);
631  if (status == -1)
632  return false;
633 
634  // send message
635  status = ::send(m_sock, s.c_str(), s.size(), MSG_NOSIGNAL);
636  if ( status == -1 )
637  return false;
638 
639  // send data
640  status = ::send(m_sock, buffer, dataLen, MSG_NOSIGNAL);
641  if ( status == -1 )
642  return false;
643 
644  return true;
645 }
646 
648 {
649  string outStr("");
650  ADD_STR(outStr, string("ERROR - ") + error);
651  send(outStr);
652 }
653 
655 {
656  // just send OK so the client knows all is well
657  // followed by the protocol version we understand
658  string outStr("");
659  ADD_STR(outStr, "OK");
660  ADD_STR(outStr, ZM_PROTOCOL_VERSION);
661  send(outStr);
662 }
663 
664 long long ZMServer::getDiskSpace(const string &filename, long long &total, long long &used)
665 {
666  struct statfs statbuf;
667  memset(&statbuf, 0, sizeof(statbuf));
668  long long freespace = -1;
669 
670  total = used = -1;
671 
672  // there are cases where statfs will return 0 (good), but f_blocks and
673  // others are invalid and set to 0 (such as when an automounted directory
674  // is not mounted but still visible because --ghost was used),
675  // so check to make sure we can have a total size > 0
676  if ((statfs(filename.c_str(), &statbuf) == 0) &&
677  (statbuf.f_blocks > 0) &&
678  (statbuf.f_bsize > 0))
679  {
680  total = statbuf.f_blocks;
681  total *= statbuf.f_bsize;
682  total = total >> 10;
683 
684  freespace = statbuf.f_bavail;
685  freespace *= statbuf.f_bsize;
686  freespace = freespace >> 10;
687 
688  used = total - freespace;
689  }
690 
691  return freespace;
692 }
693 
695 {
696  string outStr("");
697  ADD_STR(outStr, "OK")
698 
699  // server status
700  string status = runCommand(g_binPath + "/zmdc.pl check");
701  ADD_STR(outStr, status)
702 
703  // get load averages
704  double loads[3];
705  if (getloadavg(loads, 3) == -1)
706  {
707  ADD_STR(outStr, "Unknown")
708  }
709  else
710  {
711  char buf[30];
712  sprintf(buf, "%0.2lf", loads[0]);
713  ADD_STR(outStr, buf)
714  }
715 
716  // get free space on the disk where the events are stored
717  char buf[15];
718  long long total, used;
719  string eventsDir = g_webPath + "/events/";
720  getDiskSpace(eventsDir, total, used);
721  sprintf(buf, "%d%%", (int) ((100.0 / ((float) total / used))));
722  ADD_STR(outStr, buf)
723 
724  send(outStr);
725 }
726 
728 {
729  string outStr("");
730  ADD_STR(outStr, "OK")
731 
732  // add the monitor count
733  ADD_INT(outStr, m_monitors.size())
734 
735  for (int x = 0; x < (int)m_monitors.size(); x++)
736  {
737  MONITOR *monitor = m_monitors.at(x);
738 
739  // add monitor ID
740  ADD_INT(outStr, monitor->mon_id)
741 
742  // add monitor status
743  ADD_INT(outStr, monitor->getState())
744  }
745 
746  send(outStr);
747 }
748 
749 void ZMServer::handleGetEventList(vector<string> tokens)
750 {
751  string outStr("");
752 
753  if (tokens.size() != 5)
754  {
755  sendError(ERROR_TOKEN_COUNT);
756  return;
757  }
758 
759  string monitor = tokens[1];
760  bool oldestFirst = (tokens[2] == "1");
761  string date = tokens[3];
762  bool includeContinuous = (tokens[4] == "1");
763 
764  if (m_debug)
765  cout << "Loading events for monitor: " << monitor << ", date: " << date << endl;
766 
767  ADD_STR(outStr, "OK")
768 
769  MYSQL_RES *res;
770  MYSQL_ROW row;
771 
772  string sql("SELECT E.Id, E.Name, M.Id AS MonitorID, M.Name AS MonitorName, E.StartTime, "
773  "E.Length, M.Width, M.Height, M.DefaultRate, M.DefaultScale "
774  "from Events as E inner join Monitors as M on E.MonitorId = M.Id ");
775 
776  if (monitor != "<ANY>")
777  {
778  sql += "WHERE M.Name = '" + monitor + "' ";
779 
780  if (date != "<ANY>")
781  sql += "AND DATE(E.StartTime) = DATE('" + date + "') ";
782  }
783  else
784  {
785  if (date != "<ANY>")
786  {
787  sql += "WHERE DATE(E.StartTime) = DATE('" + date + "') ";
788 
789  if (!includeContinuous)
790  sql += "AND Cause != 'Continuous' ";
791  }
792  else
793  if (!includeContinuous)
794  sql += "WHERE Cause != 'Continuous' ";
795  }
796 
797  if (oldestFirst)
798  sql += "ORDER BY E.StartTime ASC";
799  else
800  sql += "ORDER BY E.StartTime DESC";
801 
802  if (mysql_query(&g_dbConn, sql.c_str()))
803  {
804  fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
805  sendError(ERROR_MYSQL_QUERY);
806  return;
807  }
808 
809  res = mysql_store_result(&g_dbConn);
810  int eventCount = mysql_num_rows(res);
811 
812  if (m_debug)
813  cout << "Got " << eventCount << " events" << endl;
814 
815  ADD_INT(outStr, eventCount)
816 
817  for (int x = 0; x < eventCount; x++)
818  {
819  row = mysql_fetch_row(res);
820  if (row)
821  {
822  ADD_STR(outStr, row[0]) // eventID
823  ADD_STR(outStr, row[1]) // event name
824  ADD_STR(outStr, row[2]) // monitorID
825  ADD_STR(outStr, row[3]) // monitor name
826  row[4][10] = 'T';
827  ADD_STR(outStr, row[4]) // start time
828  ADD_STR(outStr, row[5]) // length
829  }
830  else
831  {
832  cout << "Failed to get mysql row" << endl;
833  sendError(ERROR_MYSQL_ROW);
834  return;
835  }
836  }
837 
838  mysql_free_result(res);
839 
840  send(outStr);
841 }
842 
843 void ZMServer::handleGetEventDates(vector<string> tokens)
844 {
845  string outStr("");
846 
847  if (tokens.size() != 3)
848  {
849  sendError(ERROR_TOKEN_COUNT);
850  return;
851  }
852 
853  string monitor = tokens[1];
854  bool oldestFirst = (tokens[2] == "1");
855 
856  if (m_debug)
857  cout << "Loading event dates for monitor: " << monitor << endl;
858 
859  ADD_STR(outStr, "OK")
860 
861  MYSQL_RES *res;
862  MYSQL_ROW row;
863 
864  string sql("SELECT DISTINCT DATE(E.StartTime) "
865  "from Events as E inner join Monitors as M on E.MonitorId = M.Id ");
866 
867  if (monitor != "<ANY>")
868  sql += "WHERE M.Name = '" + monitor + "' ";
869 
870  if (oldestFirst)
871  sql += "ORDER BY E.StartTime ASC";
872  else
873  sql += "ORDER BY E.StartTime DESC";
874 
875  if (mysql_query(&g_dbConn, sql.c_str()))
876  {
877  fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
878  sendError(ERROR_MYSQL_QUERY);
879  return;
880  }
881 
882  res = mysql_store_result(&g_dbConn);
883  int dateCount = mysql_num_rows(res);
884 
885  if (m_debug)
886  cout << "Got " << dateCount << " dates" << endl;
887 
888  ADD_INT(outStr, dateCount)
889 
890  for (int x = 0; x < dateCount; x++)
891  {
892  row = mysql_fetch_row(res);
893  if (row)
894  {
895  ADD_STR(outStr, row[0]) // event date
896  }
897  else
898  {
899  cout << "Failed to get mysql row" << endl;
900  sendError(ERROR_MYSQL_ROW);
901  return;
902  }
903  }
904 
905  mysql_free_result(res);
906 
907  send(outStr);
908 }
909 
911 {
912  string outStr("");
913  ADD_STR(outStr, "OK")
914 
915  // get monitor list
916  MYSQL_RES *res;
917  MYSQL_ROW row;
918 
919  string sql("SELECT Id, Name, Type, Device, Host, Channel, Function, Enabled "
920  "FROM Monitors;");
921  if (mysql_query(&g_dbConn, sql.c_str()))
922  {
923  fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
924  sendError(ERROR_MYSQL_QUERY);
925  return;
926  }
927 
928  res = mysql_store_result(&g_dbConn);
929 
930  // add monitor count
931  int monitorCount = mysql_num_rows(res);
932 
933  if (m_debug)
934  cout << "Got " << monitorCount << " monitors" << endl;
935 
936  ADD_INT(outStr, monitorCount)
937 
938  for (int x = 0; x < monitorCount; x++)
939  {
940  row = mysql_fetch_row(res);
941  if (row)
942  {
943  string id = row[0];
944  string type = row[2];
945  string device = row[3];
946  string host = row[4];
947  string channel = row[5];
948  string function = row[6];
949  string enabled = row[7];
950  string name = row[1];
951  string events = "";
952  string zmcStatus = "";
953  string zmaStatus = "";
954  getMonitorStatus(id, type, device, host, channel, function,
955  zmcStatus, zmaStatus, enabled);
956  MYSQL_RES *res2;
957  MYSQL_ROW row2;
958 
959  string sql2("SELECT count(if(Archived=0,1,NULL)) AS EventCount "
960  "FROM Events AS E "
961  "WHERE MonitorId = " + id);
962 
963  if (mysql_query(&g_dbConn, sql2.c_str()))
964  {
965  fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
966  sendError(ERROR_MYSQL_QUERY);
967  return;
968  }
969 
970  res2 = mysql_store_result(&g_dbConn);
971  if (mysql_num_rows(res2) > 0)
972  {
973  row2 = mysql_fetch_row(res2);
974  if (row2)
975  events = row2[0];
976  else
977  {
978  cout << "Failed to get mysql row" << endl;
979  sendError(ERROR_MYSQL_ROW);
980  return;
981  }
982  }
983 
984  ADD_STR(outStr, id)
985  ADD_STR(outStr, name)
986  ADD_STR(outStr, zmcStatus)
987  ADD_STR(outStr, zmaStatus)
988  ADD_STR(outStr, events)
989  ADD_STR(outStr, function)
990  ADD_STR(outStr, enabled)
991 
992  mysql_free_result(res2);
993  }
994  else
995  {
996  cout << "Failed to get mysql row" << endl;
997  sendError(ERROR_MYSQL_ROW);
998  return;
999  }
1000  }
1001 
1002  mysql_free_result(res);
1003 
1004  send(outStr);
1005 }
1006 
1007 string ZMServer::runCommand(string command)
1008 {
1009  string outStr("");
1010  FILE *fd = popen(command.c_str(), "r");
1011  char buffer[100];
1012 
1013  while (fgets(buffer, sizeof(buffer), fd) != NULL)
1014  {
1015  outStr += buffer;
1016  }
1017  pclose(fd);
1018  return outStr;
1019 }
1020 
1021 void ZMServer::getMonitorStatus(string id, string type, string device, string host, string channel,
1022  string function, string &zmcStatus, string &zmaStatus,
1023  string enabled)
1024 {
1025  zmaStatus = "";
1026  zmcStatus = "";
1027 
1028  string command(g_binPath + "/zmdc.pl status");
1029  string status = runCommand(command);
1030 
1031  if (type == "Local")
1032  {
1033  if (enabled == "0")
1034  zmaStatus = device + "(" + channel + ") [-]";
1035  else if (status.find("'zma -m " + id + "' running") != string::npos)
1036  zmaStatus = device + "(" + channel + ") [R]";
1037  else
1038  zmaStatus = device + "(" + channel + ") [S]";
1039  }
1040  else
1041  {
1042  if (enabled == "0")
1043  zmaStatus = host + " [-]";
1044  else if (status.find("'zma -m " + id + "' running") != string::npos)
1045  zmaStatus = host + " [R]";
1046  else
1047  zmaStatus = host + " [S]";
1048  }
1049 
1050  if (type == "Local")
1051  {
1052  if (enabled == "0")
1053  zmcStatus = function + " [-]";
1054  else if (status.find("'zmc -d "+ device + "' running") != string::npos)
1055  zmcStatus = function + " [R]";
1056  else
1057  zmcStatus = function + " [S]";
1058  }
1059  else
1060  {
1061  if (enabled == "0")
1062  zmcStatus = function + " [-]";
1063  else if (status.find("'zmc -m " + id + "' running") != string::npos)
1064  zmcStatus = function + " [R]";
1065  else
1066  zmcStatus = function + " [S]";
1067  }
1068 }
1069 
1070 void ZMServer::handleGetEventFrame(vector<string> tokens)
1071 {
1072  static unsigned char buffer[MAX_IMAGE_SIZE];
1073 
1074  if (tokens.size() != 5)
1075  {
1076  sendError(ERROR_TOKEN_COUNT);
1077  return;
1078  }
1079 
1080  string monitorID(tokens[1]);
1081  string eventID(tokens[2]);
1082  int frameNo = atoi(tokens[3].c_str());
1083  string eventTime(tokens[4]);
1084 
1085  if (m_debug)
1086  cout << "Getting frame " << frameNo << " for event " << eventID
1087  << " on monitor " << monitorID << " event time is " << eventTime << endl;
1088 
1089  string outStr("");
1090 
1091  ADD_STR(outStr, "OK")
1092 
1093  // try to find the frame file
1094  string filepath("");
1095  char str[100];
1096 
1097  if (m_useDeepStorage)
1098  {
1099  filepath = g_webPath + "/events/" + monitorID + "/" + eventTime + "/";
1100  sprintf(str, m_eventFileFormat.c_str(), frameNo);
1101  filepath += str;
1102  }
1103  else
1104  {
1105  filepath = g_webPath + "/events/" + monitorID + "/" + eventID + "/";
1106  sprintf(str, m_eventFileFormat.c_str(), frameNo);
1107  filepath += str;
1108  }
1109 
1110  FILE *fd;
1111  int fileSize = 0;
1112  if ((fd = fopen(filepath.c_str(), "r" )))
1113  {
1114  fileSize = fread(buffer, 1, sizeof(buffer), fd);
1115  fclose(fd);
1116  }
1117  else
1118  {
1119  cout << "Can't open " << filepath << ": " << strerror(errno) << endl;
1120  sendError(ERROR_FILE_OPEN + string(" - ") + filepath + " : " + strerror(errno));
1121  return;
1122  }
1123 
1124  if (m_debug)
1125  cout << "Frame size: " << fileSize << endl;
1126 
1127  // get the file size
1128  ADD_INT(outStr, fileSize)
1129 
1130  // send the data
1131  send(outStr, buffer, fileSize);
1132 }
1133 
1134 void ZMServer::handleGetAnalysisFrame(vector<string> tokens)
1135 {
1136  static unsigned char buffer[MAX_IMAGE_SIZE];
1137  char str[100];
1138 
1139  if (tokens.size() != 5)
1140  {
1141  sendError(ERROR_TOKEN_COUNT);
1142  return;
1143  }
1144 
1145  string monitorID(tokens[1]);
1146  string eventID(tokens[2]);
1147  int frameNo = atoi(tokens[3].c_str());
1148  string eventTime(tokens[4]);
1149 
1150  if (m_debug)
1151  cout << "Getting analysis frame " << frameNo << " for event " << eventID
1152  << " on monitor " << monitorID << " event time is " << eventTime << endl;
1153 
1154  // get the 'alarm' frames from the Frames table for this event
1155  MYSQL_RES *res;
1156  MYSQL_ROW row = NULL;
1157 
1158  string sql("");
1159  sql += "SELECT FrameId FROM Frames ";
1160  sql += "WHERE EventID = " + eventID + " ";
1161  sql += "AND Type = 'Alarm' ";
1162  sql += "ORDER BY FrameID";
1163 
1164  if (mysql_query(&g_dbConn, sql.c_str()))
1165  {
1166  fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
1167  sendError(ERROR_MYSQL_QUERY);
1168  return;
1169  }
1170 
1171  res = mysql_store_result(&g_dbConn);
1172  int frameCount = mysql_num_rows(res);
1173  int frameID;
1174  string fileFormat = m_analysisFileFormat;
1175 
1176  // if we didn't find any analysis frames look for a normal frame instead
1177  if (frameCount == 0 || m_useAnalysisImages == false)
1178  {
1179  mysql_free_result(res);
1180 
1181  frameNo = 0;
1182  fileFormat = m_eventFileFormat;
1183  sql = "SELECT FrameId FROM Frames ";
1184  sql += "WHERE EventID = " + eventID + " ";
1185  sql += "ORDER BY FrameID";
1186 
1187  if (mysql_query(&g_dbConn, sql.c_str()))
1188  {
1189  fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
1190  sendError(ERROR_MYSQL_QUERY);
1191  return;
1192  }
1193 
1194  res = mysql_store_result(&g_dbConn);
1195  frameCount = mysql_num_rows(res);
1196  }
1197 
1198  // if the required frame mumber is 0 or out of bounds then use the middle frame
1199  if (frameNo == 0 || frameNo < 0 || frameNo > frameCount)
1200  frameNo = (frameCount / 2) + 1;
1201 
1202  // move to the required frame in the table
1203  for (int x = 0; x < frameNo; x++)
1204  {
1205  row = mysql_fetch_row(res);
1206  }
1207 
1208  if (row)
1209  {
1210  frameID = atoi(row[0]);
1211  }
1212  else
1213  {
1214  cout << "handleGetAnalyseFrame: Failed to get mysql row for frameNo " << frameNo << endl;
1215  sendError(ERROR_MYSQL_ROW);
1216  return;
1217  }
1218 
1219  mysql_free_result(res);
1220 
1221  string outStr("");
1222 
1223  ADD_STR(outStr, "OK")
1224 
1225  // try to find the analyse frame file
1226  string filepath("");
1227  if (m_useDeepStorage)
1228  {
1229  filepath = g_webPath + "/events/" + monitorID + "/" + eventTime + "/";
1230  sprintf(str, fileFormat.c_str(), frameID);
1231  filepath += str;
1232  }
1233  else
1234  {
1235  filepath = g_webPath + "/events/" + monitorID + "/" + eventID + "/";
1236  sprintf(str, fileFormat.c_str(), frameID);
1237  filepath += str;
1238  }
1239 
1240  FILE *fd;
1241  int fileSize = 0;
1242  if ((fd = fopen(filepath.c_str(), "r" )))
1243  {
1244  fileSize = fread(buffer, 1, sizeof(buffer), fd);
1245  fclose(fd);
1246  }
1247  else
1248  {
1249  cout << "Can't open " << filepath << ": " << strerror(errno) << endl;
1250  sendError(ERROR_FILE_OPEN + string(" - ") + filepath + " : " + strerror(errno));
1251  return;
1252  }
1253 
1254  if (m_debug)
1255  cout << "Frame size: " << fileSize << endl;
1256 
1257  // get the file size
1258  ADD_INT(outStr, fileSize)
1259 
1260  // send the data
1261  send(outStr, buffer, fileSize);
1262 }
1263 
1264 void ZMServer::handleGetLiveFrame(vector<string> tokens)
1265 {
1266  static unsigned char buffer[MAX_IMAGE_SIZE];
1267 
1268  // we need to periodically kick the DB connection here to make sure it
1269  // stays alive because the user may have left the frontend on the live
1270  // view which doesn't query the DB at all and eventually the connection
1271  // will timeout
1273 
1274  if (tokens.size() != 2)
1275  {
1276  sendError(ERROR_TOKEN_COUNT);
1277  return;
1278  }
1279 
1280  int monitorID = atoi(tokens[1].c_str());
1281 
1282  if (m_debug)
1283  cout << "Getting live frame from monitor: " << monitorID << endl;
1284 
1285  string outStr("");
1286 
1287  ADD_STR(outStr, "OK")
1288 
1289  // echo the monitor id
1290  ADD_INT(outStr, monitorID)
1291 
1292  // try to find the correct MONITOR
1293  MONITOR *monitor;
1294  if (m_monitorMap.find(monitorID) != m_monitorMap.end())
1295  monitor = m_monitorMap[monitorID];
1296  else
1297  {
1298  sendError(ERROR_INVALID_MONITOR);
1299  return;
1300  }
1301 
1302  // are the data pointers valid?
1303  if (!monitor->isValid())
1304  {
1305  sendError(ERROR_INVALID_POINTERS);
1306  return;
1307  }
1308 
1309  // read a frame from the shared memory
1310  int dataSize = getFrame(buffer, sizeof(buffer), monitor);
1311 
1312  if (m_debug)
1313  cout << "Frame size: " << dataSize << endl;
1314 
1315  if (dataSize == 0)
1316  {
1317  // not really an error
1318  outStr = "";
1319  ADD_STR(outStr, "WARNING - No new frame available");
1320  send(outStr);
1321  return;
1322  }
1323 
1324  // add status
1325  ADD_STR(outStr, monitor->status)
1326 
1327  // send the data size
1328  ADD_INT(outStr, dataSize)
1329 
1330  // send the data
1331  send(outStr, buffer, dataSize);
1332 }
1333 
1334 void ZMServer::handleGetFrameList(vector<string> tokens)
1335 {
1336  string eventID;
1337  string outStr("");
1338 
1339  if (tokens.size() != 2)
1340  {
1341  sendError(ERROR_TOKEN_COUNT);
1342  return;
1343  }
1344 
1345  eventID = tokens[1];
1346 
1347  if (m_debug)
1348  cout << "Loading frames for event: " << eventID << endl;
1349 
1350  ADD_STR(outStr, "OK")
1351 
1352  MYSQL_RES *res;
1353  MYSQL_ROW row;
1354  string sql("");
1355 
1356  // check to see what type of event this is
1357  sql += "SELECT Cause, Length, Frames FROM Events ";
1358  sql += "WHERE Id = " + eventID + " ";
1359 
1360  if (mysql_query(&g_dbConn, sql.c_str()))
1361  {
1362  fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
1363  sendError(ERROR_MYSQL_QUERY);
1364  return;
1365  }
1366 
1367  res = mysql_store_result(&g_dbConn);
1368  row = mysql_fetch_row(res);
1369 
1370  // make sure we have some frames to display
1371  if (row[1] == NULL || row[2] == NULL)
1372  {
1373  sendError(ERROR_NO_FRAMES);
1374  return;
1375  }
1376 
1377  string cause = row[0];
1378  double length = atof(row[1]);
1379  int frameCount = atoi(row[2]);
1380 
1381  mysql_free_result(res);
1382 
1383  if (cause == "Continuous")
1384  {
1385  // event is a continuous recording so guess the frame delta's
1386 
1387  if (m_debug)
1388  cout << "Got " << frameCount << " frames (continuous event)" << endl;
1389 
1390  ADD_INT(outStr, frameCount)
1391 
1392  if (frameCount > 0)
1393  {
1394  double delta = length / frameCount;
1395 
1396  for (int x = 0; x < frameCount; x++)
1397  {
1398  char str[10];
1399  sprintf(str, "%f", delta);
1400 
1401  ADD_STR(outStr, "Normal") // Type
1402  ADD_STR(outStr, str) // Delta
1403  }
1404  }
1405  }
1406  else
1407  {
1408  sql = "SELECT Type, Delta FROM Frames ";
1409  sql += "WHERE EventID = " + eventID + " ";
1410  sql += "ORDER BY FrameID";
1411 
1412  if (mysql_query(&g_dbConn, sql.c_str()))
1413  {
1414  fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
1415  sendError(ERROR_MYSQL_QUERY);
1416  return;
1417  }
1418 
1419  res = mysql_store_result(&g_dbConn);
1420  frameCount = mysql_num_rows(res);
1421 
1422  if (m_debug)
1423  cout << "Got " << frameCount << " frames" << endl;
1424 
1425  ADD_INT(outStr, frameCount)
1426 
1427  for (int x = 0; x < frameCount; x++)
1428  {
1429  row = mysql_fetch_row(res);
1430  if (row)
1431  {
1432  ADD_STR(outStr, row[0]) // Type
1433  ADD_STR(outStr, row[1]) // Delta
1434  }
1435  else
1436  {
1437  cout << "handleGetFrameList: Failed to get mysql row " << x << endl;
1438  sendError(ERROR_MYSQL_ROW);
1439  return;
1440  }
1441  }
1442 
1443  mysql_free_result(res);
1444  }
1445 
1446  send(outStr);
1447 }
1448 
1450 {
1451  string outStr("");
1452 
1453  ADD_STR(outStr, "OK")
1454 
1455  ADD_INT(outStr, m_monitors.size())
1456 
1457  for (uint x = 0; x < m_monitors.size(); x++)
1458  {
1459  ADD_STR(outStr, m_monitors.at(x)->name)
1460  }
1461 
1462  send(outStr);
1463 }
1464 
1466 {
1467  string outStr("");
1468 
1469  ADD_STR(outStr, "OK")
1470 
1471  if (m_debug)
1472  cout << "We have " << m_monitors.size() << " monitors" << endl;
1473 
1474  ADD_INT(outStr, m_monitors.size())
1475 
1476  for (uint x = 0; x < m_monitors.size(); x++)
1477  {
1478  MONITOR *mon = m_monitors.at(x);
1479 
1480  ADD_INT(outStr, mon->mon_id)
1481  ADD_STR(outStr, mon->name)
1482  ADD_INT(outStr, mon->width)
1483  ADD_INT(outStr, mon->height)
1484  ADD_INT(outStr, mon->bytes_per_pixel)
1485 
1486  if (m_debug)
1487  {
1488  cout << "id: " << mon->mon_id << endl;
1489  cout << "name: " << mon->name << endl;
1490  cout << "width: " << mon->width << endl;
1491  cout << "height: " << mon->height << endl;
1492  cout << "palette: " << mon->palette << endl;
1493  cout << "byte per pixel: " << mon->bytes_per_pixel << endl;
1494  cout << "sub pixel order:" << mon->getSubpixelOrder() << endl;
1495  cout << "-------------------" << endl;
1496  }
1497  }
1498 
1499  send(outStr);
1500 }
1501 
1502 void ZMServer::handleDeleteEvent(vector<string> tokens)
1503 {
1504  string eventID;
1505  string outStr("");
1506 
1507  if (tokens.size() != 2)
1508  {
1509  sendError(ERROR_TOKEN_COUNT);
1510  return;
1511  }
1512 
1513  eventID = tokens[1];
1514 
1515  if (m_debug)
1516  cout << "Deleting event: " << eventID << endl;
1517 
1518  ADD_STR(outStr, "OK")
1519 
1520  string sql("");
1521  sql += "DELETE FROM Events WHERE Id = " + eventID;
1522 
1523  if (mysql_query(&g_dbConn, sql.c_str()))
1524  {
1525  fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
1526  sendError(ERROR_MYSQL_QUERY);
1527  return;
1528  }
1529 
1530  // run zmaudit.pl to clean everything up
1531  string command(g_binPath + "/zmaudit.pl &");
1532  errno = 0;
1533  if (system(command.c_str()) < 0 && errno)
1534  cerr << "Failed to run '" << command << "'" << endl;
1535 
1536  send(outStr);
1537 }
1538 
1539 void ZMServer::handleDeleteEventList(vector<string> tokens)
1540 {
1541  string eventList("");
1542  string outStr("");
1543 
1544  vector<string>::iterator it = tokens.begin();
1545  if (it != tokens.end())
1546  ++it;
1547  while (it != tokens.end())
1548  {
1549  if (eventList == "")
1550  eventList = (*it);
1551  else
1552  eventList += "," + (*it);
1553 
1554  ++it;
1555  }
1556 
1557  if (m_debug)
1558  cout << "Deleting events: " << eventList << endl;
1559 
1560  string sql("");
1561  sql += "DELETE FROM Events WHERE Id IN (" + eventList + ")";
1562 
1563  if (mysql_query(&g_dbConn, sql.c_str()))
1564  {
1565  fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
1566  sendError(ERROR_MYSQL_QUERY);
1567  return;
1568  }
1569 
1570  ADD_STR(outStr, "OK")
1571  send(outStr);
1572 }
1573 
1575 {
1576  string outStr("");
1577 
1578  // run zmaudit.pl to clean up orphaned db entries etc
1579  string command(g_binPath + "/zmaudit.pl &");
1580 
1581  if (m_debug)
1582  cout << "Running command: " << command << endl;
1583 
1584  errno = 0;
1585  if (system(command.c_str()) < 0 && errno)
1586  cerr << "Failed to run '" << command << "'" << endl;
1587 
1588  ADD_STR(outStr, "OK")
1589  send(outStr);
1590 }
1591 
1593 {
1594  m_monitors.clear();
1595  m_monitorMap.clear();
1596 
1597  string sql("SELECT Id, Name, Width, Height, ImageBufferCount, MaxFPS, Palette, ");
1598  sql += " Type, Function, Enabled, Device, Host, Controllable, TrackMotion";
1599 
1600  if (checkVersion(1, 26, 0))
1601  sql += ", Colours";
1602 
1603  sql += " FROM Monitors";
1604  sql += " ORDER BY Sequence";
1605 
1606  MYSQL_RES *res;
1607  MYSQL_ROW row;
1608 
1609  if (mysql_query(&g_dbConn, sql.c_str()))
1610  {
1611  fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
1612  return;
1613  }
1614 
1615  res = mysql_store_result(&g_dbConn);
1616  int monitorCount = mysql_num_rows(res);
1617 
1618  if (m_debug)
1619  cout << "Got " << monitorCount << " monitors" << endl;
1620 
1621  for (int x = 0; x < monitorCount; x++)
1622  {
1623  row = mysql_fetch_row(res);
1624  if (row)
1625  {
1626  MONITOR *m = new MONITOR;
1627  m->mon_id = atoi(row[0]);
1628  m->name = row[1];
1629  m->width = atoi(row[2]);
1630  m->height = atoi(row[3]);
1631  m->image_buffer_count = atoi(row[4]);
1632  m->palette = atoi(row[6]);
1633  m->type = row[7];
1634  m->function = row[8];
1635  m->enabled = atoi(row[9]);
1636  m->device = row[10];
1637  m->host = row[11];
1638  m->controllable = atoi(row[12]);
1639  m->trackMotion = atoi(row[13]);
1640 
1641  // from version 1.26.0 ZM can have 1, 3 or 4 bytes per pixel
1642  // older versions can be 1 or 3
1643  if (checkVersion(1, 26, 0))
1644  m->bytes_per_pixel = atoi(row[14]);
1645  else
1646  if (m->palette == 1)
1647  m->bytes_per_pixel = 1;
1648  else
1649  m->bytes_per_pixel = 3;
1650 
1651  m_monitors.push_back(m);
1652  m_monitorMap[m->mon_id] = m;
1653 
1655  }
1656  else
1657  {
1658  cout << "Failed to get mysql row" << endl;
1659  return;
1660  }
1661  }
1662 
1663  mysql_free_result(res);
1664 }
1665 
1666 int ZMServer::getFrame(unsigned char *buffer, int bufferSize, MONITOR *monitor)
1667 {
1668  (void) bufferSize;
1669 
1670  // is there a new frame available?
1671  if (monitor->getLastWriteIndex() == monitor->last_read)
1672  return 0;
1673 
1674  // sanity check last_read
1675  if (monitor->getLastWriteIndex() < 0 ||
1676  monitor->getLastWriteIndex() >= monitor->image_buffer_count)
1677  return 0;
1678 
1679  monitor->last_read = monitor->getLastWriteIndex();
1680 
1681  switch (monitor->getState())
1682  {
1683  case IDLE:
1684  monitor->status = "Idle";
1685  break;
1686  case PREALARM:
1687  monitor->status = "Pre Alarm";
1688  break;
1689  case ALARM:
1690  monitor->status = "Alarm";
1691  break;
1692  case ALERT:
1693  monitor->status = "Alert";
1694  break;
1695  case TAPE:
1696  monitor->status = "Tape";
1697  break;
1698  default:
1699  monitor->status = "Unknown";
1700  break;
1701  }
1702 
1703  // FIXME: should do some sort of compression JPEG??
1704  // just copy the data to our buffer for now
1705 
1706  // fixup the colours if necessary we aim to always send RGB24 images
1707  unsigned char *data = monitor->shared_images + monitor->getFrameSize() * monitor->last_read;
1708  unsigned int rpos = 0;
1709  unsigned int wpos = 0;
1710 
1711  switch (monitor->getSubpixelOrder())
1712  {
1713  case ZM_SUBPIX_ORDER_NONE:
1714  {
1715  for (wpos = 0, rpos = 0; wpos < (unsigned int) (monitor->width * monitor->height * 3); wpos += 3, rpos += 1)
1716  {
1717  buffer[wpos + 0] = data[rpos + 0]; // r
1718  buffer[wpos + 1] = data[rpos + 0]; // g
1719  buffer[wpos + 2] = data[rpos + 0]; // b
1720  }
1721 
1722  break;
1723  }
1724 
1725  case ZM_SUBPIX_ORDER_RGB:
1726  {
1727  for (wpos = 0, rpos = 0; wpos < (unsigned int) (monitor->width * monitor->height * 3); wpos += 3, rpos += 3)
1728  {
1729  buffer[wpos + 0] = data[rpos + 0]; // r
1730  buffer[wpos + 1] = data[rpos + 1]; // g
1731  buffer[wpos + 2] = data[rpos + 2]; // b
1732  }
1733 
1734  break;
1735  }
1736 
1737  case ZM_SUBPIX_ORDER_BGR:
1738  {
1739  for (wpos = 0, rpos = 0; wpos < (unsigned int) (monitor->width * monitor->height * 3); wpos += 3, rpos += 3)
1740  {
1741  buffer[wpos + 0] = data[rpos + 2]; // r
1742  buffer[wpos + 1] = data[rpos + 1]; // g
1743  buffer[wpos + 2] = data[rpos + 0]; // b
1744  }
1745 
1746  break;
1747  }
1748  case ZM_SUBPIX_ORDER_BGRA:
1749  {
1750  for (wpos = 0, rpos = 0; wpos < (unsigned int) (monitor->width * monitor->height * 3); wpos += 3, rpos += 4)
1751  {
1752  buffer[wpos + 0] = data[rpos + 2]; // r
1753  buffer[wpos + 1] = data[rpos + 1]; // g
1754  buffer[wpos + 2] = data[rpos + 0]; // b
1755  }
1756 
1757  break;
1758  }
1759 
1760  case ZM_SUBPIX_ORDER_RGBA:
1761  {
1762  for (wpos = 0, rpos = 0; wpos < (unsigned int) (monitor->width * monitor->height * 3); wpos += 3, rpos += 4)
1763  {
1764  buffer[wpos + 0] = data[rpos + 0]; // r
1765  buffer[wpos + 1] = data[rpos + 1]; // g
1766  buffer[wpos + 2] = data[rpos + 2]; // b
1767  }
1768 
1769  break;
1770  }
1771 
1772  case ZM_SUBPIX_ORDER_ABGR:
1773  {
1774  for (wpos = 0, rpos = 0; wpos < (unsigned int) (monitor->width * monitor->height * 3); wpos += 3, rpos += 4)
1775  {
1776  buffer[wpos + 0] = data[rpos + 3]; // r
1777  buffer[wpos + 1] = data[rpos + 2]; // g
1778  buffer[wpos + 2] = data[rpos + 1]; // b
1779  }
1780 
1781  break;
1782  }
1783 
1784  case ZM_SUBPIX_ORDER_ARGB:
1785  {
1786  for (wpos = 0, rpos = 0; wpos < (unsigned int) (monitor->width * monitor->height * 3); wpos += 3, rpos += 4)
1787  {
1788  buffer[wpos + 0] = data[rpos + 1]; // r
1789  buffer[wpos + 1] = data[rpos + 2]; // g
1790  buffer[wpos + 2] = data[rpos + 3]; // b
1791  }
1792 
1793  break;
1794  }
1795  }
1796 
1797  return monitor->width * monitor->height * 3;
1798 }
1799 
1800 string ZMServer::getZMSetting(const string &setting)
1801 {
1802  string result;
1803  string sql("SELECT Name, Value FROM Config ");
1804  sql += "WHERE Name = '" + setting + "'";
1805 
1806  MYSQL_RES *res;
1807  MYSQL_ROW row;
1808 
1809  if (mysql_query(&g_dbConn, sql.c_str()))
1810  {
1811  fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
1812  return "";
1813  }
1814 
1815  res = mysql_store_result(&g_dbConn);
1816  row = mysql_fetch_row(res);
1817  if (row)
1818  {
1819  result = row[1];
1820  }
1821  else
1822  {
1823  cout << "Failed to get mysql row" << endl;
1824  result = "";
1825  }
1826 
1827  if (m_debug)
1828  cout << "getZMSetting: " << setting << " Result: " << result << endl;
1829 
1830  mysql_free_result(res);
1831 
1832  return result;
1833 }
1834 
1835 void ZMServer::handleSetMonitorFunction(vector<string> tokens)
1836 {
1837  string outStr("");
1838 
1839  if (tokens.size() != 4)
1840  {
1841  sendError(ERROR_TOKEN_COUNT);
1842  return;
1843  }
1844 
1845  string monitorID(tokens[1]);
1846  string function(tokens[2]);
1847  string enabled(tokens[3]);
1848 
1849  // Check validity of input passed to server. Does monitor exist && is function ok
1850  if (m_monitorMap.find(atoi(monitorID.c_str())) == m_monitorMap.end())
1851  {
1852  sendError(ERROR_INVALID_MONITOR);
1853  return;
1854  }
1855 
1856  if (function != FUNCTION_NONE && function != FUNCTION_MONITOR &&
1857  function != FUNCTION_MODECT && function != FUNCTION_NODECT &&
1858  function != FUNCTION_RECORD && function != FUNCTION_MOCORD)
1859  {
1860  sendError(ERROR_INVALID_MONITOR_FUNCTION);
1861  return;
1862  }
1863 
1864  if (enabled != "0" && enabled != "1")
1865  {
1866  sendError(ERROR_INVALID_MONITOR_ENABLE_VALUE);
1867  return;
1868  }
1869 
1870  if (m_debug)
1871  cout << "User input validated OK" << endl;
1872 
1873 
1874  // Now perform db update && (re)start/stop daemons as required.
1875  MONITOR *monitor = m_monitorMap[atoi(monitorID.c_str())];
1876  string oldFunction = monitor->function;
1877  string newFunction = function;
1878  int oldEnabled = monitor->enabled;
1879  int newEnabled = atoi(enabled.c_str());
1880  monitor->function = newFunction;
1881  monitor->enabled = newEnabled;
1882 
1883  if (m_debug)
1884  cout << "SetMonitorFunction MonitorId: " << monitorID << endl <<
1885  " oldEnabled: " << oldEnabled << endl <<
1886  " newEnabled: " << newEnabled << endl <<
1887  " oldFunction: " << oldFunction << endl <<
1888  " newFunction: " << newFunction << endl;
1889 
1890  if ( newFunction != oldFunction || newEnabled != oldEnabled)
1891  {
1892  string sql("UPDATE Monitors ");
1893  sql += "SET Function = '" + function + "', ";
1894  sql += "Enabled = '" + enabled + "' ";
1895  sql += "WHERE Id = '" + monitorID + "'";
1896 
1897  if (mysql_query(&g_dbConn, sql.c_str()))
1898  {
1899  fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
1900  sendError(ERROR_MYSQL_QUERY);
1901  return;
1902  }
1903 
1904  if (m_debug)
1905  cout << "Monitor function SQL update OK" << endl;
1906 
1907  string status = runCommand(g_binPath + "/zmdc.pl check");
1908 
1909  // Now refresh servers
1910  if (RUNNING.compare(0, RUNNING.size(), status, 0, RUNNING.size()) == 0)
1911  {
1912  if (m_debug)
1913  cout << "Monitor function Refreshing daemons" << endl;
1914 
1915  bool restart = (oldFunction == FUNCTION_NONE) ||
1916  (newFunction == FUNCTION_NONE) ||
1917  (newEnabled != oldEnabled);
1918 
1919  if (restart)
1920  zmcControl(monitor, RESTART);
1921  else
1922  zmcControl(monitor, "");
1923  zmaControl(monitor, RELOAD);
1924  }
1925  else
1926  if (m_debug)
1927  cout << "zm daemons are not running" << endl;
1928  }
1929  else
1930  cout << "Not updating monitor function as identical to existing configuration" << endl;
1931 
1932  ADD_STR(outStr, "OK")
1933  send(outStr);
1934 }
1935 
1936 void ZMServer::zmcControl(MONITOR *monitor, const string &mode)
1937 {
1938  string zmcArgs = "";
1939  string sql = "";
1940  sql += "SELECT count(if(Function!='None',1,NULL)) as ActiveCount ";
1941  sql += "FROM Monitors ";
1942 
1943  if (monitor->type == "Local" )
1944  {
1945  sql += "WHERE Device = '" + monitor->device + "'";
1946  zmcArgs = "-d " + monitor->device;
1947  }
1948  else
1949  {
1950  sql += "WHERE Id = '" + monitor->getIdStr() + "'";
1951  zmcArgs = "-m " + monitor->mon_id;
1952  }
1953 
1954  if (mysql_query(&g_dbConn, sql.c_str()))
1955  {
1956  fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
1957  sendError(ERROR_MYSQL_QUERY);
1958  return;
1959  }
1960 
1961  MYSQL_RES *res;
1962  MYSQL_ROW row;
1963  int activeCount;
1964 
1965  res = mysql_store_result(&g_dbConn);
1966  row = mysql_fetch_row(res);
1967 
1968  if (row)
1969  activeCount = atoi(row[0]);
1970  else
1971  {
1972  sendError(ERROR_MYSQL_QUERY);
1973  return;
1974  }
1975 
1976  if (!activeCount)
1977  runCommand(g_binPath + "/zmdc.pl stop zmc " + zmcArgs);
1978  else
1979  {
1980  if (mode == RESTART)
1981  runCommand(g_binPath + "/zmdc.pl stop zmc " + zmcArgs);
1982 
1983  runCommand(g_binPath + "/zmdc.pl start zmc " + zmcArgs);
1984  }
1985 }
1986 
1987 void ZMServer::zmaControl(MONITOR *monitor, const string &mode)
1988 {
1989  int zmOptControl = atoi(getZMSetting("ZM_OPT_CONTROL").c_str());
1990  int zmOptFrameServer = atoi(getZMSetting("ZM_OPT_FRAME_SERVER").c_str());
1991 
1992  if (monitor->function == FUNCTION_MODECT ||
1993  monitor->function == FUNCTION_RECORD ||
1994  monitor->function == FUNCTION_MOCORD ||
1995  monitor->function == FUNCTION_NODECT)
1996  {
1997  if (mode == RESTART)
1998  {
1999  if (zmOptControl)
2000  runCommand(g_binPath + "/zmdc.pl stop zmtrack.pl -m " + monitor->getIdStr());
2001 
2002  runCommand(g_binPath + "/zmdc.pl stop zma -m " + monitor->getIdStr());
2003 
2004  if (zmOptFrameServer)
2005  runCommand(g_binPath + "/zmdc.pl stop zmf -m " + monitor->getIdStr());
2006  }
2007 
2008  if (zmOptFrameServer)
2009  runCommand(g_binPath + "/zmdc.pl start zmf -m " + monitor->getIdStr());
2010 
2011  runCommand(g_binPath + "/zmdc.pl start zma -m " + monitor->getIdStr());
2012 
2013  if (zmOptControl && monitor->controllable && monitor->trackMotion &&
2014  ( monitor->function == FUNCTION_MODECT || monitor->function == FUNCTION_MOCORD) )
2015  runCommand(g_binPath + "/zmdc.pl start zmtrack.pl -m " + monitor->getIdStr());
2016 
2017  if (mode == RELOAD)
2018  runCommand(g_binPath + "/zmdc.pl reload zma -m " + monitor->getIdStr());
2019  }
2020  else
2021  {
2022  if (zmOptControl)
2023  runCommand(g_binPath + "/zmdc.pl stop zmtrack.pl -m " + monitor->getIdStr());
2024 
2025  runCommand(g_binPath + "/zmdc.pl stop zma -m " + monitor->getIdStr());
2026 
2027  if (zmOptFrameServer)
2028  runCommand(g_binPath + "/zmdc.pl stop zmf -m " + monitor->getIdStr());
2029  }
2030 }
GLuint buffer
void tokenize(const string &command, vector< string > &tokens)
Definition: zmserver.cpp:522
MYTH_GLsizeiptr const GLvoid * data
string id
Definition: zmserver.h:189
const string FUNCTION_MODECT
Definition: zmserver.h:52
open(CONF, $file) or die("\nERROR
const string RUNNING
Definition: zmserver.h:60
int mapFile
Definition: zmserver.h:184
void getMonitorStatus(string id, string type, string device, string host, string channel, string function, string &zmcStatus, string &zmaStatus, string enabled)
Definition: zmserver.cpp:1021
int getState(void)
Definition: zmserver.cpp:394
void handleGetEventFrame(vector< string > tokens)
Definition: zmserver.cpp:1070
int last_read
Definition: zmserver.h:179
typedef void(__LZO_CDECL *lzo_free_func_t)(lzo_callback_p self
int mon_id
Definition: zmserver.h:177
lzo_uint lzo_bytep out
Definition: minilzo.cpp:3060
string g_server
Definition: zmserver.cpp:86
bool m_debug
Definition: zmserver.h:232
int g_minorVersion
Definition: zmserver.cpp:93
void connectToDatabase(void)
Definition: zmserver.cpp:180
my tmp
Definition: twit.tv.pl:144
bool m_useDeepStorage
Definition: zmserver.h:236
void initMonitor(bool debug, string mmapPath, int shmKey)
Definition: zmserver.cpp:238
void handleGetCameraList(void)
Definition: zmserver.cpp:1449
string g_webPath
Definition: zmserver.cpp:88
void kickDatabase(bool debug)
Definition: zmserver.cpp:202
string device
Definition: zmserver.h:171
void zmaControl(MONITOR *monitor, const string &mode)
Definition: zmserver.cpp:1987
return memcpy(dest, src, len)
void handleRunZMAudit(void)
Definition: zmserver.cpp:1574
string host
Definition: zmserver.h:172
key_t m_shmKey
Definition: zmserver.h:240
GLenum GLsizei len
int height
Definition: zmserver.h:175
long f_bavail
Definition: compat.h:158
const char * filename
Definition: ioapi.h:135
void handleGetEventList(vector< string > tokens)
Definition: zmserver.cpp:749
void loadZMConfig(const string &configfile)
Definition: zmserver.cpp:107
unsigned int uint
Definition: compat.h:135
GLint GLenum GLsizei GLint GLenum GLenum type
const string RESTART
Definition: zmserver.h:58
string getZMSetting(const string &setting)
Definition: zmserver.cpp:1800
int palette
Definition: zmserver.h:181
void handleGetEventDates(vector< string > tokens)
Definition: zmserver.cpp:843
Definition: zmserver.h:68
string g_webUser
Definition: zmserver.cpp:90
int m_sock
Definition: zmserver.h:233
int g_revisionVersion
Definition: zmserver.cpp:94
string g_zmversion
Definition: zmserver.cpp:84
string g_binPath
Definition: zmserver.cpp:91
voidpf void * buf
Definition: ioapi.h:136
const string FUNCTION_NONE
Definition: zmserver.h:56
State state
Definition: zmserver.h:78
bool isValid(void)
Definition: zmserver.cpp:365
string m_eventFileFormat
Definition: zmserver.h:238
int g_majorVersion
Definition: zmserver.cpp:92
return memset(s, c, len)
uint32_t state
Definition: zmserver.h:100
time_t g_lastDBKick
Definition: zmserver.cpp:96
string g_password
Definition: zmserver.cpp:85
void handleGetFrameList(vector< string > tokens)
Definition: zmserver.cpp:1334
MYSQL g_dbConn
Definition: zmserver.cpp:83
char m_buf[10]
Definition: zmserver.h:242
uint32_t imagesize
Definition: zmserver.h:113
string getIdStr(void)
Definition: zmserver.cpp:375
int statfs(const char *path, struct statfs *buffer)
Definition: compat.h:165
string type
Definition: zmserver.h:168
vector< MONITOR * > m_monitors
Definition: zmserver.h:234
const string FUNCTION_MOCORD
Definition: zmserver.h:55
string g_database
Definition: zmserver.cpp:87
int errno
bool send(const string &s) const
Definition: zmserver.cpp:606
map< int, MONITOR * > m_monitorMap
Definition: zmserver.h:235
void * shm_ptr
Definition: zmserver.h:185
const string FUNCTION_RECORD
Definition: zmserver.h:54
int image_buffer_count
Definition: zmserver.h:173
void handleGetMonitorStatus(void)
Definition: zmserver.cpp:910
void handleGetServerStatus(void)
Definition: zmserver.cpp:694
int enabled
Definition: zmserver.h:170
VERBOSE_PREAMBLE Most debug(nodatabase, notimestamp, noextra)") VERBOSE_MAP(VB_IMPORTANT
unsigned char * shared_images
Definition: zmserver.h:178
then echo error
Definition: unittests.sh:42
int bytes_per_pixel
Definition: zmserver.h:176
typedef long(ZCALLBACK *tell_file_func) OF((voidpf opaque
Definition: compat.h:153
int getFrameSize(void)
Definition: zmserver.cpp:415
bool processRequest(char *buf, int nbytes)
Definition: zmserver.cpp:545
const char * name
Definition: ParseText.cpp:338
void getMonitorList(void)
Definition: zmserver.cpp:1592
string g_user
Definition: zmserver.cpp:89
string function
Definition: zmserver.h:169
SharedData26 * shared_data26
Definition: zmserver.h:188
int FILE
Definition: mythburn.py:110
int controllable
Definition: zmserver.h:182
void sendError(string error)
Definition: zmserver.cpp:647
void handleGetLiveFrame(vector< string > tokens)
Definition: zmserver.cpp:1264
return s
Definition: minilzo.cpp:2409
line height
Definition: MythXMLTest.css:26
string status
Definition: zmserver.h:180
void handleGetMonitorList(void)
Definition: zmserver.cpp:1465
string name
Definition: zmserver.h:167
GLint GLenum GLsizei width
void handleSetMonitorFunction(vector< string > tokens)
Definition: zmserver.cpp:1835
const string FUNCTION_NODECT
Definition: zmserver.h:53
ZMServer(int sock, bool debug)
Definition: zmserver.cpp:425
long f_blocks
Definition: compat.h:156
int getFrame(unsigned char *buffer, int bufferSize, MONITOR *monitor)
Definition: zmserver.cpp:1666
uint8_t format
Definition: zmserver.h:112
static int x0
Definition: mythsocket.cpp:54
int getLastWriteIndex(void)
Definition: zmserver.cpp:386
Definition: zmserver.h:66
int getSubpixelOrder(void)
Definition: zmserver.cpp:402
void zmcControl(MONITOR *monitor, const string &mode)
Definition: zmserver.cpp:1936
int width
Definition: zmserver.h:174
long f_bsize
Definition: compat.h:155
Definition: zmserver.h:64
int trackMotion
Definition: zmserver.h:183
GLuint GLfloat x
uint32_t last_write_index
Definition: zmserver.h:98
void handleDeleteEventList(vector< string > tokens)
Definition: zmserver.cpp:1539
const char int mode
Definition: ioapi.h:135
Definition: zmserver.h:67
const string FUNCTION_MONITOR
Definition: zmserver.h:51
bool m_useAnalysisImages
Definition: zmserver.h:237
void handleDeleteEvent(vector< string > tokens)
Definition: zmserver.cpp:1502
typedef int
Definition: lzoconf.h:279
int last_write_index
Definition: zmserver.h:79
void handleGetAnalysisFrame(vector< string > tokens)
Definition: zmserver.cpp:1134
string m_analysisFileFormat
Definition: zmserver.h:239
bool checkVersion(int major, int minor, int revision)
Definition: zmserver.cpp:99
const string RELOAD
Definition: zmserver.h:59
close(CACHE)
string runCommand(string command)
Definition: zmserver.cpp:1007
long long getDiskSpace(const string &filename, long long &total, long long &used)
Definition: zmserver.cpp:664
void handleHello(void)
Definition: zmserver.cpp:654
SharedData * shared_data
Definition: zmserver.h:187
void handleGetAlarmStates(void)
Definition: zmserver.cpp:727
string m_mmapPath
Definition: zmserver.h:241