hydranode.cpp

Go to the documentation of this file.
00001 /**
00002  *  Copyright (C) 2004-2005 Alo Sarv <madcat_@users.sourceforge.net>
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) 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  *  You should have received a copy of the GNU General Public License
00015  *  along with this program; if not, write to the Free Software
00016  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00017  */
00018 
00019 /** \file hydranode.cpp Implementation of HydraNode class. */
00020 
00021 #include <hn/hnprec.h>
00022 #include <hn/hydranode.h>                      // Interface
00023 #include <hn/prefs.h>                          // Configuration Subsystem init
00024 #include <hn/log.h>                            // Logging subsystem init
00025 #include <hn/modules.h>                        // Modules subsystem init
00026 #include <hn/sockets.h>                        // Sockets API init
00027 #include <hn/schedbase.h>                      // Networking Scheduler
00028 #include <hn/fileslist.h>                      // FMS init
00029 #include <hn/metadb.h>                         // MetaDb init
00030 #include <boost/filesystem/operations.hpp>     // Path operations for config
00031 #include <boost/date_time/posix_time/posix_time.hpp> // for ptime
00032 
00033 /**
00034  * @name Initialized from main(), these are passed from process starter.
00035  */
00036 //@{
00037 static char **s_argv;
00038 static int s_argc;
00039 //@}
00040 
00041 static const uint32_t METADB_SAVETIME = 60*1000*10; //!< 10 minutes
00042 
00043 // HydraNode class
00044 // ---------------
00045 IMPLEMENT_EVENT_TABLE(HydraNode, HydraNode*, HNEvent);
00046 
00047 //! Constructors/Destructors. Note: Don't do initialization here. That's what
00048 //! run() member function is for.
00049 HydraNode::HydraNode() : Object(0, "HydraNode"), m_running(false) {}
00050 HydraNode::~HydraNode() {}
00051 
00052 HydraNode& HydraNode::instance() {
00053         static HydraNode hn;
00054         return hn;
00055 }
00056 
00057 // Higher-level app entry point - do initialization here.
00058 void HydraNode::init(int argc, char **argv) {
00059         s_argc = argc;
00060         s_argv = argv;
00061         boost::filesystem::path::default_name_check(boost::filesystem::native);
00062 
00063         m_buildDate = Utils::getModDate(argv[0]);
00064 
00065         logMsg("Initializing HydraNode Core...");
00066 
00067         HydraNode::getEventTable().addHandler(this, this, &HydraNode::onEvent);
00068 
00069         logMsg(" * Initializing Configuration...");
00070         Log::instance().addPreStr("     ");
00071         initConfig();
00072         Log::instance().remPreStr("     ");
00073 
00074         logMsg(" * Initializing Logging...");
00075         Log::instance().addPreStr("     ");
00076         initLog();
00077         Log::instance().remPreStr("     ");
00078 
00079         logMsg(" * Initializing Networking...");
00080         Log::instance().addPreStr("     ");
00081         initSockets();
00082         Log::instance().remPreStr("     ");
00083 
00084         logMsg(" * Initializing Shared Files List...");
00085         Log::instance().addPreStr("     ");
00086         initFiles();
00087         Log::instance().remPreStr("     ");
00088 
00089         logMsg(" * Initializing Modules...");
00090         Log::instance().addPreStr("     ");
00091         initModules();
00092         Log::instance().remPreStr("     ");
00093 
00094         m_running = true;
00095         logMsg("HydraNode Core is up and running.");
00096 }
00097 
00098 int HydraNode::run(int argc, char **argv) {
00099         init(argc, argv);
00100         mainLoop();
00101         return cleanup();
00102 }
00103 
00104 int HydraNode::cleanup() {
00105         logMsg("Exiting HydraNode Core...");
00106 
00107         ModManager::instance().onExit();
00108         SchedBase::instance().exit();
00109 
00110         logMsg("Saving configuration...");
00111         Prefs::instance().save();
00112 
00113         logMsg("Saving temp files...");
00114         FilesList::instance().savePartFiles();
00115 
00116         logMsg("Saving MetaDb...");
00117         std::ofstream metadb(
00118                 (getConfigDir()/"metadb.dat").string().c_str(),
00119                 std::ios::binary
00120         );
00121         metadb << MetaDb::instance();
00122 
00123         logMsg("HydraNode exited cleanly.");
00124 
00125         return 0;
00126 }
00127 
00128 boost::filesystem::path HydraNode::checkCreateDir(
00129         boost::filesystem::path p, const std::string &alt
00130 ) {
00131         using namespace boost::filesystem;
00132         if (!exists(p)) try {
00133                 create_directory(p);
00134                 return p;
00135         } catch (std::exception&) {
00136                 p = path(s_argv[0], native);
00137                 p /= alt;
00138                 if (!exists(p)) try {
00139                         create_directory(p);
00140                 } catch (std::exception &e) {
00141                         logError(
00142                                 boost::format(
00143                                         "Unable to create directory %s: %s"
00144                                 ) % p.string() % e.what()
00145                         );
00146                 }
00147                 return p;
00148         }
00149         return p;
00150 }
00151 
00152 // On Win32, we use current working directory, plus "config" path. The
00153 // idea is to have the app installed in Program Files\Hydranode, and
00154 // have config dir at same point. On POSIX systems, we use system home
00155 // directory and hidden subdir ".hydranode" there, as is standard on
00156 // those platforms.
00157 void HydraNode::initConfig() {
00158         using boost::filesystem::path;
00159         using boost::filesystem::native;
00160         path confdir;
00161 #ifdef WIN32
00162         confdir = path(s_argv[0], native);
00163         confdir = confdir.branch_path();
00164         confdir /= "config";
00165 #else
00166         char *home = getenv("HOME");
00167         if (home == 0) {
00168                 // No HOME environment variable, use subdir from cwd
00169                 confdir = path(s_argv[0], native);
00170                 confdir /= "config";
00171         } else {
00172                 // Hidden subdir from $(HOME)
00173                 confdir = path(home, native);
00174                 confdir /= ".hydranode";
00175         }
00176 #endif
00177         m_confDir = checkCreateDir(confdir, "config");
00178         Prefs::instance().load((m_confDir/"config.ini").string());
00179         path tmpDir = path(
00180                 Prefs::instance().read<std::string>("Temp", ""), native
00181         );
00182         path incDir = path(
00183                 Prefs::instance().read<std::string>("Incoming",""), native
00184         );
00185         if (tmpDir.empty()) {
00186                 tmpDir = m_confDir/"temp";
00187         }
00188         if (incDir.empty()) {
00189                 incDir = m_confDir/"incoming";
00190         }
00191         tmpDir = checkCreateDir(tmpDir, "temp");
00192         incDir = checkCreateDir(incDir, "incoming");
00193         Prefs::instance().write("Temp", tmpDir.native_directory_string());
00194         Prefs::instance().write("Incoming", incDir.native_directory_string());
00195         logMsg(
00196                 boost::format("Configuration directory:   %s")
00197                 % m_confDir.string()
00198         );
00199         logMsg(
00200                 boost::format("Temporary files directory: %s") % tmpDir.string()
00201         );
00202         logMsg(
00203                 boost::format("Incoming files directory:  %s") % incDir.string()
00204         );
00205 }
00206 
00207 // Initialize logging-related systems
00208 void HydraNode::initLog() {
00209         using boost::filesystem::exists;
00210         using boost::filesystem::rename;
00211         using boost::filesystem::remove;
00212 
00213         std::string newName("hydranode.log"), oldName("hydranode.log.old");
00214         // if hydranode.log exists, rename to .old (removing previous .old)
00215         if (exists(getConfigDir()/newName)) {
00216                 if (exists(getConfigDir()/oldName)) {
00217                         remove(getConfigDir()/oldName);
00218                 }
00219                 rename(getConfigDir()/newName, getConfigDir()/oldName);
00220         }
00221         Log::instance().addLogFile(
00222                 (getConfigDir()/newName).string()
00223         );
00224         using namespace boost;
00225         posix_time::ptime t1(posix_time::second_clock::local_time());
00226         logMsg(boost::format("Logging started on %s.") % t1);
00227         logMsg(
00228                 boost::format("%s built on %s GMT.")
00229                 % getAppVerLong() % posix_time::from_time_t(m_buildDate)
00230         );
00231 }
00232 
00233 // Initialize networking
00234 void HydraNode::initSockets() {
00235         // Make sure these get initialized from main app, not from other thread
00236         // or - from some module.
00237         (void)SocketClient::getEventTable();
00238         (void)SocketServer::getEventTable();
00239         (void)UDPSocket::getEventTable();
00240         (void)SchedBase::instance();
00241 }
00242 
00243 void HydraNode::initFiles() {
00244         (void)FilesList::instance();
00245 
00246         std::ifstream metadb(
00247                 (getConfigDir()/"metadb.dat").string().c_str(),
00248                 std::ios::binary
00249         );
00250         MetaDb::instance().load(metadb);
00251         // 10-minute save interval
00252         HydraNode::getEventTable().postEvent(
00253                 this, EVT_SAVE_MDB, METADB_SAVETIME
00254         );
00255 
00256         // Get list of shared files dirs from prefs.
00257         Prefs::instance().setPath("/SharedDirs");
00258         uint32_t sharedCount = Prefs::instance().read("Count", 0);
00259 
00260         // Generate list of dirs to be added
00261         // This also has the added benefit of removing duplicate entries
00262         typedef std::map<std::string, bool> DirMap;
00263         DirMap dirs;
00264         while (sharedCount) {
00265                 boost::format key("Dir_%s");
00266                 key % sharedCount;
00267                 std::string d =Prefs::instance().read(key.str(), std::string());
00268                 bool rec = Prefs::instance().read(key.str()+"_recurse", false);
00269                 // Also ignore empty default values in case of count/value
00270                 // mismatch
00271                 if (!d.empty()) {
00272                         dirs.insert(std::make_pair(d, rec));
00273                 }
00274                 sharedCount--;
00275         }
00276         // Incoming dir is always shared
00277         Prefs::instance().setPath("/");
00278         std::string incDir = Prefs::instance().read<std::string>("Incoming","");
00279         dirs.insert(std::make_pair(incDir, false));
00280 
00281         Utils::StopWatch t;      // For performance testing/debugging
00282         for (DirMap::iterator i = dirs.begin(); i != dirs.end(); ++i) try {
00283                 FilesList::instance().addSharedDir((*i).first, (*i).second);
00284         } catch (std::exception &er) {
00285                 logError(
00286                         boost::format("Adding shared directory %s:")
00287                         % er.what()
00288                 );
00289         }
00290         logMsg(
00291                 boost::format("%d shared files loaded in %dms")
00292                 % FilesList::instance().size() % t
00293         );
00294         // Temp files
00295         std::string tmpDir = Prefs::instance().read<std::string>("Temp", "");
00296         FilesList::instance().addTempDir(tmpDir);
00297 }
00298 
00299 // Main event loop. Poll sockets, and handle events, until we exit. This isn't
00300 // very elegant really, but it'll do for now (e.g. until someone has a better
00301 // idea on how to do in a better way).
00302 int HydraNode::mainLoop() {
00303         EventMain::initialize();
00304         m_running = true;
00305         while (m_running) {
00306                 doLoop();
00307         }
00308         HydraNode::getEventTable().postEvent(this, EVT_EXIT);
00309         // Let ppl handle the EVT_EXIT too
00310         EventMain::instance().handlePending();
00311         return true;
00312 }
00313 
00314 void HydraNode::doLoop() {
00315         SocketWatcher::poll();
00316         EventMain::instance().handlePending();
00317 }
00318 
00319 void HydraNode::initModules() {
00320         ModManager::instance().onInit();
00321         std::string mod = Prefs::instance().read<std::string>(
00322                 "/LoadModules", "ed2k hnsh"
00323         );
00324         Prefs::instance().write("/LoadModules", mod);
00325         boost::tokenizer<> tok(mod);
00326         for (boost::tokenizer<>::iterator i = tok.begin(); i != tok.end(); ++i){
00327                 ModManager::instance().loadModule(*i);
00328         }
00329 }
00330 
00331 uint32_t HydraNode::getAppVer() const {
00332         return (APPVER_MAJOR << 17 | APPVER_MINOR << 10 || APPVER_PATCH << 7);
00333 }
00334 
00335 std::string HydraNode::getAppVerLong() const {
00336         return "HydraNode v0.1.0";
00337 }
00338 
00339 void HydraNode::onEvent(HydraNode*, HNEvent evt) {
00340         if (evt == EVT_SAVE_MDB) {
00341                 logMsg("Saving configuration...");
00342                 Prefs::instance().save();
00343                 logMsg("Saving temp files...");
00344                 FilesList::instance().savePartFiles();
00345                 logMsg("Saving MetaDb...");
00346                 std::ofstream metadb(
00347                         (getConfigDir()/"metadb.dat").string().c_str(),
00348                         std::ios::binary
00349                 );
00350                 metadb << MetaDb::instance();
00351                 // 10-minute interval
00352                 HydraNode::getEventTable().postEvent(
00353                         this, EVT_SAVE_MDB, METADB_SAVETIME
00354                 );
00355         }
00356 }