Various buildsystem improvements:
[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 defined(HAVE_EXECINFO) and not defined(Q_OS_MAC)
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 defined(HAVE_EXECINFO) and not defined(Q_OS_MAC)
70 void handle_crash(int sig) {
71   Q_UNUSED(sig)
72   void* callstack[128];
73   int i, frames = backtrace(callstack, 128);
74
75   QFile dumpFile(QString("Quassel-Crash-%1").arg(QDateTime::currentDateTime().toString("yyyyMMdd-hhmm.log")));
76   dumpFile.open(QIODevice::WriteOnly);
77   QTextStream dumpStream(&dumpFile);
78
79   for (i = 0; i < frames; ++i) {
80     Dl_info info;
81     dladdr (callstack[i], &info);
82     // as a reference:
83     //     typedef struct
84     //     {
85     //       __const char *dli_fname;   /* File name of defining object.  */
86     //       void *dli_fbase;           /* Load address of that object.  */
87     //       __const char *dli_sname;   /* Name of nearest symbol.  */
88     //       void *dli_saddr;           /* Exact value of nearest symbol.  */
89     //     } Dl_info;
90
91 #if __LP64__
92     int addrSize = 16;
93 #else
94     int addrSize = 8;
95 #endif
96
97     QString funcName;
98     if(info.dli_sname) {
99       char *func = abi::__cxa_demangle(info.dli_sname, 0, 0, 0);
100       if(func) {
101         funcName = QString(func);
102         free(func);
103       } else {
104         funcName = QString(info.dli_sname);
105       }
106     } else {
107       funcName = QString("0x%1").arg((long)info.dli_saddr, addrSize, QLatin1Char('0'));
108     }
109
110     // prettificating the filename
111     QString fileName("???");
112     if(info.dli_fname) {
113       fileName = QString(info.dli_fname);
114       int slashPos = fileName.lastIndexOf('/');
115       if(slashPos != -1)
116         fileName = fileName.mid(slashPos + 1);
117       if(fileName.count() < 20)
118         fileName += QString(20 - fileName.count(), ' ');
119     }
120
121     QString debugLine = QString("#%1 %2 0x%3 %4").arg(i, 3, 10)
122       .arg(fileName)
123       .arg((long)(callstack[i]), addrSize, 16, QLatin1Char('0'))
124       .arg(funcName);
125
126     dumpStream << debugLine << "\n";
127     qDebug() << qPrintable(debugLine);
128   }
129   dumpFile.close();
130   exit(27);
131 }
132 #endif // #if defined(HAVE_EXECINFO) and not defined(Q_OS_MAC)
133
134
135 int main(int argc, char **argv) {
136   // We catch SIGTERM and SIGINT (caused by Ctrl+C) to graceful shutdown Quassel.
137   signal(SIGTERM, handle_signal);
138   signal(SIGINT, handle_signal);
139
140 #if defined(HAVE_EXECINFO) and not defined(Q_OS_MAC)
141   signal(SIGABRT, handle_crash);
142   signal(SIGBUS, handle_crash);
143   signal(SIGSEGV, handle_crash);
144 #endif // #if defined(HAVE_EXECINFO) and not defined(Q_OS_MAC)
145   
146   Global::registerMetaTypes();
147   Global::setupVersion();
148
149 /*
150 #if defined BUILD_CORE
151   Global::runMode = Global::CoreOnly;
152   QCoreApplication app(argc, argv);
153 #elif defined BUILD_QTUI
154   Global::runMode = Global::ClientOnly;
155   QApplication app(argc, argv);
156 #else
157   Global::runMode = Global::Monolithic;
158   QApplication app(argc, argv);
159 #endif
160 */
161 #if defined BUILD_CORE
162   Global::runMode = Global::CoreOnly;
163   QCoreApplication app(argc, argv);
164 #elif defined BUILD_QTUI
165   Global::runMode = Global::ClientOnly;
166   QtUiApplication app(argc, argv);
167 #else
168   Global::runMode = Global::Monolithic;
169   QtUiApplication app(argc, argv);
170 #endif
171
172   Global::parser = CliParser(QCoreApplication::arguments());
173
174 #ifndef BUILD_QTUI
175 // put core-only arguments here
176   Global::parser.addOption("port",'p',"The port quasselcore will listen at",QString("4242"));
177   Global::parser.addSwitch("norestore", 'n', "Don't restore last core's state");
178   Global::parser.addOption("logfile",'l',"Path to logfile");
179   Global::parser.addOption("loglevel",'L',"Loglevel Debug|Info|Warning|Error","Info");
180   Global::parser.addOption("datadir", 0, "Specify the directory holding datafiles like the Sqlite DB and the SSL Cert");
181 #endif // BUILD_QTUI
182 #ifndef BUILD_CORE
183 // put client-only arguments here
184   Global::parser.addSwitch("debugbufferswitches",0,"Enables debugging for bufferswitches");
185   Global::parser.addSwitch("debugmodel",0,"Enables debugging for models");
186 #endif // BUILD_QTCORE
187 // put shared client&core arguments here
188   Global::parser.addSwitch("debug",'d',"Enable debug output");
189   Global::parser.addSwitch("help",'h', "Display this help and exit");
190
191   if(!Global::parser.parse() || Global::parser.isSet("help")) {
192     Global::parser.usage();
193     return 1;
194   }
195
196   /*
197    This is an initial check if logfile is writable since the warning would spam stdout if done
198    in current Logger implementation. Can be dropped whenever the logfile is only opened once.
199   */
200   if(Global::runMode != Global::ClientOnly) {
201     QFile logFile;
202     if(!Global::parser.value("logfile").isEmpty()) {
203       logFile.setFileName(Global::parser.value("logfile"));
204       if(!logFile.open(QIODevice::Append | QIODevice::Text))
205         qWarning("Warning: Couldn't open logfile '%s' - will log to stdout instead",qPrintable(logFile.fileName()));
206       else logFile.close();
207     }
208   }
209
210   qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));
211
212   // Set up i18n support
213   QLocale locale = QLocale::system();
214
215   QTranslator qtTranslator(&app);
216   qtTranslator.setObjectName("QtTr");
217   qtTranslator.load(QString(":i18n/qt_%1").arg(locale.name()));
218   app.installTranslator(&qtTranslator);
219
220   QTranslator quasselTranslator(&app);
221   quasselTranslator.setObjectName("QuasselTr");
222   quasselTranslator.load(QString(":i18n/quassel_%1").arg(locale.name()));
223   app.installTranslator(&quasselTranslator);
224
225   Network::setDefaultCodecForServer("ISO-8859-1");
226   Network::setDefaultCodecForEncoding("UTF-8");
227   Network::setDefaultCodecForDecoding("ISO-8859-15");
228
229   QCoreApplication::setOrganizationDomain("quassel-irc.org");
230   QCoreApplication::setApplicationName("Quassel IRC");
231   QCoreApplication::setOrganizationName("Quassel Project");
232
233   
234 #ifndef BUILD_QTUI
235   Core::instance();  // create and init the core
236 #endif
237
238   //Settings::init();
239
240 #ifndef BUILD_CORE
241   // session resume
242   QtUi *gui = new QtUi();
243   Client::init(gui);
244   // init gui only after the event loop has started
245   QTimer::singleShot(0, gui, SLOT(init()));
246   //gui->init();
247 #endif
248
249 #ifndef BUILD_QTUI
250   if(!Global::parser.isSet("norestore")) {
251     Core::restoreState();
252   }
253 #endif
254
255 #ifndef BUILD_CORE 
256   app.resumeSessionIfPossible();
257 #endif
258   
259   int exitCode = app.exec();
260
261 #ifndef BUILD_QTUI
262   Core::saveState();
263 #endif
264
265 #ifndef BUILD_CORE
266   // the mainWin has to be deleted before the Core
267   // if not Quassel will crash on exit under certain conditions since the gui
268   // still wants to access clientdata
269   delete gui;
270   Client::destroy();
271 #endif
272 #ifndef BUILD_QTUI
273   Core::destroy();
274 #endif
275
276   return exitCode;
277 }