5ce1ac9ccaf33cf0f31e19b5e6d2e3b7c3798180
[quassel.git] / src / common / main.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-08 by the Quassel Project                          *
3  *   devel@quassel-irc.org                                                 *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) version 3.                                           *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
19  ***************************************************************************/
20
21 #include <QDateTime>
22 #include <QString>
23 #include <QTimer>
24 #include <QTranslator>
25 #include <QFile>
26
27 #include "global.h"
28 #include "logger.h"
29 #include "network.h"
30 #include "settings.h"
31 #include "cliparser.h"
32
33 #if defined BUILD_CORE
34 #include <QDir>
35 #include "core.h"
36 #include "message.h"
37
38 #elif defined BUILD_QTUI
39 #include "client.h"
40 #include "qtuiapplication.h"
41 #include "qtui.h"
42
43 #elif defined BUILD_MONO
44 #include "client.h"
45 #include "core.h"
46 #include "coresession.h"
47 #include "qtuiapplication.h"
48 #include "qtui.h"
49
50 #else
51 #error "Something is wrong - you need to #define a build mode!"
52 #endif
53
54
55 #include <signal.h>
56
57 #if not defined(Q_OS_WIN32) && not defined(MAC_10_4_SDK)
58 #include <execinfo.h>
59 #include <dlfcn.h>
60 #include <cxxabi.h>
61 #endif
62
63 //! Signal handler for graceful shutdown.
64 void handle_signal(int sig) {
65   qWarning("%s", qPrintable(QString("Caught signal %1 - exiting.").arg(sig)));
66   QCoreApplication::quit();
67 }
68
69 #if not defined(Q_OS_WIN32) && not defined(MAC_10_4_SDK)
70 void handle_crash(int sig) {
71   void* callstack[128];
72   int i, frames = backtrace(callstack, 128);
73
74   QFile dumpFile(QString("Quassel-Crash-%1").arg(QDateTime::currentDateTime().toString("yyyyMMdd-hhmm.log")));
75   dumpFile.open(QIODevice::WriteOnly);
76   QTextStream dumpStream(&dumpFile);
77
78   for (i = 0; i < frames; ++i) {
79     Dl_info info;
80     dladdr (callstack[i], &info);
81     // as a reference:
82     //     typedef struct
83     //     {
84     //       __const char *dli_fname;   /* File name of defining object.  */
85     //       void *dli_fbase;           /* Load address of that object.  */
86     //       __const char *dli_sname;   /* Name of nearest symbol.  */
87     //       void *dli_saddr;           /* Exact value of nearest symbol.  */
88     //     } Dl_info;
89
90 #if __LP64__
91     int addrSize = 16;
92 #else
93     int addrSize = 8;
94 #endif
95
96     QString funcName;
97     if(info.dli_sname) {
98       char *func = abi::__cxa_demangle(info.dli_sname, 0, 0, 0);
99       if(func) {
100         funcName = QString(func);
101         free(func);
102       } else {
103         funcName = QString(info.dli_sname);
104       }
105     } else {
106       funcName = QString("0x%1").arg((long)info.dli_saddr, addrSize, QLatin1Char('0'));
107     }
108
109     // prettificating the filename
110     QString fileName("???");
111     if(info.dli_fname) {
112       fileName = QString(info.dli_fname);
113       int slashPos = fileName.lastIndexOf('/');
114       if(slashPos != -1)
115         fileName = fileName.mid(slashPos + 1);
116       if(fileName.count() < 20)
117         fileName += QString(20 - fileName.count(), ' ');
118     }
119
120     QString debugLine = QString("#%1 %2 0x%3 %4").arg(i, 3, 10)
121       .arg(fileName)
122       .arg((long)(callstack[i]), addrSize, 16, QLatin1Char('0'))
123       .arg(funcName);
124
125     dumpStream << debugLine << "\n";
126     qDebug() << qPrintable(debugLine);
127   }
128   dumpFile.close();
129   exit(27);
130 }
131 #endif // ifndef Q_OS_WIN32
132
133
134 int main(int argc, char **argv) {
135   // We catch SIGTERM and SIGINT (caused by Ctrl+C) to graceful shutdown Quassel.
136   signal(SIGTERM, handle_signal);
137   signal(SIGINT, handle_signal);
138
139 #if not defined(Q_OS_WIN32) && not defined(MAC_10_4_SDK)
140   signal(SIGABRT, handle_crash);
141   signal(SIGBUS, handle_crash);
142   signal(SIGSEGV, handle_crash);
143 #endif // ndef Q_OS_WIN32
144   
145   Global::registerMetaTypes();
146   Global::setupVersion();
147
148 /*
149 #if defined BUILD_CORE
150   Global::runMode = Global::CoreOnly;
151   QCoreApplication app(argc, argv);
152 #elif defined BUILD_QTUI
153   Global::runMode = Global::ClientOnly;
154   QApplication app(argc, argv);
155 #else
156   Global::runMode = Global::Monolithic;
157   QApplication app(argc, argv);
158 #endif
159 */
160 #if defined BUILD_CORE
161   Global::runMode = Global::CoreOnly;
162   QCoreApplication app(argc, argv);
163 #elif defined BUILD_QTUI
164   Global::runMode = Global::ClientOnly;
165   QtUiApplication app(argc, argv);
166 #else
167   Global::runMode = Global::Monolithic;
168   QtUiApplication app(argc, argv);
169 #endif
170
171
172
173   Global::parser = CliParser(QCoreApplication::arguments());
174
175 #ifndef BUILD_QTUI
176 // put core-only arguments here
177   Global::parser.addOption("port",'p',"The port quasselcore will listen at",QString("4242"));
178   Global::parser.addSwitch("norestore", 'n', "Don't restore last core's state");
179   Global::parser.addOption("logfile",'l',"Path to logfile");
180   Global::parser.addOption("loglevel",'L',"Loglevel Debug|Info|Warning|Error","Info");
181   Global::parser.addOption("datadir", 0, "Specify the directory holding datafiles like the Sqlite DB and the SSL Cert");
182 #endif // BUILD_QTUI
183 #ifndef BUILD_CORE
184 // put client-only arguments here
185   Global::parser.addSwitch("debugbufferswitches",0,"Enables debugging for bufferswitches");
186   Global::parser.addSwitch("debugmodel",0,"Enables debugging for models");
187 #endif // BUILD_QTCORE
188 // put shared client&core arguments here
189   Global::parser.addSwitch("debug",'d',"Enable debug output");
190   Global::parser.addSwitch("help",'h', "Display this help and exit");
191
192   if(!Global::parser.parse() || Global::parser.isSet("help")) {
193     Global::parser.usage();
194     return 1;
195   }
196
197   /*
198    This is an initial check if logfile is writable since the warning would spam stdout if done
199    in current Logger implementation. Can be dropped whenever the logfile is only opened once.
200   */
201   if(Global::runMode != Global::ClientOnly) {
202     QFile logFile;
203     if(!Global::parser.value("logfile").isEmpty()) {
204       logFile.setFileName(Global::parser.value("logfile"));
205       if(!logFile.open(QIODevice::Append | QIODevice::Text))
206         qWarning("Warning: Couldn't open logfile '%s' - will log to stdout instead",qPrintable(logFile.fileName()));
207       else logFile.close();
208     }
209   }
210
211   qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));
212
213   // Set up i18n support
214   QLocale locale = QLocale::system();
215
216   QTranslator qtTranslator(&app);
217   qtTranslator.setObjectName("QtTr");
218   qtTranslator.load(QString(":i18n/qt_%1").arg(locale.name()));
219   app.installTranslator(&qtTranslator);
220
221   QTranslator quasselTranslator(&app);
222   quasselTranslator.setObjectName("QuasselTr");
223   quasselTranslator.load(QString(":i18n/quassel_%1").arg(locale.name()));
224   app.installTranslator(&quasselTranslator);
225
226   Network::setDefaultCodecForServer("ISO-8859-1");
227   Network::setDefaultCodecForEncoding("UTF-8");
228   Network::setDefaultCodecForDecoding("ISO-8859-15");
229
230   QCoreApplication::setOrganizationDomain("quassel-irc.org");
231   QCoreApplication::setApplicationName("Quassel IRC");
232   QCoreApplication::setOrganizationName("Quassel Project");
233
234   
235 #ifndef BUILD_QTUI
236   Core::instance();  // create and init the core
237 #endif
238
239   //Settings::init();
240
241 #ifndef BUILD_CORE
242   // session resume
243   QtUi *gui = new QtUi();
244   Client::init(gui);
245   // init gui only after the event loop has started
246   QTimer::singleShot(0, gui, SLOT(init()));
247   //gui->init();
248 #endif
249
250 #ifndef BUILD_QTUI
251   if(!Global::parser.isSet("norestore")) {
252     Core::restoreState();
253   }
254 #endif
255
256 #ifndef BUILD_CORE 
257   app.resumeSessionIfPossible();
258 #endif
259   
260   int exitCode = app.exec();
261
262 #ifndef BUILD_QTUI
263   Core::saveState();
264 #endif
265
266 #ifndef BUILD_CORE
267   // the mainWin has to be deleted before the Core
268   // if not Quassel will crash on exit under certain conditions since the gui
269   // still wants to access clientdata
270   delete gui;
271   Client::destroy();
272 #endif
273 #ifndef BUILD_QTUI
274   Core::destroy();
275 #endif
276
277   return exitCode;
278 }