MythTV  0.26-pre
main.cpp
Go to the documentation of this file.
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 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends