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