665 lines
17 KiB
C
665 lines
17 KiB
C
/**************************************************************************/
|
|
/* */
|
|
/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
|
|
/* Copyright (c) 2008-2017 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
|
|
/* Copyright (c) 2011-2022 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
|
|
/* Copyright (c) 2014-2019 Mihai Moldovan <ionic@ionic.de> */
|
|
/* Copyright (c) 2014-2022 Ulrich Sibiller <uli42@gmx.de> */
|
|
/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
|
|
/* */
|
|
/* NXAGENT, NX protocol compression and NX extensions to this software */
|
|
/* are copyright of the aforementioned persons and companies. */
|
|
/* */
|
|
/* Redistribution and use of the present software is allowed according */
|
|
/* to terms specified in the file LICENSE which comes in the source */
|
|
/* distribution. */
|
|
/* */
|
|
/* All rights reserved. */
|
|
/* */
|
|
/* NOTE: This software has received contributions from various other */
|
|
/* contributors, only the core maintainers and supporters are listed as */
|
|
/* copyright holders. Please contact us, if you feel you should be listed */
|
|
/* as copyright holder, as well. */
|
|
/* */
|
|
/**************************************************************************/
|
|
|
|
/*
|
|
* Used in handling of karma on lost focus.
|
|
*/
|
|
|
|
#include <signal.h>
|
|
#include <time.h>
|
|
#include <errno.h>
|
|
|
|
#include <nx/NX.h>
|
|
|
|
#include "Xatom.h"
|
|
#include "dixstruct.h"
|
|
#include "scrnintstr.h"
|
|
#include "windowstr.h"
|
|
#include "osdep.h"
|
|
|
|
/*
|
|
* NX specific includes and definitions.
|
|
*/
|
|
|
|
#include "Agent.h"
|
|
#include "Args.h"
|
|
#include "Display.h"
|
|
#include "Client.h"
|
|
#include "Dialog.h"
|
|
#include "Handlers.h"
|
|
#include "Events.h"
|
|
#include "Drawable.h"
|
|
#include "Utils.h"
|
|
#include "Clipboard.h"
|
|
|
|
/*
|
|
* Need to include this after the stub definition of GC in Agent.h.
|
|
*/
|
|
|
|
#include "compext/Compext.h"
|
|
|
|
/*
|
|
* Set here the required log level.
|
|
*/
|
|
|
|
#define PANIC
|
|
#define WARNING
|
|
#undef TEST
|
|
#undef DEBUG
|
|
|
|
void nxagentClientStateCallback(CallbackListPtr *callbacks, void *data, void *args);
|
|
static void initClientPrivates(ClientPtr client);
|
|
static void freeClientPrivates(ClientPtr client);
|
|
static void checkIfShadowAgent(ClientPtr client);
|
|
|
|
/*
|
|
* Returns the last signal delivered to the process.
|
|
*/
|
|
|
|
extern int _X11TransSocketCheckSignal(void);
|
|
|
|
/*
|
|
* Time in milliseconds of first iteration through the dispatcher.
|
|
*/
|
|
|
|
unsigned long nxagentStartTime = -1;
|
|
|
|
/*
|
|
* If defined, add a function checking if we need a null timeout after
|
|
* a client wakeup.
|
|
*/
|
|
|
|
#undef CHECK_RESTARTED_CLIENTS
|
|
|
|
#ifdef CHECK_RESTARTED_CLIENTS
|
|
|
|
void nxagentCheckRestartedClients(struct timeval **timeout);
|
|
|
|
#endif
|
|
|
|
/*
|
|
* Allow attaching private members to the client.
|
|
*/
|
|
|
|
int nxagentClientPrivateIndex;
|
|
|
|
/*
|
|
* The master nxagent holds in nxagentShadowCounter the number of
|
|
* shadow nxagents connected to itself.
|
|
*/
|
|
|
|
int nxagentShadowCounter = 0;
|
|
|
|
/*
|
|
* For the serverclient the ClientStateCallback will not be called on
|
|
* shutdown resulting in memory allocated during initClientPrivates can
|
|
* not be freed automatically. So instead of allocating some memory we
|
|
* create a static string for the serverclient.
|
|
*/
|
|
static char *serverclientInfoString = "[0] (serverclient)";
|
|
|
|
/*
|
|
* called whenever the client state changes. See dixstruct.h for a
|
|
* list of known states.
|
|
*/
|
|
|
|
#ifdef DEBUG
|
|
const char * getClientStateString(int state)
|
|
{
|
|
switch (state)
|
|
{
|
|
case ClientStateInitial: { return "Initial"; break; };
|
|
case ClientStateAuthenticating: { return "Authenticating"; break; };
|
|
case ClientStateRunning: { return "Running"; break; };
|
|
case ClientStateRetained: { return "Retained"; break; };
|
|
case ClientStateGone: { return "Gone"; break; };
|
|
case ClientStateCheckingSecurity: { return "CheckingSecurity"; break; };
|
|
case ClientStateCheckedSecurity: { return "CheckedSecurity"; break; };
|
|
default: { return "UNKNOWN"; break; };
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void nxagentClientStateCallback(CallbackListPtr *callbacks, void *data, void *args)
|
|
{
|
|
ClientPtr client = ((NewClientInfoRec *)args)->client;
|
|
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "%s: client [%d] clientState [%s]\n", __func__, client->index,
|
|
getClientStateString(client->clientState));
|
|
#endif
|
|
|
|
switch(client->clientState)
|
|
{
|
|
case ClientStateInitial:
|
|
{
|
|
initClientPrivates(client);
|
|
break;
|
|
}
|
|
case ClientStateGone:
|
|
{
|
|
nxagentClearClipboard(client, NULL);
|
|
|
|
/*
|
|
* Check if the client is a shadow nxagent.
|
|
*/
|
|
checkIfShadowAgent(client);
|
|
|
|
freeClientPrivates(client);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void initClientPrivates(ClientPtr client)
|
|
{
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "%s: called\n", __func__);
|
|
#endif
|
|
|
|
if (nxagentClientPriv(client))
|
|
{
|
|
nxagentClientPriv(client) -> clientState = 0;
|
|
#ifdef COUNT_CLIENT_BYTES
|
|
nxagentClientPriv(client) -> clientBytes = 0;
|
|
#endif
|
|
nxagentClientPriv(client) -> clientHint = UNKNOWN;
|
|
nxagentClientPriv(client) -> clientInfoString = NULL;
|
|
|
|
char *s = NULL;
|
|
int size = 0;
|
|
|
|
if (client->index == 0)
|
|
{
|
|
s = serverclientInfoString;
|
|
}
|
|
else
|
|
{
|
|
#ifdef CLIENTIDS
|
|
size = asprintf(&s, "[%d] (addr [%p] PID [%d] Cmd [%s])",
|
|
client->index, (void *)client,
|
|
GetClientPid(client),
|
|
GetClientCmdName(client));
|
|
#else
|
|
size = asprintf(&s, "[%d] (addr [%p])",
|
|
client->index, (void *)client);
|
|
#endif
|
|
}
|
|
|
|
if (size != -1)
|
|
{
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "%s: clientInfoString: \"%s\"\n", __func__, s);
|
|
#endif
|
|
|
|
nxagentClientPriv(client) -> clientInfoString = s;
|
|
}
|
|
else
|
|
{
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "%s: could not alloc clientInfoString\n", __func__);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
static void freeClientPrivates(ClientPtr client)
|
|
{
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "%s: called\n", __func__);
|
|
#endif
|
|
|
|
if (nxagentClientPriv(client))
|
|
{
|
|
nxagentClientPriv(client) -> clientState = 0;
|
|
#ifdef COUNT_CLIENT_BYTES
|
|
nxagentClientPriv(client) -> clientBytes = 0;
|
|
#endif
|
|
nxagentClientPriv(client) -> clientHint = UNKNOWN;
|
|
|
|
if (client->index != 0)
|
|
SAFE_free(nxagentClientPriv(client) -> clientInfoString);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Guess the running application based on the properties attached to
|
|
* its main window.
|
|
*/
|
|
|
|
void nxagentGuessClientHint(ClientPtr client, Atom property, char *data)
|
|
{
|
|
#ifdef TEST
|
|
fprintf(stderr, "++++++nxagentGuessClientHint: Client [%d] setting property [%s] as [%s].\n",
|
|
client -> index, validateString(NameForAtom(property)), validateString(data));
|
|
#endif
|
|
|
|
if (nxagentClientHint(client) == UNKNOWN)
|
|
{
|
|
if (property == XA_WM_CLASS)
|
|
{
|
|
if (strcmp(data, "nxclient") == 0)
|
|
{
|
|
#ifdef TEST
|
|
fprintf(stderr, "++++++nxagentGuessClientHint: Detected nxclient as [%d].\n", client -> index);
|
|
#endif
|
|
|
|
nxagentClientHint(client) = NXCLIENT_WINDOW;
|
|
}
|
|
else if (strstr(data, "java"))
|
|
{
|
|
#ifdef TEST
|
|
fprintf(stderr, "++++++nxagentGuessClientHint: Detected java as [%d].\n", client -> index);
|
|
#endif
|
|
|
|
nxagentClientHint(client) = JAVA_WINDOW;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (nxagentClientHint(client) == NXCLIENT_WINDOW)
|
|
{
|
|
if (property == MakeAtom("WM_WINDOW_ROLE", 14, True) &&
|
|
strncmp(data, "msgBox", 6) == 0)
|
|
{
|
|
#ifdef TEST
|
|
fprintf(stderr, "++++++nxagentGuessClientHint: Detected nxclient dialog as [%d].\n", client -> index);
|
|
#endif
|
|
|
|
nxagentClientHint(client) = NXCLIENT_DIALOG;
|
|
}
|
|
}
|
|
}
|
|
|
|
void nxagentGuessShadowHint(ClientPtr client, Atom property)
|
|
{
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "nxagentGuessShadowHint: Client [%d] setting property [%s].\n",
|
|
client -> index,
|
|
validateString(NameForAtom(property)));
|
|
#endif
|
|
|
|
if (nxagentClientHint(client) == UNKNOWN)
|
|
{
|
|
if (strcmp(validateString(NameForAtom(property)), "_NX_SHADOW") == 0)
|
|
{
|
|
#ifdef TEST
|
|
fprintf(stderr, "nxagentGuessShadowHint: nxagentShadowCounter [%d].\n",
|
|
nxagentShadowCounter);
|
|
|
|
fprintf(stderr, "nxagentGuessShadowHint: Detected shadow nxagent as client [%d].\n",
|
|
client -> index);
|
|
|
|
#endif
|
|
|
|
nxagentClientHint(client) = NXAGENT_SHADOW;
|
|
|
|
nxagentShadowCounter++;
|
|
|
|
#ifdef TEST
|
|
fprintf(stderr, "nxagentGuessShadowHint: nxagentShadowCounter [%d].\n",
|
|
nxagentShadowCounter);
|
|
#endif
|
|
|
|
/*
|
|
* From this moment on we ignore the visibility checks to keep
|
|
* the windows updated.
|
|
*/
|
|
|
|
nxagentChangeOption(IgnoreVisibility, True);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void checkIfShadowAgent(ClientPtr client)
|
|
{
|
|
if (nxagentClientHint(client) == NXAGENT_SHADOW)
|
|
{
|
|
#ifdef TEST
|
|
fprintf(stderr, "nxagentCheckIfShadowAgent: nxagentShadowCounter [%d].\n",
|
|
nxagentShadowCounter);
|
|
|
|
fprintf(stderr, "nxagentCheckIfShadowAgent: Shadow nxagent as client [%d] detached.\n",
|
|
client -> index);
|
|
|
|
fprintf(stderr, "nxagentCheckIfShadowAgent: Decreasing nxagentShadowCounter.\n");
|
|
#endif
|
|
|
|
/*
|
|
* We decrease nxagentShadowCounter.
|
|
*/
|
|
|
|
nxagentShadowCounter--;
|
|
|
|
#ifdef TEST
|
|
fprintf(stderr, "nxagentCheckIfShadowAgent: nxagentShadowCounter [%d].\n",
|
|
nxagentShadowCounter);
|
|
#endif
|
|
|
|
if (nxagentShadowCounter == 0)
|
|
{
|
|
/*
|
|
* The last shadow nxagent has been detached from master
|
|
* nxagent. The master nxagent could do some action here.
|
|
*/
|
|
|
|
#ifdef TEST
|
|
fprintf(stderr, "nxagentCheckIfShadowAgent: The last shadow nxagent has been detached.\n");
|
|
#endif
|
|
|
|
nxagentChangeOption(IgnoreVisibility, False);
|
|
}
|
|
}
|
|
}
|
|
|
|
void nxagentWakeupByReconnect(void)
|
|
{
|
|
#ifdef TEST
|
|
fprintf(stderr, "++++++nxagentWakeupByReconnect: Going to wakeup all clients.\n");
|
|
#endif
|
|
|
|
for (int i = 1; i < currentMaxClients; i++)
|
|
{
|
|
if (clients[i] != NULL)
|
|
{
|
|
nxagentWakeupByReset(clients[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
void nxagentWakeupByReset(ClientPtr client)
|
|
{
|
|
#ifdef TEST
|
|
fprintf(stderr, "++++++nxagentWakeupByReset: Going to check client id [%d].\n",
|
|
client -> index);
|
|
#endif
|
|
|
|
if (nxagentNeedWakeup(client))
|
|
{
|
|
#ifdef TEST
|
|
fprintf(stderr, "++++++nxagentWakeupByReset: Going to wakeup client id [%d].\n",
|
|
client -> index);
|
|
#endif
|
|
|
|
if (client -> index < MAX_CONNECTIONS)
|
|
{
|
|
if (nxagentNeedWakeupBySplit(client))
|
|
{
|
|
nxagentWakeupBySplit(client);
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef COUNT_CLIENT_BYTES
|
|
if (client -> index < MAX_CONNECTIONS)
|
|
{
|
|
#ifdef TEST
|
|
fprintf(stderr, "++++++nxagentWakeupByReset: Going to reset bytes received for client id [%d].\n",
|
|
client -> index);
|
|
#endif
|
|
|
|
nxagentClientBytes(client) = 0;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Wait for any event.
|
|
*/
|
|
|
|
#define WAIT_ALL_EVENTS
|
|
|
|
#ifndef WAIT_ALL_EVENTS
|
|
|
|
static Bool nxagentWaitWakeupBySplitPredicate(Display *disp, XEvent *event, XPointer ptr)
|
|
{
|
|
return (event -> type == ClientMessage &&
|
|
(event -> xclient.data.l[0] == NXNoSplitNotify ||
|
|
event -> xclient.data.l[0] == NXStartSplitNotify ||
|
|
event -> xclient.data.l[0] == NXCommitSplitNotify ||
|
|
event -> xclient.data.l[0] == NXEndSplitNotify ||
|
|
event -> xclient.data.l[0] == NXEmptySplitNotify) &&
|
|
event -> xclient.window == 0 && event -> xclient.message_type == 0 &&
|
|
event -> xclient.format == 32);
|
|
}
|
|
|
|
#endif
|
|
|
|
#define USE_FINISH_SPLIT
|
|
|
|
void nxagentWaitWakeupBySplit(ClientPtr client)
|
|
{
|
|
#ifdef TEST
|
|
|
|
if (!nxagentNeedWakeupBySplit(client))
|
|
{
|
|
fprintf(stderr, "++++++nxagentWaitWakeupBySplit: WARNING! The client [%d] is already awake.\n",
|
|
client -> index);
|
|
}
|
|
|
|
fprintf(stderr, "++++++nxagentWaitWakeupBySplit: Going to wait for the client [%d].\n",
|
|
client -> index);
|
|
#endif
|
|
|
|
/*
|
|
* Be sure we intercept an I/O error as well as an interrupt.
|
|
*/
|
|
|
|
#ifdef USE_FINISH_SPLIT
|
|
|
|
NXFinishSplit(nxagentDisplay, client -> index);
|
|
|
|
#endif
|
|
|
|
NXFlushDisplay(nxagentDisplay, NXFlushBuffer);
|
|
|
|
for (;;)
|
|
{
|
|
/*
|
|
* Can we handle all the possible events here or we need to select
|
|
* only the split events? Handling all the possible events would
|
|
* preempt the queue and make a better use of the link.
|
|
*/
|
|
|
|
#ifdef WAIT_ALL_EVENTS
|
|
|
|
nxagentDispatchEvents(NULL);
|
|
|
|
#else
|
|
|
|
nxagentDispatchEvents(nxagentWaitWakeupBySplitPredicate);
|
|
|
|
#endif
|
|
|
|
if (!nxagentNeedWakeupBySplit(client) ||
|
|
NXDisplayError(nxagentDisplay) == 1)
|
|
{
|
|
#ifdef TEST
|
|
|
|
if (!nxagentNeedWakeupBySplit(client))
|
|
{
|
|
fprintf(stderr, "++++++nxagentWaitWakeupBySplit: Client [%d] can now run.\n",
|
|
client -> index);
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr, "++++++nxagentWaitWakeupBySplit: WARNING! Display error "
|
|
"detected waiting for restart.\n");
|
|
}
|
|
|
|
#endif
|
|
|
|
return;
|
|
}
|
|
|
|
#ifdef TEST
|
|
fprintf(stderr, "++++++nxagentWaitWakeupBySplit: Yielding control to the NX transport.\n");
|
|
#endif
|
|
|
|
nxagentWaitEvents(nxagentDisplay, 0);
|
|
}
|
|
}
|
|
|
|
int nxagentSuspendBySplit(ClientPtr client)
|
|
{
|
|
/*
|
|
FIXME: Should record a serial number for the client, so that the
|
|
client is not restarted because of an end of split of a
|
|
previous client with the same index.
|
|
*/
|
|
if (client -> index < MAX_CONNECTIONS)
|
|
{
|
|
if (!nxagentNeedWakeup(client))
|
|
{
|
|
#ifdef TEST
|
|
fprintf(stderr, "++++++nxagentSuspendBySplit: Suspending client [%d] with agent sequence [%ld].\n",
|
|
client -> index, NextRequest(nxagentDisplay) - 1);
|
|
#endif
|
|
|
|
if (client -> clientGone == 0)
|
|
{
|
|
#ifdef TEST
|
|
fprintf(stderr, "++++++nxagentSuspendBySplit: Client [%d] suspended.\n", client -> index);
|
|
#endif
|
|
|
|
IgnoreClient(client);
|
|
}
|
|
}
|
|
#ifdef TEST
|
|
else
|
|
{
|
|
fprintf(stderr, "++++++nxagentSuspendBySplit: WARNING! Client [%d] already ignored with state [%x].\n",
|
|
client -> index, nxagentClientPriv(client) -> clientState);
|
|
}
|
|
#endif
|
|
|
|
nxagentClientPriv(client) -> clientState |= SleepingBySplit;
|
|
|
|
return 1;
|
|
}
|
|
|
|
#ifdef WARNING
|
|
fprintf(stderr, "++++++nxagentSuspendBySplit: WARNING! Invalid client [%d] provided to function.\n",
|
|
client -> index);
|
|
#endif
|
|
|
|
return -1;
|
|
}
|
|
|
|
int nxagentWakeupBySplit(ClientPtr client)
|
|
{
|
|
/*
|
|
FIXME: Should record a serial number for the client, so that the
|
|
client is not restarted because of the end of the split for a
|
|
previous client with the same index.
|
|
*/
|
|
if (client -> index < MAX_CONNECTIONS)
|
|
{
|
|
nxagentClientPriv(client) -> clientState &= ~SleepingBySplit;
|
|
|
|
if (!nxagentNeedWakeup(client))
|
|
{
|
|
#ifdef TEST
|
|
fprintf(stderr, "++++++nxagentWakeupBySplit: Resuming client [%d] with agent sequence [%ld].\n",
|
|
client -> index, NextRequest(nxagentDisplay) - 1);
|
|
#endif
|
|
|
|
if (client -> clientGone == 0)
|
|
{
|
|
AttendClient(client);
|
|
}
|
|
}
|
|
#ifdef TEST
|
|
else
|
|
{
|
|
fprintf(stderr, "++++++nxagentWakeupBySplit: WARNING! Client [%d] still suspended with state [%x].\n",
|
|
client -> index, nxagentClientPriv(client) -> clientState);
|
|
}
|
|
#endif
|
|
|
|
return 1;
|
|
}
|
|
|
|
#ifdef WARNING
|
|
fprintf(stderr, "++++++nxagentWakeupBySplit: WARNING! Invalid client [%d] provided to function.\n",
|
|
client -> index);
|
|
#endif
|
|
|
|
return -1;
|
|
}
|
|
|
|
#ifdef CHECK_RESTARTED_CLIENTS
|
|
|
|
void nxagentCheckRestartedClients(struct timeval **timeout)
|
|
{
|
|
static struct timeval zero;
|
|
|
|
/*
|
|
* If any of the restarted clients had requests in input we'll need
|
|
* to enter the select with a null timeout, or we will block until
|
|
* any other client becomes available.
|
|
*/
|
|
|
|
for (int i = 1; i < currentMaxClients; i++)
|
|
{
|
|
if (clients[i] != NULL && clients[i] -> osPrivate != NULL &&
|
|
!nxagentNeedWakeup(clients[i]))
|
|
{
|
|
int fd = ((OsCommPtr) clients[i] -> osPrivate) -> fd;
|
|
|
|
if (FD_ISSET(fd, &ClientsWithInput))
|
|
{
|
|
#ifdef WARNING
|
|
fprintf(stderr, "nxagentCheckRestartedClients: WARNING! Client [%d] with fd [%d] has input.\n",
|
|
clients[i] -> index, fd);
|
|
#endif
|
|
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "nxagentCheckRestartedClients: Setting a null timeout with former timeout [%ld] Ms.\n",
|
|
(*timeout) -> tv_sec * 1000 + (*timeout) -> tv_usec / 1000);
|
|
#endif
|
|
|
|
if (*timeout != NULL)
|
|
{
|
|
(*timeout) -> tv_sec = 0;
|
|
(*timeout) -> tv_usec = 0;
|
|
}
|
|
else
|
|
{
|
|
zero.tv_sec = 0;
|
|
zero.tv_usec = 0;
|
|
|
|
*timeout = &zero;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|