Line data Source code
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 : #define IceSetIOErrorHandler IceSetIOErrorHandlerPtr
96 : #define IceAddConnectionWatch IceAddConnectionWatchPtr
97 : #define IceConnectionNumber IceConnectionNumberPtr
98 : #define IceProcessMessages IceProcessMessagesPtr
99 : #define IceGetConnectionContext IceGetConnectionContextPtr
100 : #define SmcInteractDone SmcInteractDonePtr
101 : #define SmcSaveYourselfDone SmcSaveYourselfDonePtr
102 : #define SmcInteractRequest SmcInteractRequestPtr
103 : #define SmcCloseConnection SmcCloseConnectionPtr
104 : #define SmcOpenConnection SmcOpenConnectionPtr
105 : #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 1 : nsNativeAppSupportUnix(): mSessionConnection(nullptr),
131 1 : mClientState(STATE_DISCONNECTED) {};
132 0 : ~nsNativeAppSupportUnix()
133 0 : {
134 : // this goes out of scope after "web-workers-shutdown" async shutdown phase
135 : // 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);
142 : NS_IMETHOD Stop(bool *aResult);
143 : NS_IMETHOD Enable();
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 0 : void SetClientState(ClientState aState)
156 : {
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 0 : process_ice_messages(IceConn connection)
169 : {
170 : IceProcessMessagesStatus status;
171 :
172 0 : status = IceProcessMessages(connection, nullptr, nullptr);
173 :
174 0 : switch (status) {
175 : case IceProcessMessagesSuccess:
176 0 : return TRUE;
177 :
178 : case IceProcessMessagesIOError: {
179 : nsNativeAppSupportUnix *native =
180 0 : static_cast<nsNativeAppSupportUnix *>(IceGetConnectionContext(connection));
181 0 : native->DisconnectFromSM();
182 : }
183 0 : return FALSE;
184 :
185 : case IceProcessMessagesConnectionClosed:
186 0 : return FALSE;
187 :
188 : default:
189 0 : g_assert_not_reached ();
190 : }
191 : }
192 :
193 : static gboolean
194 0 : ice_iochannel_watch(GIOChannel *channel, GIOCondition condition,
195 : gpointer client_data)
196 : {
197 0 : return process_ice_messages(static_cast<IceConn>(client_data));
198 : }
199 :
200 : static void
201 0 : ice_connection_watch(IceConn connection, IcePointer client_data,
202 : Bool opening, IcePointer *watch_data)
203 : {
204 : guint watch_id;
205 :
206 0 : if (opening) {
207 : GIOChannel *channel;
208 0 : int fd = IceConnectionNumber(connection);
209 :
210 0 : fcntl(fd, F_SETFD, fcntl(fd, F_GETFD, 0) | FD_CLOEXEC);
211 0 : channel = g_io_channel_unix_new(fd);
212 : watch_id = g_io_add_watch(channel,
213 : static_cast<GIOCondition>(G_IO_IN | G_IO_ERR),
214 0 : ice_iochannel_watch, connection);
215 0 : g_io_channel_unref(channel);
216 :
217 0 : *watch_data = GUINT_TO_POINTER(watch_id);
218 : } else {
219 0 : watch_id = GPOINTER_TO_UINT(*watch_data);
220 0 : g_source_remove(watch_id);
221 : }
222 0 : }
223 :
224 : static void
225 0 : ice_io_error_handler(IceConn connection)
226 : {
227 : // override the default handler which would exit the application;
228 : // do nothing and let ICELib handle the failure of the connection gracefully.
229 0 : }
230 :
231 : static void
232 0 : ice_init(void)
233 : {
234 : static bool initted = false;
235 :
236 0 : if (!initted) {
237 0 : IceSetIOErrorHandler(ice_io_error_handler);
238 0 : IceAddConnectionWatch(ice_connection_watch, nullptr);
239 0 : initted = true;
240 : }
241 0 : }
242 :
243 : void
244 0 : nsNativeAppSupportUnix::InteractCB(SmcConn smc_conn, SmPointer client_data)
245 : {
246 : nsNativeAppSupportUnix *self =
247 0 : static_cast<nsNativeAppSupportUnix *>(client_data);
248 :
249 0 : self->SetClientState(STATE_INTERACTING);
250 :
251 : // We do this asynchronously, as we spin the event loop recursively if
252 : // a dialog is displayed. If we do this synchronously, we don't finish
253 : // processing the current ICE event whilst the dialog is displayed, which
254 : // means we won't process any more. libsm hates us if we do the InteractDone
255 : // with a pending ShutdownCancelled, and we would certainly like to handle Die
256 : // whilst a dialog is displayed
257 0 : NS_DispatchToCurrentThread(
258 0 : NewRunnableMethod("nsNativeAppSupportUnix::DoInteract",
259 : self,
260 0 : &nsNativeAppSupportUnix::DoInteract));
261 0 : }
262 :
263 : void
264 0 : nsNativeAppSupportUnix::DoInteract()
265 : {
266 : 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 : }
274 :
275 : nsCOMPtr<nsISupportsPRBool> cancelQuit =
276 0 : do_CreateInstance(NS_SUPPORTS_PRBOOL_CONTRACTID);
277 :
278 0 : bool abortQuit = false;
279 0 : if (cancelQuit) {
280 0 : cancelQuit->SetData(false);
281 0 : obsServ->NotifyObservers(cancelQuit, "quit-application-requested", nullptr);
282 :
283 0 : cancelQuit->GetData(&abortQuit);
284 : }
285 :
286 0 : if (!abortQuit && mClientState == STATE_DISCONNECTED) {
287 : // The session manager disappeared, whilst we were interacting, so
288 : // quit now
289 : nsCOMPtr<nsIAppStartup> appService =
290 0 : do_GetService("@mozilla.org/toolkit/app-startup;1");
291 :
292 0 : if (appService) {
293 0 : appService->Quit(nsIAppStartup::eForceQuit);
294 0 : }
295 : } else {
296 0 : if (mClientState != STATE_SHUTDOWN_CANCELLED) {
297 : // Only do this if the shutdown wasn't cancelled
298 0 : SmcInteractDone(mSessionConnection, !!abortQuit);
299 0 : SmcSaveYourselfDone(mSessionConnection, !abortQuit);
300 : }
301 :
302 0 : SetClientState(STATE_IDLE);
303 : }
304 : }
305 :
306 : void
307 0 : nsNativeAppSupportUnix::SaveYourselfCB(SmcConn smc_conn, SmPointer client_data,
308 : int save_style, Bool shutdown,
309 : int interact_style, Bool fast)
310 : {
311 : nsNativeAppSupportUnix *self =
312 0 : static_cast<nsNativeAppSupportUnix *>(client_data);
313 :
314 : // Expect a SaveYourselfCB if we're registering a new client.
315 : // All properties are already set in Start() so just reply with
316 : // SmcSaveYourselfDone if the callback matches the expected signature.
317 : //
318 : // Ancient versions (?) of xsm do not follow such an early SaveYourself with
319 : // SaveComplete. This is a problem if the application freezes interaction
320 : // while waiting for a response to SmcSaveYourselfDone. So never freeze
321 : // interaction when in STATE_REGISTERING.
322 : //
323 : // That aside, we could treat each combination of flags appropriately and not
324 : // special-case this.
325 0 : if (self->mClientState == STATE_REGISTERING) {
326 0 : self->SetClientState(STATE_IDLE);
327 :
328 0 : if (save_style == SmSaveLocal && interact_style == SmInteractStyleNone &&
329 0 : !shutdown && !fast) {
330 0 : SmcSaveYourselfDone(self->mSessionConnection, True);
331 0 : return;
332 : }
333 : }
334 :
335 0 : if (self->mClientState == STATE_SHUTDOWN_CANCELLED) {
336 : // The last shutdown request was cancelled whilst we were interacting,
337 : // and we haven't finished interacting yet. Switch the state back again
338 0 : self->SetClientState(STATE_INTERACTING);
339 : }
340 :
341 : nsCOMPtr<nsIObserverService> obsServ =
342 0 : mozilla::services::GetObserverService();
343 0 : if (!obsServ) {
344 0 : SmcSaveYourselfDone(smc_conn, True);
345 0 : return;
346 : }
347 :
348 0 : bool status = false;
349 0 : if (save_style != SmSaveGlobal) {
350 : nsCOMPtr<nsISupportsPRBool> didSaveSession =
351 0 : do_CreateInstance(NS_SUPPORTS_PRBOOL_CONTRACTID);
352 :
353 0 : if (!didSaveSession) {
354 0 : SmcSaveYourselfDone(smc_conn, True);
355 0 : return;
356 : }
357 :
358 : // Notify observers to save the session state
359 0 : didSaveSession->SetData(false);
360 0 : obsServ->NotifyObservers(didSaveSession, "session-save", nullptr);
361 :
362 0 : didSaveSession->GetData(&status);
363 : }
364 :
365 : // If the interact style permits us to, we are shutting down and we didn't
366 : // manage to (or weren't asked to) save the local state, then notify the user
367 : // in advance that we are doing to quit (assuming that we aren't already
368 : // doing so)
369 0 : if (!status && shutdown && interact_style != SmInteractStyleNone) {
370 0 : if (self->mClientState != STATE_INTERACTING) {
371 : SmcInteractRequest(smc_conn, SmDialogNormal,
372 0 : nsNativeAppSupportUnix::InteractCB, client_data);
373 : }
374 : } else {
375 0 : SmcSaveYourselfDone(smc_conn, True);
376 : }
377 : }
378 :
379 : void
380 0 : nsNativeAppSupportUnix::DieCB(SmcConn smc_conn, SmPointer client_data)
381 : {
382 : nsCOMPtr<nsIAppStartup> appService =
383 0 : do_GetService("@mozilla.org/toolkit/app-startup;1");
384 :
385 0 : if (appService) {
386 0 : appService->Quit(nsIAppStartup::eForceQuit);
387 : }
388 : // Quit causes the shutdown to begin but the shutdown process is asynchronous
389 : // so we can't DisconnectFromSM() yet
390 0 : }
391 :
392 : void
393 0 : nsNativeAppSupportUnix::ShutdownCancelledCB(SmcConn smc_conn,
394 : SmPointer client_data)
395 : {
396 : nsNativeAppSupportUnix *self =
397 0 : static_cast<nsNativeAppSupportUnix *>(client_data);
398 :
399 : // Interacting is the only time when we wouldn't already have called
400 : // SmcSaveYourselfDone. Do that now, then set the state to make sure we
401 : // 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 : }
406 0 : }
407 :
408 : void
409 0 : nsNativeAppSupportUnix::DisconnectFromSM()
410 : {
411 : // the SM is free to exit any time after we disconnect, so callers must be
412 : // sure to have reached a sufficiently advanced phase of shutdown that there
413 : // is no risk of data loss:
414 : // 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 : }
421 0 : }
422 :
423 : static void
424 0 : SetSMValue(SmPropValue& val, const nsCString& data)
425 : {
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 0 : SetSMProperty(SmProp& prop, const char* name, const char* type, int numVals,
432 : SmPropValue vals[])
433 : {
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 0 : static void RemoveArg(char **argv)
442 : {
443 0 : do {
444 0 : *argv = *(argv + 1);
445 0 : ++argv;
446 0 : } while (*argv);
447 :
448 0 : --gArgc;
449 0 : }
450 :
451 : NS_IMETHODIMP
452 1 : nsNativeAppSupportUnix::Start(bool *aRetVal)
453 : {
454 1 : NS_ASSERTION(gAppData, "gAppData must not be null.");
455 :
456 : // The dbus library is used by both nsWifiScannerDBus and BluetoothDBusService,
457 : // from diffrent threads. This could lead to race conditions if the dbus is not
458 : // initialized before making any other library calls.
459 : #ifdef MOZ_ENABLE_DBUS
460 1 : dbus_threads_init_default();
461 : #endif
462 :
463 : #if (MOZ_WIDGET_GTK == 2)
464 : if (gtk_major_version < MIN_GTK_MAJOR_VERSION ||
465 : (gtk_major_version == MIN_GTK_MAJOR_VERSION && gtk_minor_version < MIN_GTK_MINOR_VERSION)) {
466 : GtkWidget* versionErrDialog = gtk_message_dialog_new(nullptr,
467 : GtkDialogFlags(GTK_DIALOG_MODAL |
468 : GTK_DIALOG_DESTROY_WITH_PARENT),
469 : GTK_MESSAGE_ERROR,
470 : GTK_BUTTONS_OK,
471 : UNSUPPORTED_GTK_MSG,
472 : gtk_major_version,
473 : gtk_minor_version,
474 : MIN_GTK_MAJOR_VERSION,
475 : MIN_GTK_MINOR_VERSION);
476 : gtk_dialog_run(GTK_DIALOG(versionErrDialog));
477 : gtk_widget_destroy(versionErrDialog);
478 : MozExpectedExit();
479 : exit(0);
480 : }
481 : #endif
482 :
483 1 : *aRetVal = true;
484 :
485 : #ifdef MOZ_X11
486 1 : gboolean sm_disable = FALSE;
487 1 : if (!getenv("SESSION_MANAGER")) {
488 1 : sm_disable = TRUE;
489 : }
490 :
491 2 : nsAutoCString prev_client_id;
492 :
493 1 : char **curarg = gArgv + 1;
494 7 : while (*curarg) {
495 3 : char *arg = *curarg;
496 3 : if (arg[0] == '-' && arg[1] == '-') {
497 0 : arg += 2;
498 0 : if (!strcmp(arg, "sm-disable")) {
499 0 : RemoveArg(curarg);
500 0 : sm_disable = TRUE;
501 0 : continue;
502 0 : } else if (!strcmp(arg, "sm-client-id")) {
503 0 : RemoveArg(curarg);
504 0 : if (*curarg[0] != '-') {
505 0 : prev_client_id = *curarg;
506 0 : RemoveArg(curarg);
507 : }
508 0 : continue;
509 : }
510 : }
511 :
512 3 : ++curarg;
513 : }
514 :
515 1 : if (prev_client_id.IsEmpty()) {
516 1 : prev_client_id = getenv("DESKTOP_AUTOSTART_ID");
517 : }
518 :
519 : // We don't want child processes to use the same ID
520 1 : unsetenv("DESKTOP_AUTOSTART_ID");
521 :
522 1 : char *client_id = nullptr;
523 1 : if (!sm_disable) {
524 0 : PRLibrary *iceLib = PR_LoadLibrary("libICE.so.6");
525 0 : if (!iceLib) {
526 0 : return NS_OK;
527 : }
528 :
529 0 : PRLibrary *smLib = PR_LoadLibrary("libSM.so.6");
530 0 : if (!smLib) {
531 0 : PR_UnloadLibrary(iceLib);
532 0 : return NS_OK;
533 : }
534 :
535 0 : IceSetIOErrorHandler = (IceSetIOErrorHandlerFn)PR_FindFunctionSymbol(iceLib, "IceSetIOErrorHandler");
536 0 : IceAddConnectionWatch = (IceAddConnectionWatchFn)PR_FindFunctionSymbol(iceLib, "IceAddConnectionWatch");
537 0 : IceConnectionNumber = (IceConnectionNumberFn)PR_FindFunctionSymbol(iceLib, "IceConnectionNumber");
538 0 : IceProcessMessages = (IceProcessMessagesFn)PR_FindFunctionSymbol(iceLib, "IceProcessMessages");
539 0 : IceGetConnectionContext = (IceGetConnectionContextFn)PR_FindFunctionSymbol(iceLib, "IceGetConnectionContext");
540 0 : if (!IceSetIOErrorHandler || !IceAddConnectionWatch ||
541 0 : !IceConnectionNumber || !IceProcessMessages || !IceGetConnectionContext) {
542 0 : PR_UnloadLibrary(iceLib);
543 0 : PR_UnloadLibrary(smLib);
544 0 : return NS_OK;
545 : }
546 :
547 0 : SmcInteractDone = (SmcInteractDoneFn)PR_FindFunctionSymbol(smLib, "SmcInteractDone");
548 0 : SmcSaveYourselfDone = (SmcSaveYourselfDoneFn)PR_FindFunctionSymbol(smLib, "SmcSaveYourselfDone");
549 0 : SmcInteractRequest = (SmcInteractRequestFn)PR_FindFunctionSymbol(smLib, "SmcInteractRequest");
550 0 : SmcCloseConnection = (SmcCloseConnectionFn)PR_FindFunctionSymbol(smLib, "SmcCloseConnection");
551 0 : SmcOpenConnection = (SmcOpenConnectionFn)PR_FindFunctionSymbol(smLib, "SmcOpenConnection");
552 0 : SmcSetProperties = (SmcSetPropertiesFn)PR_FindFunctionSymbol(smLib, "SmcSetProperties");
553 0 : if (!SmcInteractDone || !SmcSaveYourselfDone || !SmcInteractRequest ||
554 0 : !SmcCloseConnection || !SmcOpenConnection || !SmcSetProperties) {
555 0 : PR_UnloadLibrary(iceLib);
556 0 : PR_UnloadLibrary(smLib);
557 0 : return NS_OK;
558 : }
559 :
560 0 : ice_init();
561 :
562 : // all callbacks are mandatory in libSM 1.0, so listen even if we don't care.
563 : unsigned long mask = SmcSaveYourselfProcMask | SmcDieProcMask |
564 0 : SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask;
565 :
566 : SmcCallbacks callbacks;
567 0 : callbacks.save_yourself.callback = nsNativeAppSupportUnix::SaveYourselfCB;
568 0 : callbacks.save_yourself.client_data = static_cast<SmPointer>(this);
569 :
570 0 : callbacks.die.callback = nsNativeAppSupportUnix::DieCB;
571 0 : callbacks.die.client_data = static_cast<SmPointer>(this);
572 :
573 0 : callbacks.save_complete.callback = nsNativeAppSupportUnix::SaveCompleteCB;
574 0 : callbacks.save_complete.client_data = nullptr;
575 :
576 0 : callbacks.shutdown_cancelled.callback =
577 : nsNativeAppSupportUnix::ShutdownCancelledCB;
578 0 : callbacks.shutdown_cancelled.client_data = static_cast<SmPointer>(this);
579 :
580 : char errbuf[256];
581 0 : mSessionConnection = SmcOpenConnection(nullptr, this, SmProtoMajor,
582 : SmProtoMinor, mask, &callbacks,
583 : prev_client_id.get(), &client_id,
584 : sizeof(errbuf), errbuf);
585 : }
586 :
587 1 : if (!mSessionConnection) {
588 1 : return NS_OK;
589 : }
590 :
591 0 : LogModule::Init(); // need to make sure initialized before SetClientState
592 0 : if (prev_client_id.IsEmpty() ||
593 0 : (client_id && !prev_client_id.Equals(client_id))) {
594 0 : SetClientState(STATE_REGISTERING);
595 : } else {
596 0 : SetClientState(STATE_IDLE);
597 : }
598 :
599 0 : gdk_x11_set_sm_client_id(client_id);
600 :
601 : // Set SM Properties
602 : // SmCloneCommand, SmProgram, SmRestartCommand, SmUserID are required
603 : // properties so must be set, and must have a sensible fallback value.
604 :
605 : // Determine executable path to use for XSMP session restore
606 :
607 : // Is there a request to suppress default binary launcher?
608 0 : nsAutoCString path(getenv("MOZ_APP_LAUNCHER"));
609 :
610 0 : if (path.IsEmpty()) {
611 0 : NS_ASSERTION(gDirServiceProvider, "gDirServiceProvider is NULL! This shouldn't happen!");
612 0 : nsCOMPtr<nsIFile> executablePath;
613 : nsresult rv;
614 :
615 : bool dummy;
616 0 : rv = gDirServiceProvider->GetFile(XRE_EXECUTABLE_FILE, &dummy, getter_AddRefs(executablePath));
617 :
618 0 : if (NS_SUCCEEDED(rv)) {
619 : // Strip off the -bin suffix to get the shell script we should run; this is what Breakpad does
620 0 : nsAutoCString leafName;
621 0 : rv = executablePath->GetNativeLeafName(leafName);
622 0 : if (NS_SUCCEEDED(rv) && StringEndsWith(leafName, NS_LITERAL_CSTRING("-bin"))) {
623 0 : leafName.SetLength(leafName.Length() - strlen("-bin"));
624 0 : executablePath->SetNativeLeafName(leafName);
625 : }
626 :
627 0 : executablePath->GetNativePath(path);
628 : }
629 : }
630 :
631 0 : if (path.IsEmpty()) {
632 : // can't determine executable path. Best fallback is name from
633 : // application.ini but it might not resolve to the same executable at
634 : // launch time.
635 0 : path = gAppData->name; // will always be set
636 0 : ToLowerCase(path);
637 0 : MOZ_LOG(sMozSMLog, LogLevel::Warning,
638 : ("Could not determine executable path. Falling back to %s.", path.get()));
639 : }
640 :
641 : SmProp propRestart, propClone, propProgram, propUser, *props[4];
642 : SmPropValue valsRestart[3], valsClone[1], valsProgram[1], valsUser[1];
643 0 : int n = 0;
644 :
645 0 : NS_NAMED_LITERAL_CSTRING(kClientIDParam, "--sm-client-id");
646 :
647 0 : SetSMValue(valsRestart[0], path);
648 0 : SetSMValue(valsRestart[1], kClientIDParam);
649 0 : SetSMValue(valsRestart[2], nsDependentCString(client_id));
650 0 : SetSMProperty(propRestart, SmRestartCommand, SmLISTofARRAY8, 3, valsRestart);
651 0 : props[n++] = &propRestart;
652 :
653 0 : SetSMValue(valsClone[0], path);
654 0 : SetSMProperty(propClone, SmCloneCommand, SmLISTofARRAY8, 1, valsClone);
655 0 : props[n++] = &propClone;
656 :
657 0 : nsAutoCString appName(gAppData->name); // will always be set
658 0 : ToLowerCase(appName);
659 :
660 0 : SetSMValue(valsProgram[0], appName);
661 0 : SetSMProperty(propProgram, SmProgram, SmARRAY8, 1, valsProgram);
662 0 : props[n++] = &propProgram;
663 :
664 0 : nsAutoCString userName; // username that started the program
665 0 : struct passwd* pw = getpwuid(getuid());
666 0 : if (pw && pw->pw_name) {
667 0 : userName = pw->pw_name;
668 : } else {
669 0 : userName = NS_LITERAL_CSTRING("nobody");
670 0 : MOZ_LOG(sMozSMLog, LogLevel::Warning,
671 : ("Could not determine user-name. Falling back to %s.", userName.get()));
672 : }
673 :
674 0 : SetSMValue(valsUser[0], userName);
675 0 : SetSMProperty(propUser, SmUserID, SmARRAY8, 1, valsUser);
676 0 : props[n++] = &propUser;
677 :
678 0 : SmcSetProperties(mSessionConnection, n, props);
679 :
680 0 : g_free(client_id);
681 : #endif /* MOZ_X11 */
682 :
683 0 : return NS_OK;
684 : }
685 :
686 : NS_IMETHODIMP
687 0 : nsNativeAppSupportUnix::Stop(bool *aResult)
688 : {
689 0 : NS_ENSURE_ARG(aResult);
690 0 : *aResult = true;
691 0 : return NS_OK;
692 : }
693 :
694 : NS_IMETHODIMP
695 1 : nsNativeAppSupportUnix::Enable()
696 : {
697 1 : return NS_OK;
698 : }
699 :
700 : nsresult
701 1 : NS_CreateNativeAppSupport(nsINativeAppSupport **aResult)
702 : {
703 1 : nsNativeAppSupportBase* native = new nsNativeAppSupportUnix();
704 1 : if (!native)
705 0 : return NS_ERROR_OUT_OF_MEMORY;
706 :
707 1 : *aResult = native;
708 1 : NS_ADDREF(*aResult);
709 :
710 1 : return NS_OK;
711 : }
|