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