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