nsNativeAppSupportUnix.cpp 22 KB

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