/src/mozilla-central/toolkit/xre/nsNativeAppSupportUnix.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "nsNativeAppSupportBase.h" |
8 | | #include "nsCOMPtr.h" |
9 | | #include "nsXPCOM.h" |
10 | | #include "nsISupportsPrimitives.h" |
11 | | #include "nsIObserverService.h" |
12 | | #include "nsIAppStartup.h" |
13 | | #include "nsServiceManagerUtils.h" |
14 | | #include "prlink.h" |
15 | | #include "nsXREDirProvider.h" |
16 | | #include "nsReadableUtils.h" |
17 | | |
18 | | #include "nsIFile.h" |
19 | | #include "nsDirectoryServiceDefs.h" |
20 | | #include "nsICommandLineRunner.h" |
21 | | #include "nsIWindowMediator.h" |
22 | | #include "nsPIDOMWindow.h" |
23 | | #include "nsIDocShell.h" |
24 | | #include "nsIBaseWindow.h" |
25 | | #include "nsIWidget.h" |
26 | | #include "nsIWritablePropertyBag2.h" |
27 | | #include "nsIPrefService.h" |
28 | | #include "mozilla/Services.h" |
29 | | |
30 | | #include <stdlib.h> |
31 | | #include <glib.h> |
32 | | #include <glib-object.h> |
33 | | #include <gtk/gtk.h> |
34 | | |
35 | | #ifdef MOZ_X11 |
36 | | #include <gdk/gdkx.h> |
37 | | #include <X11/ICE/ICElib.h> |
38 | | #include <X11/SM/SMlib.h> |
39 | | #include <fcntl.h> |
40 | | #include "nsThreadUtils.h" |
41 | | |
42 | | #include <pwd.h> |
43 | | #endif |
44 | | |
45 | | #ifdef MOZ_ENABLE_DBUS |
46 | | #include <dbus/dbus.h> |
47 | | #endif |
48 | | |
49 | | #define MIN_GTK_MAJOR_VERSION 2 |
50 | | #define MIN_GTK_MINOR_VERSION 10 |
51 | | #define UNSUPPORTED_GTK_MSG "We're sorry, this application requires a version of the GTK+ library that is not installed on your computer.\n\n\ |
52 | | You have GTK+ %d.%d.\nThis application requires GTK+ %d.%d or newer.\n\n\ |
53 | | Please upgrade your GTK+ library if you wish to use this application." |
54 | | |
55 | | #if MOZ_X11 |
56 | | #undef IceSetIOErrorHandler |
57 | | #undef IceAddConnectionWatch |
58 | | #undef IceConnectionNumber |
59 | | #undef IceProcessMessages |
60 | | #undef IceGetConnectionContext |
61 | | #undef SmcInteractDone |
62 | | #undef SmcSaveYourselfDone |
63 | | #undef SmcInteractRequest |
64 | | #undef SmcCloseConnection |
65 | | #undef SmcOpenConnection |
66 | | #undef SmcSetProperties |
67 | | |
68 | | typedef IceIOErrorHandler (*IceSetIOErrorHandlerFn) (IceIOErrorHandler); |
69 | | typedef int (*IceAddConnectionWatchFn) (IceWatchProc, IcePointer); |
70 | | typedef int (*IceConnectionNumberFn) (IceConn); |
71 | | typedef IceProcessMessagesStatus (*IceProcessMessagesFn) (IceConn, IceReplyWaitInfo*, Bool*); |
72 | | typedef IcePointer (*IceGetConnectionContextFn) (IceConn); |
73 | | |
74 | | typedef void (*SmcInteractDoneFn) (SmcConn, Bool); |
75 | | typedef void (*SmcSaveYourselfDoneFn) (SmcConn, Bool); |
76 | | typedef int (*SmcInteractRequestFn) (SmcConn, int, SmcInteractProc, SmPointer); |
77 | | typedef SmcCloseStatus (*SmcCloseConnectionFn) (SmcConn, int, char**); |
78 | | typedef SmcConn (*SmcOpenConnectionFn) (char*, SmPointer, int, int, |
79 | | unsigned long, SmcCallbacks*, |
80 | | const char*, char**, int, char*); |
81 | | typedef void (*SmcSetPropertiesFn) (SmcConn, int, SmProp**); |
82 | | |
83 | | static IceSetIOErrorHandlerFn IceSetIOErrorHandlerPtr; |
84 | | static IceAddConnectionWatchFn IceAddConnectionWatchPtr; |
85 | | static IceConnectionNumberFn IceConnectionNumberPtr; |
86 | | static IceProcessMessagesFn IceProcessMessagesPtr; |
87 | | static IceGetConnectionContextFn IceGetConnectionContextPtr; |
88 | | static SmcInteractDoneFn SmcInteractDonePtr; |
89 | | static SmcSaveYourselfDoneFn SmcSaveYourselfDonePtr; |
90 | | static SmcInteractRequestFn SmcInteractRequestPtr; |
91 | | static SmcCloseConnectionFn SmcCloseConnectionPtr; |
92 | | static SmcOpenConnectionFn SmcOpenConnectionPtr; |
93 | | static SmcSetPropertiesFn SmcSetPropertiesPtr; |
94 | | |
95 | 0 | #define IceSetIOErrorHandler IceSetIOErrorHandlerPtr |
96 | 0 | #define IceAddConnectionWatch IceAddConnectionWatchPtr |
97 | 0 | #define IceConnectionNumber IceConnectionNumberPtr |
98 | 0 | #define IceProcessMessages IceProcessMessagesPtr |
99 | 0 | #define IceGetConnectionContext IceGetConnectionContextPtr |
100 | 0 | #define SmcInteractDone SmcInteractDonePtr |
101 | 0 | #define SmcSaveYourselfDone SmcSaveYourselfDonePtr |
102 | 0 | #define SmcInteractRequest SmcInteractRequestPtr |
103 | 0 | #define SmcCloseConnection SmcCloseConnectionPtr |
104 | 0 | #define SmcOpenConnection SmcOpenConnectionPtr |
105 | 0 | #define SmcSetProperties SmcSetPropertiesPtr |
106 | | |
107 | | enum ClientState { |
108 | | STATE_DISCONNECTED, |
109 | | STATE_REGISTERING, |
110 | | STATE_IDLE, |
111 | | STATE_INTERACTING, |
112 | | STATE_SHUTDOWN_CANCELLED |
113 | | }; |
114 | | |
115 | | static const char *gClientStateTable[] = { |
116 | | "DISCONNECTED", |
117 | | "REGISTERING", |
118 | | "IDLE", |
119 | | "INTERACTING", |
120 | | "SHUTDOWN_CANCELLED" |
121 | | }; |
122 | | |
123 | | static LazyLogModule sMozSMLog("MozSM"); |
124 | | #endif /* MOZ_X11 */ |
125 | | |
126 | | class nsNativeAppSupportUnix : public nsNativeAppSupportBase |
127 | | { |
128 | | public: |
129 | | #if MOZ_X11 |
130 | | nsNativeAppSupportUnix(): mSessionConnection(nullptr), |
131 | 0 | mClientState(STATE_DISCONNECTED) {}; |
132 | | ~nsNativeAppSupportUnix() |
133 | 0 | { |
134 | 0 | // this goes out of scope after "web-workers-shutdown" async shutdown phase |
135 | 0 | // so it's safe to disconnect here (i.e. the application won't lose data) |
136 | 0 | DisconnectFromSM(); |
137 | 0 | }; |
138 | | |
139 | | void DisconnectFromSM(); |
140 | | #endif |
141 | | NS_IMETHOD Start(bool* aRetVal) override; |
142 | | NS_IMETHOD Stop(bool *aResult) override; |
143 | | NS_IMETHOD Enable() override; |
144 | | |
145 | | private: |
146 | | #if MOZ_X11 |
147 | | static void SaveYourselfCB(SmcConn smc_conn, SmPointer client_data, |
148 | | int save_style, Bool shutdown, int interact_style, |
149 | | Bool fast); |
150 | | static void DieCB(SmcConn smc_conn, SmPointer client_data); |
151 | | static void InteractCB(SmcConn smc_conn, SmPointer client_data); |
152 | 0 | static void SaveCompleteCB(SmcConn smc_conn, SmPointer client_data) {}; |
153 | | static void ShutdownCancelledCB(SmcConn smc_conn, SmPointer client_data); |
154 | | void DoInteract(); |
155 | | void SetClientState(ClientState aState) |
156 | 0 | { |
157 | 0 | mClientState = aState; |
158 | 0 | MOZ_LOG(sMozSMLog, LogLevel::Debug, ("New state = %s\n", gClientStateTable[aState])); |
159 | 0 | } |
160 | | |
161 | | SmcConn mSessionConnection; |
162 | | ClientState mClientState; |
163 | | #endif |
164 | | }; |
165 | | |
166 | | #if MOZ_X11 |
167 | | static gboolean |
168 | | process_ice_messages(IceConn connection) |
169 | 0 | { |
170 | 0 | IceProcessMessagesStatus status; |
171 | 0 |
|
172 | 0 | status = IceProcessMessages(connection, nullptr, nullptr); |
173 | 0 |
|
174 | 0 | switch (status) { |
175 | 0 | case IceProcessMessagesSuccess: |
176 | 0 | return TRUE; |
177 | 0 |
|
178 | 0 | case IceProcessMessagesIOError: { |
179 | 0 | nsNativeAppSupportUnix *native = |
180 | 0 | static_cast<nsNativeAppSupportUnix *>(IceGetConnectionContext(connection)); |
181 | 0 | native->DisconnectFromSM(); |
182 | 0 | } |
183 | 0 | return FALSE; |
184 | 0 |
|
185 | 0 | case IceProcessMessagesConnectionClosed: |
186 | 0 | return FALSE; |
187 | 0 |
|
188 | 0 | default: |
189 | 0 | g_assert_not_reached (); |
190 | 0 | } |
191 | 0 | } |
192 | | |
193 | | static gboolean |
194 | | ice_iochannel_watch(GIOChannel *channel, GIOCondition condition, |
195 | | gpointer client_data) |
196 | 0 | { |
197 | 0 | return process_ice_messages(static_cast<IceConn>(client_data)); |
198 | 0 | } |
199 | | |
200 | | static void |
201 | | ice_connection_watch(IceConn connection, IcePointer client_data, |
202 | | Bool opening, IcePointer *watch_data) |
203 | 0 | { |
204 | 0 | guint watch_id; |
205 | 0 |
|
206 | 0 | if (opening) { |
207 | 0 | GIOChannel *channel; |
208 | 0 | int fd = IceConnectionNumber(connection); |
209 | 0 |
|
210 | 0 | fcntl(fd, F_SETFD, fcntl(fd, F_GETFD, 0) | FD_CLOEXEC); |
211 | 0 | channel = g_io_channel_unix_new(fd); |
212 | 0 | watch_id = g_io_add_watch(channel, |
213 | 0 | static_cast<GIOCondition>(G_IO_IN | G_IO_ERR), |
214 | 0 | ice_iochannel_watch, connection); |
215 | 0 | g_io_channel_unref(channel); |
216 | 0 |
|
217 | 0 | *watch_data = GUINT_TO_POINTER(watch_id); |
218 | 0 | } else { |
219 | 0 | watch_id = GPOINTER_TO_UINT(*watch_data); |
220 | 0 | g_source_remove(watch_id); |
221 | 0 | } |
222 | 0 | } |
223 | | |
224 | | static void |
225 | | ice_io_error_handler(IceConn connection) |
226 | 0 | { |
227 | 0 | // override the default handler which would exit the application; |
228 | 0 | // do nothing and let ICELib handle the failure of the connection gracefully. |
229 | 0 | } |
230 | | |
231 | | static void |
232 | | ice_init(void) |
233 | 0 | { |
234 | 0 | static bool initted = false; |
235 | 0 |
|
236 | 0 | if (!initted) { |
237 | 0 | IceSetIOErrorHandler(ice_io_error_handler); |
238 | 0 | IceAddConnectionWatch(ice_connection_watch, nullptr); |
239 | 0 | initted = true; |
240 | 0 | } |
241 | 0 | } |
242 | | |
243 | | void |
244 | | nsNativeAppSupportUnix::InteractCB(SmcConn smc_conn, SmPointer client_data) |
245 | 0 | { |
246 | 0 | nsNativeAppSupportUnix *self = |
247 | 0 | static_cast<nsNativeAppSupportUnix *>(client_data); |
248 | 0 |
|
249 | 0 | self->SetClientState(STATE_INTERACTING); |
250 | 0 |
|
251 | 0 | // We do this asynchronously, as we spin the event loop recursively if |
252 | 0 | // a dialog is displayed. If we do this synchronously, we don't finish |
253 | 0 | // processing the current ICE event whilst the dialog is displayed, which |
254 | 0 | // means we won't process any more. libsm hates us if we do the InteractDone |
255 | 0 | // with a pending ShutdownCancelled, and we would certainly like to handle Die |
256 | 0 | // whilst a dialog is displayed |
257 | 0 | NS_DispatchToCurrentThread( |
258 | 0 | NewRunnableMethod("nsNativeAppSupportUnix::DoInteract", |
259 | 0 | self, |
260 | 0 | &nsNativeAppSupportUnix::DoInteract)); |
261 | 0 | } |
262 | | |
263 | | void |
264 | | nsNativeAppSupportUnix::DoInteract() |
265 | 0 | { |
266 | 0 | nsCOMPtr<nsIObserverService> obsServ = |
267 | 0 | mozilla::services::GetObserverService(); |
268 | 0 | if (!obsServ) { |
269 | 0 | SmcInteractDone(mSessionConnection, False); |
270 | 0 | SmcSaveYourselfDone(mSessionConnection, True); |
271 | 0 | SetClientState(STATE_IDLE); |
272 | 0 | return; |
273 | 0 | } |
274 | 0 |
|
275 | 0 | nsCOMPtr<nsISupportsPRBool> cancelQuit = |
276 | 0 | do_CreateInstance(NS_SUPPORTS_PRBOOL_CONTRACTID); |
277 | 0 |
|
278 | 0 | bool abortQuit = false; |
279 | 0 | if (cancelQuit) { |
280 | 0 | cancelQuit->SetData(false); |
281 | 0 | obsServ->NotifyObservers(cancelQuit, "quit-application-requested", nullptr); |
282 | 0 |
|
283 | 0 | cancelQuit->GetData(&abortQuit); |
284 | 0 | } |
285 | 0 |
|
286 | 0 | if (!abortQuit && mClientState == STATE_DISCONNECTED) { |
287 | 0 | // The session manager disappeared, whilst we were interacting, so |
288 | 0 | // quit now |
289 | 0 | nsCOMPtr<nsIAppStartup> appService = |
290 | 0 | do_GetService("@mozilla.org/toolkit/app-startup;1"); |
291 | 0 |
|
292 | 0 | if (appService) { |
293 | 0 | appService->Quit(nsIAppStartup::eForceQuit); |
294 | 0 | } |
295 | 0 | } else { |
296 | 0 | if (mClientState != STATE_SHUTDOWN_CANCELLED) { |
297 | 0 | // Only do this if the shutdown wasn't cancelled |
298 | 0 | SmcInteractDone(mSessionConnection, !!abortQuit); |
299 | 0 | SmcSaveYourselfDone(mSessionConnection, !abortQuit); |
300 | 0 | } |
301 | 0 |
|
302 | 0 | SetClientState(STATE_IDLE); |
303 | 0 | } |
304 | 0 | } |
305 | | |
306 | | void |
307 | | nsNativeAppSupportUnix::SaveYourselfCB(SmcConn smc_conn, SmPointer client_data, |
308 | | int save_style, Bool shutdown, |
309 | | int interact_style, Bool fast) |
310 | 0 | { |
311 | 0 | nsNativeAppSupportUnix *self = |
312 | 0 | static_cast<nsNativeAppSupportUnix *>(client_data); |
313 | 0 |
|
314 | 0 | // Expect a SaveYourselfCB if we're registering a new client. |
315 | 0 | // All properties are already set in Start() so just reply with |
316 | 0 | // SmcSaveYourselfDone if the callback matches the expected signature. |
317 | 0 | // |
318 | 0 | // Ancient versions (?) of xsm do not follow such an early SaveYourself with |
319 | 0 | // SaveComplete. This is a problem if the application freezes interaction |
320 | 0 | // while waiting for a response to SmcSaveYourselfDone. So never freeze |
321 | 0 | // interaction when in STATE_REGISTERING. |
322 | 0 | // |
323 | 0 | // That aside, we could treat each combination of flags appropriately and not |
324 | 0 | // special-case this. |
325 | 0 | if (self->mClientState == STATE_REGISTERING) { |
326 | 0 | self->SetClientState(STATE_IDLE); |
327 | 0 |
|
328 | 0 | if (save_style == SmSaveLocal && interact_style == SmInteractStyleNone && |
329 | 0 | !shutdown && !fast) { |
330 | 0 | SmcSaveYourselfDone(self->mSessionConnection, True); |
331 | 0 | return; |
332 | 0 | } |
333 | 0 | } |
334 | 0 |
|
335 | 0 | if (self->mClientState == STATE_SHUTDOWN_CANCELLED) { |
336 | 0 | // The last shutdown request was cancelled whilst we were interacting, |
337 | 0 | // and we haven't finished interacting yet. Switch the state back again |
338 | 0 | self->SetClientState(STATE_INTERACTING); |
339 | 0 | } |
340 | 0 |
|
341 | 0 | nsCOMPtr<nsIObserverService> obsServ = |
342 | 0 | mozilla::services::GetObserverService(); |
343 | 0 | if (!obsServ) { |
344 | 0 | SmcSaveYourselfDone(smc_conn, True); |
345 | 0 | return; |
346 | 0 | } |
347 | 0 |
|
348 | 0 | bool status = false; |
349 | 0 | if (save_style != SmSaveGlobal) { |
350 | 0 | nsCOMPtr<nsISupportsPRBool> didSaveSession = |
351 | 0 | do_CreateInstance(NS_SUPPORTS_PRBOOL_CONTRACTID); |
352 | 0 |
|
353 | 0 | if (!didSaveSession) { |
354 | 0 | SmcSaveYourselfDone(smc_conn, True); |
355 | 0 | return; |
356 | 0 | } |
357 | 0 |
|
358 | 0 | // Notify observers to save the session state |
359 | 0 | didSaveSession->SetData(false); |
360 | 0 | obsServ->NotifyObservers(didSaveSession, "session-save", nullptr); |
361 | 0 |
|
362 | 0 | didSaveSession->GetData(&status); |
363 | 0 | } |
364 | 0 |
|
365 | 0 | // If the interact style permits us to, we are shutting down and we didn't |
366 | 0 | // manage to (or weren't asked to) save the local state, then notify the user |
367 | 0 | // in advance that we are doing to quit (assuming that we aren't already |
368 | 0 | // doing so) |
369 | 0 | if (!status && shutdown && interact_style != SmInteractStyleNone) { |
370 | 0 | if (self->mClientState != STATE_INTERACTING) { |
371 | 0 | SmcInteractRequest(smc_conn, SmDialogNormal, |
372 | 0 | nsNativeAppSupportUnix::InteractCB, client_data); |
373 | 0 | } |
374 | 0 | } else { |
375 | 0 | SmcSaveYourselfDone(smc_conn, True); |
376 | 0 | } |
377 | 0 | } |
378 | | |
379 | | void |
380 | | nsNativeAppSupportUnix::DieCB(SmcConn smc_conn, SmPointer client_data) |
381 | 0 | { |
382 | 0 | nsCOMPtr<nsIAppStartup> appService = |
383 | 0 | do_GetService("@mozilla.org/toolkit/app-startup;1"); |
384 | 0 |
|
385 | 0 | if (appService) { |
386 | 0 | appService->Quit(nsIAppStartup::eForceQuit); |
387 | 0 | } |
388 | 0 | // Quit causes the shutdown to begin but the shutdown process is asynchronous |
389 | 0 | // so we can't DisconnectFromSM() yet |
390 | 0 | } |
391 | | |
392 | | void |
393 | | nsNativeAppSupportUnix::ShutdownCancelledCB(SmcConn smc_conn, |
394 | | SmPointer client_data) |
395 | 0 | { |
396 | 0 | nsNativeAppSupportUnix *self = |
397 | 0 | static_cast<nsNativeAppSupportUnix *>(client_data); |
398 | 0 |
|
399 | 0 | // Interacting is the only time when we wouldn't already have called |
400 | 0 | // SmcSaveYourselfDone. Do that now, then set the state to make sure we |
401 | 0 | // don't send it again after finishing interacting |
402 | 0 | if (self->mClientState == STATE_INTERACTING) { |
403 | 0 | SmcSaveYourselfDone(smc_conn, False); |
404 | 0 | self->SetClientState(STATE_SHUTDOWN_CANCELLED); |
405 | 0 | } |
406 | 0 | } |
407 | | |
408 | | void |
409 | | nsNativeAppSupportUnix::DisconnectFromSM() |
410 | 0 | { |
411 | 0 | // the SM is free to exit any time after we disconnect, so callers must be |
412 | 0 | // sure to have reached a sufficiently advanced phase of shutdown that there |
413 | 0 | // is no risk of data loss: |
414 | 0 | // e.g. all async writes are complete by the end of "profile-before-change" |
415 | 0 | if (mSessionConnection) { |
416 | 0 | SetClientState(STATE_DISCONNECTED); |
417 | 0 | SmcCloseConnection(mSessionConnection, 0, nullptr); |
418 | 0 | mSessionConnection = nullptr; |
419 | 0 | gdk_x11_set_sm_client_id(nullptr); // follow gnome-client behaviour |
420 | 0 | } |
421 | 0 | } |
422 | | |
423 | | static void |
424 | | SetSMValue(SmPropValue& val, const nsCString& data) |
425 | 0 | { |
426 | 0 | val.value = static_cast<SmPointer>(const_cast<char*>(data.get())); |
427 | 0 | val.length = data.Length(); |
428 | 0 | } |
429 | | |
430 | | static void |
431 | | SetSMProperty(SmProp& prop, const char* name, const char* type, int numVals, |
432 | | SmPropValue vals[]) |
433 | 0 | { |
434 | 0 | prop.name = const_cast<char*>(name); |
435 | 0 | prop.type = const_cast<char*>(type); |
436 | 0 | prop.num_vals = numVals; |
437 | 0 | prop.vals = vals; |
438 | 0 | } |
439 | | #endif /* MOZ_X11 */ |
440 | | |
441 | | static void RemoveArg(char **argv) |
442 | 0 | { |
443 | 0 | do { |
444 | 0 | *argv = *(argv + 1); |
445 | 0 | ++argv; |
446 | 0 | } while (*argv); |
447 | 0 |
|
448 | 0 | --gArgc; |
449 | 0 | } |
450 | | |
451 | | NS_IMETHODIMP |
452 | | nsNativeAppSupportUnix::Start(bool *aRetVal) |
453 | 0 | { |
454 | 0 | NS_ASSERTION(gAppData, "gAppData must not be null."); |
455 | 0 |
|
456 | 0 | // The dbus library is used by both nsWifiScannerDBus and BluetoothDBusService, |
457 | 0 | // from diffrent threads. This could lead to race conditions if the dbus is not |
458 | 0 | // initialized before making any other library calls. |
459 | 0 | #ifdef MOZ_ENABLE_DBUS |
460 | 0 | dbus_threads_init_default(); |
461 | 0 | #endif |
462 | 0 |
|
463 | 0 | *aRetVal = true; |
464 | 0 |
|
465 | 0 | #ifdef MOZ_X11 |
466 | 0 | gboolean sm_disable = FALSE; |
467 | 0 | if (!getenv("SESSION_MANAGER")) { |
468 | 0 | sm_disable = TRUE; |
469 | 0 | } |
470 | 0 |
|
471 | 0 | nsAutoCString prev_client_id; |
472 | 0 |
|
473 | 0 | char **curarg = gArgv + 1; |
474 | 0 | while (*curarg) { |
475 | 0 | char *arg = *curarg; |
476 | 0 | if (arg[0] == '-' && arg[1] == '-') { |
477 | 0 | arg += 2; |
478 | 0 | if (!strcmp(arg, "sm-disable")) { |
479 | 0 | RemoveArg(curarg); |
480 | 0 | sm_disable = TRUE; |
481 | 0 | continue; |
482 | 0 | } else if (!strcmp(arg, "sm-client-id")) { |
483 | 0 | RemoveArg(curarg); |
484 | 0 | if (*curarg[0] != '-') { |
485 | 0 | prev_client_id = *curarg; |
486 | 0 | RemoveArg(curarg); |
487 | 0 | } |
488 | 0 | continue; |
489 | 0 | } |
490 | 0 | } |
491 | 0 |
|
492 | 0 | ++curarg; |
493 | 0 | } |
494 | 0 |
|
495 | 0 | if (prev_client_id.IsEmpty()) { |
496 | 0 | prev_client_id = getenv("DESKTOP_AUTOSTART_ID"); |
497 | 0 | } |
498 | 0 |
|
499 | 0 | // We don't want child processes to use the same ID |
500 | 0 | unsetenv("DESKTOP_AUTOSTART_ID"); |
501 | 0 |
|
502 | 0 | char *client_id = nullptr; |
503 | 0 | if (!sm_disable) { |
504 | 0 | PRLibrary *iceLib = PR_LoadLibrary("libICE.so.6"); |
505 | 0 | if (!iceLib) { |
506 | 0 | return NS_OK; |
507 | 0 | } |
508 | 0 | |
509 | 0 | PRLibrary *smLib = PR_LoadLibrary("libSM.so.6"); |
510 | 0 | if (!smLib) { |
511 | 0 | PR_UnloadLibrary(iceLib); |
512 | 0 | return NS_OK; |
513 | 0 | } |
514 | 0 | |
515 | 0 | IceSetIOErrorHandler = (IceSetIOErrorHandlerFn)PR_FindFunctionSymbol(iceLib, "IceSetIOErrorHandler"); |
516 | 0 | IceAddConnectionWatch = (IceAddConnectionWatchFn)PR_FindFunctionSymbol(iceLib, "IceAddConnectionWatch"); |
517 | 0 | IceConnectionNumber = (IceConnectionNumberFn)PR_FindFunctionSymbol(iceLib, "IceConnectionNumber"); |
518 | 0 | IceProcessMessages = (IceProcessMessagesFn)PR_FindFunctionSymbol(iceLib, "IceProcessMessages"); |
519 | 0 | IceGetConnectionContext = (IceGetConnectionContextFn)PR_FindFunctionSymbol(iceLib, "IceGetConnectionContext"); |
520 | 0 | if (!IceSetIOErrorHandler || !IceAddConnectionWatch || |
521 | 0 | !IceConnectionNumber || !IceProcessMessages || !IceGetConnectionContext) { |
522 | 0 | PR_UnloadLibrary(iceLib); |
523 | 0 | PR_UnloadLibrary(smLib); |
524 | 0 | return NS_OK; |
525 | 0 | } |
526 | 0 | |
527 | 0 | SmcInteractDone = (SmcInteractDoneFn)PR_FindFunctionSymbol(smLib, "SmcInteractDone"); |
528 | 0 | SmcSaveYourselfDone = (SmcSaveYourselfDoneFn)PR_FindFunctionSymbol(smLib, "SmcSaveYourselfDone"); |
529 | 0 | SmcInteractRequest = (SmcInteractRequestFn)PR_FindFunctionSymbol(smLib, "SmcInteractRequest"); |
530 | 0 | SmcCloseConnection = (SmcCloseConnectionFn)PR_FindFunctionSymbol(smLib, "SmcCloseConnection"); |
531 | 0 | SmcOpenConnection = (SmcOpenConnectionFn)PR_FindFunctionSymbol(smLib, "SmcOpenConnection"); |
532 | 0 | SmcSetProperties = (SmcSetPropertiesFn)PR_FindFunctionSymbol(smLib, "SmcSetProperties"); |
533 | 0 | if (!SmcInteractDone || !SmcSaveYourselfDone || !SmcInteractRequest || |
534 | 0 | !SmcCloseConnection || !SmcOpenConnection || !SmcSetProperties) { |
535 | 0 | PR_UnloadLibrary(iceLib); |
536 | 0 | PR_UnloadLibrary(smLib); |
537 | 0 | return NS_OK; |
538 | 0 | } |
539 | 0 | |
540 | 0 | ice_init(); |
541 | 0 |
|
542 | 0 | // all callbacks are mandatory in libSM 1.0, so listen even if we don't care. |
543 | 0 | unsigned long mask = SmcSaveYourselfProcMask | SmcDieProcMask | |
544 | 0 | SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask; |
545 | 0 |
|
546 | 0 | SmcCallbacks callbacks; |
547 | 0 | callbacks.save_yourself.callback = nsNativeAppSupportUnix::SaveYourselfCB; |
548 | 0 | callbacks.save_yourself.client_data = static_cast<SmPointer>(this); |
549 | 0 |
|
550 | 0 | callbacks.die.callback = nsNativeAppSupportUnix::DieCB; |
551 | 0 | callbacks.die.client_data = static_cast<SmPointer>(this); |
552 | 0 |
|
553 | 0 | callbacks.save_complete.callback = nsNativeAppSupportUnix::SaveCompleteCB; |
554 | 0 | callbacks.save_complete.client_data = nullptr; |
555 | 0 |
|
556 | 0 | callbacks.shutdown_cancelled.callback = |
557 | 0 | nsNativeAppSupportUnix::ShutdownCancelledCB; |
558 | 0 | callbacks.shutdown_cancelled.client_data = static_cast<SmPointer>(this); |
559 | 0 |
|
560 | 0 | char errbuf[256]; |
561 | 0 | mSessionConnection = SmcOpenConnection(nullptr, this, SmProtoMajor, |
562 | 0 | SmProtoMinor, mask, &callbacks, |
563 | 0 | prev_client_id.get(), &client_id, |
564 | 0 | sizeof(errbuf), errbuf); |
565 | 0 | } |
566 | 0 |
|
567 | 0 | if (!mSessionConnection) { |
568 | 0 | return NS_OK; |
569 | 0 | } |
570 | 0 | |
571 | 0 | LogModule::Init(gArgc, gArgv); // need to make sure initialized before SetClientState |
572 | 0 | if (prev_client_id.IsEmpty() || |
573 | 0 | (client_id && !prev_client_id.Equals(client_id))) { |
574 | 0 | SetClientState(STATE_REGISTERING); |
575 | 0 | } else { |
576 | 0 | SetClientState(STATE_IDLE); |
577 | 0 | } |
578 | 0 |
|
579 | 0 | gdk_x11_set_sm_client_id(client_id); |
580 | 0 |
|
581 | 0 | // Set SM Properties |
582 | 0 | // SmCloneCommand, SmProgram, SmRestartCommand, SmUserID are required |
583 | 0 | // properties so must be set, and must have a sensible fallback value. |
584 | 0 |
|
585 | 0 | // Determine executable path to use for XSMP session restore |
586 | 0 |
|
587 | 0 | // Is there a request to suppress default binary launcher? |
588 | 0 | nsAutoCString path(getenv("MOZ_APP_LAUNCHER")); |
589 | 0 |
|
590 | 0 | if (path.IsEmpty()) { |
591 | 0 | NS_ASSERTION(gDirServiceProvider, "gDirServiceProvider is NULL! This shouldn't happen!"); |
592 | 0 | nsCOMPtr<nsIFile> executablePath; |
593 | 0 | nsresult rv; |
594 | 0 |
|
595 | 0 | bool dummy; |
596 | 0 | rv = gDirServiceProvider->GetFile(XRE_EXECUTABLE_FILE, &dummy, getter_AddRefs(executablePath)); |
597 | 0 |
|
598 | 0 | if (NS_SUCCEEDED(rv)) { |
599 | 0 | // Strip off the -bin suffix to get the shell script we should run; this is what Breakpad does |
600 | 0 | nsAutoCString leafName; |
601 | 0 | rv = executablePath->GetNativeLeafName(leafName); |
602 | 0 | if (NS_SUCCEEDED(rv) && StringEndsWith(leafName, NS_LITERAL_CSTRING("-bin"))) { |
603 | 0 | leafName.SetLength(leafName.Length() - strlen("-bin")); |
604 | 0 | executablePath->SetNativeLeafName(leafName); |
605 | 0 | } |
606 | 0 |
|
607 | 0 | executablePath->GetNativePath(path); |
608 | 0 | } |
609 | 0 | } |
610 | 0 |
|
611 | 0 | if (path.IsEmpty()) { |
612 | 0 | // can't determine executable path. Best fallback is name from |
613 | 0 | // application.ini but it might not resolve to the same executable at |
614 | 0 | // launch time. |
615 | 0 | path = gAppData->name; // will always be set |
616 | 0 | ToLowerCase(path); |
617 | 0 | MOZ_LOG(sMozSMLog, LogLevel::Warning, |
618 | 0 | ("Could not determine executable path. Falling back to %s.", path.get())); |
619 | 0 | } |
620 | 0 |
|
621 | 0 | SmProp propRestart, propClone, propProgram, propUser, *props[4]; |
622 | 0 | SmPropValue valsRestart[3], valsClone[1], valsProgram[1], valsUser[1]; |
623 | 0 | int n = 0; |
624 | 0 |
|
625 | 0 | NS_NAMED_LITERAL_CSTRING(kClientIDParam, "--sm-client-id"); |
626 | 0 |
|
627 | 0 | SetSMValue(valsRestart[0], path); |
628 | 0 | SetSMValue(valsRestart[1], kClientIDParam); |
629 | 0 | SetSMValue(valsRestart[2], nsDependentCString(client_id)); |
630 | 0 | SetSMProperty(propRestart, SmRestartCommand, SmLISTofARRAY8, 3, valsRestart); |
631 | 0 | props[n++] = &propRestart; |
632 | 0 |
|
633 | 0 | SetSMValue(valsClone[0], path); |
634 | 0 | SetSMProperty(propClone, SmCloneCommand, SmLISTofARRAY8, 1, valsClone); |
635 | 0 | props[n++] = &propClone; |
636 | 0 |
|
637 | 0 | nsAutoCString appName(gAppData->name); // will always be set |
638 | 0 | ToLowerCase(appName); |
639 | 0 |
|
640 | 0 | SetSMValue(valsProgram[0], appName); |
641 | 0 | SetSMProperty(propProgram, SmProgram, SmARRAY8, 1, valsProgram); |
642 | 0 | props[n++] = &propProgram; |
643 | 0 |
|
644 | 0 | nsAutoCString userName; // username that started the program |
645 | 0 | struct passwd* pw = getpwuid(getuid()); |
646 | 0 | if (pw && pw->pw_name) { |
647 | 0 | userName = pw->pw_name; |
648 | 0 | } else { |
649 | 0 | userName = NS_LITERAL_CSTRING("nobody"); |
650 | 0 | MOZ_LOG(sMozSMLog, LogLevel::Warning, |
651 | 0 | ("Could not determine user-name. Falling back to %s.", userName.get())); |
652 | 0 | } |
653 | 0 |
|
654 | 0 | SetSMValue(valsUser[0], userName); |
655 | 0 | SetSMProperty(propUser, SmUserID, SmARRAY8, 1, valsUser); |
656 | 0 | props[n++] = &propUser; |
657 | 0 |
|
658 | 0 | SmcSetProperties(mSessionConnection, n, props); |
659 | 0 |
|
660 | 0 | g_free(client_id); |
661 | 0 | #endif /* MOZ_X11 */ |
662 | 0 |
|
663 | 0 | return NS_OK; |
664 | 0 | } |
665 | | |
666 | | NS_IMETHODIMP |
667 | | nsNativeAppSupportUnix::Stop(bool *aResult) |
668 | 0 | { |
669 | 0 | NS_ENSURE_ARG(aResult); |
670 | 0 | *aResult = true; |
671 | 0 | return NS_OK; |
672 | 0 | } |
673 | | |
674 | | NS_IMETHODIMP |
675 | | nsNativeAppSupportUnix::Enable() |
676 | 0 | { |
677 | 0 | return NS_OK; |
678 | 0 | } |
679 | | |
680 | | nsresult |
681 | | NS_CreateNativeAppSupport(nsINativeAppSupport **aResult) |
682 | 0 | { |
683 | 0 | nsNativeAppSupportBase* native = new nsNativeAppSupportUnix(); |
684 | 0 | if (!native) |
685 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
686 | 0 | |
687 | 0 | *aResult = native; |
688 | 0 | NS_ADDREF(*aResult); |
689 | 0 |
|
690 | 0 | return NS_OK; |
691 | 0 | } |