aboutsummaryrefslogtreecommitdiff
path: root/nx-X11/programs/Xserver/hw/nxagent/Reconnect.c
diff options
context:
space:
mode:
Diffstat (limited to 'nx-X11/programs/Xserver/hw/nxagent/Reconnect.c')
-rw-r--r--nx-X11/programs/Xserver/hw/nxagent/Reconnect.c849
1 files changed, 849 insertions, 0 deletions
diff --git a/nx-X11/programs/Xserver/hw/nxagent/Reconnect.c b/nx-X11/programs/Xserver/hw/nxagent/Reconnect.c
new file mode 100644
index 000000000..fb2303549
--- /dev/null
+++ b/nx-X11/programs/Xserver/hw/nxagent/Reconnect.c
@@ -0,0 +1,849 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2014-2016 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. */
+/* */
+/**************************************************************************/
+
+#include <signal.h>
+
+#include "X.h"
+#include "Xproto.h"
+#include "Xpoll.h"
+#include "mi.h"
+#include "fb.h"
+#include "inputstr.h"
+
+#include "Agent.h"
+#include "Atoms.h"
+#include "Drawable.h"
+#include "Client.h"
+#include "Reconnect.h"
+#include "Display.h"
+#include "Dialog.h"
+#include "Screen.h"
+#include "Windows.h"
+#include "Events.h"
+#include "Dialog.h"
+#include "Args.h"
+#include "Font.h"
+#include "GCs.h"
+#include "Trap.h"
+#include "Keyboard.h"
+#include "Composite.h"
+#include "Millis.h"
+#include "Splash.h"
+#include "Error.h"
+#include "Keystroke.h"
+
+#ifdef XKB
+#include "XKBsrv.h"
+#endif
+
+#include <nx/NX.h>
+#include "compext/Compext.h"
+#include <nx/NXalert.h>
+
+/*
+ * Set here the required log level.
+ */
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+#define NXAGENT_RECONNECT_DEFAULT_MESSAGE_SIZE 32
+
+extern Bool nxagentReconnectAllCursor(void*);
+extern Bool nxagentReconnectAllColormap(void*);
+extern Bool nxagentReconnectAllWindows(void*);
+extern Bool nxagentReconnectAllGlyphSet(void*);
+extern Bool nxagentReconnectAllPictFormat(void*);
+extern Bool nxagentReconnectAllPicture(void*);
+
+extern Bool nxagentDisconnectAllPicture(void);
+extern Bool nxagentDisconnectAllWindows(void);
+extern Bool nxagentDisconnectAllCursor(void);
+
+extern Bool nxagentReconnectFailedFonts(void*);
+extern Bool nxagentInstallFontServerPath(void);
+extern Bool nxagentUninstallFontServerPath(void);
+
+extern void nxagentRemoveXConnection(void);
+
+extern void nxagentInitPointerMap(void);
+
+static char *nxagentGetReconnectError(void);
+
+void nxagentInitializeRecLossyLevel(void);
+
+static char *nxagentReconnectErrorMessage = NULL;
+static int nxagentReconnectErrorId;
+
+extern Bool nxagentRenderEnable;
+
+extern char *nxagentKeyboard;
+
+enum SESSION_STATE nxagentSessionState = SESSION_STARTING;
+
+struct nxagentExceptionStruct nxagentException = {0, 0};
+
+enum RECONNECTION_STEP
+{
+ DISPLAY_STEP = 0,
+ SCREEN_STEP,
+ FONT_STEP,
+ PIXMAP_STEP,
+ GC_STEP,
+ CURSOR_STEP,
+ COLORMAP_STEP,
+ WINDOW_STEP,
+ GLYPHSET_STEP,
+ PICTFORMAT_STEP,
+ PICTURE_STEP,
+ STEP_NONE
+};
+
+void *reconnectLossyLevel[STEP_NONE];
+
+static enum RECONNECTION_STEP failedStep;
+
+#include <limits.h>
+
+/*
+ * Path of state File
+ */
+char stateFile[PATH_MAX];
+
+
+void setStatePath(char* path)
+{
+ strncpy(stateFile, path, PATH_MAX-1);
+}
+
+void saveAgentState(char* state)
+{
+ FILE* fptr;
+ if(strlen(stateFile))
+ {
+ fptr=fopen(stateFile, "w");
+ if(!fptr)
+ return;
+ fprintf(fptr,"%s", state);
+ fclose(fptr);
+ }
+}
+
+
+int nxagentHandleConnectionStates(void)
+{
+ #ifdef TEST
+ fprintf(stderr, "nxagentHandleConnectionStates: Handling Exception with "
+ "state [%s] and transport [%d] and generation [%ld].\n",
+ DECODE_SESSION_STATE(nxagentSessionState), NXTransRunning(NX_FD_ANY), serverGeneration);
+ fprintf(stderr, "nxagentHandleConnectionStates: Entering with nxagentException.sigHup = [%d], "
+ "nxagentException.ioError = [%d]\n",
+ nxagentException.sigHup, nxagentException.ioError);
+ #endif
+
+ if (nxagentException.sigHup > 0)
+ {
+ #ifdef TEST
+ fprintf(stderr, "nxagentHandleConnectionStates: Got SIGHUP in the exception flags.\n");
+ #endif
+
+ nxagentException.sigHup = 0;
+
+ if (nxagentSessionState == SESSION_UP)
+ {
+ if (nxagentOption(Persistent))
+ {
+ nxagentSessionState = SESSION_GOING_DOWN;
+
+ #ifdef TEST
+ fprintf(stderr, "nxagentHandleConnectionStates: Handling "
+ "signal [SIGHUP] by disconnecting the agent.\n");
+
+ #endif
+
+ }
+ else
+ {
+ nxagentTerminateSession();
+ }
+ }
+ else if (nxagentSessionState == SESSION_STARTING)
+ {
+ nxagentTerminateSession();
+
+ #ifdef WARNING
+ fprintf(stderr, "nxagentHandleConnectionStates: Handling signal [SIGHUP] by terminating the agent.\n");
+ #endif
+ }
+ else if (nxagentSessionState == SESSION_DOWN &&
+ NXTransRunning(NX_FD_ANY) == 0)
+ {
+ nxagentSessionState = SESSION_GOING_UP;
+
+ #ifdef TEST
+ fprintf(stderr, "nxagentHandleConnectionStates: Handling signal [SIGHUP] by reconnecting the agent.\n");
+ #endif
+ }
+ else
+ {
+ #ifdef TEST
+ fprintf(stderr, "nxagentHandleConnectionStates: Handling signal with state [%s] and exception [%d].\n",
+ DECODE_SESSION_STATE(nxagentSessionState), dispatchException);
+ #endif
+ }
+ }
+
+ if (nxagentNeedConnectionChange() == 1)
+ {
+ #ifdef TEST
+ fprintf(stderr, "nxagentHandleConnectionStates: Calling nxagentHandleConnectionChanges "
+ "with ioError [%d] sigHup [%d].\n", nxagentException.ioError, nxagentException.sigHup);
+ #endif
+
+ nxagentHandleConnectionChanges();
+ }
+
+ if (nxagentException.ioError > 0)
+ {
+ #ifdef TEST
+ fprintf(stderr, "nxagentHandleConnectionStates: Got I/O error in the exception flags.\n");
+ #endif
+/*
+TODO: This should be reset only when
+ the state became SESSION_DOWN.
+*/
+ nxagentException.ioError = 0;
+
+ if (nxagentOption(Persistent) == 1 && nxagentSessionState != SESSION_STARTING)
+ {
+ if (nxagentSessionState == SESSION_UP)
+ {
+ if ((dispatchException & DE_TERMINATE) == 0)
+ {
+ fprintf(stderr, "Session: Display failure detected at '%s'.\n", GetTimeAsString());
+
+ fprintf(stderr, "Session: Suspending session at '%s'.\n", GetTimeAsString());
+ saveAgentState("SUSPENDING");
+ }
+
+ nxagentDisconnectSession();
+ }
+ else if (nxagentSessionState == SESSION_GOING_DOWN)
+ {
+ #ifdef TEST
+ fprintf(stderr, "nxagentHandleConnectionStates: Got I/O error with session "
+ "[SESSION_GOING_DOWN].\n");
+ #endif
+ }
+ else if (nxagentSessionState == SESSION_GOING_UP)
+ {
+ #ifdef TEST
+ fprintf(stderr, "nxagentHandleConnectionStates: Got I/O error with session "
+ "[SESSION_GOING_UP].\n");
+ #endif
+
+ nxagentSessionState = SESSION_GOING_DOWN;
+
+ nxagentSetReconnectError(FAILED_RESUME_DISPLAY_BROKEN_ALERT,
+ "Got I/O error during reconnect.");
+
+ nxagentChangeOption(Fullscreen, False);
+
+ return 1;
+ }
+ else if (nxagentSessionState == SESSION_DOWN)
+ {
+ #ifdef TEST
+ fprintf(stderr, "nxagentHandleConnectionStates: Got I/O error with session "
+ "[SESSION_DOWN]. Ignoring.\n");
+ #endif
+
+ return 1;
+ }
+ else
+ {
+ #ifdef TEST
+ fprintf(stderr, "nxagentHandleConnectionStates: Got I/O error with session "
+ "[%d].\n", nxagentSessionState);
+ #endif
+ }
+
+ nxagentSessionState = SESSION_DOWN;
+
+ if ((dispatchException & DE_TERMINATE) == 0)
+ {
+ #ifdef NX_DEBUG_INPUT
+ fprintf(stderr, "Session: Session suspended at '%s' timestamp [%lu].\n", GetTimeAsString(), GetTimeInMillis());
+ #else
+ fprintf(stderr, "Session: Session suspended at '%s'.\n", GetTimeAsString());
+ #endif
+ }
+ saveAgentState("SUSPENDED");
+
+ nxagentResetDisplayHandlers();
+
+ return 1;
+ }
+
+ fprintf(stderr, "Info: Disconnected from display '%s'.\n", nxagentDisplayName);
+
+ nxagentTerminateSession();
+
+ return -1;
+ }
+
+ return 0;
+}
+
+void nxagentInitializeRecLossyLevel()
+{
+ *(int *)reconnectLossyLevel[DISPLAY_STEP] = 0;
+ *(int *)reconnectLossyLevel[SCREEN_STEP] = 0;
+ *(int *)reconnectLossyLevel[FONT_STEP] = 0;
+ *(int *)reconnectLossyLevel[PIXMAP_STEP] = 0;
+ *(int *)reconnectLossyLevel[GC_STEP] = 0;
+ *(int *)reconnectLossyLevel[CURSOR_STEP] = 0;
+ *(int *)reconnectLossyLevel[COLORMAP_STEP] = 0;
+ *(int *)reconnectLossyLevel[WINDOW_STEP] = 0;
+ *(int *)reconnectLossyLevel[GLYPHSET_STEP] = 0;
+ *(int *)reconnectLossyLevel[PICTFORMAT_STEP] = 0;
+ *(int *)reconnectLossyLevel[PICTURE_STEP] = 0;
+}
+
+void nxagentInitReconnector(void)
+{
+ nxagentReconnectTrap = 0;
+
+ reconnectLossyLevel[DISPLAY_STEP] = malloc(sizeof(int));
+ reconnectLossyLevel[SCREEN_STEP] = malloc(sizeof(int));
+ reconnectLossyLevel[FONT_STEP] = malloc(sizeof(int));
+ reconnectLossyLevel[PIXMAP_STEP] = malloc(sizeof(int));
+ reconnectLossyLevel[GC_STEP] = malloc(sizeof(int));
+ reconnectLossyLevel[CURSOR_STEP] = malloc(sizeof(int));
+ reconnectLossyLevel[COLORMAP_STEP] = malloc(sizeof(int));
+ reconnectLossyLevel[WINDOW_STEP] = malloc(sizeof(int));
+ reconnectLossyLevel[GLYPHSET_STEP] = malloc(sizeof(int));
+ reconnectLossyLevel[PICTFORMAT_STEP] = malloc(sizeof(int));
+ reconnectLossyLevel[PICTURE_STEP] = malloc(sizeof(int));
+}
+
+void nxagentDisconnectSession(void)
+{
+ #ifdef TEST
+ fprintf(stderr, "nxagentDisconnectSession: Disconnecting session with state [%s].\n",
+ DECODE_SESSION_STATE(nxagentSessionState));
+ #endif
+
+ /*
+ * Force an I/O error on the display
+ * and wait until the NX transport
+ * is gone.
+ */
+
+ #ifdef TEST
+ fprintf(stderr, "nxagentDisconnectSession: Disconnecting the X display.\n");
+ #endif
+
+ nxagentWaitDisplay();
+
+ /*
+ * Prepare for the next reconnection.
+ */
+
+ #ifdef TEST
+ fprintf(stderr, "nxagentDisconnectSession: Disconnecting all X resources.\n");
+ #endif
+
+ nxagentInitializeRecLossyLevel();
+
+ nxagentBackupDisplayInfo();
+
+ if (nxagentOption(Rootless))
+ {
+ nxagentFreePropertyList();
+ }
+
+ if (nxagentRenderEnable)
+ {
+ nxagentDisconnectAllPicture();
+ }
+
+ nxagentEmptyAllBackingStoreRegions();
+
+ nxagentDisconnectAllWindows();
+ nxagentDisconnectAllCursor();
+ nxagentDisconnectAllPixmaps();
+ nxagentDisconnectAllGCs();
+ nxagentDisconnectDisplay();
+
+ nxagentWMIsRunning = 0;
+
+ #ifdef TEST
+ fprintf(stderr, "nxagentDisconnectSession: Disconnection completed. SigHup is [%d]. IoError is [%d].\n",
+ nxagentException.sigHup, nxagentException.ioError);
+ #endif
+}
+
+Bool nxagentReconnectSession(void)
+{
+ char *nxagentOldKeyboard = NULL;
+
+ nxagentResizeDesktopAtStartup = False;
+
+ /*
+ * Propagate device settings if explicitly asked for.
+ */
+
+ nxagentChangeOption(DeviceControl, nxagentOption(DeviceControlUserDefined));
+
+ /*
+ * We need to zero out every new XID
+ * created by the disconnected display.
+ */
+
+ nxagentDisconnectSession();
+
+ /*
+ * Set this in order to let the screen
+ * function to behave differently at
+ * reconnection time.
+ */
+
+ nxagentReconnectTrap = True;
+
+ nxagentSetReconnectError(0, NULL);
+
+ if (nxagentKeyboard != NULL)
+ {
+ nxagentOldKeyboard = strndup(nxagentKeyboard, strlen(nxagentKeyboard));
+ if (nxagentOldKeyboard == NULL)
+ {
+ /* 0 means reconnection failed */
+ return 0;
+ }
+
+ free(nxagentKeyboard);
+ nxagentKeyboard = NULL;
+ }
+
+ nxagentSaveOptions();
+
+ nxagentResetOptions();
+
+ nxagentProcessOptionsFile();
+
+ if (nxagentReconnectDisplay(reconnectLossyLevel[DISPLAY_STEP]) == 0)
+ {
+ failedStep = DISPLAY_STEP;
+
+ #ifdef TEST
+ fprintf(stderr, "nxagentReconnect: WARNING! Failed display reconnection.\n");
+ #endif
+
+ goto nxagentReconnectError;
+ }
+
+ if (nxagentReconnectScreen(reconnectLossyLevel[SCREEN_STEP]) == 0)
+ {
+ failedStep = SCREEN_STEP;
+
+ goto nxagentReconnectError;
+ }
+
+ nxagentDisconnectAllFonts();
+
+ nxagentListRemoteFonts("*", nxagentMaxFontNames);
+
+ if (nxagentReconnectAllFonts(reconnectLossyLevel[FONT_STEP]) == 0)
+ {
+ if (nxagentReconnectFailedFonts(reconnectLossyLevel[FONT_STEP]) == 0)
+ {
+ failedStep = FONT_STEP;
+
+ goto nxagentReconnectError;
+ }
+ else
+ {
+ #ifdef WARNING
+ fprintf(stderr, "nxagentReconnect: WARNING! Unable to retrieve all the fonts currently in use. "
+ "Missing fonts have been replaced.\n");
+ #endif
+
+ nxagentLaunchDialog(DIALOG_FONT_REPLACEMENT);
+ }
+ }
+
+ /*
+ * Map the main window and send a
+ * SetSelectionOwner request to
+ * notify of the agent start.
+ */
+
+ nxagentMapDefaultWindows();
+
+ /*
+ * Ensure that the SetSelectionOwner
+ * request is sent through the link.
+ */
+
+ XFlush(nxagentDisplay);
+
+ NXTransContinue(NULL);
+
+ nxagentEmptyBSPixmapList();
+
+ if (nxagentReconnectAllPixmaps(reconnectLossyLevel[PIXMAP_STEP]) == 0)
+ {
+ failedStep = PIXMAP_STEP;
+
+ goto nxagentReconnectError;
+ }
+
+ if (nxagentReconnectAllGCs(reconnectLossyLevel[GC_STEP]) == 0)
+ {
+ failedStep = GC_STEP;
+
+ goto nxagentReconnectError;
+ }
+
+ if (nxagentReconnectAllColormap(reconnectLossyLevel[COLORMAP_STEP]) == 0)
+ {
+ failedStep = COLORMAP_STEP;
+
+ goto nxagentReconnectError;
+ }
+
+ if (nxagentReconnectAllWindows(reconnectLossyLevel[WINDOW_STEP]) == 0)
+ {
+ failedStep = WINDOW_STEP;
+
+ goto nxagentReconnectError;
+ }
+
+ if (nxagentRenderEnable)
+ {
+ if (nxagentReconnectAllGlyphSet(reconnectLossyLevel[GLYPHSET_STEP]) == 0)
+ {
+ failedStep = GLYPHSET_STEP;
+
+ goto nxagentReconnectError;
+ }
+
+ if (nxagentReconnectAllPictFormat(reconnectLossyLevel[PICTFORMAT_STEP]) == 0)
+ {
+ failedStep = PICTFORMAT_STEP;
+
+ goto nxagentReconnectError;
+ }
+
+ if (nxagentReconnectAllPicture(reconnectLossyLevel[PICTURE_STEP]) == 0)
+ {
+ failedStep = PICTURE_STEP;
+
+ goto nxagentReconnectError;
+ }
+ }
+
+ if (nxagentReconnectAllCursor(reconnectLossyLevel[CURSOR_STEP]) == 0)
+ {
+ failedStep = CURSOR_STEP;
+
+ goto nxagentReconnectError;
+ }
+
+ if (nxagentSetWindowCursors(reconnectLossyLevel[WINDOW_STEP]) == 0)
+ {
+ failedStep = WINDOW_STEP;
+
+ goto nxagentReconnectError;
+ }
+
+ if (nxagentOption(ResetKeyboardAtResume) == 1 &&
+ (nxagentKeyboard == NULL || nxagentOldKeyboard == NULL ||
+ strcmp(nxagentKeyboard, nxagentOldKeyboard) != 0 ||
+ strcmp(nxagentKeyboard, "query") == 0))
+ {
+ if (nxagentResetKeyboard() == 0)
+ {
+ #ifdef WARNING
+ if (nxagentVerbose == 1)
+ {
+ fprintf(stderr, "nxagentReconnect: Failed to reset keyboard device.\n");
+ }
+ #endif
+
+ failedStep = WINDOW_STEP;
+
+ goto nxagentReconnectError;
+ }
+ }
+ else
+ {
+ nxagentResetKeycodeConversion();
+ }
+
+ nxagentXkbState.Initialized = 0;
+
+ if (nxagentOldKeyboard != NULL)
+ {
+ free(nxagentOldKeyboard);
+
+ nxagentOldKeyboard = NULL;
+ }
+
+ nxagentInitPointerMap();
+
+ nxagentDeactivatePointerGrab();
+
+ nxagentWakeupByReconnect();
+
+ nxagentFreeGCList();
+
+ nxagentRedirectDefaultWindows();
+
+ if (nxagentResizeDesktopAtStartup || nxagentOption(Rootless) == True || nxagentOption(Xinerama) == True)
+ {
+ nxagentChangeScreenConfig(0, nxagentOption(RootWidth),
+ nxagentOption(RootHeight), 0, 0);
+
+ nxagentResizeDesktopAtStartup = False;
+ }
+
+ nxagentReconnectTrap = False;
+
+ nxagentExposeArrayIsInitialized = False;
+
+ if (nxagentSessionState != SESSION_GOING_UP)
+ {
+ #ifdef WARNING
+ fprintf(stderr, "nxagentReconnect: WARNING! Unexpected session state [%s] while reconnecting.\n",
+ DECODE_SESSION_STATE(nxagentSessionState));
+ #endif
+
+ goto nxagentReconnectError;
+ }
+
+ /* Re-read keystrokes definitions in case the keystrokes file has
+ changed while being supended */
+ nxagentInitKeystrokes(True);
+
+ #ifdef NX_DEBUG_INPUT
+ fprintf(stderr, "Session: Session resumed at '%s' timestamp [%lu].\n", GetTimeAsString(), GetTimeInMillis());
+ #else
+ fprintf(stderr, "Session: Session resumed at '%s'.\n", GetTimeAsString());
+ #endif
+ saveAgentState("RUNNING");
+
+ nxagentRemoveSplashWindow(NULL);
+
+ /*
+ * We let the proxy flush the link on our behalf
+ * after having opened the display. We are now
+ * entering again the dispatcher so can flush
+ * the link explicitly.
+ */
+
+ #ifdef TEST
+ fprintf(stderr, "nxagentReconnect: Setting the NX flush policy to deferred.\n");
+ #endif
+
+ NXSetDisplayPolicy(nxagentDisplay, NXPolicyDeferred);
+
+ nxagentCleanupBackupDisplayInfo();
+
+ return 1;
+
+nxagentReconnectError:
+
+ if (failedStep == DISPLAY_STEP)
+ {
+ #ifdef TEST
+ fprintf(stderr, "nxagentReconnect: Reconnection failed in display step. Restoring options.\n");
+ #endif
+
+ nxagentRestoreOptions();
+ }
+ else
+ {
+ nxagentCleanupBackupDisplayInfo();
+ }
+
+ if (*nxagentGetReconnectError() == '\0')
+ {
+ #ifdef WARNING
+ if (nxagentVerbose == 1)
+ {
+ fprintf(stderr, "nxagentReconnect: WARNING! The reconnect error message is not set. Failed step is [%d].\n",
+ failedStep);
+ }
+ #endif
+
+ #ifdef TEST
+ fprintf(stderr, "nxagentReconnect: Reconnection failed due to a display error.\n");
+ #endif
+ }
+ else
+ {
+ #ifdef TEST
+ fprintf(stderr, "nxagentReconnect: Reconnection failed with reason '%s'\n",
+ nxagentGetReconnectError());
+ #endif
+ }
+
+ if (NXDisplayError(nxagentDisplay) == 0)
+ {
+ nxagentUnmapWindows();
+
+ nxagentFailedReconnectionDialog(nxagentReconnectErrorId, nxagentGetReconnectError());
+ }
+ #ifdef TEST
+ else
+ {
+ fprintf(stderr, "nxagentReconnect: Cannot launch the dialog without a valid display.\n");
+ }
+ #endif
+
+ if (failedStep == FONT_STEP)
+ {
+ *((int *) reconnectLossyLevel[FONT_STEP]) = 1;
+ }
+
+ if (nxagentDisplay == NULL)
+ {
+ nxagentDisconnectDisplay();
+ }
+
+ if (nxagentOldKeyboard != NULL)
+ {
+ free(nxagentOldKeyboard);
+
+ nxagentOldKeyboard = NULL;
+ }
+
+ return 0;
+}
+
+void nxagentSetReconnectError(int id, char *format, ...)
+{
+ static int size = 0;
+
+ va_list ap;
+ int n;
+
+ if (format == NULL)
+ {
+ nxagentSetReconnectError(id, "");
+
+ return;
+ }
+
+ nxagentReconnectErrorId = id;
+
+ while (1)
+ {
+ va_start (ap, format);
+
+ n = vsnprintf(nxagentReconnectErrorMessage, size, format, ap);
+
+ va_end(ap);
+
+ if (n > -1 && n < size)
+ {
+ break;
+ }
+ if (n > -1)
+ {
+ size = n + 1;
+ }
+ else
+ {
+ /*
+ * The vsnprintf() in glibc 2.0.6 would return
+ * -1 when the output was truncated. See section
+ * NOTES on printf(3).
+ */
+
+ size = (size ? size * 2 : NXAGENT_RECONNECT_DEFAULT_MESSAGE_SIZE);
+ }
+
+ nxagentReconnectErrorMessage = realloc(nxagentReconnectErrorMessage, size);
+
+ if (nxagentReconnectErrorMessage == NULL)
+ {
+ FatalError("realloc failed");
+ }
+ }
+
+ return;
+}
+
+static char* nxagentGetReconnectError()
+{
+ if (nxagentReconnectErrorMessage == NULL)
+ {
+ nxagentSetReconnectError(nxagentReconnectErrorId, "");
+ }
+
+ return nxagentReconnectErrorMessage;
+}
+
+void nxagentHandleConnectionChanges()
+{
+ #ifdef TEST
+ fprintf(stderr, "nxagentHandleConnectionChanges: Called.\n");
+ #endif
+
+ if (nxagentSessionState == SESSION_GOING_DOWN)
+ {
+ fprintf(stderr, "Session: Suspending session at '%s'.\n", GetTimeAsString());
+ saveAgentState("SUSPENDING");
+
+ nxagentDisconnectSession();
+ }
+ else if (nxagentSessionState == SESSION_GOING_UP)
+ {
+ fprintf(stderr, "Session: Resuming session at '%s'.\n", GetTimeAsString());
+ saveAgentState("RESUMING");
+
+ if (nxagentReconnectSession())
+ {
+ nxagentSessionState = SESSION_UP;
+ }
+ else
+ {
+ nxagentSessionState = SESSION_GOING_DOWN;
+
+ fprintf(stderr, "Session: Display failure detected at '%s'.\n", GetTimeAsString());
+
+ fprintf(stderr, "Session: Suspending session at '%s'.\n", GetTimeAsString());
+ saveAgentState("SUSPENDING");
+
+ nxagentDisconnectSession();
+ }
+ }
+}
+