|
MythTV
0.26-pre
|
00001 /* main.cpp 00002 * ============================================================ 00003 * This program is free software; you can redistribute it 00004 * and/or modify it under the terms of the GNU General 00005 * Public License as published bythe Free Software Foundation; 00006 * either version 2, or (at your option) 00007 * any later version. 00008 * 00009 * This program is distributed in the hope that it will be useful, 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 * GNU General Public License for more details. 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 // default port to listen on 00034 #define PORT 6548 00035 00036 // default location of zoneminders config file 00037 #define ZM_CONFIG "/etc/zm.conf" 00038 00039 // Care should be taken to keep these in sync with the exit codes in 00040 // libmythbase/exitcodes.h (which is not included here to keep this code 00041 // separate from mythtv libraries). 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; // master file descriptor list 00053 fd_set read_fds; // temp file descriptor list for select() 00054 struct sockaddr_in myaddr; // server address 00055 struct sockaddr_in remoteaddr; // client address 00056 struct timeval timeout; // maximum time to wait for data 00057 int res; // result from select() 00058 int fdmax; // maximum file descriptor number 00059 int listener; // listening socket descriptor 00060 int newfd; // newly accept()ed socket descriptor 00061 char buf[4096]; // buffer for client data 00062 int nbytes; 00063 int yes=1; // for setsockopt() SO_REUSEADDR, below 00064 socklen_t addrlen; 00065 int i; 00066 bool quit = false; // quit flag 00067 00068 bool debug = false; // debug mode enabled 00069 bool daemon_mode = false; // is daemon mode enabled 00070 int port = PORT; // port we're listening on 00071 string logfile = ""; // log file 00072 string zmconfig = ZM_CONFIG; // location of zoneminders config file 00073 00074 // Check command line arguments 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 // set up log file 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 // Send stdout and stderr to the logfile 00183 dup2(logfd, 1); 00184 dup2(logfd, 2); 00185 00186 // Close the unduplicated logfd 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 // Switch to daemon mode? 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; // list of ZMServers 00206 00207 // load the config 00208 loadZMConfig(zmconfig); 00209 00210 cout << "ZM is version '" << g_zmversion << "'" << endl; 00211 00212 // connect to the DB 00213 connectToDatabase(); 00214 00215 // clear the master and temp sets 00216 FD_ZERO(&master); 00217 FD_ZERO(&read_fds); 00218 00219 // get the listener 00220 if ((listener = socket(AF_INET, SOCK_STREAM, 0)) == -1) 00221 { 00222 perror("socket"); 00223 return EXIT_SOCKET_ERROR; 00224 } 00225 00226 // lose the pesky "address already in use" error message 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 // bind 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 // listen 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 // add the listener to the master set 00255 FD_SET(listener, &master); 00256 00257 // keep track of the biggest file descriptor 00258 fdmax = listener; // so far, it's this one 00259 00260 // main loop 00261 while (!quit) 00262 { 00263 // the maximum time select() should wait 00264 timeout.tv_sec = DB_CHECK_TIME; 00265 timeout.tv_usec = 0; 00266 00267 read_fds = master; // copy it 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 // select timed out 00278 // just kick the DB connection to keep it alive 00279 kickDatabase(debug); 00280 continue; 00281 } 00282 00283 // run through the existing connections looking for data to read 00284 for (i = 0; i <= fdmax; i++) 00285 { 00286 if (FD_ISSET(i, &read_fds)) 00287 { 00288 // we got one!! 00289 if (i == listener) 00290 { 00291 // handle new connections 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 // add to master set 00302 FD_SET(newfd, &master); 00303 if (newfd > fdmax) 00304 { // keep track of the maximum 00305 fdmax = newfd; 00306 } 00307 00308 // create new ZMServer and add to map 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 // handle data from a client 00319 if ((nbytes = recv(i, buf, sizeof(buf), 0)) <= 0) 00320 { 00321 // got error or connection closed by client 00322 if (nbytes == 0) 00323 { 00324 // connection closed 00325 printf("socket %d hung up\n", i); 00326 } 00327 else 00328 { 00329 perror("recv"); 00330 } 00331 00332 close(i); 00333 00334 // remove from master set 00335 FD_CLR(i, &master); 00336 00337 // remove from server list 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
1.7.6.1