Vidalia
0.2.17
|
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