MythTV  0.27pre
 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 "7"
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 
59 // error messages
60 #define ERROR_TOKEN_COUNT "Invalid token count"
61 #define ERROR_MYSQL_QUERY "Mysql Query Error"
62 #define ERROR_MYSQL_ROW "Mysql Get Row Error"
63 #define ERROR_FILE_OPEN "Cannot open event file"
64 #define ERROR_INVALID_MONITOR "Invalid Monitor"
65 #define ERROR_INVALID_POINTERS "Cannot get shared memory pointers"
66 #define ERROR_INVALID_MONITOR_FUNCTION "Invalid Monitor Function"
67 #define ERROR_INVALID_MONITOR_ENABLE_VALUE "Invalid Monitor Enable Value"
68 
69 MYSQL g_dbConn;
70 string g_zmversion = "";
71 string g_password = "";
72 string g_server = "";
73 string g_database = "";
74 string g_webPath = "";
75 string g_user = "";
76 string g_webUser = "";
77 string g_binPath = "";
78 
79 time_t g_lastDBKick = 0;
80 
81 void loadZMConfig(const string &configfile)
82 {
83  cout << "loading zm config from " << configfile << endl;
84  FILE *cfg;
85  char line[512];
86  char val[250];
87 
88  if ( (cfg = fopen(configfile.c_str(), "r")) == NULL )
89  {
90  fprintf(stderr,"Can't open %s\n", configfile.c_str());
91  exit(1);
92  }
93 
94  while ( fgets( line, sizeof(line), cfg ) != NULL )
95  {
96  char *line_ptr = line;
97  // Trim off any cr/lf line endings
98  size_t chomp_len = strcspn( line_ptr, "\r\n" );
99  line_ptr[chomp_len] = '\0';
100 
101  // Remove leading white space
102  size_t white_len = strspn( line_ptr, " \t" );
103  line_ptr += white_len;
104 
105  // Check for comment or empty line
106  if ( *line_ptr == '\0' || *line_ptr == '#' )
107  continue;
108 
109  // Remove trailing white space
110  char *temp_ptr = line_ptr+strlen(line_ptr)-1;
111  while ( *temp_ptr == ' ' || *temp_ptr == '\t' )
112  {
113  *temp_ptr-- = '\0';
114  temp_ptr--;
115  }
116 
117  // Now look for the '=' in the middle of the line
118  temp_ptr = strchr( line_ptr, '=' );
119  if ( !temp_ptr )
120  {
121  fprintf(stderr,"Invalid data in %s: '%s'\n", configfile.c_str(), line );
122  continue;
123  }
124 
125  // Assign the name and value parts
126  char *name_ptr = line_ptr;
127  char *val_ptr = temp_ptr+1;
128 
129  // Trim trailing space from the name part
130  do
131  {
132  *temp_ptr = '\0';
133  temp_ptr--;
134  }
135  while ( *temp_ptr == ' ' || *temp_ptr == '\t' );
136 
137  // Remove leading white space from the value part
138  white_len = strspn( val_ptr, " \t" );
139  val_ptr += white_len;
140 
141  strncpy( val, val_ptr, strlen(val_ptr)+1 );
142  if ( strcasecmp( name_ptr, "ZM_DB_HOST" ) == 0 ) g_server = val;
143  else if ( strcasecmp( name_ptr, "ZM_DB_NAME" ) == 0 ) g_database = val;
144  else if ( strcasecmp( name_ptr, "ZM_DB_USER" ) == 0 ) g_user = val;
145  else if ( strcasecmp( name_ptr, "ZM_DB_PASS" ) == 0 ) g_password = val;
146  else if ( strcasecmp( name_ptr, "ZM_PATH_WEB" ) == 0 ) g_webPath = val;
147  else if ( strcasecmp( name_ptr, "ZM_PATH_BIN" ) == 0 ) g_binPath = val;
148  else if ( strcasecmp( name_ptr, "ZM_WEB_USER" ) == 0 ) g_webUser = val;
149  else if ( strcasecmp( name_ptr, "ZM_VERSION" ) == 0 ) g_zmversion = val;
150  }
151  fclose(cfg);
152 }
153 
155 {
156  if (!mysql_init(&g_dbConn))
157  {
158  cout << "Error: Can't initialise structure: " << mysql_error(&g_dbConn) << endl;
159  exit(mysql_errno(&g_dbConn));
160  }
161 
162  if (!mysql_real_connect(&g_dbConn, g_server.c_str(), g_user.c_str(),
163  g_password.c_str(), 0, 0, 0, 0))
164  {
165  cout << "Error: Can't connect to server: " << mysql_error(&g_dbConn) << endl;
166  exit(mysql_errno( &g_dbConn));
167  }
168 
169  if (mysql_select_db(&g_dbConn, g_database.c_str()))
170  {
171  cout << "Error: Can't select database: " << mysql_error(&g_dbConn) << endl;
172  exit(mysql_errno(&g_dbConn));
173  }
174 }
175 
176 void kickDatabase(bool debug)
177 {
178  if (time(NULL) < g_lastDBKick + DB_CHECK_TIME)
179  return;
180 
181  if (debug)
182  cout << "Kicking database connection" << endl;
183 
184  g_lastDBKick = time(NULL);
185 
186  if (mysql_query(&g_dbConn, "SELECT NULL;") == 0)
187  {
188  MYSQL_RES *res = mysql_store_result(&g_dbConn);
189  if (res)
190  mysql_free_result(res);
191  return;
192  }
193 
194  cout << "Lost connection to DB - trying to reconnect" << endl;
195 
196  // failed so try to reconnect to the DB
197  mysql_close(&g_dbConn);
199 }
200 
201 ZMServer::ZMServer(int sock, bool debug)
202 {
203  if (debug)
204  cout << "Using server protocol version '" << ZM_PROTOCOL_VERSION << "'\n";
205 
206  m_sock = sock;
207  m_debug = debug;
208 
209  // get the shared memory key
210  char buf[100];
211  m_shmKey = 0x7a6d2000;
212  string setting = getZMSetting("ZM_SHM_KEY");
213 
214  if (setting != "")
215  {
216  unsigned long long tmp = m_shmKey;
217  sscanf(setting.c_str(), "%20llx", &tmp);
218  m_shmKey = tmp;
219  }
220 
221  if (m_debug)
222  {
223  snprintf(buf, sizeof(buf), "0x%x", (unsigned int)m_shmKey);
224  cout << "Shared memory key is: " << buf << endl;
225  }
226 
227  // get the MMAP path
228  m_mmapPath = getZMSetting("ZM_PATH_MAP");
229  if (m_debug)
230  {
231  cout << "Memory path directory is: " << m_mmapPath << endl;
232  }
233 
234  // get the event filename format
235  setting = getZMSetting("ZM_EVENT_IMAGE_DIGITS");
236  int eventDigits = atoi(setting.c_str());
237  snprintf(buf, sizeof(buf), "%%0%dd-capture.jpg", eventDigits);
239  if (m_debug)
240  cout << "Event file format is: " << m_eventFileFormat << endl;
241 
242  // get the analyse filename format
243  snprintf(buf, sizeof(buf), "%%0%dd-analyse.jpg", eventDigits);
245  if (m_debug)
246  cout << "Analyse file format is: " << m_analyseFileFormat << endl;
247 
248  // is ZM using the deep storage directory format?
249  m_useDeepStorage = (getZMSetting("ZM_USE_DEEP_STORAGE") == "1");
250  if (m_debug)
251  {
252  if (m_useDeepStorage)
253  cout << "using deep storage directory structure" << endl;
254  else
255  cout << "using flat directory structure" << endl;
256  }
257 
258  getMonitorList();
259 }
260 
262 {
263  if (m_debug)
264  cout << "ZMServer destroyed\n";
265 }
266 
267 void ZMServer::tokenize(const string &command, vector<string> &tokens)
268 {
269  string token = "";
270  tokens.clear();
271  string::size_type startPos = 0;
272  string::size_type endPos = 0;
273 
274  while((endPos = command.find("[]:[]", startPos)) != string::npos)
275  {
276  token = command.substr(startPos, endPos - startPos);
277  tokens.push_back(token);
278  startPos = endPos + 5;
279  }
280 
281  // make sure we add the last token
282  if (endPos != command.length())
283  {
284  token = command.substr(startPos);
285  tokens.push_back(token);
286  }
287 }
288 
289 void ZMServer::processRequest(char* buf, int nbytes)
290 {
291 #if 0
292  // first 8 bytes is the length of the following data
293  char len[9];
294  memcpy(len, buf, 8);
295  len[8] = '\0';
296  int dataLen = atoi(len);
297 #endif
298 
299  buf[nbytes] = '\0';
300  string s(buf+8);
301  vector<string> tokens;
302  tokenize(s, tokens);
303 
304  if (tokens.empty())
305  return;
306 
307  if (m_debug)
308  cout << "Processing: '" << tokens[0] << "'" << endl;
309 
310  if (tokens[0] == "HELLO")
311  handleHello();
312  else if (tokens[0] == "GET_SERVER_STATUS")
314  else if (tokens[0] == "GET_MONITOR_STATUS")
316  else if (tokens[0] == "GET_EVENT_LIST")
317  handleGetEventList(tokens);
318  else if (tokens[0] == "GET_EVENT_DATES")
319  handleGetEventDates(tokens);
320  else if (tokens[0] == "GET_EVENT_FRAME")
321  handleGetEventFrame(tokens);
322  else if (tokens[0] == "GET_ANALYSE_FRAME")
323  handleGetAnalyseFrame(tokens);
324  else if (tokens[0] == "GET_LIVE_FRAME")
325  handleGetLiveFrame(tokens);
326  else if (tokens[0] == "GET_FRAME_LIST")
327  handleGetFrameList(tokens);
328  else if (tokens[0] == "GET_CAMERA_LIST")
330  else if (tokens[0] == "GET_MONITOR_LIST")
332  else if (tokens[0] == "DELETE_EVENT")
333  handleDeleteEvent(tokens);
334  else if (tokens[0] == "DELETE_EVENT_LIST")
335  handleDeleteEventList(tokens);
336  else if (tokens[0] == "RUN_ZMAUDIT")
338  else if (tokens[0] == "SET_MONITOR_FUNCTION")
339  handleSetMonitorFunction(tokens);
340  else
341  send("UNKNOWN_COMMAND");
342 }
343 
344 bool ZMServer::send(const string s) const
345 {
346  // send length
347  uint32_t len = s.size();
348  char buf[9];
349  sprintf(buf, "%8d", len);
350  int status = ::send(m_sock, buf, 8, MSG_NOSIGNAL);
351  if (status == -1)
352  return false;
353 
354  // send message
355  status = ::send(m_sock, s.c_str(), s.size(), MSG_NOSIGNAL);
356  if ( status == -1 )
357  return false;
358  else
359  return true;
360 }
361 
362 bool ZMServer::send(const string s, const unsigned char *buffer, int dataLen) const
363 {
364  // send length
365  uint32_t len = s.size();
366  char buf[9];
367  sprintf(buf, "%8d", len);
368  int status = ::send(m_sock, buf, 8, MSG_NOSIGNAL);
369  if (status == -1)
370  return false;
371 
372  // send message
373  status = ::send(m_sock, s.c_str(), s.size(), MSG_NOSIGNAL);
374  if ( status == -1 )
375  return false;
376 
377  // send data
378  status = ::send(m_sock, buffer, dataLen, MSG_NOSIGNAL);
379  if ( status == -1 )
380  return false;
381 
382  return true;
383 }
384 
386 {
387  string outStr("");
388  ADD_STR(outStr, string("ERROR - ") + error);
389  send(outStr);
390 }
391 
393 {
394  // just send OK so the client knows all is well
395  // followed by the protocol version we understand
396  string outStr("");
397  ADD_STR(outStr, "OK");
398  ADD_STR(outStr, ZM_PROTOCOL_VERSION);
399  send(outStr);
400 }
401 
402 long long ZMServer::getDiskSpace(const string &filename, long long &total, long long &used)
403 {
404  struct statfs statbuf;
405  memset(&statbuf, 0, sizeof(statbuf));
406  long long freespace = -1;
407 
408  total = used = -1;
409 
410  // there are cases where statfs will return 0 (good), but f_blocks and
411  // others are invalid and set to 0 (such as when an automounted directory
412  // is not mounted but still visible because --ghost was used),
413  // so check to make sure we can have a total size > 0
414  if ((statfs(filename.c_str(), &statbuf) == 0) &&
415  (statbuf.f_blocks > 0) &&
416  (statbuf.f_bsize > 0))
417  {
418  total = statbuf.f_blocks;
419  total *= statbuf.f_bsize;
420  total = total >> 10;
421 
422  freespace = statbuf.f_bavail;
423  freespace *= statbuf.f_bsize;
424  freespace = freespace >> 10;
425 
426  used = total - freespace;
427  }
428 
429  return freespace;
430 }
431 
433 {
434  string outStr("");
435  ADD_STR(outStr, "OK")
436 
437  // server status
438  string status = runCommand(g_binPath + "/zmdc.pl check");
439  ADD_STR(outStr, status)
440 
441  // get load averages
442  double loads[3];
443  if (getloadavg(loads, 3) == -1)
444  {
445  ADD_STR(outStr, "Unknown")
446  }
447  else
448  {
449  char buf[30];
450  sprintf(buf, "%0.2lf", loads[0]);
451  ADD_STR(outStr, buf)
452  }
453 
454  // get free space on the disk where the events are stored
455  char buf[15];
456  long long total, used;
457  string eventsDir = g_webPath + "/events/";
458  getDiskSpace(eventsDir, total, used);
459  sprintf(buf, "%d%%", (int) ((100.0 / ((float) total / used))));
460  ADD_STR(outStr, buf)
461 
462  send(outStr);
463 }
464 
465 void ZMServer::handleGetEventList(vector<string> tokens)
466 {
467  string outStr("");
468 
469  if (tokens.size() != 4)
470  {
471  sendError(ERROR_TOKEN_COUNT);
472  return;
473  }
474 
475  string monitor = tokens[1];
476  bool oldestFirst = (tokens[2] == "1");
477  string date = tokens[3];
478 
479  if (m_debug)
480  cout << "Loading events for monitor: " << monitor << ", date: " << date << endl;
481 
482  ADD_STR(outStr, "OK")
483 
484  MYSQL_RES *res;
485  MYSQL_ROW row;
486 
487  string sql("SELECT E.Id, E.Name, M.Id AS MonitorID, M.Name AS MonitorName, E.StartTime, "
488  "E.Length, M.Width, M.Height, M.DefaultRate, M.DefaultScale "
489  "from Events as E inner join Monitors as M on E.MonitorId = M.Id ");
490 
491  if (monitor != "<ANY>")
492  {
493  sql += "WHERE M.Name = '" + monitor + "' ";
494 
495  if (date != "<ANY>")
496  sql += "AND DATE(E.StartTime) = DATE('" + date + "') ";
497  }
498  else
499  {
500  if (date != "<ANY>")
501  sql += "WHERE DATE(E.StartTime) = DATE('" + date + "') ";
502  }
503 
504  if (oldestFirst)
505  sql += "ORDER BY E.StartTime ASC";
506  else
507  sql += "ORDER BY E.StartTime DESC";
508 
509  if (mysql_query(&g_dbConn, sql.c_str()))
510  {
511  fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
512  sendError(ERROR_MYSQL_QUERY);
513  return;
514  }
515 
516  res = mysql_store_result(&g_dbConn);
517  int eventCount = mysql_num_rows(res);
518 
519  if (m_debug)
520  cout << "Got " << eventCount << " events" << endl;
521 
522  char str[10];
523  sprintf(str, "%d", eventCount);
524  ADD_STR(outStr, str)
525 
526  for (int x = 0; x < eventCount; x++)
527  {
528  row = mysql_fetch_row(res);
529  if (row)
530  {
531  ADD_STR(outStr, row[0]) // eventID
532  ADD_STR(outStr, row[1]) // event name
533  ADD_STR(outStr, row[2]) // monitorID
534  ADD_STR(outStr, row[3]) // monitor name
535  row[4][10] = 'T';
536  ADD_STR(outStr, row[4]) // start time
537  ADD_STR(outStr, row[5]) // length
538  }
539  else
540  {
541  cout << "Failed to get mysql row" << endl;
542  sendError(ERROR_MYSQL_ROW);
543  return;
544  }
545  }
546 
547  mysql_free_result(res);
548 
549  send(outStr);
550 }
551 
552 void ZMServer::handleGetEventDates(vector<string> tokens)
553 {
554  string outStr("");
555 
556  if (tokens.size() != 3)
557  {
558  sendError(ERROR_TOKEN_COUNT);
559  return;
560  }
561 
562  string monitor = tokens[1];
563  bool oldestFirst = (tokens[2] == "1");
564 
565  if (m_debug)
566  cout << "Loading event dates for monitor: " << monitor << endl;
567 
568  ADD_STR(outStr, "OK")
569 
570  MYSQL_RES *res;
571  MYSQL_ROW row;
572 
573  string sql("SELECT DISTINCT DATE(E.StartTime) "
574  "from Events as E inner join Monitors as M on E.MonitorId = M.Id ");
575 
576  if (monitor != "<ANY>")
577  sql += "WHERE M.Name = '" + monitor + "' ";
578 
579  if (oldestFirst)
580  sql += "ORDER BY E.StartTime ASC";
581  else
582  sql += "ORDER BY E.StartTime DESC";
583 
584  if (mysql_query(&g_dbConn, sql.c_str()))
585  {
586  fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
587  sendError(ERROR_MYSQL_QUERY);
588  return;
589  }
590 
591  res = mysql_store_result(&g_dbConn);
592  int dateCount = mysql_num_rows(res);
593 
594  if (m_debug)
595  cout << "Got " << dateCount << " dates" << endl;
596 
597  char str[10];
598  sprintf(str, "%d", dateCount);
599  ADD_STR(outStr, str)
600 
601  for (int x = 0; x < dateCount; x++)
602  {
603  row = mysql_fetch_row(res);
604  if (row)
605  {
606  ADD_STR(outStr, row[0]) // event date
607  }
608  else
609  {
610  cout << "Failed to get mysql row" << endl;
611  sendError(ERROR_MYSQL_ROW);
612  return;
613  }
614  }
615 
616  mysql_free_result(res);
617 
618  send(outStr);
619 }
620 
622 {
623  string outStr("");
624  ADD_STR(outStr, "OK")
625 
626  // get monitor list
627  MYSQL_RES *res;
628  MYSQL_ROW row;
629 
630  string sql("SELECT Id, Name, Type, Device, Host, Channel, Function, Enabled "
631  "FROM Monitors;");
632  if (mysql_query(&g_dbConn, sql.c_str()))
633  {
634  fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
635  sendError(ERROR_MYSQL_QUERY);
636  return;
637  }
638 
639  res = mysql_store_result(&g_dbConn);
640 
641  // add monitor count
642  int monitorCount = mysql_num_rows(res);
643 
644  if (m_debug)
645  cout << "Got " << monitorCount << " monitors" << endl;
646 
647  char str[10];
648  sprintf(str, "%d", monitorCount);
649  ADD_STR(outStr, str)
650 
651  for (int x = 0; x < monitorCount; x++)
652  {
653  row = mysql_fetch_row(res);
654  if (row)
655  {
656  string id = row[0];
657  string type = row[2];
658  string device = row[3];
659  string host = row[4];
660  string channel = row[5];
661  string function = row[6];
662  string enabled = row[7];
663  string name = row[1];
664  string events = "";
665  string zmcStatus = "";
666  string zmaStatus = "";
667  getMonitorStatus(id, type, device, host, channel, function,
668  zmcStatus, zmaStatus, enabled);
669  MYSQL_RES *res2;
670  MYSQL_ROW row2;
671 
672  string sql2("SELECT count(if(Archived=0,1,NULL)) AS EventCount "
673  "FROM Events AS E "
674  "WHERE MonitorId = " + id);
675 
676  if (mysql_query(&g_dbConn, sql2.c_str()))
677  {
678  fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
679  sendError(ERROR_MYSQL_QUERY);
680  return;
681  }
682 
683  res2 = mysql_store_result(&g_dbConn);
684  if (mysql_num_rows(res2) > 0)
685  {
686  row2 = mysql_fetch_row(res2);
687  if (row2)
688  events = row2[0];
689  else
690  {
691  cout << "Failed to get mysql row" << endl;
692  sendError(ERROR_MYSQL_ROW);
693  return;
694  }
695  }
696 
697  ADD_STR(outStr, id)
698  ADD_STR(outStr, name)
699  ADD_STR(outStr, zmcStatus)
700  ADD_STR(outStr, zmaStatus)
701  ADD_STR(outStr, events)
702  ADD_STR(outStr, function)
703  ADD_STR(outStr, enabled)
704 
705  mysql_free_result(res2);
706  }
707  else
708  {
709  cout << "Failed to get mysql row" << endl;
710  sendError(ERROR_MYSQL_ROW);
711  return;
712  }
713  }
714 
715  mysql_free_result(res);
716 
717  send(outStr);
718 }
719 
720 string ZMServer::runCommand(string command)
721 {
722  string outStr("");
723  FILE *fd = popen(command.c_str(), "r");
724  char buffer[100];
725 
726  while (fgets(buffer, sizeof(buffer), fd) != NULL)
727  {
728  outStr += buffer;
729  }
730  pclose(fd);
731  return outStr;
732 }
733 
734 void ZMServer::getMonitorStatus(string id, string type, string device, string host, string channel,
735  string function, string &zmcStatus, string &zmaStatus,
736  string enabled)
737 {
738  zmaStatus = "";
739  zmcStatus = "";
740 
741  string command(g_binPath + "/zmdc.pl status");
742  string status = runCommand(command);
743 
744  if (type == "Local")
745  {
746  if (enabled == "0")
747  zmaStatus = device + "(" + channel + ") [-]";
748  else if (status.find("'zma -m " + id + "' running") != string::npos)
749  zmaStatus = device + "(" + channel + ") [R]";
750  else
751  zmaStatus = device + "(" + channel + ") [S]";
752  }
753  else
754  {
755  if (enabled == "0")
756  zmaStatus = host + " [-]";
757  else if (status.find("'zma -m " + id + "' running") != string::npos)
758  zmaStatus = host + " [R]";
759  else
760  zmaStatus = host + " [S]";
761  }
762 
763  if (type == "Local")
764  {
765  if (enabled == "0")
766  zmcStatus = function + " [-]";
767  else if (status.find("'zmc -d "+ device + "' running") != string::npos)
768  zmcStatus = function + " [R]";
769  else
770  zmcStatus = function + " [S]";
771  }
772  else
773  {
774  if (enabled == "0")
775  zmcStatus = function + " [-]";
776  else if (status.find("'zmc -m " + id + "' running") != string::npos)
777  zmcStatus = function + " [R]";
778  else
779  zmcStatus = function + " [S]";
780  }
781 }
782 
783 void ZMServer::handleGetEventFrame(vector<string> tokens)
784 {
785  static unsigned char buffer[MAX_IMAGE_SIZE];
786 
787  if (tokens.size() != 5)
788  {
789  sendError(ERROR_TOKEN_COUNT);
790  return;
791  }
792 
793  string monitorID(tokens[1]);
794  string eventID(tokens[2]);
795  int frameNo = atoi(tokens[3].c_str());
796  string eventTime(tokens[4]);
797 
798  if (m_debug)
799  cout << "Getting frame " << frameNo << " for event " << eventID
800  << " on monitor " << monitorID << " event time is " << eventTime << endl;
801 
802  string outStr("");
803 
804  ADD_STR(outStr, "OK")
805 
806  // try to find the frame file
807  string filepath("");
808  char str[100];
809 
810  if (m_useDeepStorage)
811  {
812  filepath = g_webPath + "/events/" + monitorID + "/" + eventTime + "/";
813  sprintf(str, m_eventFileFormat.c_str(), frameNo);
814  filepath += str;
815  }
816  else
817  {
818  filepath = g_webPath + "/events/" + monitorID + "/" + eventID + "/";
819  sprintf(str, m_eventFileFormat.c_str(), frameNo);
820  filepath += str;
821  }
822 
823  FILE *fd;
824  int fileSize = 0;
825  if ((fd = fopen(filepath.c_str(), "r" )))
826  {
827  fileSize = fread(buffer, 1, sizeof(buffer), fd);
828  fclose(fd);
829  }
830  else
831  {
832  cout << "Can't open " << filepath << ": " << strerror(errno) << endl;
833  sendError(ERROR_FILE_OPEN + string(" - ") + filepath + " : " + strerror(errno));
834  return;
835  }
836 
837  if (m_debug)
838  cout << "Frame size: " << fileSize << endl;
839 
840  // get the file size
841  sprintf(str, "%d", fileSize);
842  ADD_STR(outStr, str)
843 
844  // send the data
845  send(outStr, buffer, fileSize);
846 }
847 
848 void ZMServer::handleGetAnalyseFrame(vector<string> tokens)
849 {
850  static unsigned char buffer[MAX_IMAGE_SIZE];
851  char str[100];
852 
853  if (tokens.size() != 5)
854  {
855  sendError(ERROR_TOKEN_COUNT);
856  return;
857  }
858 
859  string monitorID(tokens[1]);
860  string eventID(tokens[2]);
861  int frameNo = atoi(tokens[3].c_str());
862  string eventTime(tokens[4]);
863 
864  if (m_debug)
865  cout << "Getting anaylse frame " << frameNo << " for event " << eventID
866  << " on monitor " << monitorID << " event time is " << eventTime << endl;
867 
868  // get the 'alarm' frames from the Frames table for this event
869  MYSQL_RES *res;
870  MYSQL_ROW row = NULL;
871 
872  string sql("");
873  sql += "SELECT FrameId FROM Frames ";
874  sql += "WHERE EventID = " + eventID + " ";
875  sql += "AND Type = 'Alarm' ";
876  sql += "ORDER BY FrameID";
877 
878  if (mysql_query(&g_dbConn, sql.c_str()))
879  {
880  fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
881  sendError(ERROR_MYSQL_QUERY);
882  return;
883  }
884 
885  res = mysql_store_result(&g_dbConn);
886  int frameCount = mysql_num_rows(res);
887  int frameID;
888 
889  // if the required frame mumber is 0 or out of bounds then use the middle frame
890  if (frameNo == 0 || frameNo < 0 || frameNo > frameCount)
891  frameNo = (frameCount / 2) + 1;
892 
893  // move to the required frame in the table
894  for (int x = 0; x < frameNo; x++)
895  {
896  row = mysql_fetch_row(res);
897  }
898 
899  if (row)
900  {
901  frameID = atoi(row[0]);
902  }
903  else
904  {
905  cout << "handleGetAnalyseFrame: Failed to get mysql row for frameNo " << frameNo << endl;
906  sendError(ERROR_MYSQL_ROW);
907  return;
908  }
909 
910  string outStr("");
911 
912  ADD_STR(outStr, "OK")
913 
914  // try to find the analyse frame file
915  string filepath("");
916  if (m_useDeepStorage)
917  {
918  filepath = g_webPath + "/events/" + monitorID + "/" + eventTime + "/";
919  sprintf(str, m_analyseFileFormat.c_str(), frameID);
920  filepath += str;
921  }
922  else
923  {
924  filepath = g_webPath + "/events/" + monitorID + "/" + eventID + "/";
925  sprintf(str, m_analyseFileFormat.c_str(), frameID);
926  filepath += str;
927  }
928 
929  FILE *fd;
930  int fileSize = 0;
931  if ((fd = fopen(filepath.c_str(), "r" )))
932  {
933  fileSize = fread(buffer, 1, sizeof(buffer), fd);
934  fclose(fd);
935  }
936  else
937  {
938  cout << "Can't open " << filepath << ": " << strerror(errno) << endl;
939  sendError(ERROR_FILE_OPEN + string(" - ") + filepath + " : " + strerror(errno));
940  return;
941  }
942 
943  if (m_debug)
944  cout << "Frame size: " << fileSize << endl;
945 
946  // get the file size
947  sprintf(str, "%d", fileSize);
948  ADD_STR(outStr, str)
949 
950  // send the data
951  send(outStr, buffer, fileSize);
952 }
953 
954 void ZMServer::handleGetLiveFrame(vector<string> tokens)
955 {
956  static unsigned char buffer[MAX_IMAGE_SIZE];
957  char str[100];
958 
959  // we need to periodically kick the DB connection here to make sure it
960  // stays alive because the user may have left the frontend on the live
961  // view which doesn't query the DB at all and eventually the connection
962  // will timeout
964 
965  if (tokens.size() != 2)
966  {
967  sendError(ERROR_TOKEN_COUNT);
968  return;
969  }
970 
971  int monitorID = atoi(tokens[1].c_str());
972 
973  if (m_debug)
974  cout << "Getting live frame from monitor: " << monitorID << endl;
975 
976  string outStr("");
977 
978  ADD_STR(outStr, "OK")
979 
980  // echo the monitor id
981  sprintf(str, "%d", monitorID);
982  ADD_STR(outStr, str)
983 
984  // try to find the correct MONITOR
985  MONITOR *monitor;
986  if (m_monitors.find(monitorID) != m_monitors.end())
987  monitor = m_monitors[monitorID];
988  else
989  {
990  sendError(ERROR_INVALID_MONITOR);
991  return;
992  }
993 
994  // are the data pointers valid?
995  if (monitor->shared_data == NULL || monitor->shared_images == NULL)
996  {
997  sendError(ERROR_INVALID_POINTERS);
998  return;
999  }
1000 
1001  // read a frame from the shared memory
1002  int dataSize = getFrame(buffer, sizeof(buffer), monitor);
1003 
1004  if (m_debug)
1005  cout << "Frame size: " << dataSize << endl;
1006 
1007  if (dataSize == 0)
1008  {
1009  // not really an error
1010  outStr = "";
1011  ADD_STR(outStr, "WARNING - No new frame available");
1012  send(outStr);
1013  return;
1014  }
1015 
1016  // add status
1017  ADD_STR(outStr, monitor->status)
1018 
1019  // send the data size
1020  sprintf(str, "%d", dataSize);
1021  ADD_STR(outStr, str)
1022 
1023  // send the data
1024  send(outStr, buffer, dataSize);
1025 }
1026 
1027 void ZMServer::handleGetFrameList(vector<string> tokens)
1028 {
1029  string eventID;
1030  string outStr("");
1031 
1032  if (tokens.size() != 2)
1033  {
1034  sendError(ERROR_TOKEN_COUNT);
1035  return;
1036  }
1037 
1038  eventID = tokens[1];
1039 
1040  if (m_debug)
1041  cout << "Loading frames for event: " << eventID << endl;
1042 
1043  ADD_STR(outStr, "OK")
1044 
1045  MYSQL_RES *res;
1046  MYSQL_ROW row;
1047 
1048  string sql("");
1049  sql += "SELECT Type, Delta FROM Frames ";
1050  sql += "WHERE EventID = " + eventID + " ";
1051  sql += "ORDER BY FrameID";
1052 
1053  if (mysql_query(&g_dbConn, sql.c_str()))
1054  {
1055  fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
1056  sendError(ERROR_MYSQL_QUERY);
1057  return;
1058  }
1059 
1060  res = mysql_store_result(&g_dbConn);
1061  int frameCount = mysql_num_rows(res);
1062 
1063  if (m_debug)
1064  cout << "Got " << frameCount << " frames" << endl;
1065 
1066  char str[10];
1067  sprintf(str, "%d\n", frameCount);
1068  ADD_STR(outStr, str)
1069 
1070  for (int x = 0; x < frameCount; x++)
1071  {
1072  row = mysql_fetch_row(res);
1073  if (row)
1074  {
1075  ADD_STR(outStr, row[0]) // Type
1076  ADD_STR(outStr, row[1]) // Delta
1077  }
1078  else
1079  {
1080  cout << "handleGetFrameList: Failed to get mysql row " << x << endl;
1081  sendError(ERROR_MYSQL_ROW);
1082  return;
1083  }
1084  }
1085 
1086  mysql_free_result(res);
1087 
1088  send(outStr);
1089 }
1090 
1092 {
1093  string outStr("");
1094 
1095  ADD_STR(outStr, "OK")
1096 
1097  MYSQL_RES *res;
1098  MYSQL_ROW row;
1099 
1100  string sql("");
1101  sql += "SELECT DISTINCT M.Name FROM Events AS E ";
1102  sql += "INNER JOIN Monitors AS M ON E.MonitorId = M.Id;";
1103 
1104  if (mysql_query(&g_dbConn, sql.c_str()))
1105  {
1106  fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
1107  sendError(ERROR_MYSQL_QUERY);
1108  return;
1109  }
1110 
1111  res = mysql_store_result(&g_dbConn);
1112  int monitorCount = mysql_num_rows(res);
1113  char str[10];
1114  sprintf(str, "%d", monitorCount);
1115  ADD_STR(outStr, str)
1116 
1117  for (int x = 0; x < monitorCount; x++)
1118  {
1119  row = mysql_fetch_row(res);
1120  if (row)
1121  {
1122  ADD_STR(outStr, row[0]) // Name
1123  }
1124  else
1125  {
1126  cout << "handleGetCameraList: Failed to get mysql row " << x << endl;
1127  sendError(ERROR_MYSQL_ROW);
1128  return;
1129  }
1130  }
1131 
1132  mysql_free_result(res);
1133 
1134  send(outStr);
1135 }
1136 
1138 {
1139  string outStr("");
1140 
1141  ADD_STR(outStr, "OK")
1142 
1143  MYSQL_RES *res;
1144  MYSQL_ROW row;
1145 
1146  string sql("");
1147  sql += "SELECT Id, Name, Width, Height, Palette FROM Monitors ORDER BY Id";
1148 
1149  if (mysql_query(&g_dbConn, sql.c_str()))
1150  {
1151  fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
1152  sendError(ERROR_MYSQL_QUERY);
1153  return;
1154  }
1155 
1156  res = mysql_store_result(&g_dbConn);
1157  int monitorCount = mysql_num_rows(res);
1158 
1159  if (m_debug)
1160  cout << "Got " << monitorCount << " monitors" << endl;
1161 
1162  char str[10];
1163  sprintf(str, "%d", monitorCount);
1164  ADD_STR(outStr, str)
1165 
1166  for (int x = 0; x < monitorCount; x++)
1167  {
1168  row = mysql_fetch_row(res);
1169  if (row)
1170  {
1171  ADD_STR(outStr, row[0]) // Id
1172  ADD_STR(outStr, row[1]) // Name
1173  ADD_STR(outStr, row[2]) // Width
1174  ADD_STR(outStr, row[3]) // Height
1175  ADD_STR(outStr, row[4]) // Palette
1176 
1177  if (m_debug)
1178  {
1179  cout << "id: " << row[0] << endl;
1180  cout << "name: " << row[1] << endl;
1181  cout << "width: " << row[2] << endl;
1182  cout << "height: " << row[3] << endl;
1183  cout << "palette: " << row[4] << endl;
1184  cout << "-------------------" << endl;
1185  }
1186  }
1187  else
1188  {
1189  cout << "Failed to get mysql row" << endl;
1190  sendError(ERROR_MYSQL_ROW);
1191  return;
1192  }
1193  }
1194 
1195  mysql_free_result(res);
1196 
1197  send(outStr);
1198 }
1199 
1200 void ZMServer::handleDeleteEvent(vector<string> tokens)
1201 {
1202  string eventID;
1203  string outStr("");
1204 
1205  if (tokens.size() != 2)
1206  {
1207  sendError(ERROR_TOKEN_COUNT);
1208  return;
1209  }
1210 
1211  eventID = tokens[1];
1212 
1213  if (m_debug)
1214  cout << "Deleting event: " << eventID << endl;
1215 
1216  ADD_STR(outStr, "OK")
1217 
1218  string sql("");
1219  sql += "DELETE FROM Events WHERE Id = " + eventID;
1220 
1221  if (mysql_query(&g_dbConn, sql.c_str()))
1222  {
1223  fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
1224  sendError(ERROR_MYSQL_QUERY);
1225  return;
1226  }
1227 
1228  // run zmaudit.pl to clean everything up
1229  string command(g_binPath + "/zmaudit.pl &");
1230  errno = 0;
1231  if (system(command.c_str()) < 0 && errno)
1232  cerr << "Failed to run '" << command << "'" << endl;
1233 
1234  send(outStr);
1235 }
1236 
1237 void ZMServer::handleDeleteEventList(vector<string> tokens)
1238 {
1239  string eventList("");
1240  string outStr("");
1241 
1242  vector<string>::iterator it = tokens.begin();
1243  if (it != tokens.end())
1244  ++it;
1245  while (it != tokens.end())
1246  {
1247  if (eventList == "")
1248  eventList = (*it);
1249  else
1250  eventList += "," + (*it);
1251 
1252  ++it;
1253  }
1254 
1255  if (m_debug)
1256  cout << "Deleting events: " << eventList << endl;
1257 
1258  string sql("");
1259  sql += "DELETE FROM Events WHERE Id IN (" + eventList + ")";
1260 
1261  if (mysql_query(&g_dbConn, sql.c_str()))
1262  {
1263  fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
1264  sendError(ERROR_MYSQL_QUERY);
1265  return;
1266  }
1267 
1268  ADD_STR(outStr, "OK")
1269  send(outStr);
1270 }
1271 
1273 {
1274  string outStr("");
1275 
1276  // run zmaudit.pl to clean up orphaned db entries etc
1277  string command(g_binPath + "/zmaudit.pl &");
1278 
1279  if (m_debug)
1280  cout << "Running command: " << command << endl;
1281 
1282  errno = 0;
1283  if (system(command.c_str()) < 0 && errno)
1284  cerr << "Failed to run '" << command << "'" << endl;
1285 
1286  ADD_STR(outStr, "OK")
1287  send(outStr);
1288 }
1289 
1291 {
1292  m_monitors.clear();
1293 
1294  string sql("SELECT Id, Name, Width, Height, ImageBufferCount, MaxFPS, Palette, ");
1295  sql += " Type, Function, Enabled, Device, Host, Controllable, TrackMotion ";
1296  sql += "FROM Monitors";
1297 
1298  MYSQL_RES *res;
1299  MYSQL_ROW row;
1300 
1301  if (mysql_query(&g_dbConn, sql.c_str()))
1302  {
1303  fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
1304  return;
1305  }
1306 
1307  res = mysql_store_result(&g_dbConn);
1308  int monitorCount = mysql_num_rows(res);
1309 
1310  if (m_debug)
1311  cout << "Got " << monitorCount << " monitors" << endl;
1312 
1313  for (int x = 0; x < monitorCount; x++)
1314  {
1315  row = mysql_fetch_row(res);
1316  if (row)
1317  {
1318  MONITOR *m = new MONITOR;
1319  m->mon_id = atoi(row[0]);
1320  m->name = row[1];
1321  m->width = atoi(row[2]);
1322  m->height = atoi(row[3]);
1323  m->image_buffer_count = atoi(row[4]);
1324  m->palette = atoi(row[6]);
1325  m->type = row[7];
1326  m->function = row[8];
1327  m->enabled = atoi(row[9]);
1328  m->device = row[10];
1329  m->host = row[11];
1330  m->controllable = atoi(row[12]);
1331  m->trackMotion = atoi(row[13]);
1332  m_monitors[m->mon_id] = m;
1333 
1334  initMonitor(m);
1335  }
1336  else
1337  {
1338  cout << "Failed to get mysql row" << endl;
1339  return;
1340  }
1341  }
1342 
1343  mysql_free_result(res);
1344 }
1345 
1347 {
1348  void *shm_ptr = NULL;
1349 
1350  monitor->shared_data = NULL;
1351  monitor->shared_images = NULL;
1352 
1353  if (monitor->palette == 1)
1354  monitor->frame_size = monitor->width * monitor->height;
1355  else
1356  monitor->frame_size = monitor->width * monitor->height * 3;
1357 
1358  int shared_data_size;
1359 
1360  if (g_zmversion == "1.22.2")
1361  shared_data_size = sizeof(SharedData) +
1362  sizeof(TriggerData_old) +
1363  ((monitor->image_buffer_count) * (sizeof(struct timeval))) +
1364  ((monitor->image_buffer_count) * monitor->frame_size);
1365  else
1366  shared_data_size = sizeof(SharedData) +
1367  sizeof(TriggerData) +
1368  ((monitor->image_buffer_count) * (sizeof(struct timeval))) +
1369  ((monitor->image_buffer_count) * monitor->frame_size);
1370 
1371 
1372 #if _POSIX_MAPPED_FILES > 0L
1373  /*
1374  * Try to open the mmap file first if the architecture supports it.
1375  * Otherwise, legacy shared memory will be used below.
1376  */
1377  stringstream mmap_filename;
1378  mmap_filename << m_mmapPath << "/zm.mmap." << monitor->mon_id;
1379 
1380  int mapFile = open(mmap_filename.str().c_str(), O_RDONLY, 0x0);
1381  if (mapFile >= 0)
1382  {
1383  if (m_debug)
1384  cout << "Opened mmap file: " << mmap_filename << endl;
1385 
1386  shm_ptr = mmap(NULL, shared_data_size, PROT_READ,
1387  MAP_SHARED, mapFile, 0x0);
1388  if (shm_ptr == MAP_FAILED)
1389  {
1390  cout << "Failed to map shared memory from file [" <<
1391  mmap_filename << "] " <<
1392  "for monitor: " <<
1393  monitor->mon_id <<
1394  endl;
1395  monitor->status = "Error";
1396  return;
1397  }
1398  }
1399  else
1400  {
1401  // this is not necessarily a problem, maybe the user is still
1402  // using the legacy shared memory support
1403  if (m_debug)
1404  {
1405  cout << "Failed to open mmap file [" <<
1406  mmap_filename << "] " <<
1407  "for monitor: " <<
1408  monitor->mon_id <<
1409  " : " << strerror(errno) <<
1410  endl;
1411  cout << "Falling back to the legacy shared memory method" << endl;
1412  }
1413  }
1414 #endif
1415 
1416  if (shm_ptr == NULL)
1417  {
1418  // fail back to shmget() functionality if mapping memory above failed.
1419  int shmid;
1420 
1421  if ((shmid = shmget((m_shmKey & 0xffffff00) | monitor->mon_id,
1422  shared_data_size, SHM_R)) == -1)
1423  {
1424  cout << "Failed to shmget for monitor: " << monitor->mon_id << endl;
1425  monitor->status = "Error";
1426  switch(errno)
1427  {
1428  case EACCES: cout << "EACCES - no rights to access segment\n"; break;
1429  case EEXIST: cout << "EEXIST - segment already exists\n"; break;
1430  case EINVAL: cout << "EINVAL - size < SHMMIN or size > SHMMAX\n"; break;
1431  case ENFILE: cout << "ENFILE - limit on open files has been reached\n"; break;
1432  case ENOENT: cout << "ENOENT - no segment exists for the given key\n"; break;
1433  case ENOMEM: cout << "ENOMEM - couldn't reserve memory for segment\n"; break;
1434  case ENOSPC: cout << "ENOSPC - shmmni or shmall limit reached\n"; break;
1435  }
1436 
1437  return;
1438  }
1439 
1440  shm_ptr = shmat(shmid, 0, SHM_RDONLY);
1441 
1442 
1443  if (shm_ptr == NULL)
1444  {
1445  cout << "Failed to shmat for monitor: " << monitor->mon_id << endl;
1446  monitor->status = "Error";
1447  return;
1448  }
1449  }
1450 
1451  monitor->shared_data = (SharedData*)shm_ptr;
1452 
1453  if (g_zmversion == "1.22.2")
1454  monitor->shared_images = (unsigned char*) shm_ptr +
1455  sizeof(SharedData) +
1456  sizeof(TriggerData_old) +
1457  ((monitor->image_buffer_count) * sizeof(struct timeval));
1458  else
1459  monitor->shared_images = (unsigned char*) shm_ptr +
1460  sizeof(SharedData) +
1461  sizeof(TriggerData) +
1462  ((monitor->image_buffer_count) * sizeof(struct timeval));
1463 }
1464 
1465 int ZMServer::getFrame(unsigned char *buffer, int bufferSize, MONITOR *monitor)
1466 {
1467  (void) bufferSize;
1468 
1469  // is there a new frame available?
1470  if (monitor->shared_data->last_write_index == monitor->last_read)
1471  return 0;
1472 
1473  // sanity check last_read
1474  if (monitor->shared_data->last_write_index < 0 ||
1475  monitor->shared_data->last_write_index >= monitor->image_buffer_count)
1476  return 0;
1477 
1478  monitor->last_read = monitor->shared_data->last_write_index;
1479 
1480  switch (monitor->shared_data->state)
1481  {
1482  case IDLE:
1483  monitor->status = "Idle";
1484  break;
1485  case PREALARM:
1486  monitor->status = "Pre Alarm";
1487  break;
1488  case ALARM:
1489  monitor->status = "Alarm";
1490  break;
1491  case ALERT:
1492  monitor->status = "Alert";
1493  break;
1494  case TAPE:
1495  monitor->status = "Tape";
1496  break;
1497  default:
1498  monitor->status = "Unknown";
1499  break;
1500  }
1501 
1502  unsigned char *data = monitor->shared_images +
1503  monitor->frame_size * monitor->last_read;
1504 
1505  // FIXME: should do some sort of compression JPEG??
1506  // just copy the data to our buffer for now
1507  memcpy(buffer, data, monitor->frame_size);
1508 
1509 
1510  return monitor->frame_size;
1511 }
1512 
1513 string ZMServer::getZMSetting(const string &setting)
1514 {
1515  string result;
1516  string sql("SELECT Name, Value FROM Config ");
1517  sql += "WHERE Name = '" + setting + "'";
1518 
1519  MYSQL_RES *res;
1520  MYSQL_ROW row;
1521 
1522  if (mysql_query(&g_dbConn, sql.c_str()))
1523  {
1524  fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
1525  return "";
1526  }
1527 
1528  res = mysql_store_result(&g_dbConn);
1529  row = mysql_fetch_row(res);
1530  if (row)
1531  {
1532  result = row[1];
1533  }
1534  else
1535  {
1536  cout << "Failed to get mysql row" << endl;
1537  result = "";
1538  }
1539 
1540  if (m_debug)
1541  cout << "getZMSetting: " << setting << " Result: " << result << endl;
1542 
1543  mysql_free_result(res);
1544 
1545  return result;
1546 }
1547 
1548 void ZMServer::handleSetMonitorFunction(vector<string> tokens)
1549 {
1550  string outStr("");
1551 
1552  if (tokens.size() != 4)
1553  {
1554  sendError(ERROR_TOKEN_COUNT);
1555  return;
1556  }
1557 
1558  string monitorID(tokens[1]);
1559  string function(tokens[2]);
1560  string enabled(tokens[3]);
1561 
1562  // Check validity of input passed to server. Does monitor exist && is function ok
1563  if (m_monitors.find(atoi(monitorID.c_str())) == m_monitors.end())
1564  {
1565  sendError(ERROR_INVALID_MONITOR);
1566  return;
1567  }
1568 
1569  if (function != FUNCTION_NONE && function != FUNCTION_MONITOR &&
1570  function != FUNCTION_MODECT && function != FUNCTION_NODECT &&
1571  function != FUNCTION_RECORD && function != FUNCTION_MOCORD)
1572  {
1573  sendError(ERROR_INVALID_MONITOR_FUNCTION);
1574  return;
1575  }
1576 
1577  if (enabled != "0" && enabled != "1")
1578  {
1579  sendError(ERROR_INVALID_MONITOR_ENABLE_VALUE);
1580  return;
1581  }
1582 
1583  if (m_debug)
1584  cout << "User input validated OK" << endl;
1585 
1586 
1587  // Now perform db update && (re)start/stop daemons as required.
1588  MONITOR *monitor = m_monitors[atoi(monitorID.c_str())];
1589  string oldFunction = monitor->function;
1590  string newFunction = function;
1591  int oldEnabled = monitor->enabled;
1592  int newEnabled = atoi(enabled.c_str());
1593  monitor->function = newFunction;
1594  monitor->enabled = newEnabled;
1595 
1596  if (m_debug)
1597  cout << "SetMonitorFunction MonitorId: " << monitorID << endl <<
1598  " oldEnabled: " << oldEnabled << endl <<
1599  " newEnabled: " << newEnabled << endl <<
1600  " oldFunction: " << oldFunction << endl <<
1601  " newFunction: " << newFunction << endl;
1602 
1603  if ( newFunction != oldFunction || newEnabled != oldEnabled)
1604  {
1605  string sql("UPDATE Monitors ");
1606  sql += "SET Function = '" + function + "', ";
1607  sql += "Enabled = '" + enabled + "' ";
1608  sql += "WHERE Id = '" + monitorID + "'";
1609 
1610  if (mysql_query(&g_dbConn, sql.c_str()))
1611  {
1612  fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
1613  sendError(ERROR_MYSQL_QUERY);
1614  return;
1615  }
1616 
1617  if (m_debug)
1618  cout << "Monitor function SQL update OK" << endl;
1619 
1620  string status = runCommand(g_binPath + "/zmdc.pl check");
1621 
1622  // Now refresh servers
1623  if (RUNNING.compare(0, RUNNING.size(), status, 0, RUNNING.size()) == 0)
1624  {
1625  if (m_debug)
1626  cout << "Monitor function Refreshing daemons" << endl;
1627 
1628  bool restart = (oldFunction == FUNCTION_NONE) ||
1629  (newFunction == FUNCTION_NONE) ||
1630  (newEnabled != oldEnabled);
1631 
1632  if (restart)
1633  zmcControl(monitor, RESTART);
1634  else
1635  zmcControl(monitor, "");
1636  zmaControl(monitor, RELOAD);
1637  }
1638  else
1639  if (m_debug)
1640  cout << "zm daemons are not running" << endl;
1641  }
1642  else
1643  cout << "Not updating monitor function as identical to existing configuration" << endl;
1644 
1645  ADD_STR(outStr, "OK")
1646  send(outStr);
1647 }
1648 
1649 void ZMServer::zmcControl(MONITOR *monitor, const string &mode)
1650 {
1651  string zmcArgs = "";
1652  string sql = "";
1653  sql += "SELECT count(if(Function!='None',1,NULL)) as ActiveCount ";
1654  sql += "FROM Monitors ";
1655 
1656  if (monitor->type == "Local" )
1657  {
1658  sql += "WHERE Device = '" + monitor->device + "'";
1659  zmcArgs = "-d " + monitor->device;
1660  }
1661  else
1662  {
1663  sql += "WHERE Id = '" + monitor->getIdStr() + "'";
1664  zmcArgs = "-m " + monitor->mon_id;
1665  }
1666 
1667  if (mysql_query(&g_dbConn, sql.c_str()))
1668  {
1669  fprintf(stderr, "%s\n", mysql_error(&g_dbConn));
1670  sendError(ERROR_MYSQL_QUERY);
1671  return;
1672  }
1673 
1674  MYSQL_RES *res;
1675  MYSQL_ROW row;
1676  int activeCount;
1677 
1678  res = mysql_store_result(&g_dbConn);
1679  row = mysql_fetch_row(res);
1680 
1681  if (row)
1682  activeCount = atoi(row[0]);
1683  else
1684  {
1685  sendError(ERROR_MYSQL_QUERY);
1686  return;
1687  }
1688 
1689  if (!activeCount)
1690  runCommand(g_binPath + "/zmdc.pl stop zmc " + zmcArgs);
1691  else
1692  {
1693  if (mode == RESTART)
1694  runCommand(g_binPath + "/zmdc.pl stop zmc " + zmcArgs);
1695 
1696  runCommand(g_binPath + "/zmdc.pl start zmc " + zmcArgs);
1697  }
1698 }
1699 
1700 void ZMServer::zmaControl(MONITOR *monitor, const string &mode)
1701 {
1702  int zmOptControl = atoi(getZMSetting("ZM_OPT_CONTROL").c_str());
1703  int zmOptFrameServer = atoi(getZMSetting("ZM_OPT_FRAME_SERVER").c_str());
1704 
1705  if (monitor->function == FUNCTION_MODECT ||
1706  monitor->function == FUNCTION_RECORD ||
1707  monitor->function == FUNCTION_MOCORD ||
1708  monitor->function == FUNCTION_NODECT)
1709  {
1710  if (mode == RESTART)
1711  {
1712  if (zmOptControl)
1713  runCommand(g_binPath + "/zmdc.pl stop zmtrack.pl -m " + monitor->getIdStr());
1714 
1715  runCommand(g_binPath + "/zmdc.pl stop zma -m " + monitor->getIdStr());
1716 
1717  if (zmOptFrameServer)
1718  runCommand(g_binPath + "/zmdc.pl stop zmf -m " + monitor->getIdStr());
1719  }
1720 
1721  if (zmOptFrameServer)
1722  runCommand(g_binPath + "/zmdc.pl start zmf -m " + monitor->getIdStr());
1723 
1724  runCommand(g_binPath + "/zmdc.pl start zma -m " + monitor->getIdStr());
1725 
1726  if (zmOptControl && monitor->controllable && monitor->trackMotion &&
1727  ( monitor->function == FUNCTION_MODECT || monitor->function == FUNCTION_MOCORD) )
1728  runCommand(g_binPath + "/zmdc.pl start zmtrack.pl -m " + monitor->getIdStr());
1729 
1730  if (mode == RELOAD)
1731  runCommand(g_binPath + "/zmdc.pl reload zma -m " + monitor->getIdStr());
1732  }
1733  else
1734  {
1735  if (zmOptControl)
1736  runCommand(g_binPath + "/zmdc.pl stop zmtrack.pl -m " + monitor->getIdStr());
1737 
1738  runCommand(g_binPath + "/zmdc.pl stop zma -m " + monitor->getIdStr());
1739 
1740  if (zmOptFrameServer)
1741  runCommand(g_binPath + "/zmdc.pl stop zmf -m " + monitor->getIdStr());
1742  }
1743 }