Compile on Wintendo again. Learn fucking POSIX!
[quassel.git] / src / common / quassel.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-08 by the Quassel IRC Team                         *
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 "quassel.h"
22
23 #include <signal.h>
24
25 #include <QCoreApplication>
26 #include <QDateTime>
27 #include <QObject>
28 #include <QMetaType>
29
30 #include "message.h"
31 #include "identity.h"
32 #include "network.h"
33 #include "bufferinfo.h"
34 #include "types.h"
35 #include "syncableobject.h"
36
37 #if defined(HAVE_EXECINFO) and not defined(Q_OS_MAC)
38 #  define BUILD_CRASHHANDLER
39 #  include <execinfo.h>
40 #  include <dlfcn.h>
41 #  include <cxxabi.h>
42 #endif
43
44 Quassel::BuildInfo Quassel::_buildInfo;
45 CliParser *Quassel::_cliParser = 0;
46 Quassel::RunMode Quassel::_runMode;
47 bool Quassel::_initialized = false;
48 bool Quassel::DEBUG = false;
49
50 Quassel::Quassel() {
51   // We catch SIGTERM and SIGINT (caused by Ctrl+C) to graceful shutdown Quassel.
52   signal(SIGTERM, handleSignal);
53   signal(SIGINT, handleSignal);
54
55 #ifdef BUILD_CRASHHANDLER
56   signal(SIGABRT, handleSignal);
57   signal(SIGBUS, handleSignal);
58   signal(SIGSEGV, handleSignal);
59 #endif // #if defined(HAVE_EXECINFO) and not defined(Q_OS_MAC)
60
61   _cliParser = new CliParser();
62
63   // put shared client&core arguments here
64   cliParser()->addSwitch("debug",'d', tr("Enable debug output"));
65   cliParser()->addSwitch("help",'h', tr("Display this help and exit"));
66 }
67
68 Quassel::~Quassel() {
69   delete _cliParser;
70 }
71
72 bool Quassel::init() {
73   if(_initialized) return true;  // allow multiple invocations because of MonolithicApplication
74
75   qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));
76
77   registerMetaTypes();
78   setupBuildInfo();
79   Global::setupVersion();
80   setupTranslations();
81
82   QCoreApplication::setApplicationName(buildInfo().applicationName);
83   QCoreApplication::setOrganizationName(buildInfo().organizationName);
84   QCoreApplication::setOrganizationDomain(buildInfo().organizationDomain);
85
86   Network::setDefaultCodecForServer("ISO-8859-1");
87   Network::setDefaultCodecForEncoding("UTF-8");
88   Network::setDefaultCodecForDecoding("ISO-8859-15");
89
90   if(!cliParser()->parse(QCoreApplication::arguments()) || isOptionSet("help")) {
91     cliParser()->usage();
92     return false;
93   }
94   DEBUG = isOptionSet("debug");
95   return true;
96 }
97
98 //! Register our custom types with Qt's Meta Object System.
99 /**  This makes them available for QVariant and in signals/slots, among other things.
100 *
101 */
102 void Quassel::registerMetaTypes() {
103   // Complex types
104   qRegisterMetaType<QVariant>("QVariant");
105   qRegisterMetaType<Message>("Message");
106   qRegisterMetaType<BufferInfo>("BufferInfo");
107   qRegisterMetaType<NetworkInfo>("NetworkInfo");
108   qRegisterMetaType<Identity>("Identity");
109   qRegisterMetaType<Network::ConnectionState>("Network::ConnectionState");
110
111   qRegisterMetaTypeStreamOperators<QVariant>("QVariant");
112   qRegisterMetaTypeStreamOperators<Message>("Message");
113   qRegisterMetaTypeStreamOperators<BufferInfo>("BufferInfo");
114   qRegisterMetaTypeStreamOperators<NetworkInfo>("NetworkInfo");
115   qRegisterMetaTypeStreamOperators<Identity>("Identity");
116   qRegisterMetaTypeStreamOperators<qint8>("Network::ConnectionState");
117
118   qRegisterMetaType<IdentityId>("IdentityId");
119   qRegisterMetaType<BufferId>("BufferId");
120   qRegisterMetaType<NetworkId>("NetworkId");
121   qRegisterMetaType<UserId>("UserId");
122   qRegisterMetaType<AccountId>("AccountId");
123   qRegisterMetaType<MsgId>("MsgId");
124
125   qRegisterMetaTypeStreamOperators<IdentityId>("IdentityId");
126   qRegisterMetaTypeStreamOperators<BufferId>("BufferId");
127   qRegisterMetaTypeStreamOperators<NetworkId>("NetworkId");
128   qRegisterMetaTypeStreamOperators<UserId>("UserId");
129   qRegisterMetaTypeStreamOperators<AccountId>("AccountId");
130   qRegisterMetaTypeStreamOperators<MsgId>("MsgId");
131 }
132
133 void Quassel::setupTranslations() {
134   // Set up i18n support
135   QLocale locale = QLocale::system();
136
137   QTranslator *qtTranslator = new QTranslator(qApp);
138   qtTranslator->setObjectName("QtTr");
139   qtTranslator->load(QString(":i18n/qt_%1").arg(locale.name()));
140   qApp->installTranslator(qtTranslator);
141
142   QTranslator *quasselTranslator = new QTranslator(qApp);
143   quasselTranslator->setObjectName("QuasselTr");
144   quasselTranslator->load(QString(":i18n/quassel_%1").arg(locale.name()));
145   qApp->installTranslator(quasselTranslator);
146 }
147
148 void Quassel::setupBuildInfo() {
149   _buildInfo.applicationName = "Quassel IRC";
150   _buildInfo.coreApplicationName = "Quassel Core";
151   _buildInfo.clientApplicationName = "Quassel Client";
152   _buildInfo.organizationName = "Quassel Project";
153   _buildInfo.organizationDomain = "quassel-irc.org";
154 /*
155 #  include "version.inc"
156 #  include "version.gen"
157
158   if(quasselGeneratedVersion.isEmpty()) {
159     if(quasselCommit.isEmpty())
160       quasselVersion = QString("v%1 (unknown rev)").arg(quasselBaseVersion);
161     else
162       quasselVersion = QString("v%1 (dist-%2, %3)").arg(quasselBaseVersion).arg(quasselCommit.left(7))
163       .arg(QDateTime::fromTime_t(quasselArchiveDate).toLocalTime().toString("yyyy-MM-dd"));
164   } else {
165     QStringList parts = quasselGeneratedVersion.split(':');
166     quasselVersion = QString("v%1").arg(parts[0]);
167     if(parts.count() >= 2) quasselVersion.append(QString(" (%1)").arg(parts[1]));
168   }
169   quasselBuildDate = __DATE__;
170   quasselBuildTime = __TIME__;
171   */
172 }
173
174 //! Signal handler for graceful shutdown.
175 void Quassel::handleSignal(int sig) {
176   switch(sig) {
177     case SIGTERM:
178     case SIGINT:
179       qWarning("%s", qPrintable(QString("Caught signal %1 - exiting.").arg(sig)));
180       QCoreApplication::quit();
181       break;
182
183 #ifdef BUILD_CRASHHANDLER
184     case SIGABRT:
185     case SIGBUS:
186     case SIGSEGV:
187       handleCrash();
188 #endif
189       break;
190     default:
191       break;
192   }
193 }
194
195 void Quassel::handleCrash() {
196 #ifdef BUILD_CRASHHANDLER
197   void* callstack[128];
198   int i, frames = backtrace(callstack, 128);
199
200   QFile dumpFile(QString("Quassel-Crash-%1").arg(QDateTime::currentDateTime().toString("yyyyMMdd-hhmm.log")));
201   dumpFile.open(QIODevice::WriteOnly);
202   QTextStream dumpStream(&dumpFile);
203
204   for (i = 0; i < frames; ++i) {
205     Dl_info info;
206     dladdr (callstack[i], &info);
207     // as a reference:
208     //     typedef struct
209     //     {
210       //       __const char *dli_fname;   /* File name of defining object.  */
211     //       void *dli_fbase;           /* Load address of that object.  */
212     //       __const char *dli_sname;   /* Name of nearest symbol.  */
213     //       void *dli_saddr;           /* Exact value of nearest symbol.  */
214     //     } Dl_info;
215
216     #if __LP64__
217     int addrSize = 16;
218     #else
219     int addrSize = 8;
220     #endif
221
222     QString funcName;
223     if(info.dli_sname) {
224       char *func = abi::__cxa_demangle(info.dli_sname, 0, 0, 0);
225       if(func) {
226         funcName = QString(func);
227         free(func);
228       } else {
229         funcName = QString(info.dli_sname);
230       }
231     } else {
232       funcName = QString("0x%1").arg((long)info.dli_saddr, addrSize, QLatin1Char('0'));
233     }
234
235     // prettificating the filename
236     QString fileName("???");
237     if(info.dli_fname) {
238       fileName = QString(info.dli_fname);
239       int slashPos = fileName.lastIndexOf('/');
240       if(slashPos != -1)
241         fileName = fileName.mid(slashPos + 1);
242       if(fileName.count() < 20)
243         fileName += QString(20 - fileName.count(), ' ');
244     }
245
246     QString debugLine = QString("#%1 %2 0x%3 %4").arg(i, 3, 10)
247     .arg(fileName)
248     .arg((long)(callstack[i]), addrSize, 16, QLatin1Char('0'))
249     .arg(funcName);
250
251     dumpStream << debugLine << "\n";
252     qDebug() << qPrintable(debugLine);
253   }
254   dumpFile.close();
255   exit(27);
256 #endif /* BUILD_CRASHHANDLER */
257 }
258
259 // FIXME temporary
260
261 void Global::setupVersion() {
262
263   #  include "version.inc"
264   #  include "version.gen"
265
266   if(quasselGeneratedVersion.isEmpty()) {
267     if(quasselCommit.isEmpty())
268       quasselVersion = QString("v%1 (unknown rev)").arg(quasselBaseVersion);
269     else
270       quasselVersion = QString("v%1 (dist-%2, %3)").arg(quasselBaseVersion).arg(quasselCommit.left(7))
271       .arg(QDateTime::fromTime_t(quasselArchiveDate).toLocalTime().toString("yyyy-MM-dd"));
272   } else {
273     QStringList parts = quasselGeneratedVersion.split(':');
274     quasselVersion = QString("v%1").arg(parts[0]);
275     if(parts.count() >= 2) quasselVersion.append(QString(" (%1)").arg(parts[1]));
276   }
277   quasselBuildDate = __DATE__;
278   quasselBuildTime = __TIME__;
279 }
280
281 QString Global::quasselVersion;
282 QString Global::quasselBaseVersion;
283 QString Global::quasselGeneratedVersion;
284 QString Global::quasselBuildDate;
285 QString Global::quasselBuildTime;
286 QString Global::quasselCommit;
287 uint Global::quasselArchiveDate;
288 uint Global::protocolVersion;
289 uint Global::clientNeedsProtocol;
290 uint Global::coreNeedsProtocol;