Coverage Report

Created: 2018-09-25 14:53

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