~ubuntu-branches/ubuntu/precise/open-vm-tools/precise

« back to all changes in this revision

Viewing changes to vmware-user/vmware-user.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Serge Hallyn
  • Date: 2011-03-31 14:20:05 UTC
  • mfrom: (1.4.3 upstream)
  • Revision ID: james.westby@ubuntu.com-20110331142005-3n9red91p7ogkweo
Tags: 2011.03.28-387002-0ubuntu1
* Merge latest upstream git tag.  This has the unlocked_ioctl change
  needed to fix dkms build failures (LP: #727342)
* Changes in debian/rules:
  - work around a bug in toolbox/Makefile, where install-exec-hook is
    not happening.  This needs to get fixed the right way.
  - don't install 'vmware-user' which seems to no longer exist
  - move /etc/xdg into open-vm-toolbox (which should be done using .install)
* debian/open-vm-tools.init: add 'modprobe [-r] vmblock'. (LP: #332323)
* debian/rules and debian/open-vm-toolbox.lintian-overrides:
  - Make vmware-user-suid-wrapper suid-root (LP: #332323)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*********************************************************
2
 
 * Copyright (C) 2009 VMware, Inc. All rights reserved.
3
 
 *
4
 
 * This program is free software; you can redistribute it and/or modify it
5
 
 * under the terms of the GNU Lesser General Public License as published
6
 
 * by the Free Software Foundation version 2.1 and no later version.
7
 
 *
8
 
 * This program is distributed in the hope that it will be useful, but
9
 
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10
 
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
11
 
 * License for more details.
12
 
 *
13
 
 * You should have received a copy of the GNU Lesser General Public License
14
 
 * along with this program; if not, write to the Free Software Foundation, Inc.,
15
 
 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
16
 
 *
17
 
 *********************************************************/
18
 
 
19
 
/**
20
 
 * @file vmware-user.cpp
21
 
 *
22
 
 * The linux vmware-user app. It's a hidden window app that is supposed
23
 
 * to run on session start. It handles tools features which we want
24
 
 * active all the time, but don't want to impose a visible window on the
25
 
 * user.
26
 
 */
27
 
 
28
 
#include "copyPasteDnDWrapper.h"
29
 
 
30
 
extern "C" {
31
 
#include <errno.h>
32
 
#include <fcntl.h>
33
 
#include <string.h>
34
 
#include <stdlib.h>
35
 
#include <pwd.h>
36
 
#include <unistd.h>
37
 
#include <glib.h>
38
 
#include <gtk/gtkinvisible.h>
39
 
#include <locale.h>
40
 
#if defined(__FreeBSD__) && (!defined(USING_AUTOCONF) || defined(HAVE_SYSLIMITS_H))
41
 
#include <syslimits.h>  // PATH_MAX
42
 
#endif
43
 
 
44
 
#include "vmwareuserInt.h"
45
 
#include "vm_assert.h"
46
 
#include "eventManager.h"
47
 
#include "hgfsServerManager.h"
48
 
#include "vmcheck.h"
49
 
#include "debug.h"
50
 
#include "rpcin.h"
51
 
#include "vmsignal.h"
52
 
#include "foundryToolsDaemon.h"
53
 
#include "strutil.h"
54
 
#include "conf.h" // for Conf_Load()
55
 
#include "dnd.h"
56
 
#include "syncDriver.h"
57
 
#include "str.h"
58
 
#include "guestApp.h"
59
 
#include "unity.h"
60
 
#include "ghIntegration.h"
61
 
#include "resolution.h"
62
 
#include "system.h"
63
 
 
64
 
#include "vm_atomic.h"
65
 
#include "hostinfo.h"
66
 
#include "vmwareuser_version.h"
67
 
#include "embed_version.h"
68
 
#include "vmware/guestrpc/tclodefs.h"
69
 
} // extern "C"
70
 
VM_EMBED_VERSION(VMWAREUSER_VERSION_STRING);
71
 
 
72
 
#ifdef _DEBUG
73
 
#define new DEBUG_NEW
74
 
#undef THIS_FILE
75
 
static char THIS_FILE[] = __FILE__;
76
 
#endif
77
 
 
78
 
#define VMUSER_TITLE    "vmware-user"
79
 
#define LOCK_ATOM_NAME  "vmware-user-lock"
80
 
 
81
 
#define INVALID_VALUE "Invalid option"
82
 
#define INVALID_OPTION "Invalid value"
83
 
#define INVALID_COMMAND "Invalid command format"
84
 
#define OPTION_BLOCK_FD "--blockFd"
85
 
 
86
 
/*
87
 
 * Forward Declarations
88
 
 */
89
 
void VMwareUser_OnDestroy(GtkWidget *widget, gpointer data);
90
 
GtkWidget* VMwareUser_CreateWindow(void);
91
 
gint EventQueuePump(gpointer data);
92
 
 
93
 
Bool VMwareUserRpcInResetCB    (RpcInData *data);
94
 
Bool VMwareUserRpcInSetOptionCB(char const **result, size_t *resultLen,
95
 
                                const char *name, const char *args,
96
 
                                size_t argsSize, void *clientData);
97
 
Bool VMwareUserRpcInCapRegCB   (char const **result, size_t *resultLen,
98
 
                                const char *name, const char *args,
99
 
                                size_t argsSize, void *clientData);
100
 
void VMwareUserRpcInErrorCB    (void *clientdata, char const *status);
101
 
 
102
 
static Bool InitGroupLeader(Window *groupLeader, Window *rootWindow);
103
 
static Bool AcquireDisplayLock(void);
104
 
static Bool QueryX11Lock(Display *dpy, Window w, Atom lockAtom);
105
 
static void ReloadSelf(void);
106
 
static void VMwareUserRegisterCopyPaste(bool reg);
107
 
static void VMwareUserRegisterDnD(bool reg);
108
 
 
109
 
 
110
 
/*
111
 
 * Globals
112
 
 */
113
 
 
114
 
static Bool gOpenUrlRegistered;
115
 
static Bool gDnDRegistered;
116
 
static Bool gHgfsServerRegistered;
117
 
static pid_t gParentPid;
118
 
static char gLogFilePath[PATH_MAX];
119
 
static Bool gRpcInStarted;      // Set after ATR handshake.  Indicates RpcIn is a go.
120
 
 
121
 
/*
122
 
 * The following are flags set by our signal handler.  They are evaluated
123
 
 * in main() only if gtk_main() ever returns.
124
 
 */
125
 
static Bool gReloadSelf;        // Set by SIGUSR2 + error handlers; triggers reload.
126
 
static Bool gYieldBlock;        // Set by SIGUSR1; triggers DND shutdown
127
 
static Bool gSigExit;           // Set by all but SIGUSR1; triggers app shutdown
128
 
 
129
 
/*
130
 
 * From vmwareuserInt.h
131
 
 */
132
 
RpcIn *gRpcIn;
133
 
Display *gXDisplay;
134
 
GtkWidget *gUserMainWidget = NULL;
135
 
 
136
 
GtkWidget *gHGWnd;
137
 
GtkWidget *gGHWnd;
138
 
 
139
 
Window gXRoot;
140
 
DblLnkLst_Links *gEventQueue;
141
 
Bool optionDnD;
142
 
Bool gCanUseVMwareCtrl;
143
 
Bool gCanUseVMwareCtrlTopologySet;
144
 
guint gTimeoutId;
145
 
DnDBlockControl gBlockCtrl = { -1 };
146
 
 
147
 
/*
148
 
 * All signals that:
149
 
 * . Can terminate the process
150
 
 * . May occur even if the program has no bugs
151
 
 */
152
 
static int const gSignals[] = {
153
 
   SIGHUP,
154
 
   SIGINT,
155
 
   SIGQUIT,
156
 
   SIGTERM,
157
 
   SIGUSR1,     // yield vmblock, uninit DnD
158
 
   SIGUSR2,     // reload vmware-user
159
 
   SIGPIPE
160
 
};
161
 
 
162
 
 
163
 
/*
164
 
 *-----------------------------------------------------------------------------
165
 
 *
166
 
 * VMwareUserCleanupRpc  --
167
 
 *
168
 
 *      Unset capabilities and cleanup the backdoor.
169
 
 *
170
 
 * Results:
171
 
 *      None.
172
 
 *
173
 
 * Side effects:
174
 
 *      The application will close.
175
 
 *
176
 
 *-----------------------------------------------------------------------------
177
 
 */
178
 
 
179
 
void VMwareUserCleanupRpc(Bool isXError) // IN
180
 
{
181
 
   Debug("%s: enter\n", __FUNCTION__);
182
 
   if (gRpcIn) {
183
 
      Unity_UnregisterCaps();
184
 
      GHI_Cleanup();
185
 
      Unity_Cleanup();
186
 
      Resolution_Cleanup();
187
 
 
188
 
      if (gHgfsServerRegistered) {
189
 
         HgfsServerManager_Unregister(gRpcIn, TOOLS_DND_NAME);
190
 
         gHgfsServerRegistered = FALSE;
191
 
      }
192
 
 
193
 
      if (!RpcIn_stop(gRpcIn)) {
194
 
         Debug("%s: failed to stop RpcIn loop\n", __FUNCTION__);
195
 
      }
196
 
      if (gOpenUrlRegistered) {
197
 
         FoundryToolsDaemon_UnregisterOpenUrl();
198
 
         gOpenUrlRegistered = FALSE;
199
 
      }
200
 
 
201
 
      CopyPasteDnDWrapper *p = CopyPasteDnDWrapper::GetInstance();
202
 
 
203
 
      /*
204
 
       * We can't call the normal APIs to tear down DnD/CP because they
205
 
       * involve Xlib calls that can't be made after X IO error. So, use
206
 
       * an entry point that performs a subset of the cleanup we normally
207
 
       * do on a reset, to ensure that any file transfers in flight get
208
 
       * failed properly. See bug 458626.
209
 
       */
210
 
      if (p) {
211
 
         if (!isXError) {
212
 
            p->UnregisterDnD();
213
 
            p->UnregisterCP();
214
 
         } else {
215
 
            p->Cancel();
216
 
         }
217
 
      }
218
 
 
219
 
      RpcIn_Destruct(gRpcIn);
220
 
      gRpcIn = NULL;
221
 
   }
222
 
}
223
 
 
224
 
 
225
 
/*
226
 
 *-----------------------------------------------------------------------------
227
 
 *
228
 
 * VMwareUserSignalHandler  --
229
 
 *
230
 
 *      Handler for Posix signals. We do this to ensure that we exit gracefully.
231
 
 *
232
 
 * Results:
233
 
 *      None.
234
 
 *
235
 
 * Side effects:
236
 
 *      Application will break out of the gtk_main() loop.  One or more of the
237
 
 *      signal flags (gReloadSelf, gYieldBlock, gSigExit) may be set.  For all
238
 
 *      signals but SIGUSR1, VMwareUserCleanupRpc() will be called.
239
 
 *
240
 
 *-----------------------------------------------------------------------------
241
 
 */
242
 
 
243
 
void VMwareUserSignalHandler(int sig) // IN
244
 
{
245
 
   switch (sig) {
246
 
   case SIGUSR1:
247
 
      gYieldBlock = TRUE;
248
 
      break;
249
 
   case SIGUSR2:
250
 
      gReloadSelf = TRUE;
251
 
      gSigExit = TRUE;
252
 
      break;
253
 
   default:
254
 
      gSigExit = TRUE;
255
 
   }
256
 
 
257
 
#if defined(HAVE_GTKMM)
258
 
   Gtk::Main::quit();
259
 
#else
260
 
   gtk_main_quit();
261
 
#endif
262
 
}
263
 
 
264
 
 
265
 
/*
266
 
 *-----------------------------------------------------------------------------
267
 
 *
268
 
 * VMwareUser_OnDestroy  --
269
 
 *
270
 
 *      Callback for the gtk signal "destroy" on the main window.
271
 
 *      Exit the gtk loop, causing main() to exit.
272
 
 *
273
 
 * Results:
274
 
 *      None.
275
 
 *
276
 
 * Side effects:
277
 
 *      The application will close.
278
 
 *
279
 
 *-----------------------------------------------------------------------------
280
 
 */
281
 
 
282
 
void
283
 
VMwareUser_OnDestroy(GtkWidget *widget, // IN: Unused
284
 
                     gpointer data)     // IN: Unused
285
 
{
286
 
#if defined(HAVE_GTKMM)
287
 
   Gtk::Main::quit();
288
 
#else
289
 
   gtk_main_quit();
290
 
#endif
291
 
}
292
 
 
293
 
 
294
 
/*
295
 
 *-----------------------------------------------------------------------------
296
 
 *
297
 
 * EventQueuePump  --
298
 
 *
299
 
 *      Handle events in the event queue. This function is re-registered as a
300
 
 *      gtk_timeout every time, since we only want to be called when it is time
301
 
 *      for the next event in the queue.
302
 
 *
303
 
 * Results:
304
 
 *      1 if there were no problems, 0 otherwise
305
 
 *
306
 
 * Side effects:
307
 
 *      The events in the queue will be called, they could do anything.
308
 
 *
309
 
 *-----------------------------------------------------------------------------
310
 
 */
311
 
 
312
 
gint
313
 
EventQueuePump(gpointer data) // IN: Unused
314
 
{
315
 
   int ret;
316
 
   uint64 sleepUsecs;
317
 
 
318
 
   gtk_timeout_remove(gTimeoutId);
319
 
   ret = EventManager_ProcessNext(gEventQueue, &sleepUsecs);
320
 
   if (ret != 1) {
321
 
      Warning("Unexpected end of EventManager loop: returned value is %d.\n\n",
322
 
              ret);
323
 
      return 0;
324
 
   }
325
 
   gTimeoutId = gtk_timeout_add(sleepUsecs/1000, &EventQueuePump, NULL);
326
 
   return 1;
327
 
}
328
 
 
329
 
 
330
 
/*
331
 
 *-----------------------------------------------------------------------------
332
 
 *
333
 
 * VMwareUser_CreateWindow  --
334
 
 *
335
 
 *      Create and initializes a hidden input only window for dnd and cp.
336
 
 *
337
 
 * Results:
338
 
 *      An invisible gtk widget.
339
 
 *
340
 
 * Side effects:
341
 
 *      None.
342
 
 *
343
 
 *-----------------------------------------------------------------------------
344
 
 */
345
 
 
346
 
GtkWidget*
347
 
VMwareUser_CreateWindow(void)
348
 
{
349
 
   GtkWidget *wnd;
350
 
 
351
 
   wnd = gtk_invisible_new();
352
 
   gtk_signal_connect(GTK_OBJECT(wnd), "destroy",
353
 
                      GTK_SIGNAL_FUNC(VMwareUser_OnDestroy), NULL);
354
 
   return wnd;
355
 
}
356
 
 
357
 
 
358
 
/*
359
 
 *-----------------------------------------------------------------------------
360
 
 *
361
 
 * VMwareUserRpcInResetCB  --
362
 
 *
363
 
 *      Callback called when the vmx has done a reset on the backdoor channel
364
 
 *
365
 
 * Results:
366
 
 *      TRUE if we reply successfully, FALSE otherwise
367
 
 *
368
 
 * Side effects:
369
 
 *      Send an "ATR" to thru the backdoor.
370
 
 *
371
 
 *-----------------------------------------------------------------------------
372
 
 */
373
 
 
374
 
Bool
375
 
VMwareUserRpcInResetCB(RpcInData *data)   // IN/OUT
376
 
{
377
 
   Debug("----------toolbox: Received 'reset' from vmware\n");
378
 
   CopyPasteDnDWrapper *p = CopyPasteDnDWrapper::GetInstance();
379
 
   if (p) {
380
 
      p->OnReset();
381
 
   }
382
 
 
383
 
   gRpcInStarted = TRUE;
384
 
 
385
 
   return RPCIN_SETRETVALS(data, "ATR " TOOLS_DND_NAME, TRUE);
386
 
}
387
 
 
388
 
 
389
 
/*
390
 
 *-----------------------------------------------------------------------------
391
 
 *
392
 
 * VMwareUserRpcInErrorCB  --
393
 
 *
394
 
 *      Callback called when there is some error on the backdoor channel.
395
 
 *
396
 
 * Results:
397
 
 *      This function calls the exit handler, VMwareUser_OnDestroy.
398
 
 *
399
 
 * Side effects:
400
 
 *      If the RpcIn channel had been up previously, as indicated by performing
401
 
 *      the ATR handshake, then this function will set the gSigExit and
402
 
 *      gReloadSelf flags.  This is only to attempt recovery from an error
403
 
 *      occurring while vmware-user was in its steady running state.
404
 
 *
405
 
 *-----------------------------------------------------------------------------
406
 
 */
407
 
 
408
 
void
409
 
VMwareUserRpcInErrorCB(void *clientdata, char const *status)
410
 
{
411
 
   Warning("Error in the RPC receive loop: %s\n", status);
412
 
   Warning("Another instance of %s may be running.\n\n", VMUSER_TITLE);
413
 
 
414
 
   if (gRpcInStarted) {
415
 
      Debug("Channel had been up previously.  Perhaps we're waking from hibernation?\n");
416
 
      gSigExit = TRUE;
417
 
      gReloadSelf = TRUE;
418
 
   }
419
 
 
420
 
   VMwareUser_OnDestroy(NULL, NULL);
421
 
}
422
 
 
423
 
 
424
 
/*
425
 
 *-----------------------------------------------------------------------------
426
 
 *
427
 
 * VMwareUserRpcInCapRegCB --
428
 
 *
429
 
 *      Handler for TCLO 'Capabilities_Register'.
430
 
 *
431
 
 * Results:
432
 
 *      TRUE if we can reply, FALSE otherwise.
433
 
 *
434
 
 * Side effects:
435
 
 *      None.
436
 
 *
437
 
 *-----------------------------------------------------------------------------
438
 
 */
439
 
 
440
 
Bool
441
 
VMwareUserRpcInCapRegCB(char const **result,     // OUT
442
 
                        size_t *resultLen,       // OUT
443
 
                        const char *name,        // IN
444
 
                        const char *args,        // IN
445
 
                        size_t argsSize,         // Unused
446
 
                        void *clientData)        // Unused
447
 
{
448
 
   Debug("VMwareUserRpcInCapRegCB got called\n");
449
 
 
450
 
   if (!gOpenUrlRegistered) {
451
 
      gOpenUrlRegistered = FoundryToolsDaemon_RegisterOpenUrl(gRpcIn);
452
 
   } else {
453
 
      FoundryToolsDaemon_RegisterOpenUrlCapability();
454
 
   }
455
 
 
456
 
   VMwareUserRegisterDnD(TRUE);
457
 
   VMwareUserRegisterCopyPaste(TRUE);
458
 
 
459
 
   if (!HgfsServerManager_CapReg(TOOLS_DND_NAME, gHgfsServerRegistered)) {
460
 
      Debug("VMwareUserRpcInCapRegCB: Failed to register HGFS server capability.\n");
461
 
   }
462
 
 
463
 
   Unity_RegisterCaps();
464
 
   Resolution_RegisterCaps();
465
 
 
466
 
   return RpcIn_SetRetVals(result, resultLen, "", TRUE);
467
 
}
468
 
 
469
 
 
470
 
/*
471
 
 *-----------------------------------------------------------------------------
472
 
 *
473
 
 * VMwareUserRegisterCopyPaste
474
 
 *
475
 
 *      Call the CopyPasteDnDWrapper singleton to register, or unregister,
476
 
 *      copy and paste with the host. The wrapper class will try
477
 
 *      whatever versions are supported, in highest to lowest order when
478
 
 *      registering.
479
 
 *
480
 
 * Results:
481
 
 *      None.
482
 
 *
483
 
 * Side effects:
484
 
 *      Copy and paste capabilities will be (un)registered. 
485
 
 *
486
 
 *-----------------------------------------------------------------------------
487
 
 */
488
 
 
489
 
static void
490
 
VMwareUserRegisterCopyPaste(bool reg)  // IN: if TRUE, register, else unregister
491
 
{
492
 
   CopyPasteDnDWrapper *p = CopyPasteDnDWrapper::GetInstance();
493
 
   if (p) {
494
 
      p->SetUserData(static_cast<void *>(gUserMainWidget));
495
 
      if (reg) {
496
 
         p->RegisterCP();
497
 
      } else {
498
 
         p->UnregisterCP();
499
 
      }
500
 
   }
501
 
}
502
 
 
503
 
 
504
 
/*
505
 
 *-----------------------------------------------------------------------------
506
 
 *
507
 
 * VMwareUserRegisterDnD
508
 
 *
509
 
 *      Call the CopyPasteDnDWrapper singleton to register, or unregister,
510
 
 *      drag and drop with the host. The wrapper class will try
511
 
 *      whatever versions are supported, in highest to lowest order when
512
 
 *      registering.
513
 
 *
514
 
 * Results:
515
 
 *      None.
516
 
 *
517
 
 * Side effects:
518
 
 *      Drag and drop capabilities will be (un)registered.
519
 
 *
520
 
 *-----------------------------------------------------------------------------
521
 
 */
522
 
 
523
 
static void
524
 
VMwareUserRegisterDnD(bool reg)  // IN: if TRUE, register, else unregister
525
 
{
526
 
   CopyPasteDnDWrapper *p = CopyPasteDnDWrapper::GetInstance();
527
 
   if (p) {
528
 
      p->SetUserData(static_cast<void *>(gUserMainWidget));
529
 
      p->SetEventQueue(gEventQueue);
530
 
      if (reg) {
531
 
         p->RegisterDnD();
532
 
      } else {
533
 
         p->UnregisterDnD();
534
 
      }
535
 
   }
536
 
}
537
 
 
538
 
 
539
 
/*
540
 
 *-----------------------------------------------------------------------------
541
 
 *
542
 
 * VMwareUserRpcInSetOptionCB
543
 
 *
544
 
 *      Parse a "Set_Option" TCLO cmd from the vmx and update the local
545
 
 *      copy of the option.
546
 
 *
547
 
 * Results:
548
 
 *      TRUE if the set option command was executed.
549
 
 *      FALSE if something failed.
550
 
 *
551
 
 * Side effects:
552
 
 *      Start or stop processes (like time syncing) that could be affected
553
 
 *      by option's new value.
554
 
 *
555
 
 *-----------------------------------------------------------------------------
556
 
 */
557
 
 
558
 
Bool
559
 
VMwareUserRpcInSetOptionCB(char const **result,     // OUT
560
 
                           size_t *resultLen,       // OUT
561
 
                           const char *name,        // IN
562
 
                           const char *args,        // IN
563
 
                           size_t argsSize,         // Unused
564
 
                           void *clientData)        // Unused
565
 
{
566
 
   char *option;
567
 
   char *value;
568
 
   unsigned int index = 0;
569
 
   Bool ret = FALSE;
570
 
   const char *retStr = NULL;
571
 
 
572
 
   /* parse the option & value string */
573
 
   option = StrUtil_GetNextToken(&index, args, " ");
574
 
   if (!option) {
575
 
      retStr = INVALID_COMMAND;
576
 
      goto exit;
577
 
   }
578
 
   index++; // ignore leading space before value
579
 
   value = StrUtil_GetNextToken(&index, args, "");
580
 
   if (!value) {
581
 
      retStr = INVALID_COMMAND;
582
 
      goto free_option;
583
 
   } else if (strlen(value) == 0) {
584
 
      retStr = INVALID_COMMAND;
585
 
      goto free_value;
586
 
   }
587
 
 
588
 
   Debug("VMwareUserRpcInSetOptionCB got option [%s], value %s\n",
589
 
         option, value);
590
 
 
591
 
   /*
592
 
    * Register or unregister features based on the Tools option setting or
593
 
    * unsetting.
594
 
    */
595
 
   if (strcmp(option, TOOLSOPTION_COPYPASTE) == 0) {
596
 
      if (strcmp(value, "1") == 0) {
597
 
         VMwareUserRegisterCopyPaste(TRUE);
598
 
      } else if (strcmp(value, "0") == 0) {
599
 
         VMwareUserRegisterCopyPaste(FALSE);
600
 
      } else {
601
 
         retStr = INVALID_VALUE;
602
 
         goto free_value;
603
 
      }
604
 
   } else if (strcmp(option, TOOLSOPTION_ENABLEDND) == 0) {
605
 
      if (strcmp(value, "1") == 0) {
606
 
         optionDnD = TRUE;
607
 
         if (!gDnDRegistered) {
608
 
            DnD_Register(gHGWnd, gGHWnd);
609
 
            gDnDRegistered = TRUE;
610
 
         }
611
 
      } else if (strcmp(value, "0") == 0) {
612
 
         optionDnD = FALSE;
613
 
         if (gDnDRegistered) {
614
 
            DnD_Unregister(gHGWnd, gGHWnd);
615
 
            gDnDRegistered = FALSE;
616
 
         }
617
 
      } else {
618
 
         retStr = INVALID_VALUE;
619
 
         goto free_value;
620
 
      }
621
 
   } else {
622
 
         retStr = INVALID_OPTION;
623
 
         goto free_value;
624
 
   }
625
 
 
626
 
   ret = TRUE;
627
 
   retStr = "";
628
 
 free_value:
629
 
   free(value);
630
 
 free_option:
631
 
   free(option);
632
 
 exit:
633
 
   return RpcIn_SetRetVals(result, resultLen, retStr, ret);
634
 
}
635
 
 
636
 
 
637
 
/*
638
 
 *-----------------------------------------------------------------------------
639
 
 *
640
 
 * VMwareUserXIOErrorHandler --
641
 
 *
642
 
 *      Handler for all X I/O errors. Xlib documentation says we should not
643
 
 *      return when handling I/O errors.
644
 
 *
645
 
 * Results:
646
 
 *      On success, and assuming we're called inside the parent vmware-user
647
 
 *      process (see comment below), we attempt to restart ourselves.  On
648
 
 *      failure, we'll exit with EXIT_FAILURE.
649
 
 *
650
 
 * Side effects:
651
 
 *      This function does not return.
652
 
 *
653
 
 *-----------------------------------------------------------------------------
654
 
 */
655
 
 
656
 
int VMwareUserXIOErrorHandler(Display *dpy)
657
 
{
658
 
   pid_t my_pid = getpid();
659
 
 
660
 
   /*
661
 
    * ProcMgr_ExecAsync() needs to fork off a child to handle
662
 
    * watching the process being run.  When it dies, it will come
663
 
    * through here, so we don't want to let it shut down the Rpc
664
 
    */
665
 
   Debug("> %s\n", __FUNCTION__);
666
 
   if (my_pid == gParentPid) {
667
 
      VMwareUserCleanupRpc(TRUE);
668
 
      ReloadSelf();
669
 
      exit(EXIT_FAILURE);
670
 
   } else {
671
 
      Debug("%s hit from forked() child, not cleaning Rpc\n", __FUNCTION__);
672
 
      _exit(EXIT_FAILURE);
673
 
   }
674
 
 
675
 
   return 1;
676
 
}
677
 
 
678
 
 
679
 
/*
680
 
 *-----------------------------------------------------------------------------
681
 
 *
682
 
 * VMwareUserConfFileLoop --
683
 
 *
684
 
 *    Run the "conf file reload" loop
685
 
 *
686
 
 * Return value:
687
 
 *    Always TRUE.
688
 
 *
689
 
 * Side effects:
690
 
 *    None
691
 
 *
692
 
 *-----------------------------------------------------------------------------
693
 
 */
694
 
 
695
 
static Bool
696
 
VMwareUserConfFileLoop(void *clientData) // IN
697
 
{
698
 
   GuestApp_Dict **pConfDict = (GuestApp_Dict **) clientData;
699
 
 
700
 
   ASSERT(pConfDict);
701
 
 
702
 
   /*
703
 
    * With the addition of the Sync Driver we can get into a state
704
 
    * where the system drive is frozen, preventing the completion of
705
 
    * any disk-based I/O. The event that periodically reloads the conf
706
 
    * file then gets blocked, which blocks the main daemon thread and
707
 
    * prevents any further GuestRPC messages from getting
708
 
    * processed. This effectively deadlocks the tools daemon and among
709
 
    * other things makes it impossible to thaw disk I/O once it's been
710
 
    * frozen.
711
 
    *
712
 
    * So, we keep track of when the disks are frozen and skip doing disk
713
 
    * I/O during that time.
714
 
    */
715
 
   if (!SyncDriver_DrivesAreFrozen()) {
716
 
      if (Conf_ReloadFile(pConfDict)) {
717
 
         const char *pathName = GuestApp_GetDictEntry(*pConfDict, CONFNAME_LOGFILE);
718
 
 
719
 
         Debug_Set(GuestApp_GetDictEntryBool(*pConfDict, CONFNAME_LOG),
720
 
                   DEBUG_PREFIX);
721
 
 
722
 
         if (pathName) {
723
 
            /*
724
 
             * 2 reasons that should put pid into vmware-user log file name:
725
 
             *
726
 
             * 1. guestd runs as super user and creates log file with limited
727
 
             *    permission. If log in as non-root user, vmware-user has no
728
 
             *    permission to write to the log file. Put log to different
729
 
             *    file will resolve this.
730
 
             * 2. If user first log in as root and start vmware-user logging,
731
 
             *    the log file is still with limited permission. Later on
732
 
             *    if user re-log in as non-root user, vmware-user has no
733
 
             *    permission to that file. With pid in the log file name,
734
 
             *    everytime if vmware-user is launched, a new log file will
735
 
             *    be created with current account.
736
 
             */
737
 
            Str_Sprintf(gLogFilePath, sizeof gLogFilePath, "%s.%u",
738
 
                        pathName, (unsigned int)getpid());
739
 
            Debug_EnableToFile(gLogFilePath, FALSE);
740
 
         } else {
741
 
            Debug_EnableToFile(NULL, FALSE);
742
 
         }
743
 
      }
744
 
   }
745
 
 
746
 
   EventManager_Add(gEventQueue, CONF_POLL_TIME, VMwareUserConfFileLoop,
747
 
                    pConfDict);
748
 
   return TRUE;
749
 
}
750
 
 
751
 
 
752
 
/*
753
 
 *-----------------------------------------------------------------------------
754
 
 *
755
 
 * main --
756
 
 *
757
 
 *      This is main
758
 
 *
759
 
 * Results:
760
 
 *      Returns either EXIT_SUCCESS or EXIT_FAILURE appropriately.
761
 
 *
762
 
 * Side effects:
763
 
 *      The linux toolbox ui will run and do a variety of tricks for your
764
 
 *      amusement.
765
 
 *
766
 
 *-----------------------------------------------------------------------------
767
 
 */
768
 
 
769
 
int
770
 
main(int argc,         // IN
771
 
     char *argv[],     // IN
772
 
     char *envp[])     // IN
773
 
{
774
 
   struct sigaction olds[ARRAYSIZE(gSignals)];
775
 
   int index;
776
 
   GuestApp_Dict *confDict;
777
 
   const char *pathName;
778
 
   int blockFd = -1;
779
 
#ifdef USE_NOTIFY
780
 
   Bool notifyPresent = TRUE;
781
 
#endif
782
 
   Bool usingBlock;
783
 
 
784
 
   gOpenUrlRegistered = FALSE;
785
 
   gDnDRegistered = FALSE;
786
 
   gHgfsServerRegistered = FALSE;
787
 
   gReloadSelf = FALSE;
788
 
   gYieldBlock = FALSE;
789
 
   gSigExit = FALSE;
790
 
 
791
 
   Atomic_Init();
792
 
   g_thread_init(NULL);
793
 
 
794
 
   if (!VmCheck_IsVirtualWorld()) {
795
 
      Warning("vmware-user must be run inside a virtual machine.\n");
796
 
      return EXIT_SUCCESS;
797
 
   }
798
 
 
799
 
   confDict = Conf_Load();
800
 
 
801
 
   /*
802
 
    * We depend on the window title when performing (primitive) vmware-user
803
 
    * session detection, and unfortunately for us, GTK has a nasty habit of
804
 
    * retitling toplevel windows.  That said, we can control GTK's default
805
 
    * title by setting Glib's application or program name.
806
 
    *
807
 
    * XXX Consider using g_set_application_name("VMware User Agent") or
808
 
    * similar.
809
 
    */
810
 
   g_set_prgname(VMUSER_TITLE);
811
 
 
812
 
   /* Set to system locale. */
813
 
   setlocale(LC_CTYPE, "");
814
 
   gtk_set_locale();
815
 
#if defined(HAVE_GTKMM)
816
 
   Gtk::Main main(&argc, &argv);
817
 
#else
818
 
   gtk_init(&argc, &argv);
819
 
#endif
820
 
 
821
 
 
822
 
   /*
823
 
    * Running more than 1 VMware user process (vmware-user) per X11 session
824
 
    * invites bad juju.  The following routine ensures that only one instance
825
 
    * will run per session.
826
 
    *
827
 
    * NB:  The lock is tied to this process, so it disappears when we exit.
828
 
    * As such, there is no corresponding unlock routine.
829
 
    */
830
 
   if (AcquireDisplayLock() == FALSE) {
831
 
      Warning("Another instance of vmware-user already running.  Exiting.\n");
832
 
      return EXIT_FAILURE;
833
 
   }
834
 
 
835
 
   gParentPid = getpid();
836
 
 
837
 
   /*
838
 
    * Parse the command line.
839
 
    *
840
 
    * We do it by hand because getopt() doesn't handle long options, and
841
 
    * getopt_long is a GNU extension
842
 
    *
843
 
    * argv[0] is the program name, as usual
844
 
    */
845
 
 
846
 
   for (index = 1; index < argc; index++) {
847
 
      if (strncmp(argv[index], "-", 1) == 0) {
848
 
         if (strncmp(argv[index], OPTION_BLOCK_FD, sizeof OPTION_BLOCK_FD) == 0) {
849
 
            /*
850
 
             * vmware-user runs as current active account, and can not initialize
851
 
             * blocking driver if it is not root. If guestd autostarts vmware-user,
852
 
             * guestd will first initialize it and pass block fd in with -blockFd.
853
 
             */
854
 
            if (++index >= argc) {
855
 
               Warning("The \""OPTION_BLOCK_FD"\" option on the command line requires an "
856
 
                       "argument.\n");
857
 
            }
858
 
 
859
 
            if (!StrUtil_StrToInt(&blockFd, argv[index])) {
860
 
               Warning("The \""OPTION_BLOCK_FD"\" option on the command line requires a "
861
 
                       "valid integer.\n");
862
 
               blockFd = -1;
863
 
            }
864
 
            Debug("vmware-user got blockFd = %d\n", blockFd);
865
 
         } else {
866
 
            Warning("Invalid \"%s\" option on the command line.\n", argv[index]);
867
 
         }
868
 
      }
869
 
   }
870
 
 
871
 
   /*
872
 
    * vmware-user runs as current active account, and can not initialize blocking
873
 
    * driver if it is not root. If guestd autostarts vmware-user, guestd will first
874
 
    * initialize it and pass block fd in. If manually run vmware-user, here will
875
 
    * try to initialize the blocking driver.
876
 
    */
877
 
   usingBlock = blockFd >= 0 ?
878
 
                DnD_CompleteBlockInitialization(blockFd, &gBlockCtrl) :
879
 
                DnD_InitializeBlocking(&gBlockCtrl);
880
 
 
881
 
   if (!usingBlock) {
882
 
      Debug("%s: vmware-user failed to initialize blocking driver.\n",
883
 
            __FUNCTION__);
884
 
   }
885
 
 
886
 
   if (Signal_SetGroupHandler(gSignals, olds, ARRAYSIZE(gSignals),
887
 
                              VMwareUserSignalHandler) == 0 ) {
888
 
      Panic("vmware-user can't set signal handler\n");
889
 
   }
890
 
 
891
 
   Debug_Set(GuestApp_GetDictEntryBool(confDict, CONFNAME_LOG), DEBUG_PREFIX);
892
 
 
893
 
   pathName = GuestApp_GetDictEntry(confDict, CONFNAME_LOGFILE);
894
 
   if (pathName) {
895
 
      /*
896
 
       * 2 reasons that should put pid into vmware-user log file name:
897
 
       *
898
 
       * 1. guestd runs as super user and creates log file with limited
899
 
       *    permission. If log in as non-root user, vmware-user has no
900
 
       *    permission to write to the log file. Put log to different
901
 
       *    file will resolve this.
902
 
       * 2. If user first log in as root and start vmware-user logging,
903
 
       *    the log file is still with limited permission. Later on
904
 
       *    if user re-log in as non-root user, vmware-user has no
905
 
       *    permission to that file. With pid in the log file name,
906
 
       *    everytime if vmware-user is launched, a new log file will
907
 
       *    be created with current account.
908
 
       */
909
 
      Str_Sprintf(gLogFilePath, sizeof gLogFilePath, "%s.%u", pathName,
910
 
                  (unsigned int)getpid());
911
 
      Debug_EnableToFile(gLogFilePath, FALSE);
912
 
   } else {
913
 
      Debug_EnableToFile(NULL, FALSE);
914
 
   }
915
 
 
916
 
   gUserMainWidget = VMwareUser_CreateWindow();
917
 
 
918
 
   gHGWnd = VMwareUser_CreateWindow();
919
 
   gGHWnd = VMwareUser_CreateWindow();
920
 
   /*
921
 
    * I don't want to show the window, but I need it's X window to exist.
922
 
    */
923
 
   gtk_widget_realize(gUserMainWidget);
924
 
   gtk_widget_realize(gHGWnd);
925
 
   gtk_widget_realize(gGHWnd);
926
 
 
927
 
 
928
 
   gXDisplay = GDK_WINDOW_XDISPLAY(gUserMainWidget->window);
929
 
   gXRoot = RootWindow(gXDisplay, DefaultScreen(gXDisplay));
930
 
 
931
 
   gEventQueue = EventManager_Init();
932
 
   if (gEventQueue == NULL) {
933
 
      Warning("Unable to create the event queue.\n\n");
934
 
      return EXIT_FAILURE;
935
 
   }
936
 
 
937
 
   CopyPasteDnDWrapper *p = CopyPasteDnDWrapper::GetInstance();
938
 
   if (p) {
939
 
      p->SetUserData(static_cast<void *>(gUserMainWidget));
940
 
      p->SetBlockControl(&gBlockCtrl);
941
 
      p->SetEventQueue(gEventQueue);
942
 
      p->SetHGWnd(gHGWnd);
943
 
      p->SetGHWnd(gGHWnd);
944
 
   }
945
 
 
946
 
   EventManager_Add(gEventQueue, CONF_POLL_TIME, VMwareUserConfFileLoop,
947
 
                    &confDict);
948
 
 
949
 
   Unity_Init(confDict, NULL, NULL);
950
 
   GHI_Init(NULL, NULL);
951
 
   Resolution_Init(TOOLS_DND_NAME, gXDisplay);
952
 
 
953
 
#ifdef USE_NOTIFY
954
 
   if (!Notify_Init(confDict)) {
955
 
      Warning("Unable to initialize notification system.\n\n");
956
 
      notifyPresent = FALSE;
957
 
   }
958
 
 
959
 
   Modules_Init();
960
 
#endif
961
 
 
962
 
   gRpcIn = RpcIn_Construct(gEventQueue);
963
 
   if (gRpcIn == NULL) {
964
 
      Warning("Unable to create the RpcIn object.\n\n");
965
 
      return EXIT_FAILURE;
966
 
   }
967
 
 
968
 
   if (!RpcIn_start(gRpcIn, RPCIN_POLL_TIME, VMwareUserRpcInResetCB,
969
 
                    NULL, VMwareUserRpcInErrorCB, NULL)) {
970
 
      Warning("Unable to start the receive loop.\n\n");
971
 
      return EXIT_FAILURE;
972
 
   }
973
 
 
974
 
   RpcIn_RegisterCallback(gRpcIn, "Capabilities_Register",
975
 
                          VMwareUserRpcInCapRegCB, NULL);
976
 
   RpcIn_RegisterCallback(gRpcIn, "Set_Option",
977
 
                          VMwareUserRpcInSetOptionCB, NULL);
978
 
 
979
 
   Unity_InitBackdoor(gRpcIn);
980
 
   GHI_InitBackdoor(gRpcIn);
981
 
   Resolution_InitBackdoor(gRpcIn);
982
 
 
983
 
#if !defined(N_PLAT_NLM) && !defined(sun)
984
 
   {
985
 
      const char **nativeEnvp;
986
 
      char **utf8NativeEnvp;
987
 
 
988
 
      /*
989
 
       * Determine our pre-VMware wrapper native environment and pass that to
990
 
       * Foundry so it can spawn applications.
991
 
       */
992
 
 
993
 
      nativeEnvp = System_GetNativeEnviron(const_cast<const char **>(envp));
994
 
 
995
 
      /* Foundry takes its strings UTF-8. */
996
 
      utf8NativeEnvp = Unicode_AllocList(const_cast<char **>(nativeEnvp),
997
 
                                         -1,  // nativeEnvp is NULL terminated
998
 
                                         STRING_ENCODING_DEFAULT);
999
 
 
1000
 
      FoundryToolsDaemon_RegisterRoutines(gRpcIn,
1001
 
                                          &confDict,
1002
 
                                          gEventQueue,
1003
 
                                          utf8NativeEnvp,
1004
 
                                          FALSE);
1005
 
 
1006
 
      Unicode_FreeList(utf8NativeEnvp, -1);
1007
 
      System_FreeNativeEnviron(nativeEnvp);
1008
 
   }
1009
 
#endif
1010
 
 
1011
 
   gHgfsServerRegistered = HgfsServerManager_Register(gRpcIn, TOOLS_DND_NAME);
1012
 
 
1013
 
   /*
1014
 
    * Setup the some events and a pump for the EventManager.
1015
 
    * We use gtk_timeouts for this.
1016
 
    */
1017
 
   gTimeoutId = gtk_timeout_add(0, &EventQueuePump, NULL);
1018
 
 
1019
 
   XSetIOErrorHandler(VMwareUserXIOErrorHandler);
1020
 
 
1021
 
   Pointer_Register(gUserMainWidget);
1022
 
 
1023
 
   for (;;) {
1024
 
      /*
1025
 
       * We'll block here until the window is destroyed or a signal is received.
1026
 
       */
1027
 
#if defined(HAVE_GTKMM)
1028
 
      main.run();
1029
 
#else
1030
 
      gtk_main();
1031
 
#endif
1032
 
 
1033
 
      if (gSigExit) {
1034
 
         break;
1035
 
      }
1036
 
 
1037
 
      if (gYieldBlock) {
1038
 
         CopyPasteDnDWrapper *p = CopyPasteDnDWrapper::GetInstance();
1039
 
         if (p) {
1040
 
            p->UnregisterDnD();
1041
 
            p->UnregisterCP();
1042
 
         }
1043
 
 
1044
 
         if (DnD_BlockIsReady(&gBlockCtrl) &&
1045
 
             !DnD_UninitializeBlocking(&gBlockCtrl)) {
1046
 
            Debug("vmware-user failed to uninitialize blocking.\n");
1047
 
         }
1048
 
         gYieldBlock = FALSE;
1049
 
      }
1050
 
   }
1051
 
 
1052
 
   Signal_ResetGroupHandler(gSignals, olds, ARRAYSIZE(gSignals));
1053
 
 
1054
 
   if (DnD_BlockIsReady(&gBlockCtrl) &&
1055
 
       !DnD_UninitializeBlocking(&gBlockCtrl)) {
1056
 
      Debug("vmware-user failed to uninitialize blocking.\n");
1057
 
   }
1058
 
 
1059
 
#ifdef USE_NOTIFY
1060
 
   Modules_Cleanup();
1061
 
 
1062
 
   if (notifyPresent) {
1063
 
      Notify_Cleanup();
1064
 
   }
1065
 
#endif
1066
 
 
1067
 
   /*
1068
 
    * Clean up everything attached to the backdoor before waving goodbye.
1069
 
    */
1070
 
   VMwareUserCleanupRpc(FALSE);
1071
 
 
1072
 
   /*
1073
 
    * SIGUSR2 sets this to TRUE, indicating that we should relaunch ourselves.
1074
 
    * This is useful during a Tools upgrade where we'd like to automatically
1075
 
    * restart a new vmware-user binary.
1076
 
    *
1077
 
    * NB:  This just makes a best effort and relies on the user's PATH
1078
 
    * environment variable.  If it fails for any reason, then we'll just exit.
1079
 
    */
1080
 
   if (gReloadSelf) {
1081
 
      ReloadSelf();
1082
 
   }
1083
 
 
1084
 
   return EXIT_SUCCESS;
1085
 
}
1086
 
 
1087
 
 
1088
 
/*
1089
 
 *-----------------------------------------------------------------------------
1090
 
 *
1091
 
 * InitGroupLeader --
1092
 
 *
1093
 
 *      This routine sets a few properties related to our main window created
1094
 
 *      by {gdk,gtk}_init.  Specifically this routine sets the window title,
1095
 
 *      sets the override_redirect X11 property, and reparents it to the root
1096
 
 *      window,
1097
 
 *
1098
 
 *      In addition, this routine will return Xlib handles for the following
1099
 
 *      objects:
1100
 
 *        - Main or group leader window
1101
 
 *        - Display's root window
1102
 
 *
1103
 
 * Results:
1104
 
 *      TRUE on success, FALSE on failure.
1105
 
 *
1106
 
 * Side effects:
1107
 
 *      Errors may be sent to stderr.
1108
 
 *      Window will have a title of VMUSER_TITLE.
1109
 
 *      Window, if not already directly parented by the root, will be.
1110
 
 *
1111
 
 *      dpy will point to our default display (ex: $DISPLAY).
1112
 
 *      groupLeader will point to the window created by gtk_init().
1113
 
 *      rootWindow will point to the root window on $DISPLAY.
1114
 
 *
1115
 
 *-----------------------------------------------------------------------------
1116
 
 */
1117
 
 
1118
 
static Bool
1119
 
InitGroupLeader(Window *groupLeader,    // OUT: group leader window
1120
 
                Window *rootWindow)     // OUT: root window
1121
 
{
1122
 
   Window myGroupLeader;
1123
 
   Window myRootWindow;
1124
 
   XSetWindowAttributes attr;
1125
 
 
1126
 
   attr.override_redirect = True;
1127
 
 
1128
 
   ASSERT(groupLeader);
1129
 
   ASSERT(rootWindow);
1130
 
 
1131
 
#if GTK_CHECK_VERSION(2,0,0)
1132
 
   {
1133
 
      GdkDisplay *gdkDisplay = gdk_display_get_default();
1134
 
      GdkWindow *gdkLeader = gdk_display_get_default_group(gdkDisplay);
1135
 
      myGroupLeader = GDK_WINDOW_XWINDOW(gdkLeader);
1136
 
   }
1137
 
#else
1138
 
   /*
1139
 
    * This requires digging around in gdk 1.x private code.  However, we'll
1140
 
    * assume that GTK 1.x isn't going anywhere, so this should remain stable.
1141
 
    */
1142
 
   myGroupLeader = gdk_leader_window;
1143
 
#endif
1144
 
 
1145
 
   myRootWindow = GDK_ROOT_WINDOW();
1146
 
 
1147
 
   ASSERT(myGroupLeader);
1148
 
   ASSERT(myRootWindow);
1149
 
 
1150
 
   /*
1151
 
    * XXX With g_set_prgname() called from main(), this can probably go
1152
 
    * away.
1153
 
    */
1154
 
   XStoreName(GDK_DISPLAY(), myGroupLeader, VMUSER_TITLE);
1155
 
 
1156
 
   /*
1157
 
    * Sanity check:  Set the override redirect property on our group leader
1158
 
    * window (not default), then re-parent it to the root window (default).
1159
 
    * This makes sure that (a) a window manager can't re-parent our window,
1160
 
    * and (b) that we remain a top-level window.
1161
 
    */
1162
 
   XChangeWindowAttributes(GDK_DISPLAY(), myGroupLeader, CWOverrideRedirect,
1163
 
                           &attr);
1164
 
   XReparentWindow(GDK_DISPLAY(), myGroupLeader, myRootWindow, 10, 10);
1165
 
   XSync(GDK_DISPLAY(), FALSE);
1166
 
 
1167
 
   *groupLeader = myGroupLeader;
1168
 
   *rootWindow = myRootWindow;
1169
 
 
1170
 
   return TRUE;
1171
 
}
1172
 
 
1173
 
 
1174
 
/*
1175
 
 *-----------------------------------------------------------------------------
1176
 
 *
1177
 
 * AcquireDisplayLock --
1178
 
 *
1179
 
 *      This function "locks" the display against being "claimed" by another
1180
 
 *      instance of vmware-user.  It will succeed if we're the first/only
1181
 
 *      instance of vmware-user, and fail otherwise.
1182
 
 *
1183
 
 *      NB:  This routine must be called -after- gtk_init().
1184
 
 *
1185
 
 *      Vmware-user enjoys per-display exclusivity using the following algorithm:
1186
 
 *
1187
 
 *        1.  Grab X server.  (I.e., get exclusive access.)
1188
 
 *        2.  Search for top-level X windows meeting the following criteria:
1189
 
 *            a.  named "vmware-user"
1190
 
 *            b.  has the property "vmware-user-lock" set.
1191
 
 *        3a. If any such windows described above found, then another vmware-user
1192
 
 *            process is attached to this display, so we consider the display
1193
 
 *            locked.
1194
 
 *        3b. Else we're the only one.  Set the "vmware-user-lock" property on
1195
 
 *            our top-level window.
1196
 
 *        4.  Ungrab the X server.
1197
 
 *
1198
 
 * Results:
1199
 
 *      TRUE if "lock" acquired (i.e., we're the first/only vmware-user process);
1200
 
 *      otherwise FALSE.
1201
 
 *
1202
 
 * Side effects:
1203
 
 *      The first time this routine is ever called during the lifetime of an X
1204
 
 *      session, a new X11 Atom, "vmware-user-lock" is created for the lifetime
1205
 
 *      of the X server.
1206
 
 *
1207
 
 *      The "vmware-user-lock" property may be set on this process's group leader
1208
 
 *      window.
1209
 
 *
1210
 
 *-----------------------------------------------------------------------------
1211
 
 */
1212
 
 
1213
 
static Bool
1214
 
AcquireDisplayLock(void)
1215
 
{
1216
 
   Display *defaultDisplay;     // Current default X11 display.
1217
 
   Window rootWindow;           // Root window of defaultDisplay; used as root node
1218
 
                                // passed to XQueryTree().
1219
 
   Window groupLeader;          // Our instance's window group leader.  This is
1220
 
                                // implicitly created by gtk_init().
1221
 
 
1222
 
   Window *children = NULL;     // Array of windows returned by XQueryTree().
1223
 
   unsigned int nchildren;      // Length of children.
1224
 
 
1225
 
   Window dummy1, dummy2;       // Throwaway window IDs for XQueryTree().
1226
 
   Atom lockAtom;               // Refers to the "vmware-user-lock" X11 Atom.
1227
 
 
1228
 
   unsigned int index;
1229
 
   Bool alreadyLocked = FALSE;  // Set to TRUE if we discover lock is held.
1230
 
   Bool retval = FALSE;
1231
 
 
1232
 
   defaultDisplay = GDK_DISPLAY();
1233
 
 
1234
 
   /*
1235
 
    * Reset some of our main window's settings & fetch Xlib handles for
1236
 
    * the GDK group leader and root windows.
1237
 
    */
1238
 
   if (InitGroupLeader(&groupLeader, &rootWindow) == FALSE) {
1239
 
      Warning("%s: unable to initialize main window.\n", __func__);
1240
 
      return FALSE;
1241
 
   }
1242
 
 
1243
 
   /*
1244
 
    * Look up the lock atom, creating it if it doesn't already exist.
1245
 
    */
1246
 
   lockAtom = XInternAtom(defaultDisplay, LOCK_ATOM_NAME, False);
1247
 
   if (lockAtom == None) {
1248
 
      Warning("%s: unable to create X11 atom: " LOCK_ATOM_NAME "\n", __func__);
1249
 
      return FALSE;
1250
 
   }
1251
 
 
1252
 
   /*
1253
 
    * Okay, so at this point the following is done:
1254
 
    *
1255
 
    *   1.  Our top-level / group leader window is a child of the display's
1256
 
    *       root window.
1257
 
    *   2.  The window manager can't get its hands on said window.
1258
 
    *   3.  We have a handle on the X11 atom which will be used to identify
1259
 
    *       the X11 property used as our lock.
1260
 
    */
1261
 
 
1262
 
   Debug("%s: Grabbing X server.\n", __func__);
1263
 
 
1264
 
   /*
1265
 
    * Neither of these can fail, or at least not in the sense that they'd
1266
 
    * return an error.  Instead we'd likely see an X11 I/O error, tearing
1267
 
    * the connection down.
1268
 
    *
1269
 
    * XSync simply blocks until the XGrabServer request is acknowledged
1270
 
    * by the server.  It makes sure that we don't continue issuing requests,
1271
 
    * such as XQueryTree, until the server grants our "grab".
1272
 
    */
1273
 
   XGrabServer(defaultDisplay);
1274
 
   XSync(defaultDisplay, False);
1275
 
 
1276
 
   /*
1277
 
    * WARNING:  At this point, we have grabbed the X server.  Consider the
1278
 
    * UI to be completely frozen.  Under -no- circumstances should we return
1279
 
    * without ungrabbing the server first.
1280
 
    */
1281
 
 
1282
 
   if (XQueryTree(defaultDisplay, rootWindow, &dummy1, &dummy2, &children,
1283
 
                  &nchildren) == 0) {
1284
 
      Warning("%s: XQueryTree failed\n", __func__);
1285
 
      goto out;
1286
 
   }
1287
 
 
1288
 
   /*
1289
 
    * Iterate over array of top-level windows.  Search for those named
1290
 
    * vmware-user and with the property "vmware-user-lock" set.
1291
 
    *
1292
 
    * If any such windows are found, then another process has already
1293
 
    * claimed this X session.
1294
 
    */
1295
 
   for (index = 0; (index < nchildren) && !alreadyLocked; index++) {
1296
 
      char *name = NULL;
1297
 
 
1298
 
      /* Skip unless window is named vmware-user. */
1299
 
      if ((XFetchName(defaultDisplay, children[index], &name) == 0) ||
1300
 
          (name == NULL) ||
1301
 
          strcmp(name, VMUSER_TITLE)) {
1302
 
         XFree(name);
1303
 
         continue;
1304
 
      }
1305
 
 
1306
 
      /*
1307
 
       * Query the window for the "vmware-user-lock" property.
1308
 
       */
1309
 
      alreadyLocked = QueryX11Lock(defaultDisplay, children[index], lockAtom);
1310
 
      XFree(name);
1311
 
   }
1312
 
 
1313
 
   /*
1314
 
    * Yay.  Lock isn't held, so go ahead and acquire it.
1315
 
    */
1316
 
   if (!alreadyLocked) {
1317
 
      unsigned char dummy[] = "1";
1318
 
      Debug("%s: Setting property " LOCK_ATOM_NAME "\n", __func__);
1319
 
      /*
1320
 
       * NB: Current Xlib always returns one.  This may generate a -fatal- IO
1321
 
       * error, though.
1322
 
       */
1323
 
      XChangeProperty(defaultDisplay, groupLeader, lockAtom, lockAtom, 8,
1324
 
                      PropModeReplace, dummy, sizeof dummy);
1325
 
      retval = TRUE;
1326
 
   }
1327
 
 
1328
 
out:
1329
 
   XUngrabServer(defaultDisplay);
1330
 
   XSync(defaultDisplay, False);
1331
 
   XFree(children);
1332
 
 
1333
 
   return retval;
1334
 
}
1335
 
 
1336
 
 
1337
 
/*
1338
 
 *-----------------------------------------------------------------------------
1339
 
 *
1340
 
 * QueryX11Lock --
1341
 
 *
1342
 
 *      This is just a wrapper around XGetWindowProperty which queries the
1343
 
 *      window described by <dpy,w> for the property described by lockAtom.
1344
 
 *
1345
 
 * Results:
1346
 
 *      TRUE if property defined by parameters exists; FALSE otherwise.
1347
 
 *
1348
 
 * Side effects:
1349
 
 *      None.
1350
 
 *
1351
 
 *-----------------------------------------------------------------------------
1352
 
 */
1353
 
 
1354
 
static Bool
1355
 
QueryX11Lock(Display *dpy,      // IN: X11 display to query
1356
 
             Window w,          // IN: window to query
1357
 
             Atom lockAtom)     // IN: atom used for locking
1358
 
{
1359
 
   Atom ptype;                  // returned property type
1360
 
   int pfmt;                    // returned property format
1361
 
   unsigned long np;            // returned # of properties
1362
 
   unsigned long remaining;     // amount of data remaining in property
1363
 
   unsigned char *data = NULL;
1364
 
 
1365
 
   if (XGetWindowProperty(dpy, w, lockAtom, 0, 1, False, lockAtom,
1366
 
                          &ptype, &pfmt, &np, &remaining, &data) != Success) {
1367
 
      Warning("%s: Unable to query window %lx for property %s\n", __func__, w,
1368
 
              LOCK_ATOM_NAME);
1369
 
      return FALSE;
1370
 
   }
1371
 
 
1372
 
   /*
1373
 
    * Xlib is wacky.  If the following test is true, then our property
1374
 
    * didn't exist for the window in question.  As a result, `data' is
1375
 
    * unset, so don't worry about the lack of XFree(data) here.
1376
 
    */
1377
 
   if (ptype == None) {
1378
 
      return FALSE;
1379
 
   }
1380
 
 
1381
 
   /*
1382
 
    * We care only about the existence of the property, not its value.
1383
 
    */
1384
 
   XFree(data);
1385
 
 
1386
 
   return TRUE;
1387
 
}
1388
 
 
1389
 
 
1390
 
/*
1391
 
 *-----------------------------------------------------------------------------
1392
 
 *
1393
 
 * ReloadSelf --
1394
 
 *
1395
 
 *      Re-launch vmware-user by attempting to execute VMUSER_TITLE
1396
 
 *      ('vmware-user'), relying on the user's search path.
1397
 
 *
1398
 
 * Results:
1399
 
 *      On success, vmware-user is relaunched in our stead.  On failure, we
1400
 
 *      exit with EXIT_FAILURE.
1401
 
 *
1402
 
 * Side effects:
1403
 
 *      None.
1404
 
 *
1405
 
 *-----------------------------------------------------------------------------
1406
 
 */
1407
 
 
1408
 
static void
1409
 
ReloadSelf(void)
1410
 
{
1411
 
   Debug("> %s\n", __func__);
1412
 
   execlp(VMUSER_TITLE, VMUSER_TITLE, NULL);
1413
 
   exit(EXIT_FAILURE);
1414
 
}