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

vos/applibs/daemon/vosdaemon.cc

Go to the documentation of this file.
00001 /* $Id: vosdaemon.cc,v 1.14 2003/08/06 08:04:10 tetron Exp $ */
00002 
00003 
00004 /** @file vosdaemon.cc
00005     @author Reed Hedges (reed@zerohour.net)
00006 
00007     Copyright 2003 Reed Hedges
00008 
00009     This program is free software; you can redistribute it and/or modify
00010     it under the terms of the GNU General Public License as published by
00011     the Free Software Foundation; either version 2 of the License, or
00012     (at your option) any later version.
00013 
00014     This program is distributed in the hope that it will be useful,
00015     but WITHOUT ANY WARRANTY; without even the implied warranty of
00016     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017     GNU General Public License for more details.
00018 
00019     You should have received a copy of the GNU General Public License
00020     along with this program; if not, write to the Free Software
00021     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00022 */
00023 
00024 #if defined(_WIN32) && defined(_MSC_VER)
00025 # include <vos/applibs/vos_applibs_config-vc7.h>
00026 #else
00027 # include <vos/applibs/vos_applibs_config.h>
00028 #endif
00029 
00030 #ifdef HAVE_GETOPT_LONG
00031 # define _GNU_SOURCE
00032 typedef struct option LongOptionStruct;
00033 #endif
00034 
00035 #ifdef HAVE_GETOPT_H
00036 # include <getopt.h>
00037 #endif
00038 
00039 #ifdef HAVE_UNISTD_H
00040 # include <unistd.h>
00041 #endif
00042 
00043 #ifdef HAVE_SIGNAL
00044 #include <signal.h>
00045 #endif
00046 
00047 #include <stdlib.h>
00048 
00049 #include <iostream>
00050 #include <fstream>
00051 
00052 #include <vos/metaobjects/property/property.hh>
00053 
00054 #include "vosdaemon.hh"
00055 
00056 
00057 #if 0
00058 // The most recently created vosdaemon.
00059 VOSDaemon* _daemon = 0;
00060 
00061 void _VOSDaemon_killsignal(int signal)  {
00062     if(_daemon) {
00063         _daemon->save();
00064         delete _daemon;
00065     }
00066     exit(0);
00067 }
00068 
00069 void _VOSDaemon_hupsignal(int signal) {
00070     if(_daemon)
00071         _daemon->save();
00072 }
00073 
00074 void _VOSDaemon_autosave(void* something) {
00075     VOSDaemon* daemon = (VOSDaemon*)something;
00076     if(daemon) {
00077         daemon->save();
00078     }
00079 }
00080 #endif
00081 
00082 VOSDaemon::~VOSDaemon() {
00083     fflush(stdout);
00084     fflush(stderr);
00085     if(logStream) {
00086         logStream->flush();
00087         delete logStream;
00088     }
00089     LOG(programName, 2, "goodbye.");
00090 }
00091 
00092 
00093 
00094 VOSDaemon::VOSDaemon(char* progName, LocalSite* s) : 
00095     saveFreq(0), rootObj(0), programName(progName), rootName(0), 
00096     mayLoad(false), maySave(false), mayNameRoot(false),
00097     loadfile(0), dontFork(false)
00098 {
00099     int n = strlen("/var/log/") + strlen(programName) + 1;
00100     logFile = (char*) malloc(n * sizeof(char));
00101     snprintf(logFile, n, "/var/log/%s", programName);
00102     if(s) 
00103         site = s;
00104     else {
00105         site = new LocalSocketSite(&NoAccessControl::static_);
00106         site->setTimeoutOnSelect(-1);
00107     }
00108 }
00109 
00110 /*
00111 void VOSDaemon::enableArg_load(bool p) {
00112     mayLoad = p;
00113 }
00114 
00115 void VOSDaemon::enableArg_save(bool p) {
00116     maySave = p;
00117 }
00118 
00119 void VOSDaemon::enableArg_nameRoot(bool p) {
00120     mayNameRoot = p;
00121 }
00122 
00123 void VOSDaemon::setRootObject(Vobject* obj) {
00124     if(rootObj)
00125         rootObj->release();
00126     rootObj = obj;
00127     rootObj->acquire(); // released at exit or next call to setRootObj
00128 }
00129 
00130 void VOSDaemon::setSaveFile(char* sf) {
00131     saveFile = sf;
00132 }
00133 
00134 void VOSDaemon::setSaveFrequency(int freq) {
00135     saveFreq = freq;
00136 }
00137 
00138 void VOSDaemon::setRootName(char* name) {
00139     rootName = name;
00140 }
00141 
00142 */
00143 
00144 void VOSDaemon::setLogFile(char* file) {
00145     logFile = file;
00146 }
00147 
00148 void VOSDaemon::setLogLevel(int lev) {
00149     Log::setDefaultLevel(lev);
00150 }
00151 
00152 
00153 
00154 void VOSDaemon::checkArgs(int argc, char** argv) {
00155 
00156     // start the getopt string and the getopt_long array out with the default
00157     // VOSDaemon arguments.
00158     
00159     string optstring = "-hE:L:N";
00160     /*
00161     if(mayLoad)
00162         optstring += "l:";
00163     if(maySave) {
00164         optstring += "s:";
00165         optstring += "S:";
00166     }
00167     if(mayNameRoot)
00168         optstring += "n:";
00169         */
00170 
00171 #ifdef HAVE_GETOPT_LONG
00172     LongOptionStruct *longopts;
00173     longopts = (LongOptionStruct*)malloc( (7 + args.size()) * sizeof(LongOptionStruct));
00174     int l = 0;
00175 
00176     longopts[l].name = "help"; 
00177     longopts[l].has_arg = no_argument;
00178     longopts[l].flag = NULL;
00179     longopts[l].val = 'h';
00180     l++;
00181 
00182     longopts[l].name = "log"; 
00183     longopts[l].has_arg = required_argument;
00184     longopts[l].flag = NULL;
00185     longopts[l].val = 'L';
00186     l++;
00187 
00188     longopts[l].name = "loglevel"; 
00189     longopts[l].has_arg = required_argument;
00190     longopts[l].flag = NULL;
00191     longopts[l].val = 'E';
00192     l++;
00193 
00194     /*
00195 #ifdef HAVE_SIGNAL
00196     if(maySave) {
00197         longopts[l].name = "save"; 
00198         longopts[l].has_arg = required_argument;
00199         longopts[l].flag = NULL;
00200         longopts[l].val = 's';
00201         l++;
00202 
00203         longopts[l].name = "save-freq"; 
00204         longopts[l].has_arg = required_argument;
00205         longopts[l].flag = NULL;
00206         longopts[l].val = 'S';
00207         l++;
00208     }
00209 #endif
00210 
00211     if(mayLoad) {
00212         longopts[l].name = "load"; 
00213         longopts[l].has_arg = required_argument;
00214         longopts[l].flag = NULL;
00215         longopts[l].val = 'l';
00216         l++;
00217     }
00218 
00219     if(mayNameRoot) {
00220         longopts[l].name = "name"; 
00221         longopts[l].has_arg = required_argument;
00222         longopts[l].flag = NULL;
00223         longopts[l].val = 'n';
00224         l++;
00225     }
00226     */
00227 #endif
00228 
00229     // Now build options string and long options array with arg the user
00230     // has added with addArg and addFlag, after checking for environment
00231     // variables and pre-setting the arg value.
00232 
00233     for(ArgMap::iterator i = args.begin(); i != args.end(); i++) {
00234 
00235         // first add any environment variables that are present
00236         if(i->second.envVar != "") {
00237             char* e = getenv(i->second.envVar.c_str());
00238             if(e) {
00239                 i->second.given = true;
00240                 if(!(i->second.isFlag)) {
00241                     i->second.value = e;
00242                 }
00243             }
00244         }
00245 
00246         // now add arg to the getopt string of short names (single chars)
00247         if(i->second.argChar != 0) {
00248             optstring += i->second.argChar;
00249             if(!(i->second.isFlag))
00250                 optstring += ":";
00251         }
00252 
00253 #ifdef HAVE_GETOPT_LONG
00254         // now add an entry to the array of long options structs.
00255         longopts[l].name = i->second.argName.c_str();
00256         if(i->second.isFlag)
00257             longopts[l].has_arg = no_argument;
00258         else
00259             longopts[l].has_arg = required_argument;
00260         longopts[l].flag = NULL;
00261         longopts[l].val = 0;
00262         l++;
00263 #endif
00264 
00265     }
00266 
00267 #ifdef HAVE_GETOPT_LONG
00268     // terminate array of long option structs
00269     memset(&longopts[l], 0, sizeof(LongOptionStruct));
00270 #endif
00271 
00272     // finally, use getopt() and getopt_long() to check command line arguments:
00273     int *indexp = new int;
00274     *indexp = -1;
00275     char o = 0;
00276     while( (o = 
00277 #ifdef HAVE_GETOPT_LONG
00278             getopt_long(argc, argv, optstring.c_str(), longopts, indexp) 
00279 #else
00280             getopt(argc, argv, optstring.c_str())
00281 #endif
00282      ) != -1 ) {
00283 
00284         if(o == 1) {        // random text given with out preceding flags
00285             printHelp();
00286             exit(-1);
00287         }
00288 
00289         ArgInfo* ai = 0;
00290 #ifdef HAVE_GETOPT_LONG
00291         if(o == 0) {
00292             assert(indexp);
00293             assert(*indexp > 0);
00294             ai = &(args[longopts[*indexp].name]);
00295         } else 
00296 #endif
00297         {
00298             if(checkBuiltinArgs(o, optarg))
00299                 continue;
00300             assert(shortArgs.count(o) > 0);   
00301             ai = shortArgs[o]; 
00302         }
00303         assert(ai);
00304         ai->given = true;
00305         if(!(ai->isFlag) && (optarg != 0)) {
00306             ai->value = strdup(optarg);
00307         }
00308     }
00309 
00310     delete indexp;
00311 #ifdef HAVE_GETOPT_LONG
00312     free(longopts);
00313 #endif
00314 
00315 }
00316 
00317 void VOSDaemon::printHelp() {
00318     bool have_long;
00319 #ifdef HAVE_GETOPT_LONG
00320     have_long = true;
00321 #else
00322     have_long = false;
00323 #endif
00324 
00325     cout << "Usage:\n\t" << programName << " [options]\nOptions:\n";
00326 
00327 #ifdef HAVE_FORK
00328     cout << "\t-N\tDon't run in the background.\n";
00329 #endif
00330 
00331     cout << "\t-h " << (have_long?"or --help":"") << "\tPrint a brief help message.\n" <<
00332         "\t-L <FILENAME>" << (have_long?" or --log <FILENAME>":"") << "\tLog to file <FILENAME>.  (default is /var/log/" << programName <<")  Use \"-L -\" to log to stdout (-N is implied in this case).\n" <<
00333         "\t-E <LEVEL>" << (have_long?" or --loglevel <LEVEL>":"") << "\tSet log level to <LEVEL> (default is " << Log::getDefaultLevel() <<").\n";
00334 
00335     /*
00336     if(mayNameRoot)
00337         cout << "\t-n <NAME>" << (have_long?" or --name <NAME>":"") << "\tName the root object <NAME> (default is \"" << rootName << "\")\n";
00338     if(mayLoad)
00339         cout << "\t-l <FILE>" << (have_long?" or --load <FILE>":"") << "\tLoad objects from vop script <FILE>.\n";
00340 #ifdef HAVE_SIGNAL
00341     if(maySave)
00342         cout << "\t-s <FILE>" << (have_long?" or --save <FILE>":"") << "\tSave state to vop script <FILE> when SIGINT, SIGTERM, SIGHUP, or SIGABRT signals are recieved or at a given frequency (see below). You can kill the program without saving by using SIGQUIT (CTRL-\\). Default save file is \"" << saveFile << "\"\n" <<
00343                 "\t-S <FREQ>" << (have_long?" or --save-freq <FREQ>":"") << "\tUsed in combination with -s, specifies frequency to save world ntate to disk. Default frequency is \"" << saveFreq << "\" sec\n";
00344 #endif
00345     */
00346 
00347     for(ArgMap::const_iterator i = args.begin(); i != args.end(); i++) {
00348         cout << "\t-" << i->second.argChar;
00349         cout << " " << i->second.valueLabel;   // will be "" if it's a flag
00350         if(have_long) {
00351             cout << " or --" << i->second.argName;
00352             cout << " " << i->second.valueLabel;   // will be "" if it's a flag
00353         }
00354         cout << "\t" << i->second.description << endl;
00355     }
00356     if(have_long)
00357         cout << "\nNote that long argument names may not be available on all platforms.\n";
00358 }
00359 
00360 bool VOSDaemon::checkBuiltinArgs(char o, char* val) {
00361     switch(o) {
00362         case '?':
00363         case 'h':
00364             printHelp();
00365             exit(0);
00366         case 'E':
00367             Log::setDefaultLevel( atoi(val) );
00368             return true;
00369         case 'L':
00370             if(logFile)
00371                 free(logFile);
00372             logFile = strdup(val);
00373             return true;
00374         /*
00375         case 'n':
00376             rootName = strdup(val);
00377             return true;
00378         case 'l':
00379             loadfile = strdup(val);
00380             return true;
00381         case 's':
00382             saveFile = strdup(val);
00383             return true;
00384         case 'S':
00385             saveFreq = atoi(val);
00386             return true;
00387         */
00388         case 'N':
00389             dontFork = true;
00390             return true;
00391     }
00392     return false;
00393 }
00394 
00395 
00396 void VOSDaemon::run() {
00397 
00398     // Send log messages to a file, if requested
00399     if(!strcmp(logFile, "-")) {
00400         Log::setDefaultOutputStream(&cout);
00401         dontFork = true;
00402     } else {
00403         ofstream* l = new ofstream(logFile, ofstream::app);
00404         if(l->bad()) {
00405             cerr << "Warning: could not open log file (" << logFile << "). No logging will occur." << endl;
00406             Log::setDefaultLevel(0);
00407             logStream = 0;
00408             fclose(stdout);
00409             fclose(stderr);
00410             fclose(stdin);
00411         } else {
00412             Log::setDefaultOutputStream(l);
00413             //cerr = cout = clog = *l; STLPort doesn't like this
00414             logStream = l;
00415 
00416             // XXX this is not the best way to do it, but it is OK for now:
00417             // streams should be set non-buffered, or some way of relaying
00418             // stdout->cout and stderr->cerr should be found
00419             // (streambuf::sync_with_stdio(true)??
00420             freopen(logFile, "a", stdout);
00421             freopen(logFile, "a", stderr);
00422             fclose(stdin);
00423         }
00424     }
00425 
00426 
00427 #if 0
00428     try {
00429         // load from disk?
00430         if(loadfile) {
00431             LOG("VOSDaemon", 0, "Oops, loading is not implemented.");
00432             /*
00433             LOG(programName, 2, "Loading \"" << loadfile << "\"...");
00434             RemoteStreamSite::runScript(string(loadfile), *this);
00435             LOG(programName, 2, "Done.");
00436             */
00437         }
00438     } catch(exception& e) {
00439         LOG(programName, 0, "Error: " << e.what() );
00440         exit(-1);
00441     }
00442 #endif
00443 
00444 #ifdef HAVE_SIGNAL
00445     // set signal hanlers
00446     /*
00447     _daemon = this;
00448     if(saveFile) {
00449         signal(SIGABRT, _VOSDaemon_killsignal);
00450         signal(SIGINT, _VOSDaemon_killsignal);
00451         signal(SIGTERM, _VOSDaemon_killsignal);
00452         signal(SIGHUP, _VOSDaemon_hupsignal);
00453     }
00454     */
00455 #endif
00456 
00457 
00458     /* Set periodic autosave */
00459     /*
00460     if(saveFreq > 0) {
00461         site->addCallback(_VOSDaemon_autosave, this, saveFreq, true);
00462     }
00463     */
00464 
00465 #ifdef HAVE_FORK
00466     if(!dontFork) {
00467         // fork
00468         int pid;
00469         pid = fork();
00470         if (pid == -1)  {
00471             cerr << "Error forking daemon!\n";
00472             exit(-2);
00473         }
00474         if (pid != 0) {   // exit parent process
00475             cerr << "Running in background with PID " << pid << endl;
00476             exit(0);
00477         }
00478     }
00479 #else
00480 //#warning HAVE_FORK was not defined, so daemons will not run in the background.
00481 #endif
00482 
00483 
00484     // Run
00485     try {
00486         LOG(programName, 3, "Site running... ");
00487         if(rootObj) LOG(programName, 1, "Root object is: " << rootObj->getURL().getString() );
00488         while(true)  {
00489             site->flushIncomingBuffers();
00490             loop();
00491         }
00492     } catch(exception& e) {
00493         LOG(programName, 0, "Error: " << e.what() << " (" << typeid(e).name() << ")" );
00494         exit(-1);
00495     }
00496 
00497 }
00498 
00499 
00500 void VOSDaemon::save() {
00501     LOG("VOSDaemon", 0, "Oops, saving is not implemented.");
00502 /*
00503     if(_VOSDaemon_rootObj && _VOSDaemon_saveFile) {
00504         LOG(_VOSDaemon_progName, 2, "Saving world state to \"" << _VOSDaemon_saveFile << "\"...");
00505         MessageBlock block;
00506         _VOSDaemon_rootObj->saveState(block, true, false, true, true );
00507         FILE* f = fopen(_VOSDaemon_saveFile, "w");
00508         if(!f) {
00509             LOG(_VOSDaemon_progName, 0, "Error saving world state: could not open file \"" << _VOSDaemon_saveFile << "\" for writing.");
00510             exit(-1);
00511         }
00512         pREF(Message*, m, new Message(),
00513             m->setType("message");
00514             m->setMethod("core:hello");
00515             fwrite(m->getFormattedString().c_str(), m->getFormattedString().length(), 1, f);
00516             fwrite("\n", 1, 1, f);
00517         );
00518         fwrite(block.getString().c_str(), block.getString().length(), 1, f);
00519         fclose(f);
00520     }
00521 */
00522 }
00523 
00524     
00525 
00526 void VOSDaemon::addArg(string argName, string valueLabel, string description, char argChar, string envVar) {
00527     args[argName].argName = argName;
00528     args[argName].valueLabel = valueLabel;
00529     args[argName].description = description;
00530     args[argName].argChar = argChar;
00531     args[argName].isFlag = false;
00532     args[argName].given = false;
00533     args[argName].value = "";
00534     args[argName].envVar = envVar;
00535     if(argChar != 0)
00536         shortArgs[argChar] = &args[argName];
00537 }
00538 
00539 void VOSDaemon::addArg(string argName, string valueLabel, string description, string defaultValue, char argChar, string envVar) {
00540     addArg(argName, valueLabel, description, argChar, envVar);
00541     args[argName].value = defaultValue;
00542     args[argName].given = true;
00543 }
00544 
00545 void VOSDaemon::addFlag(string name, string descrip, char flagch, string envVar) {
00546     addArg(name, "", descrip, flagch, envVar);
00547     args[name].isFlag = true;
00548 }
00549 
00550 
00551 string VOSDaemon::getArg(string name) {
00552     if(args.count(name) == 0)
00553         throw runtime_error("No such argument registered. use addArg().");
00554     if(!args[name].given)
00555         throw runtime_error("Argument not given.");
00556     return args[name].value;
00557 }
00558 
00559 bool VOSDaemon::argGiven(string argName) {
00560     if(args.count(argName) == 0)
00561         throw runtime_error("No such argument registered. use addArg().");
00562     return args[argName].given;
00563 }
00564 
00565 void VOSDaemon::debugArgs() {
00566     for(ArgMap::const_iterator i = args.begin(); i != args.end(); i++) {
00567         cerr << "Argument name=" << i->second.argName << endl;
00568         cerr << "\tvalueLabel=" << i->second.valueLabel << endl;
00569         cerr << "\tvalue=" << i->second.value << endl;
00570         cerr << "\targChar=" << i->second.argChar << endl;
00571         cerr << "\tisFlag=" << ((i->second.isFlag)?"true":"false") << endl;
00572         cerr << "\tdescription=" << i->second.description << endl;
00573         cerr << "\tgiven=" << ((i->second.given)?"true":"false") << endl;
00574         cerr << "\tenvVar=" << i->second.envVar << endl;
00575     }
00576 }
00577 
00578 
00579 
00580 

Generated on Tue Aug 12 03:55:37 2003 for Interreality Project - VOS by doxygen 1.3.2