Vidalia 0.2.12

vidalia/main.cpp

Go to the documentation of this file.
00001 /*
00002 **  This file is part of Vidalia, and is subject to the license terms in the
00003 **  LICENSE file, found in the top level directory of this distribution. If you
00004 **  did not receive the LICENSE file with this file, you may obtain it from the
00005 **  Vidalia source package distributed by the Vidalia Project at
00006 **  http://www.vidalia-project.net/. No part of Vidalia, including this file,
00007 **  may be copied, modified, propagated, or distributed except according to the
00008 **  terms described in the LICENSE file.
00009 */
00010 
00011 /*
00012 ** \file main.cpp
00013 ** \brief Main Vidalia entry point
00014 */
00015 
00016 #include "config.h"
00017 #include "Vidalia.h"
00018 #include "MainWindow.h"
00019 #include "VMessageBox.h"
00020 #if defined(USE_BREAKPAD)
00021 #include "CrashReporter.h"
00022 #endif
00023 
00024 #include "procutil.h"
00025 #include "stringutil.h"
00026 
00027 #include <QObject>
00028 #if defined(Q_OS_WIN32)
00029 #include <QSysInfo>
00030 #endif
00031 #if defined(HAVE_SIGNAL_H)
00032 #include <signal.h>
00033 #endif
00034 
00035 #if defined(USE_BREAKPAD)
00036 void
00037 setup_crash_reporter()
00038 {
00039   /* Set the crash reporting application used to submit minidumps. On Windows,
00040    * crashreporter.exe is assumed to live in the same directory as vidalia.exe.
00041    */
00042 #if defined(Q_OS_WIN32)
00043   QString crashReporter = Vidalia::applicationDirPath() + "\\crashreporter.exe";
00044 #elif defined(Q_OS_MAC)
00045   QString crashReporter = Vidalia::applicationDirPath() + "/CrashReporter.app";
00046 #endif
00047   if (! QFileInfo(crashReporter).isExecutable()) {
00048     vWarn("Unable to find crash reporting application. Crash reporting will "
00049           "be unavailable.");
00050     return;
00051   }
00052   if (! CrashReporter::set_crash_reporter(crashReporter)) {
00053     vWarn("Vidalia found the crash reporting application, but the path is "
00054           "longer than your platform supports. Skipping crash reporting.");
00055     return;
00056   }
00057 
00058   /* Set the Vidalia executable and options used to restart Vidalia after a 
00059    * crash. We strip off the first argument in Vidalia::arguments(), since
00060    * it's the application name again anyway. */
00061   CrashReporter::set_restart_options(Vidalia::applicationFilePath(),
00062                                      Vidalia::arguments().mid(1));
00063 
00064   /* Set the build version that gets saved with a minidump. */
00065   CrashReporter::set_build_version(VIDALIA_VERSION);
00066 
00067   /* Install the exception handler and give it the location to which Breakpad
00068    * should write its minidumps. */
00069   QString dumpPath = Vidalia::dataDirectory() + "/crashreports";
00070   if (! CrashReporter::install_exception_handler(dumpPath)) {
00071     vWarn("Unable to setup Breakpad exception handler. Crash reporting "
00072           "will be unavailable.");
00073   } else {
00074     vInfo("Installed Breakpad exception handler.");
00075   }
00076 }
00077 #endif
00078 
00079 extern "C" void
00080 signal_handler(int signal)
00081 {
00082 #ifdef HAVE_SIGNAL_H
00083   if (signal == SIGINT || signal == SIGTERM)
00084     vApp->quit();
00085 #endif
00086 }
00087 
00088 void
00089 install_signal_handler()
00090 {
00091 #if defined(HAVE_SIGACTION)
00092   struct sigaction action;
00093 
00094   sigemptyset(&action.sa_mask);
00095   action.sa_handler = signal_handler;
00096   action.sa_flags   = 0;
00097 
00098   if (sigaction(SIGINT,  &action, NULL) < 0)
00099     vWarn("Failed to install SIGINT handler.");
00100   if (sigaction(SIGTERM, &action, NULL) < 0)
00101     vWarn("Failed to install SIGTERM handler.");
00102 #elif defined(HAVE_SIGNAL)
00103   if (signal(SIGINT, signal_handler) == SIG_ERR)
00104     vWarn("Failed to install SIGINT handler.");
00105   if (signal(SIGTERM, signal_handler) == SIG_ERR)
00106     vWarn("Failed to install SIGTERM handler.");
00107 #endif
00108 }
00109 
00110 /** Returns true if there is already another Vidalia process running. */
00111 bool
00112 is_vidalia_running(const QString &pidfile)
00113 {
00114   /* Read the pidfile and find out if that process still exists */
00115   qint64 pid = read_pidfile(pidfile);
00116   if (pid > 0) {
00117 #if defined(Q_OS_WIN32)
00118     if (QSysInfo::WindowsVersion == QSysInfo::WV_NT) {
00119       /* We currently can't get a list of running processes on Windows NT, so
00120        * be pessimistic and assume the existence of a nonzero pidfile means
00121        * Vidalia is running. */
00122       return true;
00123     } else
00124       return (is_process_running(pid));
00125 #else
00126     return (is_process_running(pid));
00127 #endif
00128   }
00129   return false;
00130 }
00131 
00132 /** Main application entry point. */
00133 int
00134 main(int argc, char *argv[])
00135 {
00136   Q_INIT_RESOURCE(vidalia);
00137   QStringList args = char_array_to_stringlist(argv+1, argc-1);
00138  
00139   /* Construct the application object. Qt strips any command-line arguments
00140    * that it recognizes in argv, so we'll pass a stringlist of the original
00141    * list of command-line arguments too. */
00142   Vidalia vidalia(args, argc, argv);
00143   vNotice("Vidalia %1 using Qt %2").arg(Vidalia::version())
00144                                    .arg(QT_VERSION_STR);
00145 
00146 #if defined(USE_BREAKPAD)
00147   /* Set up the crash reporting application and exception handlers. */
00148   setup_crash_reporter();
00149 #endif
00150 #if defined(USE_MARBLE) && defined(Q_OS_WIN32)
00151   vApp->addLibraryPath(vApp->applicationDirPath() + "/plugins/qt");
00152 #endif
00153 
00154   /* Install a signal handler to clean up properly after a catching a 
00155    * SIGINT or SIGTERM. */
00156   install_signal_handler();
00157 
00158   /* Validate any command-line arguments, or show usage message box, if
00159    * necessary. */
00160   QString errmsg;
00161   if (vidalia.showUsage()) {
00162     Vidalia::showUsageMessageBox();
00163     return 0;
00164   } else if (!vidalia.validateArguments(errmsg)) {
00165     vError("Unable to apply command-line arguments: %1").arg(errmsg);
00166     VMessageBox::critical(0,
00167       vApp->translate("Vidalia",
00168         QT_TRANSLATE_NOOP("Vidalia", "Invalid Argument")), errmsg,
00169       VMessageBox::Ok);
00170     return 1;
00171   }
00172 
00173   /* Check if Vidalia is already running. */
00174   QString pidfile = vidalia.pidFile();
00175   if (is_vidalia_running(pidfile)) {
00176     vWarn("Detected another process with pid %1. Is Vidalia already running?")
00177                                                                .arg(get_pid());
00178     /* Let the user know another Vidalia is running and we are going to exit
00179      * now. */
00180     int ret = VMessageBox::critical(0, 
00181                 vApp->translate("Vidalia",
00182                   QT_TRANSLATE_NOOP("Vidalia", "Vidalia is already running")),
00183                 vApp->translate("Vidalia",
00184                   QT_TRANSLATE_NOOP("Vidalia", 
00185                     "Another Vidalia process is possibly already running. "
00186                     "If there really is not another Vidalia process running, "
00187                     "you can choose to continue anyway.\n\n"
00188                     "Would you like to continue starting Vidalia?")),
00189                 VMessageBox::Continue, VMessageBox::Quit|VMessageBox::Default);
00190     if (ret != VMessageBox::Continue) {
00191       /* Don't start a second instance of Vidalia */
00192       vError("Exiting duplicate Vidalia process.");
00193       return 1;
00194     }
00195   }
00196   write_pidfile(pidfile);
00197 
00198   /* Since we don't have a visible main window, if we were to display a
00199    * QMessageBox (for example, to display an error when starting or stopping
00200    * Tor) then the application would exit when that message box was closed.
00201    * Setting quitOnLastWindowClosed to false fixes this behavior. */
00202   Vidalia::setQuitOnLastWindowClosed(false);
00203 
00204   /* Create an instance of the main window  */
00205   MainWindow mainWin;
00206 
00207   /* Run Vidalia */
00208   int ret = vidalia.run();
00209 
00210   /* Vidalia exited, so cleanup our pidfile and return */
00211   QFile::remove(pidfile);
00212   vNotice("Vidalia is exiting cleanly (return code %1).").arg(ret);
00213 
00214 #if defined(USE_BREAKPAD)
00215   vInfo("Removing Breakpad exception handler.");
00216   CrashReporter::remove_exception_handler();
00217 #endif
00218 
00219   return ret;
00220 }
00221