00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016 #include <iostream>
00017 #include <string>
00018 #include <unistd.h>
00019 #include <cstdlib>
00020 #include <cstring>
00021 #include <cstdio>
00022 #include <map>
00023 #include <sys/types.h>
00024 #include <sys/socket.h>
00025 #include <sys/time.h>
00026 #include <netinet/in.h>
00027 #include <arpa/inet.h>
00028 #include <fcntl.h>
00029 #include <signal.h>
00030
00031 #include "zmserver.h"
00032
00033
00034 #define PORT 6548
00035
00036
00037 #define ZM_CONFIG "/etc/zm.conf"
00038
00039 #define EXIT_OK 0
00040 #define EXIT_INVALID_CMDLINE 255
00041 #define EXIT_OPENING_LOGFILE_ERROR 254
00042 #define EXIT_DAEMONIZING_ERROR 253
00043 #define EXIT_SOCKET_ERROR 252
00044 #define EXIT_VERSION_ERROR 251
00045
00046 using namespace std;
00047
00048 int main(int argc, char **argv)
00049 {
00050 fd_set master;
00051 fd_set read_fds;
00052 struct sockaddr_in myaddr;
00053 struct sockaddr_in remoteaddr;
00054 struct timeval timeout;
00055 int res;
00056 int fdmax;
00057 int listener;
00058 int newfd;
00059 char buf[4096];
00060 int nbytes;
00061 int yes=1;
00062 socklen_t addrlen;
00063 int i;
00064 bool quit = false;
00065
00066 bool debug = false;
00067 bool daemon_mode = false;
00068 int port = PORT;
00069 string logfile = "";
00070 string zmconfig = ZM_CONFIG;
00071
00072
00073 for (int argpos = 1; argpos < argc; ++argpos)
00074 {
00075 if (!strcmp(argv[argpos],"-d") ||
00076 !strcmp(argv[argpos],"--daemon"))
00077 {
00078 daemon_mode = true;
00079 }
00080 else if (!strcmp(argv[argpos],"-n") ||
00081 !strcmp(argv[argpos],"--nodaemon"))
00082 {
00083 daemon_mode = false;
00084 }
00085 else if (!strcmp(argv[argpos],"-p") ||
00086 !strcmp(argv[argpos],"--port"))
00087 {
00088 if (argc > argpos)
00089 {
00090 port = atoi(argv[argpos+1]);
00091
00092 if (port < 1 || port > 65534)
00093 {
00094 cout << "Bad port number: " << port << endl;
00095 return EXIT_INVALID_CMDLINE;
00096 }
00097 ++argpos;
00098 }
00099 else
00100 {
00101 cout << "Missing argument to -p/--port option\n";
00102 return EXIT_INVALID_CMDLINE;
00103 }
00104 }
00105 else if (!strcmp(argv[argpos],"-l") ||
00106 !strcmp(argv[argpos],"--logfile"))
00107 {
00108 if (argc > argpos)
00109 {
00110 logfile = argv[argpos+1];
00111 if (logfile[0] == '-')
00112 {
00113 cerr << "Invalid or missing argument to -l/--logfile option\n";
00114 return EXIT_INVALID_CMDLINE;
00115 }
00116
00117 ++argpos;
00118 }
00119 else
00120 {
00121 cerr << "Missing argument to -l/--logfile option\n";
00122 return EXIT_INVALID_CMDLINE;
00123 }
00124 }
00125 else if (!strcmp(argv[argpos],"-c") ||
00126 !strcmp(argv[argpos],"--zmconfig"))
00127 {
00128 if (argc > argpos)
00129 {
00130 zmconfig = argv[argpos+1];
00131 if (zmconfig[0] == '-')
00132 {
00133 cerr << "Invalid or missing argument to -c/--zmconfig option\n";
00134 return EXIT_INVALID_CMDLINE;
00135 }
00136
00137 ++argpos;
00138 }
00139 else
00140 {
00141 cerr << "Missing argument to -c/--zmconfig option\n";
00142 return EXIT_INVALID_CMDLINE;
00143 }
00144 }
00145 else if (!strcmp(argv[argpos],"-v") ||
00146 !strcmp(argv[argpos],"--verbose"))
00147 {
00148 debug = true;
00149 }
00150 else
00151 {
00152 cerr << "Invalid argument: " << argv[argpos] << endl <<
00153 "Valid options are: " << endl <<
00154 "-p or --port number A port number to listen on (default is 6548) " << endl <<
00155 "-d or --daemon Runs mythzmserver as a daemon " << endl <<
00156 "-n or --nodaemon Does not run mythzmserver as a daemon (default)" << endl <<
00157 "-c or --zmconfig Location of zoneminders config file (default is " << ZM_CONFIG << ")" << endl <<
00158 "-l or --logfile filename Writes STDERR and STDOUT messages to filename" << endl <<
00159 "-v or --verbose Prints more debug output" << endl;
00160 return EXIT_INVALID_CMDLINE;
00161 }
00162 }
00163
00164
00165 int logfd = -1;
00166
00167 if (logfile != "")
00168 {
00169 logfd = open(logfile.c_str(), O_WRONLY|O_CREAT|O_APPEND, 0664);
00170
00171 if (logfd < 0)
00172 {
00173 perror("open(logfile)");
00174 return EXIT_OPENING_LOGFILE_ERROR;
00175 }
00176 }
00177
00178 if (logfd != -1)
00179 {
00180
00181 dup2(logfd, 1);
00182 dup2(logfd, 2);
00183
00184
00185 if (logfd != 1 && logfd != 2)
00186 close(logfd);
00187 }
00188
00189 if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
00190 cout << "Unable to ignore SIGPIPE\n";
00191
00192
00193 if (daemon_mode)
00194 {
00195 if (daemon(0, 0) < 0)
00196 {
00197 cout << "Failed to run as a daemon. Bailing out.\n";
00198 return EXIT_DAEMONIZING_ERROR;
00199 }
00200 cout << endl;
00201 }
00202
00203 map<int, ZMServer*> serverList;
00204
00205
00206 loadZMConfig(zmconfig);
00207
00208 cout << "ZM is version '" << g_zmversion << "'" << endl;
00209
00210
00211 connectToDatabase();
00212
00213
00214 FD_ZERO(&master);
00215 FD_ZERO(&read_fds);
00216
00217
00218 if ((listener = socket(AF_INET, SOCK_STREAM, 0)) == -1)
00219 {
00220 perror("socket");
00221 return EXIT_SOCKET_ERROR;
00222 }
00223
00224
00225 if (setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &yes,
00226 sizeof(int)) == -1)
00227 {
00228 perror("setsockopt");
00229 return EXIT_SOCKET_ERROR;
00230 }
00231
00232
00233 myaddr.sin_family = AF_INET;
00234 myaddr.sin_addr.s_addr = INADDR_ANY;
00235 myaddr.sin_port = htons(port);
00236 memset(&(myaddr.sin_zero), '\0', 8);
00237 if (bind(listener, (struct sockaddr *)&myaddr, sizeof(myaddr)) == -1)
00238 {
00239 perror("bind");
00240 return EXIT_SOCKET_ERROR;
00241 }
00242
00243
00244 if (listen(listener, 10) == -1)
00245 {
00246 perror("listen");
00247 return EXIT_SOCKET_ERROR;
00248 }
00249
00250 cout << "Listening on port: " << port << endl;
00251
00252
00253 FD_SET(listener, &master);
00254
00255
00256 fdmax = listener;
00257
00258
00259 while (!quit)
00260 {
00261
00262 timeout.tv_sec = DB_CHECK_TIME;
00263 timeout.tv_usec = 0;
00264
00265 read_fds = master;
00266 res = select(fdmax+1, &read_fds, NULL, NULL, &timeout);
00267
00268 if (res == -1)
00269 {
00270 perror("select");
00271 return EXIT_SOCKET_ERROR;
00272 }
00273 else if (res == 0)
00274 {
00275
00276
00277 kickDatabase(debug);
00278 continue;
00279 }
00280
00281
00282 for (i = 0; i <= fdmax; i++)
00283 {
00284 if (FD_ISSET(i, &read_fds))
00285 {
00286
00287 if (i == listener)
00288 {
00289
00290 addrlen = sizeof(remoteaddr);
00291 if ((newfd = accept(listener, (struct sockaddr *) &remoteaddr,
00292 &addrlen)) == -1)
00293 {
00294 perror("accept");
00295 }
00296 else
00297 {
00298
00299 FD_SET(newfd, &master);
00300 if (newfd > fdmax)
00301 {
00302 fdmax = newfd;
00303 }
00304
00305
00306 ZMServer *server = new ZMServer(newfd, debug);
00307 serverList[newfd] = server;
00308
00309 printf("new connection from %s on socket %d\n",
00310 inet_ntoa(remoteaddr.sin_addr), newfd);
00311 }
00312 }
00313 else
00314 {
00315
00316 if ((nbytes = recv(i, buf, sizeof(buf), 0)) <= 0)
00317 {
00318
00319 if (nbytes == 0)
00320 {
00321
00322 printf("socket %d hung up\n", i);
00323 }
00324 else
00325 {
00326 perror("recv");
00327 }
00328
00329 close(i);
00330
00331
00332 FD_CLR(i, &master);
00333
00334
00335 ZMServer *server = serverList[i];
00336 if (server)
00337 delete server;
00338 serverList.erase(i);
00339 }
00340 else
00341 {
00342 ZMServer *server = serverList[i];
00343 server->processRequest(buf, nbytes);
00344 }
00345 }
00346 }
00347 }
00348 }
00349
00350 mysql_close(&g_dbConn);
00351
00352 return EXIT_OK;
00353 }
00354