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