Main Page | Modules | Namespace List | Class Hierarchy | Class List | File List | Class Members | File Members | Related Pages

main.c

Go to the documentation of this file.
00001 
00014 /*
00015  * Copyright (c) 1996-1997 Chip Norkus
00016  * Copyright (c) 1997 Max Byrd
00017  * Copyright (c) 1997 Greg Poma
00018  * Copyright (c) 2000-2001 James Hess
00019  * All rights reserved.
00020  *
00021  * Redistribution and use in source and binary forms, with or without
00022  * modification, are permitted provided that the following conditions
00023  * are met:
00024  * 1. Redistributions of source code must retain the above copyright
00025  *    notice, this list of conditions and the following disclaimer.
00026  * 2. Redistributions in binary form must reproduce the above copyright
00027  *    notice, this list of conditions and the following disclaimer in the
00028  *    documentation and/or other materials provided with the distribution.
00029  * 3. Neither the name of the authors nor the names of its contributors
00030  *    may be used to endorse or promote products derived from this software
00031  *    without specific prior written permission.
00032  *
00033  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
00034  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00035  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00036  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
00037  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00038  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00039  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00040  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00041  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00042  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00043  * SUCH DAMAGE.
00044  */
00045 
00046 #include "services.h"
00047 #include "hash.h"
00048 #include "nickserv.h"
00049 #include "memoserv.h"
00050 #include "infoserv.h"
00051 #include "sipc.h"
00052 #include "db.h"
00053 #include "mass.h"
00054 #include "log.h"
00055 
00056 #include <getopt.h>
00057 
00058 #ifdef USE_SQL
00059 extern PGconn *dbConn;
00060 #endif
00061 
00062 extern int ipcPort;
00063 extern int svcOptFork;
00064 
00065 /*
00066  * my server name, and my server password 
00067  */
00068 extern char myname[255];
00069 extern char mypass[33];
00070 
00071 /*
00072  * Their hostname, and port 
00073  */
00074 extern char hostname[255];
00075 extern int port;
00076 
00077 /*
00078  * the servers socket... 
00079  */
00080 extern int server;
00081 
00082 /*
00083  * array of services, and their data... 
00084  * see above for Service struct. 
00085  */
00086 extern Service services[NUMSERVS];
00087 extern database db;
00088 
00090 extern SLogfile *operlog, *nicklog, *chanlog;
00091 
00093 extern FILE *corelog;
00094 
00096 extern u_long totalusers;
00097 
00099 extern u_long mostusers, mostnicks, mostchans, mostmemos;
00100 
00102 extern unsigned long top_akill_stamp;
00103 
00105 extern long startup, firstup;
00106 
00108 extern char *OperServ, *NickServ, *ChanServ, *MemoServ, *InfoServ, *GameServ;
00109 
00111 extern char coreBuffer[IRCBUF];
00112 
00114 extern u_int AccessLimit, OpLimit, AkickLimit, ChanLimit, NickLimit;
00115 
00117 extern time_t CTime;                    /* current time, keep this in mind... */
00118 
00120 extern time_t nextNsync, nextCsync, nextMsync;
00121 
00122 static int lockfile(char *);
00123 time_t updateCloneAlerts(time_t);
00124 
00126 IpcType servicesIpc;
00127 
00128 int runAsRootOverride = 0;
00129 
00131 void ServicesProgramHelp()
00132 {
00133     printf("Usage: services [OPTION] ...\n");
00134     printf("SorceryNet Services server\n");
00135     printf("Information:\n");
00136     printf("  -h, --help       Show this information\n");
00137     printf("  -v, --version    Print version information\n");
00138     printf("  -f, --fork       Run as a daemon, fork into background\n");
00139     printf("  -F, --nofork     Stay in foreground\n");
00140     printf("  -i, --ipc=[PORT] Enable services IPC using PORT\n");
00141     printf("  -I, --noipc      Disable services IPC\n");
00142     printf("\nReport bugs to services-bugs@sorcery.net\n");
00143     return;
00144 }
00145 
00147 int main(int argc, char *argv[])
00148 {
00149     char buffer[4097];
00150     fd_set readme, writeme, xceptme;
00151     FILE *fp, *pidf;
00152     struct timeval tv;
00153     int cc;
00154     time_t next_alert_update = 0;
00155 #ifdef USE_SQL
00156     int PQsockfd;
00157 #endif
00158 #ifndef NORLIMIT
00159     struct rlimit corelim;
00160 #endif
00161 
00162 #ifndef DEBUG
00163 #ifdef FORK_EM
00164     int pid;
00165 #endif
00166 #endif
00167 
00168     static struct option servOpts[] =
00169     {
00170         /*{ "help", 1, 0, 0  },*/
00171         { "help", 0, 0, 0  },
00172         { "version", 0, 0, 1  },
00173         { "fork", 0, 0, 2  },
00174         { "nofork", 0, 0, 3  },
00175         { "ipc", 2, 0, 4  },
00176         { "noipc", 0, 0, 5  },
00177         { 0, 0, 0, 0 },
00178     };
00179 
00180     int lastOpt;
00181     char ch;
00182 
00183     ipcPort = 0;
00184 
00185     /* Handle options */
00186     while ( optind < argc )
00187     {
00188         static const char *libUf = ( "\n"
00189             "Copyr. 1996-2001 Chip Norkus, Max Byrd, Greg Poma, Michael Graff\n"
00190             "                 James Hess, Dafydd James\n"
00191             "All Rights Reserved.\n\n"
00192             "This product includes software developed by Chip Norkus, Max Byrd,\n"
00193             "Greg Poma, Michael Graff, James Hess, Dafydd James, the University\n"
00194             "of California, Berkeley and its contributors.\n\n"
00195             "THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND\n"
00196             "ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n"
00197             "IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n"
00198             "ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE\n"
00199             "FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n"
00200             "DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n"
00201             "OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n"
00202             "HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n"
00203             "LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n"
00204             "OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n"
00205             "SUCH DAMAGE.\n"
00206         );
00207 
00208         ch = getopt_long(argc, argv, "RhvfFi:I", servOpts, &lastOpt);
00209 
00210         if (ch == '?' || ch == ':')
00211             exit(2);
00212 
00213         if (ch == -1) {
00214             printf("%s: invalid option -- %s\n", PACKAGE, argv[optind]);
00215             exit(2);
00216         }
00217 
00218         switch(ch)
00219         {
00220             case 'h':
00221             case 0: /* --help */
00222                 ServicesProgramHelp();
00223                 exit(0);
00224             break;
00225 
00226             case 'v':
00227             case 1: /* --version */
00228                 printf("%s\n", (PACKAGE "-" VERSION));
00229                 fprintf(stdout, "%s\n", libUf);
00230                 exit(0);
00231             break;
00232 
00233             case 'f':
00234             case 2: /* --fork */
00235                 svcOptFork = 1;
00236             break;
00237 
00238             case 'F':
00239             case 3: /* --nofork */
00240                 svcOptFork = 0;
00241             break;
00242 
00243             case 'R':
00244                 runAsRootOverride = 1;
00245             break;
00246 
00247             case 'i':
00248             case 4: /* --ipc */
00249                 if (optarg == NULL)
00250                     ipcPort = 3030;
00251                 else if (isdigit(*optarg))
00252                     ipcPort = atoi(optarg);
00253 
00254                 if (ipcPort <= 1024 || ipcPort >= SHRT_MAX) {
00255                     printf("services: Invalid port number.\n"
00256                            "Must be between 1025 and %d.\n",
00257                         SHRT_MAX);
00258                     exit(2);
00259                 }
00260             break;
00261 
00262             case 'I':
00263             case 5: /* --noipc */
00264                 ipcPort = -1;
00265             break;
00266 
00267             default:
00268                 printf("HUH?\n");
00269                 exit(100);
00270         }
00271     }
00272 
00273     /* End option handling */
00274 
00275     if (geteuid() == 0 && runAsRootOverride == 0) {
00276         fprintf(stderr, "ERROR: %s should not be run as root.\n", argv[0]);
00277         exit(1);
00278     }
00279 
00280 
00281     CTime = time(NULL);
00282 
00283     /*
00284      * initialize the debugging log file
00285      */
00286     dlogInit();
00287 
00288     dlogEntry("Starting...");
00289 
00290     /*
00291      * If there's a .pid file still, then services is most likely running...
00292      * scream really loudly
00293      */
00294     pidf = fopen("services.pid", "r");
00295     if (pidf != NULL) {
00296         fprintf(stderr,
00297                 "Error, services.pid already exists.  If services isn't\n"
00298                 "actually running, delete the .pid file and run me again.\n"
00299                 "If it is running, don't load me again.\n");
00300         fclose(pidf);
00301         return 1;
00302     }
00303 
00304 #ifdef DEBUG
00305     printf("Using %s for sending mail from stdin\n", SENDMAIL);
00306 #endif
00307 #ifndef NORLIMIT
00308     corelim.rlim_cur = corelim.rlim_max = RLIM_INFINITY;
00309     if (setrlimit(RLIMIT_CORE, &corelim))
00310         fprintf(stderr, "Cannot unlimit core! -> %i", errno);
00311 #endif
00312 
00313     umask(077);
00314     setenv("TZ", "GMT", 1);
00315     tzset();
00316     dlogEntry("Locking files");
00317 
00318     if (lockfile("chanserv/chanserv.db")) {
00319         printf("Exiting...\n");
00320         sshutdown(-1);
00321     }
00322 
00323     if (lockfile("nickserv/nickserv.db")) {
00324         printf("Exiting...\n");
00325         sshutdown(-1);
00326     }
00327 
00328     if (lockfile("memoserv/memoserv.db")) {
00329         printf("Exiting...\n");
00330         sshutdown(-1);
00331     }
00332 
00333     /*
00334      * logging files 
00335      */
00336     dlogEntry("Opening logs");
00337     operlog = new SLogfile("operserv/operserv.log", "operserv/operserv-w");
00338     nicklog = new SLogfile("nickserv/nickserv.log", "nickserv/nickserv-w");
00339     chanlog = new SLogfile("chanserv/chanserv.log", "chanserv/chanserv-w");
00340     corelog = fopen("core.log", "a");
00341 
00342     if (!operlog || !nicklog || !chanlog || !corelog) {
00343         if (corelog)
00344             dlogEntry("Unable to open logfile(s)");
00345         fprintf(stderr, "Unable to open: %s%s%s%s.\n",
00346                 operlog ? "" : " operlog",
00347                 nicklog ? "" : " nicklog",
00348                 chanlog ? "" : " chanlog", corelog ? "" : " corelog");
00349         sshutdown(0);
00350     }
00351 
00352     dlogEntry("Reading nickname data");
00353     readNickData();
00354 
00355     dlogEntry("Reading channel data");
00356     readChanData();
00357 
00358     dlogEntry("Reading article data");
00359     readInfoData();
00360 
00361     dlogEntry("Reading trigger data");
00362     readTriggerData();
00363 
00364     dlogEntry("Reading config");
00365     readConf();
00366 
00367 #ifndef DEBUG
00368 #ifdef FORK_EM
00369     if (svcOptFork)
00370     {
00371         dlogEntry("Forked");
00372 
00373         pid = fork();
00374 
00375         if (pid) {
00376             pidf = fopen("services.pid", "w");
00377             if (pidf != NULL) {
00378                 fprintf(pidf, "%d", pid);
00379                 fclose(pidf);
00380                 return 0;
00381             }
00382             else
00383             {
00384                 perror("fopen(services.pid)");
00385                 exit(5);
00386             }
00387         }
00388     }
00389     else
00390     {
00391         pid = getpid();
00392         pidf = fopen("services.pid", "w");
00393 
00394         if (pidf != NULL) {
00395             fprintf(pidf, "%d", pid);
00396             fclose(pidf);
00397         }
00398         else {
00399             perror("fopen(services.pid)");
00400             exit(5);
00401         }
00402         return 0;
00403     }
00404 #endif
00405 #endif
00406 
00407     if (chdir(CFGPATH) == -1)
00408         fprintf(stderr, "Cannot chdir(%s)\n", CFGPATH);
00409 
00410 #ifdef DEBUG
00411     printf
00412         ("\n\n\nMyName:%s   MyPass:%s\nConnectHost:%s   ConnectPort:%i\n",
00413          myname, mypass, hostname, port);
00414 #endif
00415 
00416     dlogEntry("Connecting to server %s, port %d", hostname, port);
00417 
00418 #ifdef USE_SQL
00419     if (dbConn != NULL)
00420         PQfinish(dbConn);
00421     dbConn = PQconnectdb(""
00422 #ifdef SQL_HOST
00423                          " host=" SQL_HOST
00424 #endif
00425 #ifdef SQL_HOSTADDR
00426                          " hostaddr=" SQL_HOSTADDR
00427 #endif
00428 #ifdef SQL_PORT
00429                          " port=" SQL_PORT
00430 #endif
00431                          " dbname=" SQL_DB
00432                          " user=" SQL_USER " password=" SQL_PASS "");
00433     if (!dbConn) {
00434         fprintf(stderr,
00435                 "Error allocating memory for database connection.\n");
00436         sshutdown(-1);
00437     }
00438     if (PQstatus(dbConn) != CONNECTION_OK) {
00439         fprintf(stderr, "Error connecting to the database.\n");
00440         sshutdown(-1);
00441     }
00442     PQsetnonblocking(dbConn, TRUE);
00443 #endif
00444 
00445     if (ipcPort == -1 || servicesIpc.start(ipcPort) < 0) {
00446         if (ipcPort > 0)
00447             printf("Could not startup ipc!\n");
00448         else
00449             printf("Ipc not enabled.\n");
00450     }
00451 
00452     srand48(time(NULL));
00453     srandom(time(NULL));
00454 
00455     if ((server = ConnectToServer(hostname, port)) < 0) {
00456         printf
00457             ("Could not connect to server!, exiting now.\nSocket # is: %i\n",
00458              server);
00459         exit(0);
00460     }
00461 
00462     dlogEntry("Logging in");
00463     sSend("PASS %s", mypass);
00464     sSend("SERVER %s 1 :Services Impenetrable Fortress ;)", myname);
00465     addUser(services[0].name, services[0].uname, services[0].host,
00466             services[0].rname, services[0].mode);
00467     addUser(services[1].name, services[1].uname, services[1].host,
00468             services[1].rname, services[1].mode);
00469     addUser(services[2].name, services[2].uname, services[2].host,
00470             services[2].rname, services[2].mode);
00471     addUser(services[3].name, services[3].uname, services[3].host,
00472             services[3].rname, services[3].mode);
00473     addUser(services[4].name, services[4].uname, services[4].host,
00474             services[4].rname, services[4].mode);
00475     addUser(services[5].name, services[5].uname, services[5].host,
00476             services[5].rname, services[5].mode);
00477     addUser(services[6].name, services[6].uname, services[6].host,
00478             services[6].rname, services[5].mode);
00479 
00480     dlogEntry("Reading services total files");
00481     fp = fopen("services.totals", "r");
00482 
00483     if (fp != NULL) {
00484         mostusers = mostnicks = mostchans = mostmemos = 0;
00485         firstup = 0;
00486 
00487         if (sfgets(buffer, 256, fp))
00488             mostusers = atoi(buffer);
00489 
00490         if (sfgets(buffer, 256, fp))
00491             mostnicks = atoi(buffer);
00492 
00493         if (sfgets(buffer, 256, fp))
00494             mostchans = atoi(buffer);
00495 
00496         if (sfgets(buffer, 256, fp))
00497             mostmemos = atoi(buffer);
00498 
00499         if (sfgets(buffer, 10, fp))
00500             firstup = atol(buffer);
00501 
00502         if (sfgets(buffer, 10, fp))
00503             top_akill_stamp = atol(buffer);
00504 
00505         fclose(fp);
00506     }
00507 
00508     totalusers = 0;
00509 
00510     startup = time(NULL);
00511     dlogEntry("Operational at", startup);
00512 
00513 #if 1                           /* set to 0 for reasonable GDB debugging of segv */
00514     signal(SIGPIPE, handler);
00515     signal(SIGSEGV, handler);
00516     signal(SIGTERM, handler);
00517 #endif
00518 
00519     signal(SIGALRM, SIG_IGN);
00520     signal(SIGHUP, SIG_IGN);
00521     /* signal(SIGINT, SIG_IGN); */
00522     signal(SIGINT, handler);
00523 
00524     OperServ = services[0].name;
00525     NickServ = services[1].name;
00526     ChanServ = services[2].name;
00527     MemoServ = services[3].name;
00528     InfoServ = services[4].name;
00529     GameServ = services[5].name;
00530     MassServ = services[6].name;
00531     /*
00532      * the only exit out of this loop is death.
00533      */
00534     timer(90, timed_akill_queue, NULL);
00535     timer(20, timed_advert_maint, NULL);
00536 
00537     while (1) {
00538         tv.tv_sec = 1;
00539         tv.tv_usec = 0;
00540 
00541         FD_ZERO(&readme);
00542         FD_ZERO(&writeme);
00543         FD_ZERO(&xceptme);
00544         FD_SET(server, &readme);
00545 #ifdef USE_SQL
00546         if (dbConn) {
00547             PQsockfd = PQsocket(dbConn);
00548             PQflush(dbConn);
00549             FD_SET(PQsockfd, &readme);
00550             FD_SET(PQsockfd, &xceptme);
00551         }
00552 #endif
00553         memset(buffer, 0, sizeof(buffer));
00554 
00555 
00556         servicesIpc.fdFillSet(readme);
00557         servicesIpc.fdFillSet(writeme);
00558         servicesIpc.fdFillSet(xceptme);
00559 
00560         /*
00561          * wait for something interesting.
00562          */
00563 
00564 #ifdef USE_SQL
00565         cc =
00566             select(MAX(1+servicesIpc.getTopfd(), MAX(server, PQsockfd) + 1), &readme, &writeme, &xceptme,
00567                    &tv);
00568 #else
00569         cc = select(MAX(1+servicesIpc.getTopfd(), server + 1), &readme, &writeme, &xceptme, &tv);
00570 #endif
00571 
00572         /*
00573          * select error is fatal
00574          */
00575         if (cc < 0) {
00576             dlogEntry("Error on select(): %s", strerror(errno));
00577             sshutdown(0);
00578         }
00579 
00580         /*
00581          * if the timer expired, run the timer code
00582          */
00583         if (cc == 0) {
00584             timeralarm();
00585             continue;
00586         }
00587 
00588         if (next_alert_update < CTime)
00589             next_alert_update = updateCloneAlerts(CTime);
00590 
00591         servicesIpc.pollAndHandle(readme, writeme, xceptme);
00592 
00593 #ifdef USE_SQL
00594         if (dbConn) {
00595             if (FD_ISSET(PQsockfd, &xceptme)
00596                 || FD_ISSET(PQsockfd, &readme)) {
00597                 switch (PQstatus(dbConn)) {
00598                 default:
00599                     break;
00600                 case CONNECTION_BAD:
00601                     logDump(corelog,
00602                             "DB connection went bad, attempting to reset...");
00603                     sSend
00604                         ("GLOBOPS :DB connection went bad, attempting to reset...");
00605                     PQreset(dbConn);
00606                     if (PQstatus(dbConn) != CONNECTION_OK) {
00607                         logDump(corelog,
00608                                 "Unable to re-establish db connection, dying.");
00609                         sSend
00610                             ("GLOBOPS :Unable to re-establish db connection, dying.");
00611                         sshutdown(-1);
00612                     }
00613                     break;
00614                 }
00615             }
00616 
00617             if (FD_ISSET(PQsockfd, &readme)) {
00618                 PGresult *res;
00619 
00620                 PQconsumeInput(dbConn);
00621                 if (!PQisBusy(dbConn)) {
00622                 }
00623                 while ((res = PQgetResult(dbConn))) {
00624                     PQclear(res);
00625                 }
00626             }
00627         }
00628 #endif
00629 
00630         /*
00631          * receive one less than the size of the buffer, just in case...
00632          */
00633         if (FD_ISSET(server, &readme)) {
00634             cc = net_read(server, buffer, sizeof(buffer) - 1);
00635 
00636             /*
00637              * If we have a read error, shut down.  This isn't perfect, but it is
00638              * good enough for now I guess.
00639              */
00640             if (cc < 0 || cc == 0) {
00641                 dlogEntry("Closing server connection due to error: %s",
00642                           (cc ==
00643                            0 ? "Connection closed" : strerror(errno)));
00644 
00645                 sshutdown(0);
00646             }
00647         }
00648 
00649         breakLine(buffer);
00650     }
00651 
00652     /* NOTREACHED */
00653     return 0;
00654 }
00655 
00664 static int lockfile(char *fname)
00665 {
00666     int fd;
00667     int i;
00668 
00669     fd = open(fname, O_RDONLY);
00670     if (fd < 0) {
00671         printf("Cannot open %s\n", fname);
00672         return -1;
00673     }
00674 
00675     i = flock(fd, LOCK_EX | LOCK_NB);
00676     if (i != 0) {
00677         printf("Cannot lock file %s\n", fname);
00678         return -1;
00679     }
00680 
00681     return 0;
00682 }
00683 
00684 /**********************************************************************/
00685 
00686 /* $Id: main.c,v 1.1.1.1 2003/07/04 02:54:29 Mysid Exp $ */

Generated at Sat Oct 25 20:56:08 2003 for Services using Doxygen.
Services Copyr. 1996-2001 Chip Norkus, Max Byrd, Greg Poma, Michael Graff, James Hess, Dafydd James. All rights reserved See LICENSE for licensing information.