Ticket #5758: zmserver.cpp

File zmserver.cpp, 44.2 KB (added by noisymime@…, 16 years ago)

Patched zmserver.cpp

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