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