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