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