Vidalia  0.3.1
vidalia/main.cpp
Go to the documentation of this file.
1 /*
2 ** This file is part of Vidalia, and is subject to the license terms in the
3 ** LICENSE file, found in the top level directory of this distribution. If you
4 ** did not receive the LICENSE file with this file, you may obtain it from the
5 ** Vidalia source package distributed by the Vidalia Project at
6 ** http://www.torproject.org/projects/vidalia.html. No part of Vidalia,
7 ** including this file, may be copied, modified, propagated, or distributed
8 ** except according to the terms described in the LICENSE file.
9 */
10 
11 /*
12 ** \file main.cpp
13 ** \brief Main Vidalia entry point
14 */
15 
16 #include "config.h"
17 #include "Vidalia.h"
18 #include "MainWindow.h"
19 #include "VMessageBox.h"
20 #if defined(USE_BREAKPAD)
21 #include "CrashReporter.h"
22 #endif
23 
24 #include "procutil.h"
25 #include "stringutil.h"
26 
27 #include <QObject>
28 #if defined(Q_OS_WIN32)
29 #include <QSysInfo>
30 #endif
31 #if defined(HAVE_SIGNAL_H)
32 #include <signal.h>
33 #endif
34 
35 #if defined(USE_BREAKPAD)
36 void
37 setup_crash_reporter()
38 {
39  /* Set the crash reporting application used to submit minidumps. On Windows,
40  * crashreporter.exe is assumed to live in the same directory as vidalia.exe.
41  */
42 #if defined(Q_OS_WIN32)
43  QString crashReporter = Vidalia::applicationDirPath() + "\\crashreporter.exe";
44 #elif defined(Q_OS_MAC)
45  QString crashReporter = Vidalia::applicationDirPath() + "/CrashReporter.app";
46 #else
47  QString crashReporter = Vidalia::applicationDirPath() + "/crashreporter";
48 #endif
49  if (! QFileInfo(crashReporter).isExecutable()) {
50  vWarn("Unable to find crash reporting application. Crash reporting will "
51  "be unavailable.");
52  return;
53  }
54  if (! CrashReporter::set_crash_reporter(crashReporter)) {
55  vWarn("Vidalia found the crash reporting application, but the path is "
56  "longer than your platform supports. Skipping crash reporting.");
57  return;
58  }
59 
60  /* Set the Vidalia executable and options used to restart Vidalia after a
61  * crash. We strip off the first argument in Vidalia::arguments(), since
62  * it's the application name again anyway. */
63  CrashReporter::set_restart_options(Vidalia::applicationFilePath(),
64  Vidalia::arguments().mid(1));
65 
66  /* Set the build version that gets saved with a minidump. */
68 
69  /* Install the exception handler and give it the location to which Breakpad
70  * should write its minidumps. */
71  QString dumpPath = Vidalia::dataDirectory() + "/crashreports";
73  vWarn("Unable to setup Breakpad exception handler. Crash reporting "
74  "will be unavailable.");
75  } else {
76  vInfo("Installed Breakpad exception handler.");
77  }
78 }
79 #endif
80 
81 extern "C" void
83 {
84 #ifdef HAVE_SIGNAL_H
85  if (signal == SIGINT || signal == SIGTERM)
86  vApp->quit();
87 #endif
88 }
89 
90 void
92 {
93 #if defined(HAVE_SIGACTION)
94  struct sigaction action;
95 
96  sigemptyset(&action.sa_mask);
97  action.sa_handler = signal_handler;
98  action.sa_flags = 0;
99 
100  if (sigaction(SIGINT, &action, NULL) < 0)
101  vWarn("Failed to install SIGINT handler.");
102  if (sigaction(SIGTERM, &action, NULL) < 0)
103  vWarn("Failed to install SIGTERM handler.");
104 #elif defined(HAVE_SIGNAL)
105  if (signal(SIGINT, signal_handler) == SIG_ERR)
106  vWarn("Failed to install SIGINT handler.");
107  if (signal(SIGTERM, signal_handler) == SIG_ERR)
108  vWarn("Failed to install SIGTERM handler.");
109 #endif
110 }
111 
112 /** Returns true if there is already another Vidalia process running. */
113 bool
114 is_vidalia_running(const QString &pidfile)
115 {
116  /* Read the pidfile and find out if that process still exists */
117  qint64 pid = read_pidfile(pidfile);
118  if (pid > 0) {
119 #if defined(Q_OS_WIN32)
120  if (QSysInfo::WindowsVersion == QSysInfo::WV_NT) {
121  /* We currently can't get a list of running processes on Windows NT, so
122  * be pessimistic and assume the existence of a nonzero pidfile means
123  * Vidalia is running. */
124  return true;
125  } else
126  return (is_process_running(pid));
127 #else
128  return (is_process_running(pid));
129 #endif
130  }
131  return false;
132 }
133 
134 /** Main application entry point. */
135 int
136 main(int argc, char *argv[])
137 {
138  Q_INIT_RESOURCE(vidalia);
139  QStringList args = char_array_to_stringlist(argv+1, argc-1);
140 
141  /* Construct the application object. Qt strips any command-line arguments
142  * that it recognizes in argv, so we'll pass a stringlist of the original
143  * list of command-line arguments too. */
144  Vidalia vidalia(args, argc, argv);
145  vNotice("Vidalia %1 using Qt %2").arg(Vidalia::version())
146  .arg(QT_VERSION_STR);
147 
148 #if defined(USE_BREAKPAD)
149  /* Set up the crash reporting application and exception handlers. */
150  setup_crash_reporter();
151 #endif
152 #if defined(USE_MARBLE) && defined(Q_OS_WIN32)
153  vApp->addLibraryPath(vApp->applicationDirPath() + "/plugins/qt");
154 #endif
155 
156  /* Install a signal handler to clean up properly after a catching a
157  * SIGINT or SIGTERM. */
159 
160  /* Validate any command-line arguments, or show usage message box, if
161  * necessary. */
162  QString errmsg;
163  if (vidalia.showUsage()) {
165  return 0;
166  } else if (!vidalia.validateArguments(errmsg)) {
167  vError("Unable to apply command-line arguments: %1").arg(errmsg);
169  vApp->translate("Vidalia",
170  QT_TRANSLATE_NOOP("Vidalia", "Invalid Argument")), errmsg,
172  return 1;
173  }
174 
175  /* Check if Vidalia is already running. */
176  QString pidfile = vidalia.pidFile();
177  if (is_vidalia_running(pidfile)) {
178  vWarn("Detected another process with pid %1. Is Vidalia already running?")
179  .arg(get_pid());
180  /* Let the user know another Vidalia is running and we are going to exit
181  * now. */
182  int ret = VMessageBox::critical(0,
183  vApp->translate("Vidalia",
184  QT_TRANSLATE_NOOP("Vidalia", "Vidalia is already running")),
185  vApp->translate("Vidalia",
186  QT_TRANSLATE_NOOP("Vidalia",
187  "Another Vidalia process is possibly already running. "
188  "If there really is not another Vidalia process running, "
189  "you can choose to continue anyway.\n\n"
190  "Would you like to continue starting Vidalia?")),
191  VMessageBox::Continue, VMessageBox::Quit|VMessageBox::Default);
192  if (ret != VMessageBox::Continue) {
193  /* Don't start a second instance of Vidalia */
194  vError("Exiting duplicate Vidalia process.");
195  return 1;
196  }
197  }
198  write_pidfile(pidfile);
199 
200  /* Since we don't have a visible main window, if we were to display a
201  * QMessageBox (for example, to display an error when starting or stopping
202  * Tor) then the application would exit when that message box was closed.
203  * Setting quitOnLastWindowClosed to false fixes this behavior. */
204  Vidalia::setQuitOnLastWindowClosed(false);
205 
206  /* Create an instance of the main window */
207  MainWindow *mainWin = new MainWindow();
208 
209  /* Run Vidalia */
210  int ret = vidalia.run();
211 
212  delete mainWin;;
213 
214  /* Vidalia exited, so cleanup our pidfile and return */
215  QFile::remove(pidfile);
216  vNotice("Vidalia is exiting cleanly (return code %1).").arg(ret);
217 
218 #if defined(USE_BREAKPAD)
219  vInfo("Removing Breakpad exception handler.");
221 #endif
222 
223  return ret;
224 }
225 
static int run()
Definition: Vidalia.cpp:157
void install_signal_handler()
bool install_exception_handler(const QString &dumpPath)
void remove_exception_handler(void)
qint64 read_pidfile(const QString &pidFileName, QString *errmsg)
Definition: procutil.cpp:89
static QString pidFile()
Definition: Vidalia.cpp:378
#define vInfo(fmt)
Definition: Vidalia.h:40
qint64 get_pid()
Definition: procutil.cpp:33
bool write_pidfile(const QString &pidFileName, QString *errmsg)
Definition: procutil.cpp:66
bool validateArguments(QString &errmsg)
Definition: Vidalia.cpp:272
stop errmsg signal(TorSignal::Signal sig)
bool set_crash_reporter(const QString &crashReporter)
#define vNotice(fmt)
Definition: Vidalia.h:41
bool set_build_version(const QString &version)
bool is_process_running(qint64 pid)
Definition: procutil.cpp:44
static QString dataDirectory()
Definition: Vidalia.cpp:355
void signal_handler(int signal)
static QString version()
Definition: Vidalia.h:73
static int critical(QWidget *parent, QString caption, QString text, int button0, int button1=NoButton, int button2=NoButton)
#define VIDALIA_VERSION
Definition: config.h:17
#define vWarn(fmt)
Definition: Vidalia.h:42
int main(int argc, char *argv[])
#define vApp
Definition: Vidalia.h:37
bool is_vidalia_running(const QString &pidfile)
#define vError(fmt)
Definition: Vidalia.h:43
bool set_restart_options(const QString &executable, const QStringList &arguments)
QStringList char_array_to_stringlist(char **arr, int len)
Definition: stringutil.cpp:24
static bool showUsage()
Definition: Vidalia.cpp:187
static void showUsageMessageBox()
Definition: Vidalia.cpp:194