mirror of
https://github.com/mumble-voip/mumble.git
synced 2025-10-26 11:19:16 +00:00
547 lines
13 KiB
C++
547 lines
13 KiB
C++
// Copyright 2005-2016 The Mumble Developers. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license
|
|
// that can be found in the LICENSE file at the root of the
|
|
// Mumble source tree or at <https://www.mumble.info/LICENSE>.
|
|
|
|
#include "murmur_pch.h"
|
|
|
|
#ifdef Q_OS_WIN
|
|
#include "Tray.h"
|
|
#endif
|
|
|
|
#include "Server.h"
|
|
#include "ServerDB.h"
|
|
#include "DBus.h"
|
|
#include "Meta.h"
|
|
#include "Version.h"
|
|
#include "SSL.h"
|
|
|
|
#ifdef Q_OS_UNIX
|
|
#include "UnixMurmur.h"
|
|
#endif
|
|
|
|
QFile *qfLog = NULL;
|
|
|
|
static bool bVerbose = false;
|
|
#ifdef QT_NO_DEBUG
|
|
static bool detach = true;
|
|
#else
|
|
static bool detach = false;
|
|
#endif
|
|
|
|
#ifdef Q_OS_UNIX
|
|
static UnixMurmur *unixMurmur = NULL;
|
|
#endif
|
|
|
|
Meta *meta = NULL;
|
|
|
|
static LogEmitter le;
|
|
|
|
static QStringList qlErrors;
|
|
|
|
static void murmurMessageOutputQString(QtMsgType type, const QString &msg) {
|
|
#ifdef Q_OS_UNIX
|
|
if (unixMurmur->logToSyslog) {
|
|
int level;
|
|
switch (type) {
|
|
case QtDebugMsg:
|
|
level = LOG_DEBUG;
|
|
break;
|
|
case QtWarningMsg:
|
|
level = LOG_WARNING;
|
|
break;
|
|
case QtCriticalMsg:
|
|
level = LOG_CRIT;
|
|
break;
|
|
case QtFatalMsg:
|
|
default:
|
|
level = LOG_ALERT;
|
|
break;
|
|
}
|
|
syslog(level, "%s", qPrintable(msg));
|
|
if (type == QtFatalMsg) {
|
|
exit(1);
|
|
}
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
char c;
|
|
switch (type) {
|
|
case QtDebugMsg:
|
|
if (! bVerbose)
|
|
return;
|
|
c='D';
|
|
break;
|
|
case QtWarningMsg:
|
|
c='W';
|
|
break;
|
|
case QtCriticalMsg:
|
|
c='C';
|
|
break;
|
|
case QtFatalMsg:
|
|
c='F';
|
|
break;
|
|
default:
|
|
c='X';
|
|
}
|
|
QString m= QString::fromLatin1("<%1>%2 %3").arg(QChar::fromLatin1(c)).arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz")).arg(msg);
|
|
|
|
if (! qfLog || ! qfLog->isOpen()) {
|
|
#ifdef Q_OS_UNIX
|
|
if (! detach)
|
|
fprintf(stderr, "%s\n", qPrintable(m));
|
|
else
|
|
qlErrors << m;
|
|
#else
|
|
qlErrors << m;
|
|
#ifndef QT_NO_DEBUG
|
|
fprintf(stderr, "%s\n", qPrintable(m));
|
|
#endif
|
|
#endif
|
|
} else {
|
|
if (! qlErrors.isEmpty()) {
|
|
foreach(const QString &e, qlErrors) {
|
|
qfLog->write(e.toUtf8());
|
|
qfLog->write("\n");
|
|
}
|
|
qlErrors.clear();
|
|
}
|
|
qfLog->write(m.toUtf8());
|
|
qfLog->write("\n");
|
|
qfLog->flush();
|
|
}
|
|
le.addLogEntry(m);
|
|
if (type == QtFatalMsg) {
|
|
#ifdef Q_OS_UNIX
|
|
if (detach) {
|
|
if (qlErrors.isEmpty())
|
|
qlErrors << msg;
|
|
qlErrors << QString();
|
|
m = qlErrors.join(QLatin1String("\n"));
|
|
fprintf(stderr, "%s", qPrintable(m));
|
|
}
|
|
#else
|
|
::MessageBoxA(NULL, qPrintable(m), "Murmur", MB_OK | MB_ICONWARNING);
|
|
#endif
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
#if QT_VERSION >= 0x050000
|
|
static void murmurMessageOutputWithContext(QtMsgType type, const QMessageLogContext &ctx, const QString &msg) {
|
|
Q_UNUSED(ctx);
|
|
murmurMessageOutputQString(type, msg);
|
|
}
|
|
#else
|
|
static void murmurMessageOutput(QtMsgType type, const char *msg) {
|
|
murmurMessageOutputQString(type, QString::fromUtf8(msg));
|
|
}
|
|
#endif
|
|
|
|
#ifdef USE_ICE
|
|
void IceParse(int &argc, char *argv[]);
|
|
void IceStart();
|
|
void IceStop();
|
|
#endif
|
|
|
|
#ifdef USE_GRPC
|
|
// From MurmurGRPCImpl.cpp.
|
|
void GRPCStart();
|
|
void GRPCStop();
|
|
#endif
|
|
|
|
int main(int argc, char **argv) {
|
|
// Check for SSE and MMX, but only in the windows binaries
|
|
#ifdef Q_OS_WIN
|
|
int cpuinfo[4];
|
|
__cpuid(cpuinfo, 1);
|
|
|
|
#define MMXSSE 0x02800000
|
|
if ((cpuinfo[3] & MMXSSE) != MMXSSE) {
|
|
::MessageBoxA(NULL, "Mumble requires a SSE capable processor (Pentium 3 / Ahtlon-XP)", "Mumble", MB_OK | MB_ICONERROR);
|
|
exit(0);
|
|
}
|
|
|
|
SetDllDirectory(L"");
|
|
|
|
#endif
|
|
int res;
|
|
|
|
#ifdef USE_ICE
|
|
IceParse(argc, argv);
|
|
#endif
|
|
|
|
#ifdef Q_OS_WIN
|
|
QApplication a(argc, argv);
|
|
a.setQuitOnLastWindowClosed(false);
|
|
|
|
QIcon icon;
|
|
icon.addFile(QLatin1String(":/murmur.16x16.png"));
|
|
icon.addFile(QLatin1String(":/murmur.32x32.png"));
|
|
icon.addFile(QLatin1String(":/murmur.64x64.png"));
|
|
a.setWindowIcon(icon);
|
|
#else
|
|
#ifndef Q_OS_MAC
|
|
setenv("AVAHI_COMPAT_NOWARN", "1", 1);
|
|
#endif
|
|
QCoreApplication a(argc, argv);
|
|
UnixMurmur unixhandler;
|
|
unixMurmur = &unixhandler;
|
|
unixhandler.initialcap();
|
|
#endif
|
|
a.setApplicationName("Murmur");
|
|
a.setOrganizationName("Mumble");
|
|
a.setOrganizationDomain("mumble.sourceforge.net");
|
|
|
|
MumbleSSL::initialize();
|
|
|
|
QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));
|
|
#if QT_VERSION < 0x050000
|
|
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
|
|
#endif
|
|
|
|
#ifdef Q_OS_WIN
|
|
{
|
|
size_t reqSize;
|
|
_wgetenv_s(&reqSize, NULL, 0, L"PATH");
|
|
if (reqSize > 0) {
|
|
STACKVAR(wchar_t, buff, reqSize+1);
|
|
_wgetenv_s(&reqSize, buff, reqSize, L"PATH");
|
|
QString path = QString::fromLatin1("%1;%2").arg(QDir::toNativeSeparators(a.applicationDirPath())).arg(QString::fromWCharArray(buff));
|
|
STACKVAR(wchar_t, buffout, path.length() + 1);
|
|
path.toWCharArray(buffout);
|
|
_wputenv_s(L"PATH", buffout);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
QString inifile;
|
|
QString supw;
|
|
bool wipeSsl = false;
|
|
bool wipeLogs = false;
|
|
int sunum = 1;
|
|
#ifdef Q_OS_UNIX
|
|
bool readPw = false;
|
|
#endif
|
|
|
|
qsrand(QDateTime::currentDateTime().toTime_t());
|
|
#if QT_VERSION >= 0x050000
|
|
qInstallMessageHandler(murmurMessageOutputWithContext);
|
|
#else
|
|
qInstallMsgHandler(murmurMessageOutput);
|
|
#endif
|
|
|
|
#ifdef Q_OS_WIN
|
|
Tray tray(NULL, &le);
|
|
#endif
|
|
|
|
QStringList args = a.arguments();
|
|
for (int i = 1; i < args.size(); i++) {
|
|
bool bLast = false;
|
|
QString arg = args.at(i).toLower();
|
|
if ((arg == "-supw")) {
|
|
detach = false;
|
|
if (i+1 < args.size()) {
|
|
i++;
|
|
supw = args.at(i);
|
|
if (i+1 < args.size()) {
|
|
i++;
|
|
sunum = args.at(i).toInt();
|
|
}
|
|
bLast = true;
|
|
} else {
|
|
#ifdef Q_OS_UNIX
|
|
qFatal("-supw expects the password on the command line - maybe you meant -readsupw?");
|
|
#else
|
|
qFatal("-supw expects the password on the command line");
|
|
#endif
|
|
}
|
|
#ifdef Q_OS_UNIX
|
|
} else if ((arg == "-readsupw")) {
|
|
detach = false;
|
|
readPw = true;
|
|
if (i+1 < args.size()) {
|
|
i++;
|
|
sunum = args.at(i).toInt();
|
|
}
|
|
bLast = true;
|
|
#endif
|
|
} else if ((arg == "-ini") && (i+1 < args.size())) {
|
|
i++;
|
|
inifile = args.at(i);
|
|
} else if ((arg == "-wipessl")) {
|
|
wipeSsl = true;
|
|
} else if ((arg == "-wipelogs")) {
|
|
wipeLogs = true;
|
|
} else if ((arg == "-fg")) {
|
|
detach = false;
|
|
} else if ((arg == "-v")) {
|
|
bVerbose = true;
|
|
} else if ((arg == "-version") || (arg == "--version")) {
|
|
detach = false;
|
|
qFatal("%s -- %s", qPrintable(args.at(0)), MUMBLE_RELEASE);
|
|
} else if ((arg == "-h") || (arg == "-help") || (arg == "--help")) {
|
|
detach = false;
|
|
qFatal("Usage: %s [-ini <inifile>] [-supw <password>]\n"
|
|
" -ini <inifile> Specify ini file to use.\n"
|
|
" -supw <pw> [srv] Set password for 'SuperUser' account on server srv.\n"
|
|
#ifdef Q_OS_UNIX
|
|
" -readsupw [srv] Reads password for server srv from standard input.\n"
|
|
" -limits Tests and shows how many file descriptors and threads can be created.\n"
|
|
" The purpose of this option is to test how many clients Murmur can handle.\n"
|
|
" Murmur will exit after this test.\n"
|
|
#endif
|
|
" -v Add verbose output.\n"
|
|
#ifdef Q_OS_UNIX
|
|
" -fg Don't detach from console.\n"
|
|
#else
|
|
" -fg Don't write to the log file.\n"
|
|
#endif
|
|
" -wipessl Remove SSL certificates from database.\n"
|
|
" -wipelogs Remove all log entries from database.\n"
|
|
" -version Show version information.\n"
|
|
"If no inifile is provided, murmur will search for one in \n"
|
|
"default locations.", qPrintable(args.at(0)));
|
|
#ifdef Q_OS_UNIX
|
|
} else if (arg == "-limits") {
|
|
Meta::mp.read(inifile);
|
|
unixhandler.setuid();
|
|
unixhandler.finalcap();
|
|
LimitTest::testLimits(a);
|
|
#endif
|
|
} else {
|
|
detach = false;
|
|
qFatal("Unknown argument %s", qPrintable(args.at(i)));
|
|
}
|
|
if (bLast && (i+1 != args.size())) {
|
|
detach = false;
|
|
qFatal("Password arguments must be last.");
|
|
}
|
|
}
|
|
|
|
#ifdef Q_OS_UNIX
|
|
inifile = unixhandler.trySystemIniFiles(inifile);
|
|
#endif
|
|
|
|
Meta::mp.read(inifile);
|
|
|
|
// need to open log file early so log dir can be root owned:
|
|
// http://article.gmane.org/gmane.comp.security.oss.general/4404
|
|
#ifdef Q_OS_UNIX
|
|
unixhandler.logToSyslog = Meta::mp.qsLogfile == QLatin1String("syslog");
|
|
if (detach && ! Meta::mp.qsLogfile.isEmpty() && !unixhandler.logToSyslog) {
|
|
#else
|
|
if (detach && ! Meta::mp.qsLogfile.isEmpty()) {
|
|
#endif
|
|
qfLog = new QFile(Meta::mp.qsLogfile);
|
|
if (! qfLog->open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) {
|
|
delete qfLog;
|
|
qfLog = NULL;
|
|
#ifdef Q_OS_UNIX
|
|
fprintf(stderr, "murmurd: failed to open logfile %s: no logging will be done\n", qPrintable(Meta::mp.qsLogfile));
|
|
#else
|
|
qWarning("Failed to open logfile %s. No logging will be performed.", qPrintable(Meta::mp.qsLogfile));
|
|
#endif
|
|
} else {
|
|
qfLog->setTextModeEnabled(true);
|
|
QFileInfo qfi(*qfLog);
|
|
Meta::mp.qsLogfile = qfi.absoluteFilePath();
|
|
#ifdef Q_OS_UNIX
|
|
if (Meta::mp.uiUid != 0 && fchown(qfLog->handle(), Meta::mp.uiUid, Meta::mp.uiGid) == -1) {
|
|
qFatal("can't change log file owner to %d %d:%d - %s", qfLog->handle(), Meta::mp.uiUid, Meta::mp.uiGid, strerror(errno));
|
|
}
|
|
#endif
|
|
}
|
|
#ifdef Q_OS_UNIX
|
|
} else if (detach && unixhandler.logToSyslog) {
|
|
openlog("murmurd", LOG_PID, LOG_DAEMON);
|
|
syslog(LOG_DEBUG, "murmurd syslog adapter up and running");
|
|
#endif
|
|
} else {
|
|
detach = false;
|
|
}
|
|
|
|
#ifdef Q_OS_UNIX
|
|
unixhandler.setuid();
|
|
#endif
|
|
|
|
MumbleSSL::addSystemCA();
|
|
|
|
ServerDB db;
|
|
|
|
meta = new Meta();
|
|
|
|
#ifdef Q_OS_UNIX
|
|
if (readPw) {
|
|
char password[256];
|
|
char *p;
|
|
|
|
printf("Password: ");
|
|
fflush(NULL);
|
|
if (fgets(password, 255, stdin) != password)
|
|
qFatal("No password provided");
|
|
p = strchr(password, '\r');
|
|
if (p)
|
|
*p = 0;
|
|
p = strchr(password, '\n');
|
|
if (p)
|
|
*p = 0;
|
|
|
|
supw = QLatin1String(password);
|
|
}
|
|
#endif
|
|
|
|
if (!supw.isNull()) {
|
|
if (supw.isEmpty()) {
|
|
qFatal("Superuser password can not be empty");
|
|
}
|
|
ServerDB::setSUPW(sunum, supw);
|
|
qFatal("Superuser password set on server %d", sunum);
|
|
}
|
|
|
|
if (wipeSsl) {
|
|
qWarning("Removing all per-server SSL certificates from the database.");
|
|
foreach(int sid, ServerDB::getAllServers()) {
|
|
ServerDB::setConf(sid, "key");
|
|
ServerDB::setConf(sid, "certificate");
|
|
ServerDB::setConf(sid, "passphrase");
|
|
ServerDB::setConf(sid, "sslDHParams");
|
|
}
|
|
}
|
|
|
|
if (wipeLogs) {
|
|
qWarning("Removing all log entries from the database.");
|
|
ServerDB::wipeLogs();
|
|
}
|
|
|
|
#ifdef Q_OS_UNIX
|
|
if (detach) {
|
|
if (fork() != 0) {
|
|
_exit(0);
|
|
}
|
|
setsid();
|
|
if (fork() != 0) {
|
|
_exit(0);
|
|
}
|
|
|
|
if (! Meta::mp.qsPid.isEmpty()) {
|
|
QFile pid(Meta::mp.qsPid);
|
|
if (pid.open(QIODevice::WriteOnly)) {
|
|
QFileInfo fi(pid);
|
|
Meta::mp.qsPid = fi.absoluteFilePath();
|
|
|
|
QTextStream out(&pid);
|
|
out << getpid();
|
|
pid.close();
|
|
}
|
|
}
|
|
|
|
if (chdir("/") != 0)
|
|
fprintf(stderr, "Failed to chdir to /");
|
|
int fd;
|
|
|
|
fd = open("/dev/null", O_RDONLY);
|
|
dup2(fd, 0);
|
|
close(fd);
|
|
|
|
fd = open("/dev/null", O_WRONLY);
|
|
dup2(fd, 1);
|
|
close(fd);
|
|
|
|
fd = open("/dev/null", O_WRONLY);
|
|
dup2(fd, 2);
|
|
close(fd);
|
|
}
|
|
unixhandler.finalcap();
|
|
#endif
|
|
|
|
#ifdef USE_DBUS
|
|
MurmurDBus::registerTypes();
|
|
|
|
if (! Meta::mp.qsDBus.isEmpty()) {
|
|
if (Meta::mp.qsDBus == "session")
|
|
MurmurDBus::qdbc = QDBusConnection::sessionBus();
|
|
else if (Meta::mp.qsDBus == "system")
|
|
MurmurDBus::qdbc = QDBusConnection::systemBus();
|
|
else {
|
|
// QtDBus is not quite finished yet.
|
|
qWarning("Warning: Peer-to-peer session support is currently nonworking.");
|
|
MurmurDBus::qdbc = QDBusConnection::connectToBus(Meta::mp.qsDBus, "mainbus");
|
|
if (! MurmurDBus::qdbc.isConnected()) {
|
|
QDBusServer *qdbs = new QDBusServer(Meta::mp.qsDBus, &a);
|
|
qWarning("%s",qPrintable(qdbs->lastError().name()));
|
|
qWarning("%d",qdbs->isConnected());
|
|
qWarning("%s",qPrintable(qdbs->address()));
|
|
MurmurDBus::qdbc = QDBusConnection::connectToBus(Meta::mp.qsDBus, "mainbus");
|
|
}
|
|
}
|
|
if (! MurmurDBus::qdbc.isConnected()) {
|
|
qWarning("Failed to connect to D-Bus %s",qPrintable(Meta::mp.qsDBus));
|
|
}
|
|
}
|
|
new MetaDBus(meta);
|
|
if (MurmurDBus::qdbc.isConnected()) {
|
|
if (! MurmurDBus::qdbc.registerObject("/", meta) || ! MurmurDBus::qdbc.registerService(Meta::mp.qsDBusService)) {
|
|
QDBusError e=MurmurDBus::qdbc.lastError();
|
|
qWarning("Failed to register on DBus: %s %s", qPrintable(e.name()), qPrintable(e.message()));
|
|
} else {
|
|
qWarning("DBus registration succeeded");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef USE_ICE
|
|
IceStart();
|
|
#endif
|
|
|
|
#ifdef USE_GRPC
|
|
GRPCStart();
|
|
#endif
|
|
|
|
meta->getOSInfo();
|
|
|
|
int major, minor, patch;
|
|
QString strver;
|
|
meta->getVersion(major, minor, patch, strver);
|
|
|
|
qWarning("Murmur %d.%d.%d (%s) running on %s: %s: Booting servers", major, minor, patch, qPrintable(strver), qPrintable(meta->qsOS), qPrintable(meta->qsOSVersion));
|
|
|
|
meta->bootAll();
|
|
|
|
res=a.exec();
|
|
|
|
qWarning("Killing running servers");
|
|
|
|
meta->killAll();
|
|
|
|
qWarning("Shutting down");
|
|
|
|
#ifdef USE_ICE
|
|
IceStop();
|
|
#endif
|
|
|
|
#ifdef USE_GRPC
|
|
GRPCStop();
|
|
#endif
|
|
|
|
delete qfLog;
|
|
qfLog = NULL;
|
|
|
|
delete meta;
|
|
|
|
#if QT_VERSION >= 0x050000
|
|
qInstallMessageHandler(NULL);
|
|
#else
|
|
qInstallMsgHandler(NULL);
|
|
#endif
|
|
|
|
#ifdef Q_OS_UNIX
|
|
if (! Meta::mp.qsPid.isEmpty()) {
|
|
QFile pid(Meta::mp.qsPid);
|
|
pid.remove();
|
|
}
|
|
#endif
|
|
return res;
|
|
}
|