b5e205b8e6360a46c44dfede86778c6889e89b18
[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 #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   QtUiApplication app(argc, argv);
155 #else
156   Global::runMode = Global::Monolithic;
157   QtUiApplication app(argc, argv);
158 #endif
159
160   Global::parser = CliParser(QCoreApplication::arguments());
161
162 #ifndef BUILD_QTUI
163 // put core-only arguments here
164   Global::parser.addOption("port",'p',"The port quasselcore will listen at",QString("4242"));
165   Global::parser.addSwitch("norestore", 'n', "Don't restore last core's state");
166   Global::parser.addOption("logfile",'l',"Path to logfile");
167   Global::parser.addOption("loglevel",'L',"Loglevel Debug|Info|Warning|Error","Info");
168   Global::parser.addOption("datadir", 0, "Specify the directory holding datafiles like the Sqlite DB and the SSL Cert");
169 #endif // BUILD_QTUI
170 #ifndef BUILD_CORE
171 // put client-only arguments here
172   Global::parser.addSwitch("debugbufferswitches",0,"Enables debugging for bufferswitches");
173   Global::parser.addSwitch("debugmodel",0,"Enables debugging for models");
174 #endif // BUILD_QTCORE
175 // put shared client&core arguments here
176   Global::parser.addSwitch("debug",'d',"Enable debug output");
177   Global::parser.addSwitch("help",'h', "Display this help and exit");
178
179   if(!Global::parser.parse() || Global::parser.isSet("help")) {
180     Global::parser.usage();
181     return 1;
182   }
183
184   /*
185    This is an initial check if logfile is writable since the warning would spam stdout if done
186    in current Logger implementation. Can be dropped whenever the logfile is only opened once.
187   */
188   if(Global::runMode != Global::ClientOnly) {
189     QFile logFile;
190     if(!Global::parser.value("logfile").isEmpty()) {
191       logFile.setFileName(Global::parser.value("logfile"));
192       if(!logFile.open(QIODevice::Append | QIODevice::Text))
193         qWarning("Warning: Couldn't open logfile '%s' - will log to stdout instead",qPrintable(logFile.fileName()));
194       else logFile.close();
195     }
196   }
197
198   qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));
199
200   // Set up i18n support
201   QLocale locale = QLocale::system();
202
203   QTranslator qtTranslator(&app);
204   qtTranslator.setObjectName("QtTr");
205   qtTranslator.load(QString(":i18n/qt_%1").arg(locale.name()));
206   app.installTranslator(&qtTranslator);
207
208   QTranslator quasselTranslator(&app);
209   quasselTranslator.setObjectName("QuasselTr");
210   quasselTranslator.load(QString(":i18n/quassel_%1").arg(locale.name()));
211   app.installTranslator(&quasselTranslator);
212
213   Network::setDefaultCodecForServer("ISO-8859-1");
214   Network::setDefaultCodecForEncoding("UTF-8");
215   Network::setDefaultCodecForDecoding("ISO-8859-15");
216
217   QCoreApplication::setOrganizationDomain("quassel-irc.org");
218   QCoreApplication::setApplicationName("Quassel IRC");
219   QCoreApplication::setOrganizationName("Quassel Project");
220
221
222 #ifndef BUILD_QTUI
223   Core::instance();  // create and init the core
224 #endif
225
226   //Settings::init();
227
228 #ifndef BUILD_CORE
229   // session resume
230   QtUi *gui = new QtUi();
231   Client::init(gui);
232   // init gui only after the event loop has started
233   QTimer::singleShot(0, gui, SLOT(init()));
234   //gui->init();
235 #endif
236
237 #ifndef BUILD_QTUI
238   if(!Global::parser.isSet("norestore")) {
239     Core::restoreState();
240   }
241 #endif
242
243 #ifndef BUILD_CORE
244   app.resumeSessionIfPossible();
245 #endif
246
247   int exitCode = app.exec();
248
249 #ifndef BUILD_QTUI
250   Core::saveState();
251 #endif
252
253 #ifndef BUILD_CORE
254   // the mainWin has to be deleted before the Core
255   // if not Quassel will crash on exit under certain conditions since the gui
256   // still wants to access clientdata
257   delete gui;
258   Client::destroy();
259 #endif
260 #ifndef BUILD_QTUI
261   Core::destroy();
262 #endif
263
264   return exitCode;
265 }