qBittorrent
main.cpp File Reference
#include <QtGlobal>
#include <csignal>
#include <cstdlib>
#include <memory>
#include <unistd.h>
#include <QDebug>
#include <QThread>
#include <QFont>
#include <QMessageBox>
#include <QPainter>
#include <QPen>
#include <QPushButton>
#include <QSplashScreen>
#include <QTimer>
#include "base/preferences.h"
#include "base/profile.h"
#include "base/version.h"
#include "application.h"
#include "cmdoptions.h"
#include "upgrade.h"
#include "gui/utils.h"
Include dependency graph for main.cpp:

Go to the source code of this file.

Functions

void sigNormalHandler (int signum)
 
void reportToUser (const char *str)
 
void displayVersion ()
 
bool userAgreesWithLegalNotice ()
 
void displayBadArgMessage (const QString &message)
 
void showSplashScreen ()
 
int main (int argc, char *argv[])
 

Variables

const char *const sysSigName []
 

Function Documentation

◆ displayBadArgMessage()

void displayBadArgMessage ( const QString &  message)

Definition at line 398 of file main.cpp.

399 {
400  const QString help = QObject::tr("Run application with -h option to read about command line parameters.");
401 #if defined(Q_OS_WIN) && !defined(DISABLE_GUI)
402  QMessageBox msgBox(QMessageBox::Critical, QObject::tr("Bad command line"),
403  message + QLatin1Char('\n') + help, QMessageBox::Ok);
404  msgBox.show(); // Need to be shown or to moveToCenter does not work
405  msgBox.move(Utils::Gui::screenCenter(&msgBox));
406  msgBox.exec();
407 #else
408  const QString errMsg = QObject::tr("Bad command line: ") + '\n'
409  + message + '\n'
410  + help + '\n';
411  fprintf(stderr, "%s", qUtf8Printable(errMsg));
412 #endif
413 }
QPoint screenCenter(const QWidget *w)
Definition: utils.cpp:119
help
Definition: tstool.py:145

References tstool::help, and Utils::Gui::screenCenter().

Referenced by main().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ displayVersion()

void displayVersion ( )

Definition at line 393 of file main.cpp.

394 {
395  printf("%s %s\n", qUtf8Printable(qApp->applicationName()), QBT_VERSION);
396 }

Referenced by main().

Here is the caller graph for this function:

◆ main()

int main ( int  argc,
char *  argv[] 
)

Definition at line 128 of file main.cpp.

129 {
130 #if defined(Q_OS_UNIX)
131  adjustFileDescriptorLimit();
132 #endif
133 
134  // We must save it here because QApplication constructor may change it
135  bool isOneArg = (argc == 2);
136 
137 #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && !defined(DISABLE_GUI)
138  // Attribute Qt::AA_EnableHighDpiScaling must be set before QCoreApplication is created
139  if (qgetenv("QT_ENABLE_HIGHDPI_SCALING").isEmpty() && qgetenv("QT_AUTO_SCREEN_SCALE_FACTOR").isEmpty())
140  Application::setAttribute(Qt::AA_EnableHighDpiScaling, true);
141  // HighDPI scale factor policy must be set before QGuiApplication is created
142  if (qgetenv("QT_SCALE_FACTOR_ROUNDING_POLICY").isEmpty())
143  Application::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
144 #endif
145 
146  try
147  {
148  // Create Application
149  auto app = std::make_unique<Application>(argc, argv);
150 
151  const QBtCommandLineParameters params = app->commandLineArgs();
152  if (!params.unknownParameter.isEmpty())
153  {
154  throw CommandLineParameterError(QObject::tr("%1 is an unknown command line parameter.",
155  "--random-parameter is an unknown command line parameter.")
156  .arg(params.unknownParameter));
157  }
158 #if !defined(Q_OS_WIN) || defined(DISABLE_GUI)
159  if (params.showVersion)
160  {
161  if (isOneArg)
162  {
163  displayVersion();
164  return EXIT_SUCCESS;
165  }
166  throw CommandLineParameterError(QObject::tr("%1 must be the single command line parameter.")
167  .arg(QLatin1String("-v (or --version)")));
168  }
169 #endif
170  if (params.showHelp)
171  {
172  if (isOneArg)
173  {
174  displayUsage(argv[0]);
175  return EXIT_SUCCESS;
176  }
177  throw CommandLineParameterError(QObject::tr("%1 must be the single command line parameter.")
178  .arg(QLatin1String("-h (or --help)")));
179  }
180 
181  // Set environment variable
182  if (!qputenv("QBITTORRENT", QBT_VERSION))
183  fprintf(stderr, "Couldn't set environment variable...\n");
184 
185  const bool firstTimeUser = !Preferences::instance()->getAcceptedLegal();
186  if (firstTimeUser)
187  {
188 #ifndef DISABLE_GUI
190  return EXIT_SUCCESS;
191 #elif defined(Q_OS_WIN)
192  if (_isatty(_fileno(stdin))
193  && _isatty(_fileno(stdout))
195  return EXIT_SUCCESS;
196 #else
197  if (!params.shouldDaemonize
198  && isatty(fileno(stdin))
199  && isatty(fileno(stdout))
201  return EXIT_SUCCESS;
202 #endif
203 
205  }
206 
207  // Check if qBittorrent is already running for this user
208  if (app->isRunning())
209  {
210 #if defined(DISABLE_GUI) && !defined(Q_OS_WIN)
211  if (params.shouldDaemonize)
212  {
213  throw CommandLineParameterError(QObject::tr("You cannot use %1: qBittorrent is already running for this user.")
214  .arg(QLatin1String("-d (or --daemon)")));
215  }
216  else
217 #endif
218  qDebug("qBittorrent is already running for this user.");
219 
220  QThread::msleep(300);
221  app->sendParams(params.paramList());
222 
223  return EXIT_SUCCESS;
224  }
225 
226 #if defined(Q_OS_WIN)
227  // This affects only Windows apparently and Qt5.
228  // When QNetworkAccessManager is instantiated it regularly starts polling
229  // the network interfaces to see what's available and their status.
230  // This polling creates jitter and high ping with wifi interfaces.
231  // So here we disable it for lack of better measure.
232  // It will also spew this message in the console: QObject::startTimer: Timers cannot have negative intervals
233  // For more info see:
234  // 1. https://github.com/qbittorrent/qBittorrent/issues/4209
235  // 2. https://bugreports.qt.io/browse/QTBUG-40332
236  // 3. https://bugreports.qt.io/browse/QTBUG-46015
237 
238  qputenv("QT_BEARER_POLL_TIMEOUT", QByteArray::number(-1));
239 #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && !defined(DISABLE_GUI)
240  // this is the default in Qt6
241  app->setAttribute(Qt::AA_DisableWindowContextHelpButton);
242 #endif
243 #endif // Q_OS_WIN
244 
245 #if defined(Q_OS_MACOS)
246  // Since Apple made difficult for users to set PATH, we set here for convenience.
247  // Users are supposed to install Homebrew Python for search function.
248  // For more info see issue #5571.
249  QByteArray path = "/usr/local/bin:";
250  path += qgetenv("PATH");
251  qputenv("PATH", path.constData());
252 
253  // On OS X the standard is to not show icons in the menus
254  app->setAttribute(Qt::AA_DontShowIconsInMenus);
255 #else
256  if (!Preferences::instance()->iconsInMenusEnabled())
257  app->setAttribute(Qt::AA_DontShowIconsInMenus);
258 #endif
259 
260  if (!firstTimeUser)
261  {
263 
264 #ifndef DISABLE_GUI
265  if (!upgrade()) return EXIT_FAILURE;
266 #elif defined(Q_OS_WIN)
267  if (!upgrade(_isatty(_fileno(stdin))
268  && _isatty(_fileno(stdout)))) return EXIT_FAILURE;
269 #else
270  if (!upgrade(!params.shouldDaemonize
271  && isatty(fileno(stdin))
272  && isatty(fileno(stdout)))) return EXIT_FAILURE;
273 #endif
274  }
275  else
276  {
278  }
279 
280 #if defined(DISABLE_GUI) && !defined(Q_OS_WIN)
281  if (params.shouldDaemonize)
282  {
283  app.reset(); // Destroy current application
284  if (daemon(1, 0) == 0)
285  {
286  app = std::make_unique<Application>(argc, argv);
287  if (app->isRunning())
288  {
289  // Another instance had time to start.
290  return EXIT_FAILURE;
291  }
292  }
293  else
294  {
295  qCritical("Something went wrong while daemonizing, exiting...");
296  return EXIT_FAILURE;
297  }
298  }
299 #elif !defined(DISABLE_GUI)
302 #endif
303 
304  signal(SIGINT, sigNormalHandler);
305  signal(SIGTERM, sigNormalHandler);
306 #ifdef STACKTRACE
307  signal(SIGABRT, sigAbnormalHandler);
308  signal(SIGSEGV, sigAbnormalHandler);
309 #endif
310 
311  return app->exec(params.paramList());
312  }
313  catch (const CommandLineParameterError &er)
314  {
316  return EXIT_FAILURE;
317  }
318 }
QString message() const noexcept
Definition: exceptions.cpp:36
static Preferences * instance()
bool isSplashScreenDisabled() const
bool getAcceptedLegal() const
void displayUsage(const QString &prgName)
Definition: cmdoptions.cpp:582
bool userAgreesWithLegalNotice()
Definition: main.cpp:415
void displayVersion()
Definition: main.cpp:393
void showSplashScreen()
Definition: main.cpp:378
void sigNormalHandler(int signum)
Definition: main.cpp:338
void displayBadArgMessage(const QString &message)
Definition: main.cpp:398
QStringList paramList() const
Definition: cmdoptions.cpp:363
bool upgrade(const bool)
Definition: upgrade.cpp:331
void handleChangedDefaults(const DefaultPreferencesMode mode)
Definition: upgrade.cpp:360
void setCurrentMigrationVersion()
Definition: upgrade.cpp:355

References Current, displayBadArgMessage(), displayUsage(), displayVersion(), Preferences::getAcceptedLegal(), handleChangedDefaults(), Preferences::instance(), Preferences::isSplashScreenDisabled(), Legacy, Exception::message(), QBtCommandLineParameters::noSplash, QBtCommandLineParameters::paramList(), setCurrentMigrationVersion(), QBtCommandLineParameters::showHelp, showSplashScreen(), QBtCommandLineParameters::showVersion, sigNormalHandler(), QBtCommandLineParameters::unknownParameter, upgrade(), and userAgreesWithLegalNotice().

Here is the call graph for this function:

◆ reportToUser()

void reportToUser ( const char *  str)

Definition at line 321 of file main.cpp.

322 {
323  const size_t strLen = strlen(str);
324 #ifndef Q_OS_WIN
325  if (write(STDERR_FILENO, str, strLen) < static_cast<ssize_t>(strLen))
326  {
327  const auto dummy = write(STDOUT_FILENO, str, strLen);
328 #else
329  if (_write(STDERR_FILENO, str, strLen) < static_cast<ssize_t>(strLen))
330  {
331  const auto dummy = _write(STDOUT_FILENO, str, strLen);
332 #endif
333  Q_UNUSED(dummy);
334  }
335 }

Referenced by sigNormalHandler().

Here is the caller graph for this function:

◆ showSplashScreen()

void showSplashScreen ( )

Definition at line 378 of file main.cpp.

379 {
380  QPixmap splashImg(":/icons/splash.png");
381  QPainter painter(&splashImg);
382  const QString version = QBT_VERSION;
383  painter.setPen(QPen(Qt::white));
384  painter.setFont(QFont("Arial", 22, QFont::Black));
385  painter.drawText(224 - painter.fontMetrics().horizontalAdvance(version), 270, version);
386  QSplashScreen *splash = new QSplashScreen(splashImg);
387  splash->show();
388  QTimer::singleShot(1500, splash, &QObject::deleteLater);
389  qApp->processEvents();
390 }

Referenced by main().

Here is the caller graph for this function:

◆ sigNormalHandler()

void sigNormalHandler ( int  signum)

Definition at line 338 of file main.cpp.

339 {
340 #if !(defined Q_OS_WIN && !defined DISABLE_GUI) && !defined Q_OS_HAIKU
341  const char msg1[] = "Catching signal: ";
342  const char msg2[] = "\nExiting cleanly\n";
343  reportToUser(msg1);
344  reportToUser(sysSigName[signum]);
345  reportToUser(msg2);
346 #endif // !defined Q_OS_WIN && !defined Q_OS_HAIKU
347  signal(signum, SIG_DFL);
348  qApp->exit(); // unsafe, but exit anyway
349 }
void reportToUser(const char *str)
Definition: main.cpp:321
const char *const sysSigName[]
Definition: main.cpp:96

References reportToUser(), and sysSigName.

Referenced by main().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ userAgreesWithLegalNotice()

bool userAgreesWithLegalNotice ( )

Definition at line 415 of file main.cpp.

416 {
417  Preferences *const pref = Preferences::instance();
418  Q_ASSERT(!pref->getAcceptedLegal());
419 
420 #ifdef DISABLE_GUI
421  const QString eula = QString::fromLatin1("\n*** %1 ***\n").arg(QObject::tr("Legal Notice"))
422  + QObject::tr("qBittorrent is a file sharing program. When you run a torrent, its data will be made available to others by means of upload. Any content you share is your sole responsibility.") + "\n\n"
423  + QObject::tr("No further notices will be issued.") + "\n\n"
424  + QObject::tr("Press %1 key to accept and continue...").arg("'y'") + '\n';
425  printf("%s", qUtf8Printable(eula));
426 
427  const char ret = getchar(); // Read pressed key
428  if ((ret == 'y') || (ret == 'Y'))
429  {
430  // Save the answer
431  pref->setAcceptedLegal(true);
432  return true;
433  }
434 #else
435  QMessageBox msgBox;
436  msgBox.setText(QObject::tr("qBittorrent is a file sharing program. When you run a torrent, its data will be made available to others by means of upload. Any content you share is your sole responsibility.\n\nNo further notices will be issued."));
437  msgBox.setWindowTitle(QObject::tr("Legal notice"));
438  msgBox.addButton(QObject::tr("Cancel"), QMessageBox::RejectRole);
439  const QAbstractButton *agreeButton = msgBox.addButton(QObject::tr("I Agree"), QMessageBox::AcceptRole);
440  msgBox.show(); // Need to be shown or to moveToCenter does not work
441  msgBox.move(Utils::Gui::screenCenter(&msgBox));
442  msgBox.exec();
443  if (msgBox.clickedButton() == agreeButton)
444  {
445  // Save the answer
446  pref->setAcceptedLegal(true);
447  return true;
448  }
449 #endif // DISABLE_GUI
450 
451  return false;
452 }
void setAcceptedLegal(bool accepted)

References Preferences::getAcceptedLegal(), Preferences::instance(), Utils::Gui::screenCenter(), and Preferences::setAcceptedLegal().

Referenced by main().

Here is the call graph for this function:
Here is the caller graph for this function:

Variable Documentation

◆ sysSigName

const char* const sysSigName[]
Initial value:
=
{
"", "SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGTRAP", "SIGABRT", "SIGBUS", "SIGFPE", "SIGKILL",
"SIGUSR1", "SIGSEGV", "SIGUSR2", "SIGPIPE", "SIGALRM", "SIGTERM", "SIGSTKFLT", "SIGCHLD", "SIGCONT", "SIGSTOP",
"SIGTSTP", "SIGTTIN", "SIGTTOU", "SIGURG", "SIGXCPU", "SIGXFSZ", "SIGVTALRM", "SIGPROF", "SIGWINCH", "SIGIO",
"SIGPWR", "SIGUNUSED"
}

Definition at line 96 of file main.cpp.

Referenced by sigNormalHandler().