1
/**************************************************************************/
3
/* Copyright (c) 2001, 2007 NoMachine, http://www.nomachine.com/. */
5
/* NXCOMP, NX protocol compression and NX extensions to this software */
6
/* are copyright of NoMachine. Redistribution and use of the present */
7
/* software is allowed according to terms specified in the file LICENSE */
8
/* which comes in the source distribution. */
10
/* Check http://www.nomachine.com/licensing.html for applicability. */
12
/* NX and NoMachine are trademarks of NoMachine S.r.l. */
14
/* All rights reserved. */
16
/**************************************************************************/
34
#include <sys/types.h>
37
#include <sys/resource.h>
38
#include <sys/utsname.h>
41
#include <netinet/in.h>
42
#include <arpa/inet.h>
51
// MacOSX 10.4 defines socklen_t. This is
52
// intended to ensure compatibility with
57
#include <AvailabilityMacros.h>
58
#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_3
59
typedef int socklen_t;
65
#include <sys/select.h>
69
#include <netinet/tcp.h>
83
#include "Statistics.h"
88
#include "ClientProxy.h"
89
#include "ServerProxy.h"
94
// System specific defines.
97
#if defined(__EMX__) || defined(__CYGWIN32__)
108
// HP-UX hides this define.
111
#if defined(hpux) && !defined(RLIM_INFINITY)
113
#define RLIM_INFINITY 0x7fffffff
118
// Set the verbosity level.
128
// Enable log output in signal handler.
129
// This is likely to hang the proxy at
130
// random, at least on Linux.
136
// Let all logs go to the standard error.
137
// This is useful to interleave the Xlib
138
// log output with the proxy output in a
145
// Define this to check if the client and
146
// server caches match at shutdown. This
147
// is a test facility as it requires that
148
// both proxies are running on the same
155
// If defined, reduce the size of the log
156
// file and be sure it never exceeds the
163
// If defined, force very strict limits for
164
// the proxy tokens and force the proxy to
165
// enter often in congestion state.
171
// Print a line in the log if the time we
172
// spent inside the select or handling the
173
// messages exceeded a given time value.
179
// This can be useful when testing the forwarding
180
// of the SSHD connection by nxssh to the agent.
181
// The debug output will go to a well known file
182
// that will be opened also by nxssh when BINDER
189
// Define this to override the limits on
190
// the core dump size.
196
// Upper limit of pre-allocated buffers
197
// for string parameters.
200
#define DEFAULT_STRING_LENGTH 256
203
// Maximum length of remote options data
204
// passed by peer proxy at startup.
207
#define DEFAULT_REMOTE_OPTIONS_LENGTH 512
210
// Maximum length of NX display string.
213
#define DEFAULT_DISPLAY_OPTIONS_LENGTH 1024
216
// Maximum number of cache file names to
217
// send to the server side.
220
#define DEFAULT_REMOTE_CACHE_ENTRIES 100
223
// Maximum length of remote options string
224
// that can be received from the peer proxy.
227
#define MAXIMUM_REMOTE_OPTIONS_LENGTH 4096
230
// Macro is true if we determined our proxy
234
#define WE_SET_PROXY_MODE (control -> ProxyMode != proxy_undefined)
237
// Macro is true if our side is the one that
238
// should connect to remote.
241
#define WE_INITIATE_CONNECTION (*connectHost != '\0')
244
// Is true if we must provide our credentials
245
// to the remote peer.
248
#define WE_PROVIDE_CREDENTIALS (control -> ProxyMode == proxy_server)
251
// Is true if we listen for a local forwarder
252
// that will tunnel the traffic through a SSH
256
#define WE_LISTEN_FORWARDER (control -> ProxyMode == proxy_server && \
260
// You must define FLUSH in Misc.h if
261
// you want an immediate flush of the
265
ostream *logofs = NULL;
268
// Other stream destriptors used for
272
ostream *statofs = NULL;
273
ostream *errofs = NULL;
276
// Save standard error's rdbuf here
277
// and restore it when exiting.
280
static streambuf *errsbuf = NULL;
283
// Allow faults to be recovered by
284
// jumping back into the main loop.
290
// Provide operational parameters.
293
Control *control = NULL;
296
// Collect and print statistics.
299
Statistics *statistics = NULL;
302
// Keep data for X11 authentication.
308
// This class makes the hard work.
314
// Used to handle memory-to-memory
315
// transport to the X agent.
321
// The image cache house-keeping class.
324
Keeper *keeper = NULL;
327
// Callback set by the child process
328
// to be notified about signals.
331
int (*handler)(int) = NULL;
334
// Signal handling functions.
337
void DisableSignals();
338
void EnableSignals();
339
void InstallSignals();
341
static void RestoreSignals();
342
static void HandleSignal(int signal);
345
// Signal handling utilities.
348
static void InstallSignal(int signal, int action);
349
static void RestoreSignal(int signal);
351
static int HandleChildren();
353
static int HandleChild(int child);
354
static int CheckChild(int pid, int status);
355
static int WaitChild(int child, const char *label, int force);
357
int CheckParent(char *name, char *type, int parent);
359
void RegisterChild(int child);
361
static int CheckAbort();
364
// Timer handling utilities.
367
void SetTimer(int timeout);
370
static void HandleTimer(int signal);
373
// Kill or check a running child.
376
static int KillProcess(int pid, const char *label, int signal, int wait);
377
static int CheckProcess(int pid, const char *label);
380
// Macros used to test the pid of a child.
383
#define IsFailed(pid) ((pid) < 0)
384
#define IsRunning(pid) ((pid) > 1)
385
#define IsNotRunning(pid) ((pid) == 0)
386
#define IsRestarting(pid) ((pid) == 1)
388
#define SetNotRunning(pid) ((pid) = 0)
389
#define SetRestarting(pid) ((pid) = 1)
392
// Start or restart the house-keeper process.
395
static int StartKeeper();
398
// Cleanup functions.
401
void CleanupConnections();
402
void CleanupListeners();
403
void CleanupSockets();
404
void CleanupGlobal();
406
static void CleanupChildren();
407
static void CleanupLocal();
408
static void CleanupKeeper();
409
static void CleanupStreams();
412
// Loop forever until the connections
413
// to the peer proxy is dropped.
416
static void WaitCleanup();
419
// Initialization functions.
422
static int InitBeforeNegotiation();
423
static int SetupProxyConnection();
424
static int InitAfterNegotiation();
425
static int SetupProxyInstance();
426
static int SetupAuthInstance();
427
static int SetupAgentInstance();
429
static int SetupTcpSocket();
430
static int SetupUnixSocket();
431
static int SetupServiceSockets();
432
static int SetupDisplaySocket(int &xServerAddrFamily, sockaddr *&xServerAddr,
433
unsigned int &xServerAddrLength);
436
// Setup a listening socket and accept
440
static int ListenConnection(int port, const char *label);
441
static int AcceptConnection(int fd, int domain, const char *label);
444
// Other convenience functions.
447
static int WaitForRemote(int portNum);
448
static int ConnectToRemote(const char *const hostName, int portNum);
450
static int SendProxyOptions(int fd);
451
static int SendProxyCaches(int fd);
452
static int ReadProxyVersion(int fd);
453
static int ReadProxyOptions(int fd);
454
static int ReadProxyCaches(int fd);
455
static int ReadForwarderVersion(int fd);
456
static int ReadForwarderOptions(int fd);
458
static int ReadRemoteData(int fd, char *buffer, int size, char stop);
459
static int WriteLocalData(int fd, const char *buffer, int size);
461
static void PrintVersionInfo();
462
static void PrintProcessInfo();
463
static void PrintConnectionInfo();
464
static void PrintUsageInfo(const char *option, const int error);
465
static void PrintOptionIgnored(const char *type, const char *name, const char *value);
468
// This is not static to avoid a warning.
471
void PrintCopyrightInfo();
473
static const char *GetOptions(const char *options);
474
static const char *GetArg(int &argi, int argc, const char **argv);
475
static int CheckArg(const char *type, const char *name, const char *value);
476
static int ParseArg(const char *type, const char *name, const char *value);
477
static int ValidateArg(const char *type, const char *name, const char *value);
478
static int LowercaseArg(const char *type, const char *name, char *value);
479
static int CheckSignal(int signal);
483
int ParseCommandLineOptions(int argc, const char **argv);
484
int ParseEnvironmentOptions(const char *env, int force);
485
int ParseBindOptions(char **host, int *port);
488
static int ParseFileOptions(const char *file);
489
static int ParseRemoteOptions(char *opts);
490
static int ParseForwarderOptions(char *opts);
493
// These functions are used to parse literal
494
// values provided by the user and set the
495
// control parameters accordingly.
498
static int ParseLinkOption(const char *opt);
499
static int ParseBitrateOption(const char *opt);
500
static int ParseCacheOption(const char *opt);
501
static int ParseShmemOption(const char *opt);
502
static int ParseImagesOption(const char *opt);
503
static int ParsePackOption(const char *opt);
506
// Set host and port where NX proxy is supposed
507
// to be listening in case such parameters are
508
// given on the command line.
511
static int ParseHostOption(const char *opt, char *host, int &port);
514
// Translate a font server port specification
515
// to the corresponding Unix socket path.
518
static int ParseFontPath(char *path);
521
// Determine the interface where to listen for
522
// the remote proxy connection or the local
526
static int ParseListenOption(int &interface);
529
// Translate a pack method id in a literal.
532
static int ParsePackMethod(const int method, const int quality);
535
// Try to increase the size of the allowed
539
static int SetCore();
542
// Set the proxy mode to either client or
546
static int SetMode(int mode);
549
// Determine the path of the NX_* directories
550
// from the environment.
553
static int SetDirectories();
556
// Set the defaults used for the log file and
560
static int SetLogs();
563
// Check if local and remote protocol versions
564
// are compatible and, eventually, downgrade
565
// local version to the minimum level that is
569
static int SetVersion();
572
// Setup the listening TCP ports used for the
573
// additional channels according to user's
577
static int SetPorts();
580
// Set the maximum number of open descriptors.
583
static int SetDescriptors();
586
// Set the path used for choosing the cache.
587
// It must be selected after determining the
591
static int SetCaches();
594
// Initialize, one after the other, all the
595
// configuration parameters.
598
static int SetParameters();
601
// Set the specific configuration parameter.
604
static int SetSession();
605
static int SetStorage();
606
static int SetShmem();
607
static int SetPack();
608
static int SetImages();
609
static int SetLimits();
612
// Set up the control parameters based on
613
// the link speed negotiated between the
617
static int SetLink();
619
static int SetLinkModem();
620
static int SetLinkIsdn();
621
static int SetLinkAdsl();
622
static int SetLinkWan();
623
static int SetLinkLan();
626
// Adjust the compression parameters.
629
static int SetCompression();
631
static int SetCompressionModem();
632
static int SetCompressionIsdn();
633
static int SetCompressionAdsl();
634
static int SetCompressionWan();
635
static int SetCompressionLan();
638
// Determine the NX paths based on the
639
// user's parameters or the environment.
642
char *GetClientPath();
644
static char *GetSystemPath();
645
static char *GetHomePath();
646
static char *GetTempPath();
647
static char *GetRootPath();
648
static char *GetCachePath();
649
static char *GetImagesPath();
650
static char *GetSessionPath();
651
static char *GetLastCache(char *list, const char *path);
653
static int OpenLogFile(char *name, ostream *&stream);
654
static int ReopenLogFile(char *name, ostream *&stream, int limit);
657
// Perform operations on the managed
658
// descriptors in the main loop.
661
static void handleCheckSessionInLoop();
662
static void handleCheckBitrateInLoop();
664
#if defined(TEST) || defined(INFO)
665
static void handleCheckSelectInLoop(int &setFDs, fd_set &readSet,
666
fd_set &writeSet, T_timestamp selectTs);
667
static void handleCheckResultInLoop(int &resultFDs, int &errorFDs, int &setFDs, fd_set &readSet,
668
fd_set &writeSet, struct timeval &selectTs,
669
struct timeval &startTs);
670
static void handleCheckStateInLoop(int &setFDs);
673
static void handleCheckSessionInConnect();
675
static inline void handleSetReadInLoop(fd_set &readSet, int &setFDs, struct timeval &selectTs);
676
static inline void handleSetWriteInLoop(fd_set &writeSet, int &setFDs, struct timeval &selectTs);
677
static inline void handleSetListenersInLoop(fd_set &writeSet, int &setFDs);
678
static inline void handleSetAgentInLoop(int &setFDs, fd_set &readSet, fd_set &writeSet,
679
struct timeval &selectTs);
681
static void handleAlertInLoop();
682
static void handleStatisticsInLoop();
684
static inline void handleAgentInLoop(int &resultFDs, int &errorFDs, int &setFDs, fd_set &readSet,
685
fd_set &writeSet, struct timeval &selectTs);
686
static inline void handleAgentLateInLoop(int &resultFDs, int &errorFDs, int &setFDs, fd_set &readSet,
687
fd_set &writeSet, struct timeval &selectTs);
689
static inline void handleReadableInLoop(int &resultFDs, fd_set &readSet);
690
static inline void handleWritableInLoop(int &resultFDs, fd_set &writeSet);
692
static inline void handleRotateInLoop();
693
static inline void handleEventsInLoop();
694
static inline void handleFlushInLoop();
697
// Manage the proxy link during the negotiation
701
static void handleNegotiationInLoop(int &setFDs, fd_set &readSet,
702
fd_set &writeSet, T_timestamp &selectTs);
705
// Print the 'terminating' messages in the
709
static inline void handleTerminatingInLoop();
710
static inline void handleTerminatedInLoop();
713
// Monitor the size of the log file.
716
static void handleLogReopenInLoop(T_timestamp &logsTs, T_timestamp &nowTs);
719
// Directory where the NX binaries and libraries reside.
722
static char systemDir[DEFAULT_STRING_LENGTH] = { 0 };
725
// Directory used for temporary files.
728
static char tempDir[DEFAULT_STRING_LENGTH] = { 0 };
731
// Actually the full path to the client.
734
static char clientDir[DEFAULT_STRING_LENGTH] = { 0 };
737
// User's home directory.
740
static char homeDir[DEFAULT_STRING_LENGTH] = { 0 };
743
// Root of directory structure to be created by proxy.
746
static char rootDir[DEFAULT_STRING_LENGTH] = { 0 };
749
// Root of statistics and log files to be created by proxy.
752
static char sessionDir[DEFAULT_STRING_LENGTH] = { 0 };
755
// Log files for errors and statistics. Error log is
756
// the place where we print also debug informations.
757
// Both files are closed, deleted and reopened as
758
// their size exceed the limit set in control class.
759
// The session log is not reopened, as it is used by
760
// the NX client and server to track the advance of
764
static char errorsFileName[DEFAULT_STRING_LENGTH] = { 0 };
765
static char statsFileName[DEFAULT_STRING_LENGTH] = { 0 };
766
static char sessionFileName[DEFAULT_STRING_LENGTH] = { 0 };
767
static char optionsFileName[DEFAULT_STRING_LENGTH] = { 0 };
770
// String literal representing selected link speed
771
// parameter. Value is translated in control values
772
// used by proxies to stay synchronized.
775
static char linkSpeedName[DEFAULT_STRING_LENGTH] = { 0 };
778
// String literal representing selected
782
static char cacheSizeName[DEFAULT_STRING_LENGTH] = { 0 };
785
// String literal representing selected
786
// shared memory segment size.
789
static char shsegSizeName[DEFAULT_STRING_LENGTH] = { 0 };
792
// String literal of images cache size.
795
static char imagesSizeName[DEFAULT_STRING_LENGTH] = { 0 };
798
// String literal for bandwidth limit.
801
static char bitrateLimitName[DEFAULT_STRING_LENGTH] = { 0 };
804
// String literal for image packing method.
807
static char packMethodName[DEFAULT_STRING_LENGTH] = { 0 };
810
// Product name provided by the server or client.
813
static char productName[DEFAULT_STRING_LENGTH] = { 0 };
816
// Its corresponding value from NXpack.h.
819
static int packMethod = -1;
820
static int packQuality = -1;
823
// String literal for session type. Persistent caches
824
// are searched in directory whose name matches this
828
static char sessionType[DEFAULT_STRING_LENGTH] = { 0 };
831
// Unique id assigned to session. It is used as
832
// name of directory where all files are placed.
835
static char sessionId[DEFAULT_STRING_LENGTH] = { 0 };
838
// Set if we already parsed the options.
841
static int parsedOptions = 0;
842
static int parsedCommand = 0;
845
// Buffer data received from the remote proxy at
846
// session negotiation.
849
static char remoteData[MAXIMUM_REMOTE_OPTIONS_LENGTH] = { 0 };
850
static int remotePosition = 0;
853
// Main loop file descriptors.
856
static int tcpFD = -1;
857
static int unixFD = -1;
858
static int cupsFD = -1;
859
static int auxFD = -1;
860
static int smbFD = -1;
861
static int mediaFD = -1;
862
static int httpFD = -1;
863
static int fontFD = -1;
864
static int slaveFD = -1;
865
static int proxyFD = -1;
868
// Used for internal communication
872
static int agentFD[2] = { -1, -1 };
875
// Flags determining which protocols and
876
// ports are forwarded.
879
int useUnixSocket = 1;
881
static int useTcpSocket = 1;
882
static int useCupsSocket = 0;
883
static int useAuxSocket = 0;
884
static int useSmbSocket = 0;
885
static int useMediaSocket = 0;
886
static int useHttpSocket = 0;
887
static int useFontSocket = 0;
888
static int useSlaveSocket = 0;
889
static int useAgentSocket = 0;
892
// Set if the launchd service is running
893
// and its socket must be used as X socket.
896
static int useLaunchdSocket = 0;
899
// Set by user if he/she wants to modify
900
// the default TCP_NODELAY option as set
904
static int useNoDelay = -1;
907
// Set if user wants to override default
908
// flush timeout set according to link.
911
static int usePolicy = -1;
914
// Set if user wants to hide the RENDER
915
// extension or wants to short-circuit
916
// some simple replies at client side.
919
static int useRender = -1;
920
static int useTaint = -1;
923
// Set if the user wants to reduce the
924
// nominal size of the token messages
925
// exchanged between the proxies.
928
static int useStrict = -1;
931
// Set if the proxy is running as part
932
// of SSH on the client.
935
static int useEncryption = -1;
938
// Name of Unix socket created by the client proxy to
939
// accept client connections. File must be unlinked
940
// by cleanup function.
943
static char unixSocketName[DEFAULT_STRING_LENGTH] = { 0 };
949
static char connectHost[DEFAULT_STRING_LENGTH] = { 0 };
950
static char acceptHost[DEFAULT_STRING_LENGTH] = { 0 };
951
static char listenHost[DEFAULT_STRING_LENGTH] = { 0 };
952
static char displayHost[DEFAULT_STRING_LENGTH] = { 0 };
953
static char authCookie[DEFAULT_STRING_LENGTH] = { 0 };
955
static int proxyPort = DEFAULT_NX_PROXY_PORT;
956
static int xPort = DEFAULT_NX_X_PORT;
959
// Used to setup the connection the real
963
static int xServerAddrFamily = -1;
964
static sockaddr *xServerAddr = NULL;
965
static unsigned int xServerAddrLength = 0;
968
// The port where the local proxy will await
969
// the peer connection or where the remote
970
// proxy will be contacted.
973
static int listenPort = -1;
974
static int connectPort = -1;
977
// Helper channels are disabled by default.
980
static int cupsPort = -1;
981
static int auxPort = -1;
982
static int smbPort = -1;
983
static int mediaPort = -1;
984
static int httpPort = -1;
985
static int slavePort = -1;
988
// Can be either a port number or a Unix
992
static char fontPort[DEFAULT_STRING_LENGTH] = { 0 };
995
// Host and port where the existing proxy
999
static char bindHost[DEFAULT_STRING_LENGTH] = { 0 };
1000
static int bindPort = -1;
1003
// Pointers to the callback functions and
1004
// parameter set by the agent
1007
static void (*flushCallback)(void *, int) = NULL;
1008
static void *flushParameter = NULL;
1010
static void (*statisticsCallback)(void *, int) = NULL;
1011
static void *statisticsParameter = NULL;
1014
// State variables shared between the init
1015
// function and the main loop.
1019
T_timestamp startTs;
1026
// This is set to the main proxy process id.
1032
// Set to last dialog process launched by proxy.
1038
// Set to watchdog process launched by proxy.
1041
int lastWatchdog = 0;
1044
// Set if a cache house-keeper process is running.
1050
// Let an inner routine register the pid of a slave
1054
static int lastChild = 0;
1057
// Exit code of the last child process exited.
1060
static int lastStatus = 0;
1063
// Set if shutdown was requested through a signal.
1066
static int lastKill = 0;
1069
// Set if the agent confirmed the destruction of
1070
// the NX transport.
1073
static int lastDestroy = 0;
1076
// This is set to the code and local flag of the
1077
// last requested alert.
1088
// Manage the current signal masks.
1101
struct sigaction action[32];
1106
// Manage the current timer.
1111
struct sigaction action;
1112
struct itimerval value;
1113
struct timeval start;
1114
struct timeval next;
1119
// This is set to last signal received in handler.
1122
static int lastSignal = 0;
1125
// Set to the last time bytes readable were queried
1129
static T_timestamp lastReadableTs = nullTimestamp();
1132
// Here are interfaces declared in NX.h.
1135
int NXTransProxy(int fd, int mode, const char* options)
1138
// Let the log temporarily go to the standard
1139
// error. Be also sure we have a jump context,
1140
// in the case any subsequent operation will
1149
if (setjmp(context) == 1)
1152
*logofs << "NXTransProxy: Out of the long jump with pid '"
1153
<< lastProxy << "'.\n" << logofs_flush;
1160
// Check if have already performed a parsing of
1161
// parameters, as in the case we are running as
1162
// a stand-alone process. If needed create the
1163
// parameters repository
1166
if (control == NULL)
1168
control = new Control();
1171
lastProxy = getpid();
1174
*logofs << "NXTransProxy: Main process started with pid '"
1175
<< lastProxy << "'.\n" << logofs_flush;
1180
if (mode == NX_MODE_CLIENT)
1182
if (fd != NX_FD_ANY)
1186
*logofs << "NXTransProxy: Agent descriptor for X client connections is FD#"
1187
<< fd << ".\n" << logofs_flush;
1189
*logofs << "NXTransProxy: Disabling listening on further X client connections.\n"
1201
else if (mode == NX_MODE_SERVER)
1203
if (fd != NX_FD_ANY)
1206
*logofs << "NXTransProxy: PANIC! Agent descriptor for X server connections "
1207
<< "not supported yet.\n" << logofs_flush;
1210
cerr << "Error" << ": Agent descriptor for X server connections "
1211
<< "not supported yet.\n";
1217
const char *env = GetOptions(options);
1219
if (ParseEnvironmentOptions(env, 0) < 0)
1221
cerr << "Error" << ": Parsing of NX transport options failed.\n";
1227
// Set the path of the NX directories.
1233
// Open the log files.
1239
*logofs << "NXTransProxy: Going to run the NX transport loop.\n"
1246
// This function should never return.
1252
void NXTransExit(int code)
1264
*logofs << "NXTransExit: Aborting process with pid '"
1265
<< getpid() << "' due to recursion through "
1266
<< "exit.\n" << logofs_flush;
1273
*logofs << "NXTransExit: Process with pid '"
1274
<< getpid() << "' called exit with code '"
1275
<< code << "'.\n" << logofs_flush;
1278
if (control != NULL)
1281
// Be sure that there we can detect the
1282
// termination of the watchdog.
1288
// Close the NX transport if it was not
1289
// shut down already.
1292
NXTransDestroy(NX_FD_ANY);
1298
int NXTransParseCommandLine(int argc, const char **argv)
1300
return ParseCommandLineOptions(argc, argv);
1303
int NXTransParseEnvironment(const char *env, int force)
1305
return ParseEnvironmentOptions(env, force);
1308
void NXTransCleanup()
1314
// Check the parameters for subsequent
1315
// initialization of the NX transport.
1318
int NXTransCreate(int fd, int mode, const char* options)
1326
// Be sure we have a jump context, in the
1327
// case a subsequent operation will cause
1331
if (setjmp(context) == 1)
1337
// Create the parameters repository
1340
if (control != NULL)
1343
*logofs << "NXTransCreate: PANIC! The NX transport seems "
1344
<< "to be already running.\n" << logofs_flush;
1347
cerr << "Error" << ": The NX transport seems "
1348
<< "to be already running.\n";
1353
control = new Control();
1355
if (control == NULL)
1358
*logofs << "Loop: PANIC! Error creating the NX transport.\n"
1362
cerr << "Error" << ": Error creating the NX transport.\n";
1367
lastProxy = getpid();
1370
*logofs << "NXTransCreate: Caller process running with pid '"
1371
<< lastProxy << "'.\n" << logofs_flush;
1375
// Set the local proxy mode an parse the
1376
// display NX options.
1381
const char *env = GetOptions(options);
1383
if (ParseEnvironmentOptions(env, 0) < 0)
1385
cerr << "Error" << ": Parsing of NX transport options failed.\n";
1391
// Set the path of the NX directories.
1397
// Open the log files.
1403
// Use the provided descriptor.
1409
*logofs << "NXTransCreate: Called with NX proxy descriptor '"
1410
<< proxyFD << "'.\n" << logofs_flush;
1414
*logofs << "NXTransCreate: Creation of the NX transport completed.\n"
1422
// Tell the proxy to use the descriptor as the internal
1423
// connection to the X client side NX agent. This will
1424
// have the side effect of disabling listening for add-
1425
// itional X client connections.
1428
int NXTransAgent(int fd[2])
1431
// Be sure we have a jump context, in the
1432
// case a subsequent operation will cause
1441
if (setjmp(context) == 1)
1446
if (control == NULL)
1448
cerr << "Error" << ": Can't set the NX agent without a NX transport.\n";
1452
else if (control -> ProxyMode != proxy_client)
1455
*logofs << "NXTransAgent: Invalid mode while setting the NX agent.\n"
1459
cerr << "Error" << ": Invalid mode while setting the NX agent.\n\n";
1473
*logofs << "NXTransAgent: Internal descriptors for agent are FD#"
1474
<< agentFD[0] << " and FD#" << agentFD[1] << ".\n"
1477
*logofs << "NXTransAgent: Disabling listening for further X client "
1478
<< "connections.\n" << logofs_flush;
1482
agent = new Agent(agentFD);
1484
if (agent == NULL || agent -> isValid() != 1)
1487
*logofs << "Loop: PANIC! Error creating the NX memory transport .\n"
1491
cerr << "Error" << ": Error creating the NX memory transport.\n";
1497
*logofs << "NXTransAgent: Enabling memory-to-memory transport.\n"
1504
int NXTransClose(int fd)
1512
* Only handle the proxy connection. The X
1513
* transport will take care of closing its
1514
* end of the socket pair.
1517
if (control != NULL && (agent != NULL &&
1518
(fd == agentFD[0] || fd == NX_FD_ANY)) ||
1519
(fd == proxyFD || fd == NX_FD_ANY))
1524
*logofs << "NXTransClose: Closing down all the X connections.\n"
1528
CleanupConnections();
1534
*logofs << "NXTransClose: The NX transport is not running.\n"
1543
// Close down the transport and free the
1544
// allocated NX resources.
1547
int NXTransDestroy(int fd)
1554
if (control != NULL && (agent != NULL &&
1555
(fd == agentFD[0] || fd == NX_FD_ANY)) ||
1556
(fd == proxyFD || fd == NX_FD_ANY))
1559
// Shut down the X connections and
1560
// wait the cleanup to complete.
1566
*logofs << "NXTransDestroy: Closing down all the X connections.\n"
1570
CleanupConnections();
1574
*logofs << "NXTransDestroy: Waiting for the NX transport to terminate.\n"
1585
*logofs << "NXTransDestroy: The NX transport is not running.\n"
1594
// Assume that the NX transport is valid
1595
// as long as the control class has not
1599
int NXTransRunning(int fd)
1601
return (control != NULL);
1604
int NXTransContinue(struct timeval *selectTs)
1606
if (control != NULL)
1609
// If no timeout is provided use
1615
if (selectTs == NULL)
1617
setTimestamp(newTs, control -> PingTimeout);
1623
// Use empty masks and only get the
1624
// descriptors set by the proxy.
1640
// Run a new loop. If the transport
1641
// is gone avoid sleeping until the
1645
if (NXTransPrepare(&setFDs, &readSet, &writeSet, selectTs) != 0)
1647
NXTransSelect(&resultFDs, &errorFDs, &setFDs, &readSet, &writeSet, selectTs);
1649
NXTransExecute(&resultFDs, &errorFDs, &setFDs, &readSet, &writeSet, selectTs);
1653
return (control != NULL);
1656
int NXTransSignal(int signal, int action)
1663
if (control == NULL)
1668
if (action == NX_SIGNAL_RAISE)
1671
*logofs << "NXTransSignal: Raising signal '" << DumpSignal(signal)
1672
<< "' in the proxy handler.\n" << logofs_flush;
1675
HandleSignal(signal);
1679
else if (signal == NX_SIGNAL_ANY)
1682
*logofs << "NXTransSignal: Setting action of all signals to '"
1683
<< action << "'.\n" << logofs_flush;
1686
for (int i = 0; i < 32; i++)
1688
if (CheckSignal(i) == 1)
1690
NXTransSignal(i, action);
1696
else if (CheckSignal(signal) == 1)
1699
*logofs << "NXTransSignal: Setting action of signal '"
1700
<< DumpSignal(signal) << "' to '" << action
1701
<< "'.\n" << logofs_flush;
1704
if (action == NX_SIGNAL_ENABLE ||
1705
action == NX_SIGNAL_FORWARD)
1707
InstallSignal(signal, action);
1711
else if (action == NX_SIGNAL_DISABLE)
1713
RestoreSignal(signal);
1720
*logofs << "NXTransSignal: WARNING! Unable to perform action '"
1721
<< action << "' on signal '" << DumpSignal(signal)
1722
<< "'.\n" << logofs_flush;
1725
cerr << "Warning" << ": Unable to perform action '" << action
1726
<< "' on signal '" << DumpSignal(signal)
1732
int NXTransCongestion(int fd)
1734
if (control != NULL && proxy != NULL)
1738
int congestion = proxy -> getCongestion(proxyFD);
1740
*logofs << "NXTransCongestion: Returning " << congestion
1741
<< " as current congestion level.\n" << logofs_flush;
1747
return (proxy -> getCongestion(proxyFD));
1753
int NXTransHandler(int fd, int type, void (*handler)(void *parameter,
1754
int reason), void *parameter)
1763
case NX_HANDLER_FLUSH:
1765
flushCallback = handler;
1766
flushParameter = parameter;
1770
case NX_HANDLER_STATISTICS:
1773
// Reporting of statistics by the agent
1774
// still needs to be implemented.
1777
statisticsCallback = handler;
1778
statisticsParameter = parameter;
1785
*logofs << "NXTransHandler: WARNING! Failed to set "
1786
<< "the NX callback for event '" << type << "' to '"
1787
<< (void *) handler << "' and parameter '"
1788
<< parameter << "'.\n" << logofs_flush;
1796
*logofs << "NXTransHandler: Set the NX "
1797
<< "callback for event '" << type << "' to '"
1798
<< (void *) handler << "' and parameter '"
1799
<< parameter << "'.\n" << logofs_flush;
1805
int NXTransRead(int fd, char *data, int size)
1812
if (control != NULL && agent != NULL &&
1816
*logofs << "NXTransRead: Dequeuing " << size << " bytes "
1817
<< "from FD#" << agentFD[0] << ".\n" << logofs_flush;
1820
int result = agent -> dequeueData(data, size);
1824
if (result < 0 && EGET() == EAGAIN)
1826
*logofs << "NXTransRead: WARNING! Dequeuing from FD#"
1827
<< agentFD[0] << " would block.\n" << logofs_flush;
1831
*logofs << "NXTransRead: Dequeued " << result << " bytes "
1832
<< "to FD#" << agentFD[0] << ".\n" << logofs_flush;
1842
*logofs << "NXTransRead: Reading " << size << " bytes "
1843
<< "from FD#" << fd << ".\n" << logofs_flush;
1846
return read(fd, data, size);
1850
int NXTransReadVector(int fd, struct iovec *iovdata, int iovsize)
1857
if (control != NULL && agent != NULL &&
1862
if (control -> ProxyStage >= stage_operational &&
1863
agent -> localReadable() > 0)
1865
*logofs << "NXTransReadVector: WARNING! Agent has data readable.\n"
1876
struct iovec *vector = iovdata;
1877
int count = iovsize;
1884
for (; i < count; i++, vector++)
1886
length = vector -> iov_len;
1887
base = (char *) vector -> iov_base;
1892
*logofs << "NXTransReadVector: Dequeuing " << length
1893
<< " bytes " << "from FD#" << agentFD[0] << ".\n"
1897
result = agent -> dequeueData(base, length);
1901
if (result < 0 && EGET() == EAGAIN)
1903
*logofs << "NXTransReadVector: WARNING! Dequeuing from FD#"
1904
<< agentFD[0] << " would block.\n" << logofs_flush;
1908
*logofs << "NXTransReadVector: Dequeued " << result
1909
<< " bytes " << "from FD#" << agentFD[0] << ".\n"
1915
if (result < 0 && total == 0)
1919
else if (result <= 0)
1937
*logofs << "NXTransReadVector: Reading vector with "
1938
<< iovsize << " elements from FD#" << fd << ".\n"
1942
return readv(fd, iovdata, iovsize);
1946
int NXTransReadable(int fd, int *readable)
1953
if (control == NULL || agent == NULL ||
1958
int result = GetBytesReadable(fd, readable);
1962
*logofs << "NXTransReadable: Error detected on FD#"
1963
<< fd << ".\n" << logofs_flush;
1967
*logofs << "NXTransReadable: Returning " << *readable
1968
<< " bytes as readable from FD#" << fd
1969
<< ".\n" << logofs_flush;
1976
return GetBytesReadable(fd, readable);
1981
int result = agent -> dequeuableData();
1988
// The client might have enqueued data to our side
1989
// and is now checking for the available events. As
1990
// _XEventsQueued() may omit to call _XSelect(), we
1991
// handle here the new data that is coming from the
1992
// proxy to avoid spinning through this function
1996
if (proxy != NULL && proxy -> canRead() == 1)
1998
#if defined(TEST) || defined(INFO)
1999
*logofs << "NXTransReadable: WARNING! Trying to "
2000
<< "read to generate new agent data.\n"
2005
// Set the context as the function
2006
// can cause a cleanup.
2009
if (setjmp(context) == 1)
2014
if (proxy -> handleRead() < 0)
2016
#if defined(TEST) || defined(INFO)
2017
*logofs << "NXTransReadable: Failure reading "
2018
<< "messages from proxy FD#" << proxyFD
2019
<< ".\n" << logofs_flush;
2026
// Call again the routine. By reading
2027
// new control messages from the proxy
2028
// the agent channel may be gone.
2031
return NXTransReadable(fd, readable);
2035
*logofs << "NXTransReadable: Returning " << 0
2036
<< " bytes as readable from FD#" << fd
2037
<< " with result 0.\n" << logofs_flush;
2047
*logofs << "NXTransReadable: Returning " << 0
2048
<< " bytes as readable from FD#" << fd
2049
<< " with result -1.\n" << logofs_flush;
2059
*logofs << "NXTransReadable: Returning " << result
2060
<< " bytes as readable from FD#" << fd
2061
<< " with result 0.\n" << logofs_flush;
2071
int NXTransWrite(int fd, char *data, int size)
2074
// Be sure we have a valid log file.
2082
if (control != NULL && agent != NULL &&
2089
if (proxy -> canRead(agentFD[1]) == 0)
2091
#if defined(DUMP) || defined(TEST)
2092
*logofs << "NXTransWrite: WARNING! Delayed enqueuing to FD#"
2093
<< agentFD[0] << " with proxy unable to read.\n"
2103
// Set the context as the function
2104
// can cause a cleanup.
2107
if (setjmp(context) == 1)
2113
// Don't enqueue the data to the transport
2114
// but let the channel borrow the buffer.
2118
*logofs << "NXTransWrite: Letting the channel borrow "
2119
<< size << " bytes from FD#" << agentFD[0]
2120
<< ".\n" << logofs_flush;
2123
result = proxy -> handleRead(agentFD[1], data, size);
2146
// We don't have a proxy connection, yet.
2147
// Enqueue the data to the agent transport.
2151
*logofs << "NXTransWrite: Enqueuing " << size << " bytes "
2152
<< "to FD#" << agentFD[0] << ".\n" << logofs_flush;
2155
result = agent -> enqueueData(data, size);
2162
if (EGET() == EAGAIN)
2164
*logofs << "NXTransWrite: WARNING! Enqueuing to FD#"
2165
<< agentFD[0] << " would block.\n"
2170
*logofs << "NXTransWrite: WARNING! Error enqueuing to FD#"
2171
<< agentFD[0] << ".\n" << logofs_flush;
2176
*logofs << "NXTransWrite: Enqueued " << result << " bytes "
2177
<< "to FD#" << agentFD[0] << ".\n" << logofs_flush;
2187
*logofs << "NXTransWrite: Writing " << size << " bytes "
2188
<< "to FD#" << fd << ".\n" << logofs_flush;
2191
return write(fd, data, size);
2195
int NXTransWriteVector(int fd, struct iovec *iovdata, int iovsize)
2198
// Be sure we have a valid log file and a
2199
// jump context because we will later call
2200
// functions that can perform a cleanup.
2210
if (control != NULL && agent != NULL &&
2214
// See the comment in NXTransWrite().
2219
if (proxy -> canRead(agentFD[1]) == 0)
2221
#if defined(DUMP) || defined(TEST)
2222
*logofs << "NXTransWriteVector: WARNING! Delayed enqueuing to FD#"
2223
<< agentFD[0] << " with proxy unable to read.\n"
2234
// Set the context as the function
2235
// can cause a cleanup.
2238
if (setjmp(context) == 1)
2247
struct iovec *vector = iovdata;
2248
int count = iovsize;
2255
for (; i < count; i++, vector++)
2257
length = vector -> iov_len;
2258
base = (char *) vector -> iov_base;
2265
// Don't enqueue the data to the transport
2266
// but let the channel borrow the buffer.
2270
*logofs << "NXTransWriteVector: Letting the channel borrow "
2271
<< length << " bytes from FD#" << agentFD[0]
2272
<< ".\n" << logofs_flush;
2275
result = proxy -> handleRead(agentFD[1], base, length);
2298
// We don't have a proxy connection, yet.
2299
// Enqueue the data to the agent transport.
2303
*logofs << "NXTransWriteVector: Enqueuing " << length
2304
<< " bytes " << "to FD#" << agentFD[0] << ".\n"
2308
result = agent -> enqueueData(base, length);
2315
if (EGET() == EAGAIN)
2317
*logofs << "NXTransWriteVector: WARNING! Enqueuing to FD#"
2318
<< agentFD[0] << " would block.\n"
2323
*logofs << "NXTransWriteVector: WARNING! Error enqueuing to FD#"
2324
<< agentFD[0] << ".\n" << logofs_flush;
2329
*logofs << "NXTransWriteVector: Enqueued " << result
2330
<< " bytes " << "to FD#" << agentFD[0] << ".\n"
2336
if (result < 0 && total == 0)
2340
else if (result <= 0)
2358
*logofs << "NXTransWriteVector: Writing vector with "
2359
<< iovsize << " elements to FD#" << fd << ".\n"
2363
return writev(fd, iovdata, iovsize);
2367
int NXTransPolicy(int fd, int type)
2369
if (control != NULL)
2371
if (usePolicy == -1)
2373
#if defined(TEST) || defined(INFO)
2374
*logofs << "NXTransPolicy: Setting flush policy on "
2375
<< "proxy FD#" << proxyFD << " to '"
2376
<< DumpPolicy(type == NX_POLICY_DEFERRED ?
2377
policy_deferred : policy_immediate)
2378
<< "'.\n" << logofs_flush;
2381
control -> FlushPolicy = (type == NX_POLICY_DEFERRED ?
2382
policy_deferred : policy_immediate);
2386
proxy -> handleFlush();
2393
#if defined(TEST) || defined(INFO)
2394
*logofs << "NXTransPolicy: Ignoring the agent "
2395
<< "setting with user policy set to '"
2396
<< DumpPolicy(control -> FlushPolicy)
2397
<< "'.\n" << logofs_flush;
2407
int NXTransFlushable(int fd)
2409
if (proxy == NULL || agent == NULL ||
2413
*logofs << "NXTransFlushable: Returning 0 bytes as "
2414
<< "flushable for unrecognized FD#" << fd
2415
<< ".\n" << logofs_flush;
2422
#if defined(DUMP) || defined(INFO)
2423
*logofs << "NXTransFlushable: Returning " << proxy ->
2424
getFlushable(proxyFD) << " as bytes flushable on "
2425
<< "proxy FD#" << proxyFD << ".\n"
2429
return proxy -> getFlushable(proxyFD);
2433
int NXTransFlush(int fd)
2437
#if defined(TEST) || defined(INFO)
2438
*logofs << "NXTransFlush: Requesting an immediate flush of "
2439
<< "proxy FD#" << proxyFD << ".\n"
2443
return proxy -> handleFlush();
2449
int NXTransChannel(int fd, int channelFd, int type)
2454
// Set the context as the function
2455
// can cause a cleanup.
2458
if (setjmp(context) == 1)
2463
#if defined(TEST) || defined(INFO)
2464
*logofs << "NXTransChannel: Going to create a new channel "
2465
<< "with type '" << type << "' on FD#" << channelFd
2466
<< ".\n" << logofs_flush;
2473
case NX_CHANNEL_X11:
2475
if (useUnixSocket == 1 || useTcpSocket == 1 ||
2476
useAgentSocket == 1 || useAuxSocket == 1)
2478
result = proxy -> handleNewConnection(channel_x11, channelFd);
2483
case NX_CHANNEL_CUPS:
2485
if (useCupsSocket == 1)
2487
result = proxy -> handleNewConnection(channel_cups, channelFd);
2492
case NX_CHANNEL_SMB:
2494
if (useSmbSocket == 1)
2496
result = proxy -> handleNewConnection(channel_smb, channelFd);
2501
case NX_CHANNEL_MEDIA:
2503
if (useMediaSocket == 1)
2505
result = proxy -> handleNewConnection(channel_media, channelFd);
2510
case NX_CHANNEL_HTTP:
2512
if (useHttpSocket == 1)
2514
result = proxy -> handleNewConnection(channel_http, channelFd);
2519
case NX_CHANNEL_FONT:
2521
if (useFontSocket == 1)
2523
result = proxy -> handleNewConnection(channel_font, channelFd);
2528
case NX_CHANNEL_SLAVE:
2530
if (useSlaveSocket == 1)
2532
result = proxy -> handleNewConnection(channel_slave, channelFd);
2540
*logofs << "NXTransChannel: WARNING! Unrecognized channel "
2541
<< "type '" << type << "'.\n" << logofs_flush;
2552
*logofs << "NXTransChannel: WARNING! Could not create the "
2553
<< "new channel with type '" << type << "' on FD#"
2554
<< channelFd << ".\n" << logofs_flush;
2565
const char *NXTransFile(int type)
2571
case NX_FILE_SESSION:
2573
name = sessionFileName;
2577
case NX_FILE_ERRORS:
2579
name = errorsFileName;
2583
case NX_FILE_OPTIONS:
2585
name = optionsFileName;
2591
name = statsFileName;
2597
if (name != NULL && *name != '\0')
2607
static T_timestamp last = getTimestamp();
2609
T_timestamp now = getTimestamp();
2611
long diff = diffTimestamp(last, now);
2618
int NXTransAlert(int code, int local)
2622
#if defined(DUMP) || defined(INFO)
2623
*logofs << "NXTransAlert: Requesting a NX dialog with code "
2624
<< code << " and local " << local << ".\n"
2631
// Set the context as the function
2632
// can cause a cleanup.
2635
if (setjmp(context) == 1)
2640
proxy -> handleAlert(code);
2645
// Show the alert at the next loop.
2648
HandleAlert(code, local);
2653
#if defined(DUMP) || defined(INFO)
2661
*logofs << "NXTransAlert: Can't request an alert without "
2662
<< "a valid NX transport.\n" << logofs_flush;
2670
// Prepare the file sets and the timeout
2671
// for a later execution of the select().
2674
int NXTransPrepare(int *setFDs, fd_set *readSet,
2675
fd_set *writeSet, struct timeval *selectTs)
2683
// Control is NULL if the NX transport was
2684
// reset or was never created. If control
2685
// is valid then prepare to jump back when
2686
// the transport is destroyed.
2689
if (control == NULL || setjmp(context) == 1)
2694
#if defined(TEST) || defined(INFO)
2695
*logofs << "\nNXTransPrepare: Going to prepare the NX transport.\n"
2699
if (control -> ProxyStage < stage_operational)
2701
handleNegotiationInLoop(*setFDs, *readSet, *writeSet, *selectTs);
2705
#if defined(TEST) || defined(INFO)
2707
if (isTimestamp(*selectTs) == 0)
2709
*logofs << "Loop: WARNING! Preparing the select with requested "
2710
<< "timeout of " << selectTs -> tv_sec << " S and "
2711
<< (double) selectTs -> tv_usec / 1000 << " Ms.\n"
2716
*logofs << "Loop: Preparing the select with requested "
2717
<< "timeout of " << selectTs -> tv_sec << " S and "
2718
<< (double) selectTs -> tv_usec / 1000 << " Ms.\n"
2725
// Set descriptors of listening sockets.
2728
handleSetListenersInLoop(*readSet, *setFDs);
2731
// Set descriptors of both proxy and X
2735
handleSetReadInLoop(*readSet, *setFDs, *selectTs);
2738
// Find out which file descriptors have
2742
handleSetWriteInLoop(*writeSet, *setFDs, *selectTs);
2746
// Prepare the masks for handling the memory-
2747
// to-memory transport. This is required even
2748
// during session negotiation.
2753
handleSetAgentInLoop(*setFDs, *readSet, *writeSet, *selectTs);
2757
// Register time spent handling messages.
2760
nowTs = getNewTimestamp();
2762
diffTs = diffTimestamp(startTs, nowTs);
2765
*logofs << "Loop: Mark - 0 - at " << strMsTimestamp()
2766
<< " with " << diffTs << " Ms elapsed.\n"
2771
// TODO: Should add the read time in two
2772
// parts otherwise the limits are checked
2773
// before the counters are updated with
2774
// time spent in the last loop.
2777
if (control -> ProxyStage >= stage_operational)
2779
statistics -> addReadTime(diffTs);
2785
*logofs << "Loop: New timestamp is " << strMsTimestamp(startTs)
2786
<< ".\n" << logofs_flush;
2793
// Let's say that we call select() to find out
2794
// if any of the handled descriptors has data,
2795
// but actually things are a bit more complex
2799
int NXTransSelect(int *resultFDs, int *errorFDs, int *setFDs, fd_set *readSet,
2800
fd_set *writeSet, struct timeval *selectTs)
2804
static T_timestamp lastTs;
2814
// Control is NULL if the NX transport was
2815
// reset or never created. If control is
2816
// valid then prepare for jumping back in
2817
// the case of an error.
2820
if (control == NULL || setjmp(context) == 1)
2822
*resultFDs = select(*setFDs, readSet, writeSet, NULL, selectTs);
2829
#if defined(TEST) || defined(INFO)
2830
*logofs << "\nNXTransSelect: Going to select the NX descriptors.\n"
2834
#if defined(TEST) || defined(INFO)
2836
handleCheckSelectInLoop(*setFDs, *readSet, *writeSet, *selectTs);
2842
diffTs = diffTimestamp(lastTs, getNewTimestamp());
2846
*logofs << "Loop: TIME! Spent " << diffTs
2847
<< " Ms handling messages for proxy FD#"
2848
<< proxyFD << ".\n" << logofs_flush;
2851
lastTs = getNewTimestamp();
2855
#if defined(TEST) || defined(INFO)
2857
if (isTimestamp(*selectTs) == 0)
2859
*logofs << "Loop: WARNING! Executing the select with requested "
2860
<< "timeout of " << selectTs -> tv_sec << " S and "
2861
<< (double) selectTs -> tv_usec / 1000 << " Ms.\n"
2864
else if (proxy != NULL && proxy -> getFlushable(proxyFD) > 0)
2866
*logofs << "Loop: WARNING! Proxy FD#" << proxyFD
2867
<< " has " << proxy -> getFlushable(proxyFD)
2868
<< " bytes to write but timeout is "
2869
<< selectTs -> tv_sec << " S and "
2870
<< selectTs -> tv_usec / 1000 << " Ms.\n"
2877
// Wait for the selected sockets
2883
*resultFDs = select(*setFDs, readSet, writeSet, NULL, selectTs);
2889
diffTs = diffTimestamp(lastTs, getNewTimestamp());
2893
*logofs << "Loop: TIME! Spent " << diffTs
2894
<< " Ms waiting for new data for proxy FD#"
2895
<< proxyFD << ".\n" << logofs_flush;
2898
lastTs = getNewTimestamp();
2903
// Check the result of the select.
2906
#if defined(TEST) || defined(INFO)
2908
handleCheckResultInLoop(*resultFDs, *errorFDs, *setFDs, *readSet, *writeSet, *selectTs, startTs);
2913
// Get time spent in select. The accouting is done
2914
// in milliseconds. This is a real problem on fast
2915
// machines where each loop is unlikely to take
2916
// more than 500 us, so consider that the results
2917
// can be inaccurate.
2920
nowTs = getNewTimestamp();
2922
diffTs = diffTimestamp(startTs, nowTs);
2925
*logofs << "Loop: Out of select after " << diffTs << " Ms "
2926
<< "at " << strMsTimestamp(nowTs) << " with result "
2927
<< *resultFDs << ".\n" << logofs_flush;
2933
*logofs << "Loop: New timestamp is " << strMsTimestamp(startTs)
2934
<< ".\n" << logofs_flush;
2937
if (control -> ProxyStage >= stage_operational)
2939
statistics -> addIdleTime(diffTs);
2945
// Check if the call was interrupted or if any of the
2946
// managed descriptors has become invalid. This can
2947
// happen to the X11 code, before the descriptor is
2948
// removed from the managed set.
2953
if (*errorFDs == EINTR || *errorFDs == EBADF ||
2954
*errorFDs == EINVAL)
2958
if (*errorFDs == EINTR || *errorFDs == EBADF)
2965
if (*errorFDs == EINTR)
2967
*logofs << "Loop: Select failed due to EINTR error.\n"
2972
*logofs << "Loop: WARNING! Call to select failed. Error is "
2973
<< EGET() << " '" << ESTR() << "'.\n"
2982
*logofs << "Loop: PANIC! Call to select failed. Error is "
2983
<< EGET() << " '" << ESTR() << "'.\n"
2987
cerr << "Error" << ": Call to select failed. Error is "
2988
<< EGET() << " '" << ESTR() << "'.\n";
2998
// Perform the required actions on all
2999
// the descriptors having I/O pending.
3002
int NXTransExecute(int *resultFDs, int *errorFDs, int *setFDs, fd_set *readSet,
3003
fd_set *writeSet, struct timeval *selectTs)
3011
// Control is NULL if the NX transport was
3012
// reset or never created. If control is
3013
// valid then prepare for jumping back in
3014
// the case of an error.
3017
if (control == NULL || setjmp(context) == 1)
3022
#if defined(TEST) || defined(INFO)
3023
*logofs << "\nNXTransExecute: Going to execute I/O on the NX descriptors.\n"
3027
if (control -> ProxyStage >= stage_operational)
3030
// Check if I/O is possible on the proxy and
3031
// local agent descriptors.
3036
handleAgentInLoop(*resultFDs, *errorFDs, *setFDs, *readSet, *writeSet, *selectTs);
3040
*logofs << "Loop: Mark - 1 - at " << strMsTimestamp()
3041
<< " with " << diffTimestamp(startTs, getTimestamp())
3042
<< " Ms elapsed.\n" << logofs_flush;
3046
// Rotate the channel that will be handled
3050
handleRotateInLoop();
3053
// Flush any data on newly writable sockets.
3056
handleWritableInLoop(*resultFDs, *writeSet);
3059
*logofs << "Loop: Mark - 2 - at " << strMsTimestamp()
3060
<< " with " << diffTimestamp(startTs, getTimestamp())
3061
<< " Ms elapsed.\n" << logofs_flush;
3065
// Check if any socket has become readable.
3068
handleReadableInLoop(*resultFDs, *readSet);
3071
*logofs << "Loop: Mark - 3 - at " << strMsTimestamp()
3072
<< " with " << diffTimestamp(startTs, getTimestamp())
3073
<< " Ms elapsed.\n" << logofs_flush;
3077
// Handle the scheduled events on channels.
3079
// - Restart, if possible, any client that was
3082
// - Check if there are pointer motion events to
3083
// flush. This applies only to X server side.
3085
// - Check if any channel has exited the conges-
3088
// - Check if there are images that are currently
3092
handleEventsInLoop();
3095
*logofs << "Loop: Mark - 4 - at " << strMsTimestamp()
3096
<< " with " << diffTimestamp(startTs, getTimestamp())
3097
<< " Ms elapsed.\n" << logofs_flush;
3101
// Check if user sent a signal to produce
3105
handleStatisticsInLoop();
3108
// We may have flushed the proxy link or
3109
// handled data coming from the remote.
3110
// Post-process the masks and set the
3111
// selected agent descriptors as ready.
3116
handleAgentLateInLoop(*resultFDs, *errorFDs, *setFDs, *readSet, *writeSet, *selectTs);
3120
*logofs << "Loop: Mark - 5 - at " << strMsTimestamp()
3121
<< " with " << diffTimestamp(startTs, getTimestamp())
3122
<< " Ms elapsed.\n" << logofs_flush;
3126
// Check if there is any data to flush.
3127
// Agents should flush the proxy link
3131
handleFlushInLoop();
3134
*logofs << "Loop: Mark - 6 - at " << strMsTimestamp()
3135
<< " with " << diffTimestamp(startTs, getTimestamp())
3136
<< " Ms elapsed.\n" << logofs_flush;
3141
// Check if we have an alert to show.
3144
handleAlertInLoop();
3146
if (control -> ProxyStage >= stage_operational)
3149
// Check if it's time to give up.
3152
handleCheckSessionInLoop();
3155
// Check if local proxy is consuming
3156
// too many resources.
3159
handleCheckBitrateInLoop();
3162
// Check coherency of internal state.
3165
#if defined(TEST) || defined(INFO)
3167
handleCheckStateInLoop(*setFDs);
3172
*logofs << "Loop: Mark - 7 - at " << strMsTimestamp()
3173
<< " with " << diffTimestamp(startTs, getTimestamp())
3174
<< " Ms elapsed.\n" << logofs_flush;
3179
// Truncate the logs if needed.
3182
handleLogReopenInLoop(logsTs, nowTs);
3188
// Initialize the connection parameters and
3189
// prepare for negotiating the link with the
3193
int InitBeforeNegotiation()
3196
// Disable limits on core dumps.
3202
// Install the signal handlers.
3208
// Track how much time we spent in initialization.
3211
nowTs = getNewTimestamp();
3217
*logofs << "Loop: INIT! Taking mark for initialization at "
3218
<< strMsTimestamp(initTs) << ".\n"
3223
// If not explicitly specified, determine if local
3224
// mode is client or server according to parameters
3228
if (WE_SET_PROXY_MODE == 0)
3230
cerr << "Error" << ": Please specify either the -C or -S option.\n";
3236
// Start a watchdog. If initialization cannot
3237
// be completed before timeout, then clean up
3238
// everything and exit.
3241
if (control -> ProxyMode == proxy_client)
3244
*logofs << "Loop: Starting watchdog process with timeout of "
3245
<< control -> InitTimeout / 1000 << " seconds.\n"
3249
lastWatchdog = NXTransWatchdog(control -> InitTimeout);
3251
if (IsFailed(lastWatchdog))
3254
*logofs << "Loop: PANIC! Can't start the NX watchdog process.\n"
3258
SetNotRunning(lastWatchdog);
3263
*logofs << "Loop: Watchdog started with pid '"
3264
<< lastWatchdog << "'.\n" << logofs_flush;
3270
// Print preliminary info.
3276
// Set cups, multimedia and other
3283
// Increase the number of maximum open
3284
// file descriptors for this process.
3290
// Set local endianess.
3293
unsigned int test = 1;
3295
setHostBigEndian(*((unsigned char *) (&test)) == 0);
3298
*logofs << "Loop: Local host is "
3299
<< (hostBigEndian() ? "big endian" : "little endian")
3300
<< ".\n" << logofs_flush;
3303
if (control -> ProxyMode == proxy_client)
3306
// Listen on sockets that mimic an X display to
3307
// which X clients will be able to connect (e.g.
3308
// unix:8 and/or localhost:8).
3311
if (useTcpSocket == 1)
3316
if (useUnixSocket == 1)
3324
// Don't listen for X connections.
3332
// Get ready to open the local display.
3335
SetupDisplaySocket(xServerAddrFamily, xServerAddr, xServerAddrLength);
3339
// If we are the NX server-side proxy we need to
3340
// complete our initializazion. We will mandate
3341
// our parameters at the time the NX client will
3345
if (control -> ProxyMode == proxy_client)
3353
int SetupProxyConnection()
3357
if (WE_INITIATE_CONNECTION)
3359
if (connectPort < 0)
3361
connectPort = DEFAULT_NX_PROXY_PORT_OFFSET + proxyPort;
3365
*logofs << "Loop: Going to connect to " << connectHost
3366
<< ":" << connectPort << ".\n" << logofs_flush;
3369
proxyFD = ConnectToRemote(connectHost, connectPort);
3372
*logofs << "Loop: Connected to remote proxy on FD#"
3373
<< proxyFD << ".\n" << logofs_flush;
3376
cerr << "Info" << ": Connection to remote proxy '" << connectHost
3377
<< ":" << connectPort << "' established.\n";
3383
listenPort = DEFAULT_NX_PROXY_PORT_OFFSET + proxyPort;
3387
*logofs << "Loop: Going to wait for connection on port "
3388
<< listenPort << ".\n" << logofs_flush;
3391
proxyFD = WaitForRemote(listenPort);
3395
if (WE_LISTEN_FORWARDER)
3397
*logofs << "Loop: Connected to remote forwarder on FD#"
3398
<< proxyFD << ".\n" << logofs_flush;
3402
*logofs << "Loop: Connected to remote proxy on FD#"
3403
<< proxyFD << ".\n" << logofs_flush;
3412
*logofs << "Loop: Using the inherited connection on FD#"
3413
<< proxyFD << ".\n" << logofs_flush;
3418
// Set TCP_NODELAY on proxy descriptor
3419
// to reduce startup time. Option will
3420
// later be disabled if needed.
3423
SetNoDelay(proxyFD, 1);
3426
// We need non-blocking input since the
3427
// negotiation phase.
3430
SetNonBlocking(proxyFD, 1);
3436
// Create the required proxy and channel classes
3437
// and get ready for handling the encoded traffic.
3440
int InitAfterNegotiation()
3443
*logofs << "Loop: Connection with remote proxy completed.\n"
3447
cerr << "Info" << ": Connection with remote proxy completed.\n"
3451
// If we are the server proxy we completed
3452
// our initializazion phase according to
3453
// the values provided by the client side.
3456
if (control -> ProxyMode == proxy_server)
3462
// Set up the listeners for the additional
3466
SetupServiceSockets();
3469
// Create the proxy class and the statistics
3470
// repository and pass all the configuration
3471
// data we negotiated with the remote peer.
3474
SetupProxyInstance();
3477
// We completed both parsing of user's parameters
3478
// and handlshaking with remote proxy. Now print
3479
// a brief summary including the most significant
3483
PrintConnectionInfo();
3486
// Cancel the initialization watchdog.
3489
if (IsRunning(lastWatchdog))
3491
KillProcess(lastWatchdog, "watchdog", SIGTERM, 1);
3493
SetNotRunning(lastWatchdog);
3499
// Start the house-keeper process. It will
3500
// remove the oldest persistent caches, if
3501
// the amount of storage exceed the limits
3508
// Set the log size check timestamp.
3511
nowTs = getNewTimestamp();
3516
// TODO: Due to the way the new NX transport is working,
3517
// the accounting of time spent handling messages must
3518
// be rewritten. In particular, at the moment it only
3519
// shows the time spent encoding and decoding messages
3520
// in the main loop, after executing a select. It doesn't
3521
// take into account the time spent in the NXTrans* calls
3522
// where messages can be encoded and decoded implicitly,
3523
// on demand of the agent. When the agent transport is
3524
// in use, these calls constitute the vast majority of
3525
// the encoding activity. The result is that the number
3526
// of KB encoded per second shown by the proxy statistics
3527
// is actually much lower than the real throughput gene-
3528
// rated by the proxy.
3532
*logofs << "Loop: INIT! Completed initialization at "
3533
<< strMsTimestamp(nowTs) << " with "
3534
<< diffTimestamp(initTs, nowTs) << " Ms "
3535
<< "since the init mark.\n" << logofs_flush;
3538
initTs = getNewTimestamp();
3541
// We can now start handling binary data from
3547
cerr << "Session" << ": Session started at '"
3548
<< strTimestamp() << "'.\n";
3554
int SetMode(int mode)
3557
// Set the local proxy mode.
3560
if (control -> ProxyMode == proxy_undefined)
3562
if (mode == NX_MODE_CLIENT)
3565
*logofs << "Loop: INIT! Initializing with mode "
3566
<< "NX_MODE_CLIENT at " << strMsTimestamp()
3567
<< ".\n" << logofs_flush;
3570
control -> ProxyMode = proxy_client;
3572
else if (mode == NX_MODE_SERVER)
3575
*logofs << "Loop: INIT! Initializing with mode "
3576
<< "NX_MODE_SERVER at " << strMsTimestamp()
3577
<< ".\n" << logofs_flush;
3580
control -> ProxyMode = proxy_server;
3584
cerr << "Error" << ": Please specify either "
3585
<< "the -C or -S option.\n";
3594
int SetupProxyInstance()
3596
if (control -> ProxyMode == proxy_client)
3598
proxy = new ClientProxy(proxyFD);
3602
proxy = new ServerProxy(proxyFD);
3608
*logofs << "Loop: PANIC! Error creating the NX proxy.\n"
3612
cerr << "Error" << ": Error creating the NX proxy.\n";
3618
// Create the statistics repository.
3621
statistics = new Statistics(proxy);
3623
if (statistics == NULL)
3626
*logofs << "Loop: PANIC! Error creating the NX statistics.\n"
3630
cerr << "Error" << ": Error creating the NX statistics.\n";
3636
// If user gave us a proxy cookie than create the
3637
// X11 authorization repository and find the real
3638
// cookie to be used for our X display.
3641
SetupAuthInstance();
3644
// Reset the static members in channels.
3647
proxy -> handleChannelConfiguration();
3650
// Inform the proxies about the ports where they
3651
// will have to forward the network connections.
3654
proxy -> handleDisplayConfiguration(displayHost, xServerAddrFamily,
3655
xServerAddr, xServerAddrLength);
3657
proxy -> handlePortConfiguration(cupsPort, smbPort, mediaPort,
3658
httpPort, fontPort);
3661
// We handed over the sockaddr structure we
3662
// created when we set up the display socket
3669
// Set socket options on proxy link, then propagate link
3670
// configuration to proxy. This includes translating some
3671
// control parameters in 'local' and 'remote'. Finally
3672
// adjust cache parameters according to pack method and
3673
// session type selected by user.
3676
if (proxy -> handleSocketConfiguration() < 0 ||
3677
proxy -> handleLinkConfiguration() < 0 ||
3678
proxy -> handleCacheConfiguration() < 0)
3681
*logofs << "Loop: PANIC! Error configuring the NX transport.\n"
3685
cerr << "Error" << ": Error configuring the NX transport.\n";
3691
// Load the message stores from the persistent
3695
proxy -> handleLoad(load_if_first);
3698
// Inform the proxy that from now on it can
3699
// start handling the encoded data.
3702
proxy -> setOperational();
3705
// Are we going to use an internal IPC connection
3706
// with an agent? In this case create the channel
3707
// by using the socket descriptor provided by the
3708
// caller at the proxy initialization.
3711
SetupAgentInstance();
3714
// Check if we need to verify the existence of
3715
// a matching client cache at shutdown.
3720
control -> PersistentCacheCheckOnShutdown = 1;
3725
// Flush any data produced so far.
3728
proxy -> handleFlush();
3730
#if defined(TEST) || defined(INFO)
3732
if (proxy -> getFlushable(proxyFD) > 0)
3734
*logofs << "Loop: WARNING! Proxy FD#" << proxyFD << " has data "
3735
<< "to flush after setup of the NX transport.\n"
3744
int SetupAuthInstance()
3747
// If user gave us a proxy cookie, then create the
3748
// X11 authorization repository and find the real
3749
// cookie to be used for our X display.
3752
if (control -> ProxyMode == proxy_server)
3754
if (authCookie != NULL && *authCookie != '\0')
3756
if (useLaunchdSocket == 1)
3759
// If we are going to retrieve the X11 autho-
3760
// rization through the launchd service, make
3761
// a connection to its socket to trigger the
3762
// X server starting.
3765
sockaddr_un launchdAddrUnix;
3767
unsigned int launchdAddrLength = sizeof(sockaddr_un);
3769
int launchdAddrFamily = AF_UNIX;
3771
launchdAddrUnix.sun_family = AF_UNIX;
3773
const int launchdAddrNameLength = 108;
3777
strncpy(launchdAddrUnix.sun_path, displayHost, launchdAddrNameLength);
3779
*(launchdAddrUnix.sun_path + launchdAddrNameLength - 1) = '\0';
3782
*logofs << "Loop: Connecting to launchd service "
3783
<< "on Unix port '" << displayHost << "'.\n" << logofs_flush;
3786
int launchdFd = socket(launchdAddrFamily, SOCK_STREAM, PF_UNSPEC);
3791
*logofs << "Loop: PANIC! Call to socket failed. "
3792
<< "Error is " << EGET() << " '" << ESTR()
3793
<< "'.\n" << logofs_flush;
3796
else if ((success = connect(launchdFd, (sockaddr *) &launchdAddrUnix, launchdAddrLength)) < 0)
3799
*logofs << "Loop: WARNING! Connection to launchd service "
3800
<< "on Unix port '" << displayHost << "' failed "
3801
<< "with error " << EGET() << ", '" << ESTR() << "'.\n"
3812
// The real cookie will not be available
3813
// until the X server starts. Query for the
3814
// cookie in a loop, unless the connection
3815
// to the launchd service failed.
3818
int attempts = (success < 0 ? 1 : 10);
3820
for (int i = 0; i < attempts; i++)
3824
auth = new Auth(displayHost, authCookie);
3826
if (auth != NULL && auth -> isFake() == 1)
3838
auth = new Auth(displayHost, authCookie);
3841
if (auth == NULL || auth -> isValid() != 1)
3844
*logofs << "Loop: PANIC! Error creating the X authorization.\n"
3848
cerr << "Error" << ": Error creating the X authorization.\n";
3852
else if (auth -> isFake() == 1)
3855
*logofs << "Loop: WARNING! Could not retrieve the X server "
3856
<< "authentication cookie.\n"
3860
cerr << "Warning" << ": Failed to read data from the X "
3861
<< "auth command.\n";
3863
cerr << "Warning" << ": Generated a fake cookie for X "
3864
<< "authentication.\n";
3870
*logofs << "Loop: No proxy cookie was provided for "
3871
<< "authentication.\n" << logofs_flush;
3874
cerr << "Info" << ": No proxy cookie was provided for "
3875
<< "authentication.\n";
3878
*logofs << "Loop: Forwarding the real X authorization "
3879
<< "cookie.\n" << logofs_flush;
3882
cerr << "Info" << ": Forwarding the real X authorization "
3890
int SetupAgentInstance()
3892
if (control -> ProxyMode == proxy_client &&
3893
useAgentSocket == 1)
3896
// This will temporarily disable signals to safely
3897
// load the cache, then will send a control packet
3898
// to the remote end, telling that cache has to be
3899
// loaded, so it's important that proxy is already
3900
// set in operational state.
3907
result = proxy -> handleNewAgentConnection(agent);
3911
result = proxy -> handleNewConnection(channel_x11, agentFD[1]);
3917
*logofs << "Loop: PANIC! Error creating the NX agent connection.\n"
3921
cerr << "Error" << ": Error creating the NX agent connection.\n";
3930
int SetupTcpSocket()
3933
// Open TCP socket emulating local display.
3936
tcpFD = socket(AF_INET, SOCK_STREAM, PF_UNSPEC);
3941
*logofs << "Loop: PANIC! Call to socket failed for TCP socket"
3942
<< ". Error is " << EGET() << " '" << ESTR() << "'.\n"
3946
cerr << "Error" << ": Call to socket failed for TCP socket"
3947
<< ". Error is " << EGET() << " '" << ESTR() << "'.\n";
3951
else if (SetReuseAddress(tcpFD) < 0)
3956
unsigned int proxyPortTCP = X_TCP_PORT + proxyPort;
3958
sockaddr_in tcpAddr;
3960
tcpAddr.sin_family = AF_INET;
3961
tcpAddr.sin_port = htons(proxyPortTCP);
3962
tcpAddr.sin_addr.s_addr = htonl(INADDR_ANY);
3964
if (bind(tcpFD, (sockaddr *) &tcpAddr, sizeof(tcpAddr)) == -1)
3967
*logofs << "Loop: PANIC! Call to bind failed for TCP port "
3968
<< proxyPortTCP << ". Error is " << EGET() << " '" << ESTR()
3969
<< "'.\n" << logofs_flush;
3972
cerr << "Error" << ": Call to bind failed for TCP port "
3973
<< proxyPortTCP << ". Error is " << EGET() << " '" << ESTR()
3979
if (listen(tcpFD, 8) == -1)
3982
*logofs << "Loop: PANIC! Call to listen failed for TCP port "
3983
<< proxyPortTCP << ". Error is " << EGET() << " '" << ESTR()
3984
<< "'.\n" << logofs_flush;
3987
cerr << "Error" << ": Call to listen failed for TCP port "
3988
<< proxyPortTCP << ". Error is " << EGET() << " '" << ESTR()
3997
int SetupUnixSocket()
4000
// Open UNIX domain socket for display.
4003
unixFD = socket(AF_UNIX, SOCK_STREAM, PF_UNSPEC);
4008
*logofs << "Loop: PANIC! Call to socket failed for UNIX domain"
4009
<< ". Error is " << EGET() << " '" << ESTR() << "'.\n"
4013
cerr << "Error" << ": Call to socket failed for UNIX domain"
4014
<< ". Error is " << EGET() << " '" << ESTR() << "'.\n";
4019
sockaddr_un unixAddr;
4020
unixAddr.sun_family = AF_UNIX;
4022
char dirName[DEFAULT_STRING_LENGTH];
4024
snprintf(dirName, DEFAULT_STRING_LENGTH - 1, "%s/.X11-unix",
4025
control -> TempPath);
4027
*(dirName + DEFAULT_STRING_LENGTH - 1) = '\0';
4029
struct stat dirStat;
4031
if ((stat(dirName, &dirStat) == -1) && (EGET() == ENOENT))
4033
mkdir(dirName, (0777 | S_ISVTX));
4034
chmod(dirName, (0777 | S_ISVTX));
4037
snprintf(unixSocketName, DEFAULT_STRING_LENGTH - 1, "%s/X%d",
4038
dirName, proxyPort);
4040
strncpy(unixAddr.sun_path, unixSocketName, 108);
4043
*logofs << "Loop: Assuming Unix socket with name '"
4044
<< unixAddr.sun_path << "'.\n"
4048
*(unixAddr.sun_path + 107) = '\0';
4050
if (bind(unixFD, (sockaddr *) &unixAddr, sizeof(unixAddr)) == -1)
4053
*logofs << "Loop: PANIC! Call to bind failed for UNIX domain socket "
4054
<< unixSocketName << ". Error is " << EGET() << " '" << ESTR()
4055
<< "'.\n" << logofs_flush;
4058
cerr << "Error" << ": Call to bind failed for UNIX domain socket "
4059
<< unixSocketName << ". Error is " << EGET() << " '" << ESTR()
4065
if (listen(unixFD, 8) == -1)
4068
*logofs << "Loop: PANIC! Call to listen failed for UNIX domain socket "
4069
<< unixSocketName << ". Error is " << EGET() << " '" << ESTR()
4070
<< "'.\n" << logofs_flush;
4073
cerr << "Error" << ": Call to listen failed for UNIX domain socket "
4074
<< unixSocketName << ". Error is " << EGET() << " '" << ESTR()
4081
// Let any local user to gain access to socket.
4084
chmod(unixSocketName, 0777);
4090
// The following is a dumb copy-paste. The
4091
// nxcompsh library should offer a better
4095
int SetupDisplaySocket(int &xServerAddrFamily, sockaddr *&xServerAddr,
4096
unsigned int &xServerAddrLength)
4098
xServerAddrFamily = AF_INET;
4100
xServerAddrLength = 0;
4104
if (*displayHost == '\0')
4107
// Assume DISPLAY as the X server to which
4108
// we will forward the proxied connections.
4109
// This means that NX parameters have been
4110
// passed through other means.
4113
display = getenv("DISPLAY");
4115
if (display == NULL || *display == '\0')
4118
*logofs << "Loop: PANIC! Host X server DISPLAY is not set.\n"
4122
cerr << "Error" << ": Host X server DISPLAY is not set.\n";
4126
else if (strncasecmp(display, "nx/nx,", 6) == 0 ||
4127
strncasecmp(display, "nx,", 3) == 0 ||
4128
strncasecmp(display, "nx:", 3) == 0)
4131
*logofs << "Loop: PANIC! NX transport on host X server '"
4132
<< display << "' not supported.\n" << logofs_flush;
4135
cerr << "Error" << ": NX transport on host X server '"
4136
<< display << "' not supported.\n";
4138
cerr << "Error" << ": Please run the local proxy specifying "
4139
<< "the host X server to connect to.\n";
4143
else if (strlen(display) >= DEFAULT_STRING_LENGTH)
4146
*logofs << "Loop: PANIC! Host X server DISPLAY cannot exceed "
4147
<< DEFAULT_STRING_LENGTH << " characters.\n"
4151
cerr << "Error" << ": Host X server DISPLAY cannot exceed "
4152
<< DEFAULT_STRING_LENGTH << " characters.\n";
4157
strcpy(displayHost, display);
4160
display = new char[strlen(displayHost) + 1];
4162
if (display == NULL)
4165
*logofs << "Loop: PANIC! Out of memory handling DISPLAY variable.\n"
4169
cerr << "Error" << ": Out of memory handling DISPLAY variable.\n";
4174
strcpy(display, displayHost);
4178
if (strncasecmp(display, "/tmp/launch", 11) == 0)
4181
*logofs << "Loop: Using launchd service on socket '"
4182
<< display << "'.\n" << logofs_flush;
4185
useLaunchdSocket = 1;
4190
char *separator = rindex(display, ':');
4192
if ((separator == NULL) || !isdigit(*(separator + 1)))
4195
*logofs << "Loop: PANIC! Invalid display '" << display << "'.\n"
4199
cerr << "Error" << ": Invalid display '" << display << "'.\n";
4206
xPort = atoi(separator + 1);
4209
*logofs << "Loop: Using local X display '" << displayHost
4210
<< "' with host '" << display << "' and port '"
4211
<< xPort << "'.\n" << logofs_flush;
4216
if (separator == display || strcmp(display, "unix") == 0 ||
4217
useLaunchdSocket == 1)
4221
if (separator == display || strcmp(display, "unix") == 0)
4226
// UNIX domain port.
4230
*logofs << "Loop: Using real X server on UNIX domain socket.\n"
4234
sockaddr_un *xServerAddrUNIX = new sockaddr_un;
4236
xServerAddrFamily = AF_UNIX;
4237
xServerAddrUNIX -> sun_family = AF_UNIX;
4240
// The scope of this function is to fill either the sockaddr_un
4241
// (when the display is set to the Unix Domain socket) or the
4242
// sockaddr_in structure (when connecting by TCP) only once, so
4243
// that the structure will be later used at the time the server
4244
// proxy will try to forward the connection to the X server. We
4245
// don't need to verify that the socket does exist at the pre-
4246
// sent moment. The method that forwards the connection will
4247
// perform the required checks and will retry, if needed. Anyway
4248
// we need to select the name of the socket, so we check if the
4249
// well-known directory exists and take that as an indication of
4250
// where the socket will be created.
4253
struct stat statInfo;
4255
char unixSocketDir[DEFAULT_STRING_LENGTH];
4257
snprintf(unixSocketDir, DEFAULT_STRING_LENGTH - 1, "%s/.X11-unix",
4258
control -> TempPath);
4262
if (useLaunchdSocket == 1)
4264
snprintf(unixSocketDir, DEFAULT_STRING_LENGTH - 1, "%s", display);
4269
*(unixSocketDir + DEFAULT_STRING_LENGTH - 1) = '\0';
4272
*logofs << "Loop: Assuming X socket in directory '"
4273
<< unixSocketDir << "'.\n" << logofs_flush;
4276
if (stat(unixSocketDir, &statInfo) < 0)
4279
*logofs << "Loop: PANIC! Can't determine the location of "
4280
<< "the X display socket.\n" << logofs_flush;
4283
cerr << "Error" << ": Can't determine the location of "
4284
<< "the X display socket.\n";
4287
*logofs << "Loop: PANIC! Error " << EGET() << " '" << ESTR()
4288
<< "' checking '" << unixSocketDir << "'.\n"
4292
cerr << "Error" << ": Error " << EGET() << " '" << ESTR()
4293
<< "' checking '" << unixSocketDir << "'.\n";
4298
sprintf(unixSocketName, "%s/X%d", unixSocketDir, xPort);
4302
if (useLaunchdSocket == 1)
4304
sprintf(unixSocketName, "%s:%d", unixSocketDir, xPort);
4310
*logofs << "Loop: Assuming X socket name '" << unixSocketName
4311
<< "'.\n" << logofs_flush;
4314
strcpy(xServerAddrUNIX -> sun_path, unixSocketName);
4316
xServerAddr = (sockaddr *) xServerAddrUNIX;
4317
xServerAddrLength = sizeof(sockaddr_un);
4326
*logofs << "Loop: Using real X server on TCP port.\n"
4330
xServerAddrFamily = AF_INET;
4332
int xServerIPAddr = GetHostAddress(display);
4334
if (xServerIPAddr == 0)
4337
*logofs << "Loop: PANIC! Unknown display host '" << display
4338
<< "'.\n" << logofs_flush;
4341
cerr << "Error" << ": Unknown display host '" << display
4347
sockaddr_in *xServerAddrTCP = new sockaddr_in;
4349
xServerAddrTCP -> sin_family = AF_INET;
4350
xServerAddrTCP -> sin_port = htons(X_TCP_PORT + xPort);
4351
xServerAddrTCP -> sin_addr.s_addr = xServerIPAddr;
4353
xServerAddr = (sockaddr *) xServerAddrTCP;
4354
xServerAddrLength = sizeof(sockaddr_in);
4362
int SetupServiceSockets()
4364
if (control -> ProxyMode == proxy_client)
4368
if ((cupsFD = ListenConnection(cupsPort, "CUPS")) < 0)
4376
if ((auxFD = ListenConnection(auxPort, "auxiliary X11")) < 0)
4384
if ((smbFD = ListenConnection(smbPort, "SMB")) < 0)
4392
if ((mediaFD = ListenConnection(mediaPort, "media")) < 0)
4400
if ((httpFD = ListenConnection(httpPort, "http")) < 0)
4411
// Disable the font server connections if
4412
// they are not supported by the remote
4418
if (control -> isProtoStep7() == 1)
4420
int port = atoi(fontPort);
4422
if ((fontFD = ListenConnection(port, "font")) < 0)
4430
*logofs << "Loop: WARNING! Font server connections not supported "
4431
<< "by the remote proxy.\n" << logofs_flush;
4434
cerr << "Warning" << ": Font server connections not supported "
4435
<< "by the remote proxy.\n";
4449
// Slave channels can be originated
4455
if (control -> isProtoStep7() == 1)
4457
if ((slaveFD = ListenConnection(slavePort, "slave")) < 0)
4465
*logofs << "Loop: WARNING! Slave connections not supported "
4466
<< "by the remote proxy.\n" << logofs_flush;
4469
cerr << "Warning" << ": Slave connections not supported "
4470
<< "by the remote proxy.\n";
4479
int ListenConnection(int port, const char *label)
4481
sockaddr_in tcpAddr;
4483
unsigned int portTCP = port;
4485
int newFD = socket(AF_INET, SOCK_STREAM, PF_UNSPEC);
4490
*logofs << "Loop: PANIC! Call to socket failed for " << label
4491
<< " TCP socket. Error is " << EGET() << " '"
4492
<< ESTR() << "'.\n" << logofs_flush;
4495
cerr << "Error" << ": Call to socket failed for " << label
4496
<< " TCP socket. Error is " << EGET() << " '"
4497
<< ESTR() << "'.\n";
4499
goto SetupSocketError;
4501
else if (SetReuseAddress(newFD) < 0)
4503
goto SetupSocketError;
4506
tcpAddr.sin_family = AF_INET;
4507
tcpAddr.sin_port = htons(portTCP);
4508
tcpAddr.sin_addr.s_addr = htonl(INADDR_ANY);
4510
if (bind(newFD, (sockaddr *) &tcpAddr, sizeof(tcpAddr)) == -1)
4513
*logofs << "Loop: PANIC! Call to bind failed for " << label
4514
<< " TCP port " << port << ". Error is " << EGET()
4515
<< " '" << ESTR() << "'.\n" << logofs_flush;
4518
cerr << "Error" << ": Call to bind failed for " << label
4519
<< " TCP port " << port << ". Error is " << EGET()
4520
<< " '" << ESTR() << "'.\n";
4522
goto SetupSocketError;
4525
if (listen(newFD, 8) == -1)
4528
*logofs << "Loop: PANIC! Call to bind failed for " << label
4529
<< " TCP port " << port << ". Error is " << EGET()
4530
<< " '" << ESTR() << "'.\n" << logofs_flush;
4533
cerr << "Error" << ": Call to bind failed for " << label
4534
<< " TCP port " << port << ". Error is " << EGET()
4535
<< " '" << ESTR() << "'.\n";
4537
goto SetupSocketError;
4550
// May optionally return. The session would
4551
// continue without the service. The problem
4552
// with this approach is that it would make
4553
// harder to track problems with allocation
4554
// of ports in clients and server.
4560
static int AcceptConnection(int fd, int domain, const char *label)
4562
struct sockaddr newAddr;
4564
socklen_t addrLen = sizeof(newAddr);
4568
if (domain == AF_UNIX)
4570
*logofs << "Loop: Going to accept new Unix " << label
4571
<< " connection on FD#" << fd << ".\n"
4576
*logofs << "Loop: Going to accept new TCP " << label
4577
<< " connection on FD#" << fd << ".\n"
4583
int newFD = accept(fd, &newAddr, &addrLen);
4588
*logofs << "Loop: PANIC! Call to accept failed for "
4589
<< label << " connection. Error is " << EGET()
4590
<< " '" << ESTR() << "'.\n" << logofs_flush;
4593
cerr << "Error" << ": Call to accept failed for "
4594
<< label << " connection. Error is " << EGET()
4595
<< " '" << ESTR() << "'.\n";
4601
void HandleShutdown()
4603
if (proxy -> getShutdown() == 0)
4606
*logofs << "Loop: PANIC! No shutdown of proxy link "
4607
<< "performed by remote proxy.\n"
4612
// Close the socket before showing the alert.
4613
// It seems that the closure of the socket can
4614
// sometimes take several seconds, even after
4615
// the connection is broken. The result is that
4616
// the dialog can be shown long after the user
4617
// has gone after the failed session. Note that
4618
// disabling the linger timeout does not seem
4619
// to make any difference.
4624
cerr << "Error" << ": Connection with remote peer broken.\n";
4627
*logofs << "Loop: Bytes received so far are "
4628
<< (unsigned long long) statistics -> getBytesIn()
4629
<< ".\n" << logofs_flush;
4632
cerr << "Error" << ": Please check the state of your "
4633
<< "network and retry.\n";
4635
handleTerminatingInLoop();
4637
if (control -> ProxyMode == proxy_server)
4640
*logofs << "Loop: Showing the proxy abort dialog.\n"
4644
HandleAlert(ABORT_PROXY_CONNECTION_ALERT, 1);
4646
handleAlertInLoop();
4652
*logofs << "Loop: Finalized the remote proxy shutdown.\n"
4663
T_timestamp selectTs;
4665
while (NXTransRunning(NX_FD_ANY))
4667
setTimestamp(selectTs, control -> PingTimeout);
4669
NXTransContinue(&selectTs);
4673
int KillProcess(int pid, const char *label, int signal, int wait)
4677
#if defined(TEST) || defined(INFO)
4678
*logofs << "Loop: Killing the " << label << " process '"
4679
<< pid << "' from process with pid '" << getpid()
4680
<< "' with signal '" << DumpSignal(signal)
4681
<< "'.\n" << logofs_flush;
4684
signal = (signal == 0 ? SIGTERM : signal);
4686
if (kill(pid, signal) < 0 && EGET() != ESRCH)
4689
*logofs << "Loop: PANIC! Couldn't kill the " << label
4690
<< " process with pid '" << pid << "'.\n"
4694
cerr << "Error" << ": Couldn't kill the " << label
4695
<< " process with pid '" << pid << "'.\n";
4700
WaitChild(pid, label, 1);
4708
*logofs << "Loop: No " << label << " process "
4709
<< "to kill with pid '" << pid
4710
<< "'.\n" << logofs_flush;
4717
int CheckProcess(int pid, const char *label)
4719
#if defined(TEST) || defined(INFO)
4720
*logofs << "Loop: Checking the " << label << " process '"
4721
<< pid << "' from process with pid '" << getpid()
4722
<< "'.\n" << logofs_flush;
4725
if (kill(pid, SIGCONT) < 0 && EGET() == ESRCH)
4728
*logofs << "Loop: WARNING! The " << label << " process "
4729
<< "with pid '" << pid << "' has exited.\n"
4733
cerr << "Warning" << ": The " << label << " process "
4734
<< "with pid '" << pid << "' has exited.\n";
4744
#if defined(TEST) || defined(INFO)
4746
if (IsRunning(lastKeeper) == 1 ||
4747
IsRestarting(lastKeeper) == 1)
4750
*logofs << "Loop: PANIC! The house-keeping process is "
4751
<< "alreay running with pid '" << lastKeeper
4752
<< "'.\n" << logofs_flush;
4761
// Don't care harvesting the persistent caches if
4762
// the memory cache is not enabled. If the memory
4763
// cache is not enabled neither we produced per-
4764
// sistent caches. The user can still delete any
4765
// persistent cache produced by the previous runs
4766
// by using the client GUI.
4768
// TODO: At the moment the user doesn't have a way
4769
// to specify the amount of disk space to use for
4770
// the persistent caches, but only the amount of
4771
// space to use for images.
4774
if (control -> LocalTotalStorageSize > 0)
4777
*logofs << "Loop: Starting the house-keeping process with "
4778
<< "storage size " << control -> PersistentCacheDiskLimit
4779
<< ".\n" << logofs_flush;
4782
lastKeeper = NXTransKeeper(control -> PersistentCacheDiskLimit,
4783
0, control -> RootPath);
4785
if (IsFailed(lastKeeper))
4788
*logofs << "Loop: WARNING! Failed to start the NX keeper process.\n"
4792
cerr << "Warning" << ": Failed to start the NX keeper process.\n";
4794
SetNotRunning(lastKeeper);
4799
*logofs << "Loop: Keeper started with pid '"
4800
<< lastKeeper << "'.\n" << logofs_flush;
4807
*logofs << "Loop: Nothing to do for the keeper process "
4808
<< "with persistent cache not enabled.\n"
4816
void HandleCleanup(int code)
4819
*logofs << "Loop: Going to clean up system resources "
4820
<< "in process '" << getpid() << "'.\n"
4824
handleTerminatedInLoop();
4827
// Suspend any signal while cleaning up.
4832
if (getpid() == lastProxy)
4835
// Terminate all the children.
4841
// Close all listeners.
4847
// Close all sockets.
4853
// Release the global objects.
4859
// Restore the original signal handlers.
4866
// This is our last chance to print a message. If this
4867
// is the process which created the transport we will
4868
// jump back into the loop, letting the caller find out
4869
// that the connection is broken, otherwise we assume
4870
// that this is a child of the proxy and so we will
4876
if (getpid() == lastProxy)
4878
*logofs << "Loop: Reverting to loop context in process with "
4879
<< "pid '" << getpid() << "' at " << strMsTimestamp()
4880
<< ".\n" << logofs_flush;
4884
*logofs << "Loop: Exiting from child process with pid '"
4885
<< getpid() << "' at " << strMsTimestamp()
4886
<< ".\n" << logofs_flush;
4891
if (getpid() == lastProxy)
4894
// Reset all values to their default.
4901
longjmp(context, 1);
4906
// Give a last chance to the process
4907
// to cleanup the ancillary classes.
4918
void CleanupKeeper()
4923
*logofs << "Loop: Freeing up keeper in process "
4924
<< "with pid '" << getpid() << "'.\n"
4934
void CleanupStreams()
4937
// TODO: The cleanup procedure skips deletion of
4938
// the I/O streams under Windows. This is intended
4939
// to avoid a strange segfault occurring randomly,
4940
// at the time the proxy is being shut down.
4943
#ifndef __CYGWIN32__
4946
*logofs << "Loop: Freeing up streams in process "
4947
<< "with pid '" << getpid() << "'.\n"
4951
if (logofs != NULL && logofs != &cerr &&
4952
*errorsFileName != '\0')
4959
// Let the log go again to the standard
4966
if (statofs != NULL && statofs != &cerr &&
4967
*statsFileName != '\0')
4980
if (errofs == &cerr)
4984
else if (errsbuf != NULL)
4986
cerr.rdbuf(errsbuf);
4996
#endif /* #ifndef __CYGWIN32__ */
4999
// Reset these as they can't be reset
5000
// in CleanupLocal().
5003
*sessionFileName = '\0';
5004
*errorsFileName = '\0';
5005
*optionsFileName = '\0';
5006
*statsFileName = '\0';
5009
void CleanupChildren()
5012
// Remove any watchdog.
5015
if (IsRunning(lastWatchdog))
5017
KillProcess(lastWatchdog, "watchdog", SIGTERM, 1);
5019
SetNotRunning(lastWatchdog);
5025
// Kill the cache house-keeping process.
5028
if (IsRunning(lastKeeper))
5030
KillProcess(lastKeeper, "house-keeping", SIGTERM, 1);
5032
SetNotRunning(lastKeeper);
5036
// Let any running dialog to continue until it is
5037
// closed by the user. In general this is the exp-
5038
// ected behaviour, as for example when we are
5039
// exiting because the link was abrouptedly shut
5043
if (IsRunning(lastDialog))
5045
#if defined(TEST) || defined(INFO)
5046
*logofs << "Loop: WARNING! Leaving the dialog process '"
5047
<< lastDialog << "' running in process "
5048
<< "with pid '" << getpid() << "'.\n"
5052
SetNotRunning(lastDialog);
5056
// Give user a chance to start a new session.
5059
if (control -> EnableRestartOnShutdown == 1)
5062
*logofs << "Loop: WARNING! Respawning the NX client "
5063
<< "on display '" << displayHost << "'.\n"
5067
NXTransClient(displayHost);
5070
for (int i = 0; i < control -> KillDaemonOnShutdownNumber; i++)
5073
*logofs << "Loop: WARNING! Killing the NX daemon with "
5074
<< "pid '" << control -> KillDaemonOnShutdown[i]
5075
<< "'.\n" << logofs_flush;
5078
KillProcess(control -> KillDaemonOnShutdown[i], "daemon", SIGTERM, 0);
5082
void CleanupGlobal()
5087
*logofs << "Loop: Freeing up proxy in process "
5088
<< "with pid '" << getpid() << "'.\n"
5100
*logofs << "Loop: Freeing up agent in process "
5101
<< "with pid '" << getpid() << "'.\n"
5113
*logofs << "Loop: Freeing up auth data in process "
5114
<< "with pid '" << getpid() << "'.\n"
5123
if (statistics != NULL)
5126
*logofs << "Loop: Freeing up statistics in process "
5127
<< "with pid '" << getpid() << "'.\n"
5136
if (control != NULL)
5139
*logofs << "Loop: Freeing up control in process "
5140
<< "with pid '" << getpid() << "'.\n"
5150
void CleanupConnections()
5152
if (proxy -> getChannels(channel_x11) != 0)
5155
*logofs << "Loop: Closing any remaining X connections.\n"
5159
proxy -> handleCloseAllXConnections();
5162
*logofs << "Loop: Closing any remaining listener.\n"
5166
proxy -> handleCloseAllListeners();
5169
proxy -> handleFinish();
5172
void CleanupListeners()
5174
if (useTcpSocket == 1)
5179
*logofs << "Loop: Closing TCP listener in process "
5180
<< "with pid '" << getpid() << "'.\n"
5192
if (useUnixSocket == 1)
5197
*logofs << "Loop: Closing UNIX listener in process "
5198
<< "with pid '" << getpid() << "'.\n"
5207
if (*unixSocketName != '\0')
5210
*logofs << "Loop: Going to remove the Unix domain socket '"
5211
<< unixSocketName << "' in process " << "with pid '"
5212
<< getpid() << "'.\n" << logofs_flush;
5215
unlink(unixSocketName);
5221
if (useAgentSocket == 1)
5224
// There is no listener for the
5225
// agent descriptor.
5231
if (useCupsSocket == 1)
5236
*logofs << "Loop: Closing CUPS listener in process "
5237
<< "with pid '" << getpid() << "'.\n"
5249
if (useAuxSocket == 1)
5254
*logofs << "Loop: Closing auxiliary X11 listener "
5255
<< "in process " << "with pid '" << getpid()
5256
<< "'.\n" << logofs_flush;
5267
if (useSmbSocket == 1)
5272
*logofs << "Loop: Closing SMB listener in process "
5273
<< "with pid '" << getpid() << "'.\n"
5285
if (useMediaSocket == 1)
5290
*logofs << "Loop: Closing multimedia listener in process "
5291
<< "with pid '" << getpid() << "'.\n"
5303
if (useHttpSocket == 1)
5308
*logofs << "Loop: Closing http listener in process "
5309
<< "with pid '" << getpid() << "'.\n"
5321
if (useFontSocket == 1)
5326
*logofs << "Loop: Closing font server listener in process "
5327
<< "with pid '" << getpid() << "'.\n"
5339
if (useSlaveSocket == 1)
5344
*logofs << "Loop: Closing slave listener in process "
5345
<< "with pid '" << getpid() << "'.\n"
5358
void CleanupSockets()
5363
*logofs << "Loop: Closing proxy FD in process "
5364
<< "with pid '" << getpid() << "'.\n"
5373
if (agentFD[1] != -1)
5376
*logofs << "Loop: Closing agent FD in process "
5377
<< "with pid '" << getpid() << "'.\n"
5396
*linkSpeedName = '\0';
5397
*cacheSizeName = '\0';
5398
*shsegSizeName = '\0';
5399
*imagesSizeName = '\0';
5400
*bitrateLimitName = '\0';
5401
*packMethodName = '\0';
5402
*productName = '\0';
5407
*sessionType = '\0';
5446
*unixSocketName = '\0';
5448
*connectHost = '\0';
5451
*displayHost = '\0';
5454
proxyPort = DEFAULT_NX_PROXY_PORT;
5455
xPort = DEFAULT_NX_X_PORT;
5457
xServerAddrFamily = -1;
5458
xServerAddrLength = 0;
5479
initTs = nullTimestamp();
5480
startTs = nullTimestamp();
5481
logsTs = nullTimestamp();
5482
nowTs = nullTimestamp();
5494
lastReadableTs = nullTimestamp();
5497
lastAlert.local = 0;
5499
lastMasks.blocked = 0;
5500
lastMasks.installed = 0;
5502
memset(&lastMasks.saved, 0, sizeof(sigset_t));
5504
for (int i = 0; i < 32; i++)
5506
lastMasks.enabled[i] = 0;
5507
lastMasks.forward[i] = 0;
5509
memset(&lastMasks.action[i], 0, sizeof(struct sigaction));
5514
memset(&lastTimer.action, 0, sizeof(struct sigaction));
5515
memset(&lastTimer.value, 0, sizeof(struct itimerval));
5517
lastTimer.start = nullTimestamp();
5518
lastTimer.next = nullTimestamp();
5523
if (lastSignal != 0)
5526
*logofs << "Loop: Aborting the procedure due to signal '"
5527
<< lastSignal << "', '" << DumpSignal(lastSignal)
5528
<< "'.\n" << logofs_flush;
5531
cerr << "Info" << ": Aborting the procedure due to signal '"
5532
<< lastSignal << "'.\n";
5551
handleTerminatingInLoop();
5553
if (lastSignal == SIGHUP)
5559
// The current default is to just quit the program.
5560
// Code has not been updated to deal with the new
5561
// NX transport loop.
5564
if (control -> EnableCoreDumpOnAbort == 1)
5568
cerr << "Session" << ": Terminating session at '"
5569
<< strTimestamp() << "'.\n";
5572
cerr << "Error" << ": Generating a core file to help "
5573
<< "the investigations.\n";
5575
cerr << "Session" << ": Session terminated at '"
5576
<< strTimestamp() << "'.\n";
5580
signal(SIGABRT, SIG_DFL);
5586
*logofs << "Loop: Showing the proxy abort dialog.\n"
5590
if (control -> ProxyMode == proxy_server)
5593
// Close the socket before showing the alert.
5594
// It seems that the closure of the socket can
5595
// sometimes take several seconds, even after
5596
// the connection is broken.
5603
HandleAlert(ABORT_PROXY_CONNECTION_ALERT, 1);
5607
HandleAlert(ABORT_PROXY_SHUTDOWN_ALERT, 1);
5610
handleAlertInLoop();
5616
void HandleAlert(int code, int local)
5618
if (lastAlert.code == 0)
5620
#if defined(TEST) || defined(INFO)
5621
*logofs << "Loop: Requesting an alert dialog with code "
5622
<< code << " and local " << local << ".\n"
5626
lastAlert.code = code;
5627
lastAlert.local = local;
5629
#if defined(TEST) || defined(INFO)
5632
*logofs << "Loop: WARNING! Alert dialog already requested "
5633
<< "with code " << lastAlert.code << ".\n"
5641
void FlushCallback(int length)
5643
if (flushCallback != NULL)
5645
#if defined(TEST) || defined(INFO)
5646
*logofs << "Loop: Reporting a flush request at "
5647
<< strMsTimestamp() << " with " << length
5648
<< " bytes written.\n" << logofs_flush;
5651
(*flushCallback)(flushParameter, length);
5653
#if defined(TEST) || defined(INFO)
5654
else if (control -> ProxyMode == proxy_client)
5656
*logofs << "Loop: WARNING! Can't find a flush "
5657
<< "callback in process with pid '" << getpid()
5658
<< "'.\n" << logofs_flush;
5663
void KeeperCallback()
5665
if (IsRunning(lastKeeper) == 0)
5668
// Let the house-keeping process take care
5669
// of the persistent image cache.
5672
if (control -> ImageCacheEnableLoad == 1 ||
5673
control -> ImageCacheEnableSave == 1)
5676
*logofs << "Loop: Starting the house-keeping process with "
5677
<< "image storage size " << control -> ImageCacheDiskLimit
5678
<< ".\n" << logofs_flush;
5681
lastKeeper = NXTransKeeper(0, control -> ImageCacheDiskLimit,
5682
control -> RootPath);
5684
if (IsFailed(lastKeeper))
5687
*logofs << "Loop: WARNING! Can't start the NX keeper process.\n"
5691
SetNotRunning(lastKeeper);
5696
*logofs << "Loop: Keeper started with pid '"
5697
<< lastKeeper << "'.\n" << logofs_flush;
5704
*logofs << "Loop: Nothing to do for the keeper process "
5705
<< "with image cache not enabled.\n"
5713
*logofs << "Loop: Nothing to do with the keeper process "
5714
<< "already running.\n" << logofs_flush;
5719
void InstallSignals()
5722
*logofs << "Loop: Installing signals in process with pid '"
5723
<< getpid() << "'.\n" << logofs_flush;
5726
for (int i = 0; i < 32; i++)
5728
if (CheckSignal(i) == 1 &&
5729
lastMasks.enabled[i] == 0)
5731
InstallSignal(i, NX_SIGNAL_ENABLE);
5735
lastMasks.installed = 1;
5738
void RestoreSignals()
5741
*logofs << "Loop: Restoring signals in process with pid '"
5742
<< getpid() << "'.\n" << logofs_flush;
5745
if (lastMasks.installed == 1)
5748
// Need to keep monitoring the children.
5751
for (int i = 0; i < 32; i++)
5753
if (lastMasks.enabled[i] == 1)
5760
lastMasks.installed = 0;
5762
if (lastMasks.blocked == 1)
5768
void DisableSignals()
5770
if (lastMasks.blocked == 0)
5774
sigemptyset(&newMask);
5777
// Block also the other signals that may be
5778
// installed by the agent, that are those
5779
// signals for which the function returns 2.
5782
for (int i = 0; i < 32; i++)
5784
if (CheckSignal(i) > 0)
5787
*logofs << "Loop: Disabling signal " << i << " '"
5788
<< DumpSignal(i) << "' in process with pid '"
5789
<< getpid() << "'.\n" << logofs_flush;
5792
sigaddset(&newMask, i);
5796
sigprocmask(SIG_BLOCK, &newMask, &lastMasks.saved);
5798
lastMasks.blocked++;
5803
*logofs << "Loop: WARNING! Signals were already blocked in "
5804
<< "process with pid '" << getpid() << "'.\n"
5810
void EnableSignals()
5812
if (lastMasks.blocked == 1)
5815
*logofs << "Loop: Enabling signals in process with pid '"
5816
<< getpid() << "'.\n" << logofs_flush;
5819
sigprocmask(SIG_SETMASK, &lastMasks.saved, NULL);
5821
lastMasks.blocked = 0;
5826
*logofs << "Loop: WARNING! Signals were not blocked in "
5827
<< "process with pid '" << getpid() << "'.\n"
5831
cerr << "Warning" << ": Signals were not blocked in "
5832
<< "process with pid '" << getpid() << "'.\n";
5836
void InstallSignal(int signal, int action)
5838
if (lastMasks.enabled[signal] == 1)
5840
if (action == NX_SIGNAL_FORWARD)
5843
*logofs << "Loop: Forwarding handler for signal " << signal
5844
<< " '" << DumpSignal(signal) << "' in process "
5845
<< "with pid '" << getpid() << "'.\n"
5849
lastMasks.forward[signal] = 1;
5856
*logofs << "Loop: Reinstalling handler for signal " << signal
5857
<< " '" << DumpSignal(signal) << "' in process "
5858
<< "with pid '" << getpid() << "'.\n"
5866
*logofs << "Loop: Installing handler for signal " << signal
5867
<< " '" << DumpSignal(signal) << "' in process "
5868
<< "with pid '" << getpid() << "'.\n"
5873
if (signal == SIGALRM && isTimestamp(lastTimer.start))
5878
struct sigaction newAction;
5880
newAction.sa_handler = HandleSignal;
5883
// This field doesn't exist on most OSes except
5884
// Linux. We keep setting the field to NULL to
5885
// avoid side-effects in the case the field is
5889
#if defined(__linux__)
5891
newAction.sa_restorer = NULL;
5895
sigemptyset(&(newAction.sa_mask));
5897
if (signal == SIGCHLD)
5899
newAction.sa_flags = SA_NOCLDSTOP;
5903
newAction.sa_flags = 0;
5906
sigaction(signal, &newAction, &lastMasks.action[signal]);
5908
lastMasks.enabled[signal] = 1;
5910
if (action == NX_SIGNAL_FORWARD)
5912
lastMasks.forward[signal] = 1;
5916
void RestoreSignal(int signal)
5918
if (lastMasks.enabled[signal] == 0)
5921
*logofs << "Loop: WARNING! Signal '" << DumpSignal(signal)
5922
<< " not installed in process with pid '"
5923
<< getpid() << "'.\n" << logofs_flush;
5926
cerr << "Warning" << ": Signal '" << DumpSignal(signal)
5927
<< " not installed in process with pid '"
5928
<< getpid() << "'.\n";
5934
*logofs << "Loop: Restoring handler for signal " << signal
5935
<< " '" << DumpSignal(signal) << "' in process "
5936
<< "with pid '" << getpid() << "'.\n"
5940
if (signal == SIGALRM && isTimestamp(lastTimer.start))
5945
sigaction(signal, &lastMasks.action[signal], NULL);
5947
lastMasks.enabled[signal] = 0;
5948
lastMasks.forward[signal] = 0;
5951
void HandleSignal(int signal)
5958
#if defined(UNSAFE) && (defined(TEST) || defined(INFO))
5960
if (lastSignal != 0)
5962
*logofs << "Loop: WARNING! Last signal is '" << lastSignal
5963
<< "', '" << DumpSignal(signal) << "' and not zero "
5964
<< "in process with pid '" << getpid() << "'.\n"
5968
*logofs << "Loop: Signal '" << signal << "', '"
5969
<< DumpSignal(signal) << "' received in process "
5970
<< "with pid '" << getpid() << "'.\n" << logofs_flush;
5974
if (getpid() != lastProxy && handler != NULL)
5976
#if defined(UNSAFE) && (defined(TEST) || defined(INFO))
5977
*logofs << "Loop: Calling slave handler in process "
5978
<< "with pid '" << getpid() << "'.\n"
5982
if ((*handler)(signal) == 0)
5992
if (proxy != NULL && lastSignal == 0)
5994
lastSignal = SIGUSR1;
6001
if (proxy != NULL && lastSignal == 0)
6003
lastSignal = SIGUSR2;
6011
// It can happen that SIGPIPE is delivered
6012
// to the proxy even in the case some other
6013
// descriptor is unexpectedly closed.
6015
// if (agentFD[1] != -1)
6017
// cerr << "Info" << ": Received signal 'SIGPIPE'. "
6018
// << "Closing agent conection.\n";
6020
// shutdown(agentFD[1], SHUT_RDWR);
6029
// Nothing to do. Just wake up the
6030
// process on blocking operations.
6038
// Check if any of our children has exited.
6041
if (HandleChildren() != 0)
6047
// Don't save this signal or it will override
6048
// any previous signal sent by child before
6060
// Nothing to do. This signal is what is delivered
6061
// by the Cygwin library when trying use a shared
6062
// memory function if the daemon is not running.
6066
*logofs << "Loop: WARNING! Received signal '12' in "
6067
<< "process with pid '" << getpid() << "'.\n"
6070
*logofs << "Loop: WARNING! Please check that the "
6071
<< "cygserver daemon is running.\n"
6083
// Register the signal so we can handle it
6084
// inside the main loop. We will probably
6085
// dispose any resource and exit.
6088
if (getpid() == lastProxy)
6090
#if defined(UNSAFE) && defined(TEST)
6091
*logofs << "Loop: Registering end of session request "
6092
<< "due to signal '" << signal << "', '"
6093
<< DumpSignal(signal) << "'.\n"
6097
lastSignal = signal;
6102
// This is a child, so exit immediately.
6110
if (signal != 0 && lastMasks.forward[signal] == 1)
6112
if (lastMasks.action[signal].sa_handler != NULL &&
6113
lastMasks.action[signal].sa_handler != HandleSignal)
6115
#if defined(UNSAFE) && defined(TEST)
6116
*logofs << "Loop: Forwarding signal '" << signal << "', '"
6117
<< DumpSignal(signal) << "' to previous handler.\n"
6121
lastMasks.action[signal].sa_handler(signal);
6124
else if (lastMasks.action[signal].sa_handler == NULL)
6126
*logofs << "Loop: WARNING! Parent requested to forward "
6127
<< "signal '" << signal << "', '" << DumpSignal(signal)
6128
<< "' but didn't set a handler.\n" << logofs_flush;
6134
int HandleChildren()
6137
// Try to waitpid() for each child because the
6138
// call might have return ECHILD and so we may
6139
// have lost any of the processes.
6142
if (IsRunning(lastDialog) && HandleChild(lastDialog) == 1)
6144
#if defined(UNSAFE) && defined(TEST)
6145
*logofs << "Loop: Resetting pid of last dialog process "
6146
<< "in handler.\n" << logofs_flush;
6149
SetNotRunning(lastDialog);
6153
proxy -> handleResetAlert();
6159
if (IsRunning(lastWatchdog) && HandleChild(lastWatchdog) == 1)
6161
#if defined(UNSAFE) && defined(TEST)
6162
*logofs << "Loop: Watchdog is gone. Setting the last "
6163
<< "signal to SIGHUP.\n" << logofs_flush;
6166
lastSignal = SIGHUP;
6168
#if defined(UNSAFE) && defined(TEST)
6169
*logofs << "Loop: Resetting pid of last watchdog process "
6170
<< "in handler.\n" << logofs_flush;
6173
SetNotRunning(lastWatchdog);
6179
// The house-keeping process exits after a
6180
// number of iterations to keep the memory
6181
// pollution low. It is restarted on demand
6182
// by the lower layers, using the callback
6186
if (IsRunning(lastKeeper) && HandleChild(lastKeeper) == 1)
6188
#if defined(UNSAFE) && defined(TEST)
6189
*logofs << "Loop: Resetting pid of last house-keeping "
6190
<< "process in handler.\n" << logofs_flush;
6193
SetNotRunning(lastKeeper);
6199
// The pid will be checked by the code
6200
// that registered the child.
6203
if (IsRunning(lastChild))
6205
#if defined(UNSAFE) && defined(TEST)
6206
*logofs << "Loop: Resetting pid of last child process "
6207
<< "in handler.\n" << logofs_flush;
6210
SetNotRunning(lastChild);
6216
// This can actually happen either because we
6217
// reset the pid of the child process as soon
6218
// as we kill it, or because of a child process
6222
#if defined(UNSAFE) && (defined(TEST) || defined(INFO))
6223
*logofs << "Loop: Ignoring signal received for the "
6224
<< "unregistered child.\n" << logofs_flush;
6230
int HandleChild(int child)
6235
int options = WNOHANG | WUNTRACED;
6237
while ((pid = waitpid(child, &status, options)) &&
6238
pid == -1 && EGET() == EINTR);
6240
return CheckChild(pid, status);
6243
int WaitChild(int child, const char* label, int force)
6248
int options = WUNTRACED;
6252
#if defined(TEST) || defined(INFO)
6253
*logofs << "Loop: Waiting for the " << label
6254
<< " process '" << child << "' to die.\n"
6258
pid = waitpid(child, &status, options);
6260
if (pid == -1 && EGET() == EINTR)
6268
*logofs << "Loop: WARNING! Ignoring signal while "
6269
<< "waiting for the " << label << " process '"
6270
<< child << "' to die.\n"
6280
return (EGET() == ECHILD ? 1 : CheckChild(pid, status));
6283
int CheckChild(int pid, int status)
6289
if (WIFSTOPPED(status))
6291
#if defined(UNSAFE) && defined(TEST)
6292
*logofs << "Loop: Child process '" << pid << "' was stopped "
6293
<< "with signal " << (WSTOPSIG(status)) << ".\n"
6301
if (WIFEXITED(status))
6303
#if defined(UNSAFE) && defined(TEST)
6304
*logofs << "Loop: Child process '" << pid << "' exited "
6305
<< "with status '" << (WEXITSTATUS(status))
6306
<< "'.\n" << logofs_flush;
6309
lastStatus = WEXITSTATUS(status);
6311
else if (WIFSIGNALED(status))
6313
if (CheckSignal(WTERMSIG(status)) != 1)
6316
*logofs << "Loop: WARNING! Child process '" << pid
6317
<< "' died because of signal " << (WTERMSIG(status))
6318
<< ", '" << DumpSignal(WTERMSIG(status)) << "'.\n"
6322
cerr << "Warning" << ": Child process '" << pid
6323
<< "' died because of signal " << (WTERMSIG(status))
6324
<< ", '" << DumpSignal(WTERMSIG(status)) << "'.\n";
6326
#if defined(UNSAFE) && defined(TEST)
6329
*logofs << "Loop: Child process '" << pid
6330
<< "' died because of signal " << (WTERMSIG(status))
6331
<< ", '" << DumpSignal(WTERMSIG(status)) << "'.\n"
6344
if (EGET() != ECHILD)
6347
*logofs << "Loop: PANIC! Call to waitpid failed. "
6348
<< "Error is " << EGET() << " '" << ESTR()
6349
<< "'.\n" << logofs_flush;
6352
cerr << "Error" << ": Call to waitpid failed. "
6353
<< "Error is " << EGET() << " '" << ESTR()
6360
// This can happen when the waitpid() is
6361
// blocking, as the SIGCHLD is received
6366
*logofs << "Loop: No more children processes running.\n"
6376
void RegisterChild(int child)
6378
#if defined(TEST) || defined(INFO)
6380
if (IsNotRunning(lastChild))
6382
*logofs << "Loop: Registering child process '" << child
6383
<< "' in process with pid '" << getpid()
6384
<< "'.\n" << logofs_flush;
6388
*logofs << "Loop: WARNING! Overriding registered child '"
6389
<< lastChild << "' with new child '" << child
6390
<< "' in process with pid '" << getpid()
6391
<< "'.\n" << logofs_flush;
6399
int CheckParent(char *name, char *type, int parent)
6401
if (parent != getppid() || parent == 1)
6404
*logofs << name << ": WARNING! Parent process appears "
6405
<< "to be dead. Exiting " << type << ".\n"
6409
cerr << "Warning" << ": Parent process appears "
6410
<< "to be dead. Exiting " << type << ".\n";
6418
void HandleTimer(int signal)
6420
if (signal == SIGALRM)
6422
if (isTimestamp(lastTimer.start))
6424
#if defined(UNSAFE) && defined(TEST)
6425
*logofs << "Loop: Timer expired at " << strMsTimestamp()
6426
<< " in process with pid '" << getpid() << "'.\n"
6432
proxy -> handleTimer();
6440
*logofs << "Loop: PANIC! Inconsistent timer state "
6441
<< " in process with pid '" << getpid() << "'.\n"
6445
cerr << "Error" << ": Inconsistent timer state "
6446
<< " in process with pid '" << getpid() << "'.\n";
6452
*logofs << "Loop: PANIC! Inconsistent signal '"
6453
<< signal << "', '" << DumpSignal(signal)
6454
<< "' received in process with pid '"
6455
<< getpid() << "'.\n" << logofs_flush;
6458
cerr << "Error" << ": Inconsistent signal '"
6459
<< signal << "', '" << DumpSignal(signal)
6460
<< "' received in process with pid '"
6461
<< getpid() << "'.\n";
6465
void SetTimer(int value)
6469
if (isTimestamp(lastTimer.start))
6471
int diffTs = diffTimestamp(lastTimer.start, getTimestamp());
6473
if (diffTs > lastTimer.next.tv_usec / 1000 * 2)
6476
*logofs << "Loop: WARNING! Timer missed to expire at "
6477
<< strMsTimestamp() << " in process with pid '"
6478
<< getpid() << "'.\n" << logofs_flush;
6481
cerr << "Warning" << ": Timer missed to expire at "
6482
<< strMsTimestamp() << " in process with pid '"
6483
<< getpid() << "'.\n";
6485
HandleTimer(SIGALRM);
6490
*logofs << "Loop: Timer already running at "
6491
<< strMsTimestamp() << " in process with pid '"
6492
<< getpid() << "'.\n" << logofs_flush;
6500
// Save the former handler.
6503
struct sigaction action;
6505
action.sa_handler = HandleTimer;
6507
#if defined(__linux__)
6509
action.sa_restorer = NULL;
6513
sigemptyset(&action.sa_mask);
6515
action.sa_flags = 0;
6517
sigaction(SIGALRM, &action, &lastTimer.action);
6523
lastTimer.next = getTimestamp(value);
6525
struct itimerval timer;
6527
timer.it_interval = lastTimer.next;
6528
timer.it_value = lastTimer.next;
6531
*logofs << "Loop: Timer set to " << lastTimer.next.tv_sec
6532
<< " S and " << lastTimer.next.tv_usec / 1000
6533
<< " Ms at " << strMsTimestamp() << " in process "
6534
<< "with pid '" << getpid() << "'.\n"
6538
if (setitimer(ITIMER_REAL, &timer, &lastTimer.value) < 0)
6541
*logofs << "Loop: PANIC! Call to setitimer failed. "
6542
<< "Error is " << EGET() << " '" << ESTR()
6543
<< "'.\n" << logofs_flush;
6546
cerr << "Error" << ": Call to setitimer failed. "
6547
<< "Error is " << EGET() << " '" << ESTR()
6550
lastTimer.next = nullTimestamp();
6555
lastTimer.start = getTimestamp();
6560
if (isTimestamp(lastTimer.start) == 0)
6562
#if defined(UNSAFE) && defined(TEST)
6563
*logofs << "Loop: Timer not running in process "
6564
<< "with pid '" << getpid() << "'.\n"
6571
#if defined(UNSAFE) && defined(TEST)
6572
*logofs << "Loop: Timer reset at " << strMsTimestamp()
6573
<< " in process with pid '" << getpid()
6574
<< "'.\n" << logofs_flush;
6578
// Restore the old signal mask and timer.
6581
if (setitimer(ITIMER_REAL, &lastTimer.value, NULL) < 0)
6584
*logofs << "Loop: PANIC! Call to setitimer failed. "
6585
<< "Error is " << EGET() << " '" << ESTR()
6586
<< "'.\n" << logofs_flush;
6589
cerr << "Error" << ": Call to setitimer failed. "
6590
<< "Error is " << EGET() << " '" << ESTR()
6594
if (sigaction(SIGALRM, &lastTimer.action, NULL) < 0)
6597
*logofs << "Loop: PANIC! Call to sigaction failed. "
6598
<< "Error is " << EGET() << " '" << ESTR()
6599
<< "'.\n" << logofs_flush;
6602
cerr << "Error" << ": Call to sigaction failed. "
6603
<< "Error is " << EGET() << " '" << ESTR()
6607
lastTimer.start = lastTimer.next = nullTimestamp();
6611
// Open TCP socket to listen for remote proxy and
6612
// block until remote connects. If successful close
6613
// the listening socket and return FD on which the
6614
// other party is connected.
6617
int WaitForRemote(int portNum)
6619
char hostLabel[DEFAULT_STRING_LENGTH] = { 0 };
6621
int retryAccept = -1;
6622
int listenIPAddr = -1;
6628
// Get IP address of host to be awaited.
6631
int acceptIPAddr = 0;
6633
if (*acceptHost != '\0')
6635
acceptIPAddr = GetHostAddress(acceptHost);
6637
if (acceptIPAddr == 0)
6640
*logofs << "Loop: PANIC! Cannot accept connections from unknown host '"
6641
<< acceptHost << "'.\n" << logofs_flush;
6644
cerr << "Error" << ": Cannot accept connections from unknown host '"
6645
<< acceptHost << "'.\n";
6647
goto WaitForRemoteError;
6651
proxyFD = socket(AF_INET, SOCK_STREAM, PF_UNSPEC);
6656
*logofs << "Loop: PANIC! Call to socket failed for TCP socket. "
6657
<< "Error is " << EGET() << " '" << ESTR() << "'.\n"
6661
cerr << "Error" << ": Call to socket failed for TCP socket. "
6662
<< "Error is " << EGET() << " '" << ESTR() << "'.\n";
6664
goto WaitForRemoteError;
6666
else if (SetReuseAddress(proxyFD) < 0)
6668
goto WaitForRemoteError;
6673
ParseListenOption(listenIPAddr);
6675
sockaddr_in tcpAddr;
6677
tcpAddr.sin_family = AF_INET;
6678
tcpAddr.sin_port = htons(portNum);
6681
// Quick patch to run on MacOS/X where inet_addr("127.0.0.1")
6682
// alone seems to fail to return a valid interface. It probably
6683
// just needs a htonl() or something like that.
6685
// TODO: We have to give another look at inet_addr("127.0.0.1")
6691
tcpAddr.sin_addr.s_addr = htonl(INADDR_ANY);
6695
tcpAddr.sin_addr.s_addr = listenIPAddr;
6699
if (bind(proxyFD, (sockaddr *) &tcpAddr, sizeof(tcpAddr)) == -1)
6702
*logofs << "Loop: PANIC! Call to bind failed for TCP port "
6703
<< portNum << ". Error is " << EGET() << " '" << ESTR()
6704
<< "'.\n" << logofs_flush;
6707
cerr << "Error" << ": Call to bind failed for TCP port "
6708
<< portNum << ". Error is " << EGET() << " '" << ESTR()
6711
goto WaitForRemoteError;
6714
if (listen(proxyFD, 4) == -1)
6717
*logofs << "Loop: PANIC! Call to listen failed for TCP port "
6718
<< portNum << ". Error is " << EGET() << " '" << ESTR()
6719
<< "'.\n" << logofs_flush;
6722
cerr << "Error" << ": Call to listen failed for TCP port "
6723
<< portNum << ". Error is " << EGET() << " '" << ESTR()
6726
goto WaitForRemoteError;
6729
if (*acceptHost != '\0')
6731
strcat(hostLabel, "'");
6732
strcat(hostLabel, acceptHost);
6733
strcat(hostLabel, "'");
6737
strcpy(hostLabel, "any host");
6741
*logofs << "Loop: Waiting for connection from "
6742
<< hostLabel << " on port '" << portNum
6743
<< "'.\n" << logofs_flush;
6746
cerr << "Info" << ": Waiting for connection from "
6747
<< hostLabel << " on port '" << portNum
6751
// How many times to loop waiting for connections
6752
// from the selected host? Each loop wait for at
6753
// most 20 seconds so a default value of 3 gives
6754
// a timeout of 1 minute.
6756
// TODO: Handling of timeouts and retry attempts
6757
// must be rewritten.
6760
retryAccept = control -> OptionProxyRetryAccept;
6767
FD_SET(proxyFD, &readSet);
6769
T_timestamp selectTs;
6771
selectTs.tv_sec = 20;
6772
selectTs.tv_usec = 0;
6774
int result = select(proxyFD + 1, &readSet, NULL, NULL, &selectTs);
6780
if (EGET() == EINTR)
6782
if (CheckAbort() != 0)
6784
goto WaitForRemoteError;
6791
*logofs << "Loop: PANIC! Call to select failed. Error is "
6792
<< EGET() << " '" << ESTR() << "'.\n"
6796
cerr << "Error" << ": Call to select failed. Error is "
6797
<< EGET() << " '" << ESTR() << "'.\n";
6799
goto WaitForRemoteError;
6801
else if (result > 0 && FD_ISSET(proxyFD, &readSet))
6803
sockaddr_in newAddr;
6805
size_t addrLen = sizeof(sockaddr_in);
6807
newFD = accept(proxyFD, (sockaddr *) &newAddr, (socklen_t *) &addrLen);
6812
*logofs << "Loop: PANIC! Call to accept failed. Error is "
6813
<< EGET() << " '" << ESTR() << "'.\n"
6817
cerr << "Error" << ": Call to accept failed. Error is "
6818
<< EGET() << " '" << ESTR() << "'.\n";
6820
goto WaitForRemoteError;
6823
char *connectedHost = inet_ntoa(newAddr.sin_addr);
6825
if (*acceptHost == '\0' || (int) newAddr.sin_addr.s_addr == acceptIPAddr)
6829
unsigned int connectedPort = ntohs(newAddr.sin_port);
6831
*logofs << "Loop: Accepted connection from '" << connectedHost
6832
<< "' with port '" << connectedPort << "'.\n"
6836
cerr << "Info" << ": Accepted connection from '"
6837
<< connectedHost << "'.\n";
6844
*logofs << "Loop: WARNING! Refusing connection from '" << connectedHost
6845
<< "' on port '" << portNum << "'.\n" << logofs_flush;
6848
cerr << "Warning" << ": Refusing connection from '"
6849
<< connectedHost << "'.\n";
6853
// Not the best way to elude a DOS attack...
6861
if (--retryAccept == 0)
6863
if (*acceptHost == '\0')
6866
*logofs << "Loop: PANIC! Connection with remote host "
6867
<< "could not be established.\n"
6871
cerr << "Error" << ": Connection with remote host "
6872
<< "could not be established.\n";
6877
*logofs << "Loop: PANIC! Connection with remote host '"
6878
<< acceptHost << "' could not be established.\n"
6882
cerr << "Error" << ": Connection with remote host '"
6883
<< acceptHost << "' could not be established.\n";
6886
goto WaitForRemoteError;
6890
handleCheckSessionInConnect();
6906
// Connect to remote proxy. If successful
6907
// return FD of connection, else return -1.
6910
int ConnectToRemote(const char *const hostName, int portNum)
6914
int remoteIPAddr = GetHostAddress(hostName);
6916
if (remoteIPAddr == 0)
6919
*logofs << "Loop: PANIC! Unknown remote host '"
6920
<< hostName << "'.\n" << logofs_flush;
6923
cerr << "Error" << ": Unknown remote host '"
6924
<< hostName << "'.\n";
6930
*logofs << "Loop: Connecting to remote host '"
6931
<< hostName << ":" << portNum << "'.\n"
6935
cerr << "Info" << ": Connecting to remote host '"
6936
<< hostName << ":" << portNum << "'.\n"
6940
// How many times we retry to connect to remote
6941
// host in case of failure?
6944
int retryConnect = control -> OptionProxyRetryConnect;
6947
// Show an alert after 20 seconds and use the
6948
// same timeout to interrupt the connect. The
6949
// retry timeout is incremental, starting from
6950
// 100 miliseconds up to 1 second.
6953
int alertTimeout = 20000;
6954
int connectTimeout = 20000;
6955
int retryTimeout = 100;
6957
T_timestamp lastRetry = getNewTimestamp();
6961
addr.sin_family = AF_INET;
6962
addr.sin_port = htons(portNum);
6963
addr.sin_addr.s_addr = remoteIPAddr;
6967
proxyFD = socket(AF_INET, SOCK_STREAM, PF_UNSPEC);
6972
*logofs << "Loop: PANIC! Call to socket failed. "
6973
<< "Error is " << EGET() << " '" << ESTR()
6974
<< "'.\n" << logofs_flush;
6977
cerr << "Error" << ": Call to socket failed. "
6978
<< "Error is " << EGET() << " '" << ESTR()
6981
goto ConnectToRemoteError;
6983
else if (SetReuseAddress(proxyFD) < 0)
6985
goto ConnectToRemoteError;
6989
// Ensure operation is timed out
6990
// if there is a network problem.
6994
*logofs << "Loop: Timer set to " << connectTimeout / 1000
6995
<< " S " << "with retry set to " << retryConnect
6996
<< " in process with pid '" << getpid()
6997
<< "'.\n" << logofs_flush;
7000
SetTimer(connectTimeout);
7002
int result = connect(proxyFD, (sockaddr *) &addr, sizeof(sockaddr_in));
7004
int reason = EGET();
7012
if (CheckAbort() != 0)
7014
goto ConnectToRemoteError;
7016
else if (--retryConnect == 0)
7021
*logofs << "Loop: PANIC! Connection to '" << hostName
7022
<< ":" << portNum << "' failed. Error is "
7023
<< EGET() << " '" << ESTR() << "'.\n"
7027
cerr << "Error" << ": Connection to '" << hostName
7028
<< ":" << portNum << "' failed. Error is "
7029
<< EGET() << " '" << ESTR() << "'.\n";
7031
goto ConnectToRemoteError;
7036
*logofs << "Loop: Sleeping " << retryTimeout
7037
<< " Ms before retrying.\n"
7041
usleep(retryTimeout * 1000);
7045
if (retryTimeout > 1000 * 1000)
7047
retryTimeout = 1000 * 1000;
7052
// Check if it is time to show an alert dialog.
7055
if (diffTimestamp(lastRetry, getNewTimestamp()) >=
7056
(alertTimeout - control -> LatencyTimeout))
7058
if (IsNotRunning(lastDialog))
7060
handleCheckSessionInConnect();
7063
// Wait for the dialog process to die
7064
// unless a signal is received.
7067
while (IsRunning(lastDialog))
7069
WaitChild(lastDialog, "dialog", 0);
7071
if (CheckAbort() != 0)
7074
// The client ignores the TERM signal
7080
KillProcess(lastDialog, "dialog", SIGKILL, 1);
7084
KillProcess(lastDialog, "dialog", SIGTERM, 1);
7088
goto ConnectToRemoteError;
7092
lastRetry = getTimestamp();
7097
*logofs << "Loop: Not showing the dialog with "
7098
<< (diffTimestamp(lastRetry, getTimestamp()) / 1000)
7099
<< " seconds elapsed.\n" << logofs_flush;
7106
*logofs << "Loop: Connection to '" << hostName
7107
<< ":" << portNum << "' failed with error '"
7108
<< ESTR() << "'. Retrying.\n"
7115
// Connection was successful.
7124
ConnectToRemoteError:
7135
// Make a string of options for the remote
7136
// proxy and write it to the descriptor.
7137
// The string includes the local version.
7140
int SendProxyOptions(int fd)
7142
char options[DEFAULT_REMOTE_OPTIONS_LENGTH];
7145
// Send the "compatibility" version first, then our
7146
// actual version. Old proxies will take the first
7147
// value and ignore the second.
7150
sprintf(options, "NXPROXY-1.5.0-%i.%i.%i", control -> LocalVersionMajor,
7151
control -> LocalVersionMinor, control -> LocalVersionPatch);
7154
// If you want to send options from proxy
7155
// initiating the connection use something
7158
// if (WE_PROVIDE_CREDENTIALS)
7160
// sprintf(options + strlen(options), "%s=%s", option, value);
7163
// If you want to send options according to
7164
// local proxy mode use something like this:
7166
// if (control -> ProxyMode == proxy_client)
7168
// sprintf(options + strlen(options), "%s=%s", option, value);
7173
// Send the authorization cookie if any. We assume
7174
// user can choose to not provide any auth cookie
7175
// and allow any connection to be accepted.
7178
if (WE_PROVIDE_CREDENTIALS && *authCookie != '\0')
7180
sprintf(options + strlen(options), " cookie=%s,", authCookie);
7184
sprintf(options + strlen(options), " ");
7188
// Now link characteristics and compression
7189
// options. Delta compression, as well as
7190
// preferred pack method, are imposed by
7194
if (control -> ProxyMode == proxy_client)
7196
sprintf(options + strlen(options), "link=%s,pack=%s,cache=%s,",
7197
linkSpeedName, packMethodName, cacheSizeName);
7199
if (*bitrateLimitName != '\0')
7201
sprintf(options + strlen(options), "limit=%s,",
7206
// Let the user disable the render extension
7207
// and let the X client proxy know if it can
7208
// short-circuit the X replies. Also pass
7209
// along the session type to ensure that the
7210
// remote proxy gets the right value.
7213
sprintf(options + strlen(options), "render=%d,taint=%d,",
7214
(control -> HideRender == 0),
7215
control -> TaintReplies);
7217
if (*sessionType != '\0')
7219
sprintf(options + strlen(options), "type=%s,", sessionType);
7223
sprintf(options + strlen(options), "type=default,");
7227
// Add the 'strict' option, if needed.
7230
if (control -> isProtoStep7() == 1 &&
7233
sprintf(options + strlen(options), "strict=%d,", useStrict);
7237
// Tell the remote the size of the shared
7241
if (control -> isProtoStep7() == 1 &&
7242
*shsegSizeName != '\0')
7244
sprintf(options + strlen(options), "shseg=%s,", shsegSizeName);
7248
// Send image cache parameters.
7251
sprintf(options + strlen(options), "images=%s,", imagesSizeName);
7253
sprintf(options + strlen(options), "delta=%d,stream=%d,data=%d ",
7254
control -> LocalDeltaCompression,
7255
control -> LocalStreamCompressionLevel,
7256
control -> LocalDataCompressionLevel);
7261
// If no special compression level was selected,
7262
// server side will use compression levels set
7266
if (control -> LocalStreamCompressionLevel < 0)
7268
sprintf(options + strlen(options), "stream=default,");
7272
sprintf(options + strlen(options), "stream=%d,",
7273
control -> LocalStreamCompressionLevel);
7276
if (control -> LocalDataCompressionLevel < 0)
7278
sprintf(options + strlen(options), "data=default ");
7282
sprintf(options + strlen(options), "data=%d ",
7283
control -> LocalDataCompressionLevel);
7288
*logofs << "Loop: Sending remote options '"
7289
<< options << "'.\n" << logofs_flush;
7292
return WriteLocalData(fd, options, strlen(options));
7295
int ReadProxyVersion(int fd)
7298
*logofs << "Loop: Going to read the remote proxy version "
7299
<< "from FD#" << fd << ".\n" << logofs_flush;
7303
// Read until the first space in string.
7304
// We expect the remote version number.
7307
char options[DEFAULT_REMOTE_OPTIONS_LENGTH];
7309
int result = ReadRemoteData(fd, options, sizeof(options), ' ');
7315
if (control -> ProxyMode == proxy_server)
7317
HandleAlert(ABORT_PROXY_NEGOTIATION_ALERT, 1);
7320
handleAlertInLoop();
7327
*logofs << "Loop: Received remote version string '"
7328
<< options << "' from FD#" << fd << ".\n"
7332
if (strncmp(options, "NXPROXY-", strlen("NXPROXY-")) != 0)
7335
*logofs << "Loop: PANIC! Parse error in remote options string '"
7336
<< options << "'.\n" << logofs_flush;
7339
cerr << "Error" << ": Parse error in remote options string '"
7340
<< options << "'.\n";
7346
// Try to determine if this is a pre-2.0.0
7347
// version advertising itself as compatible
7355
sscanf(options, "NXPROXY-%i.%i.%i-%i.%i.%i", &(control -> RemoteVersionMajor),
7356
&(control -> RemoteVersionMinor), &(control -> RemoteVersionPatch),
7357
&major, &minor, &patch);
7359
if (control -> RemoteVersionMajor == 1 &&
7360
control -> RemoteVersionMinor == 2 &&
7361
control -> RemoteVersionPatch == 2 &&
7362
major != -1 && minor != -1 && patch != -1)
7365
*logofs << "Loop: Read trailing remote version '" << major
7366
<< "." << minor << "." << patch << "'.\n"
7370
control -> CompatVersionMajor = major;
7371
control -> CompatVersionMinor = minor;
7372
control -> CompatVersionPatch = patch;
7374
control -> RemoteVersionMajor = major;
7375
control -> RemoteVersionMinor = minor;
7376
control -> RemoteVersionPatch = patch;
7381
// We read the remote version at the first
7382
// round. If the second version is missing,
7383
// we will retain the values read before.
7386
sscanf(options, "NXPROXY-%i.%i.%i-%i.%i.%i", &(control -> CompatVersionMajor),
7387
&(control -> CompatVersionMinor), &(control -> CompatVersionPatch),
7388
&(control -> RemoteVersionMajor), &(control -> RemoteVersionMinor),
7389
&(control -> RemoteVersionPatch));
7393
*logofs << "Loop: Identified remote version '" << control -> RemoteVersionMajor
7394
<< "." << control -> RemoteVersionMinor << "." << control -> RemoteVersionPatch
7395
<< "'.\n" << logofs_flush;
7397
*logofs << "Loop: Remote compatibility version '" << control -> CompatVersionMajor
7398
<< "." << control -> CompatVersionMinor << "." << control -> CompatVersionPatch
7399
<< "'.\n" << logofs_flush;
7401
*logofs << "Loop: Local version '" << control -> LocalVersionMajor
7402
<< "." << control -> LocalVersionMinor << "." << control -> LocalVersionPatch
7403
<< "'.\n" << logofs_flush;
7406
if (SetVersion() < 0)
7408
if (control -> ProxyMode == proxy_server)
7410
HandleAlert(WRONG_PROXY_VERSION_ALERT, 1);
7413
handleAlertInLoop();
7421
int ReadProxyOptions(int fd)
7424
*logofs << "Loop: Going to read the remote proxy options "
7425
<< "from FD#" << fd << ".\n" << logofs_flush;
7428
char options[DEFAULT_REMOTE_OPTIONS_LENGTH];
7430
int result = ReadRemoteData(fd, options, sizeof(options), ' ');
7438
*logofs << "Loop: Received remote options string '"
7439
<< options << "' from FD#" << fd << ".\n"
7444
// Get the remote options, delimited by a space character.
7445
// Note that there will be a further initialization phase
7446
// at the time proxies negotiate cache file to restore.
7449
if (ParseRemoteOptions(options) < 0)
7452
*logofs << "Loop: PANIC! Couldn't negotiate a valid "
7453
<< "session with remote NX proxy.\n"
7457
cerr << "Error" << ": Couldn't negotiate a valid "
7458
<< "session with remote NX proxy.\n";
7466
int SendProxyCaches(int fd)
7469
*logofs << "Loop: Synchronizing local and remote caches.\n"
7473
if (control -> ProxyMode == proxy_client)
7476
// Prepare a list of caches matching this
7477
// session type and send it to the remote.
7481
*logofs << "Loop: Going to send the list of local caches.\n"
7487
int entries = DEFAULT_REMOTE_CACHE_ENTRIES;
7489
const char prefix = 'C';
7491
if (control -> LocalDeltaCompression == 0 ||
7492
control -> PersistentCacheEnableLoad == 0)
7495
*logofs << "Loop: Writing an empty list to FD#" << fd
7496
<< ".\n" << logofs_flush;
7499
return WriteLocalData(fd, "cachelist=none ", strlen("cachelist=none "));
7505
*logofs << "Loop: Looking for cache files in directory '"
7506
<< control -> PersistentCachePath << "'.\n" << logofs_flush;
7509
DIR *cacheDir = opendir(control -> PersistentCachePath);
7511
if (cacheDir != NULL)
7517
while (((dirEntry = readdir(cacheDir)) != NULL) && (count < entries))
7519
if (*dirEntry -> d_name == prefix &&
7520
strlen(dirEntry -> d_name) == (MD5_LENGTH * 2 + 2))
7524
WriteLocalData(fd, "cachelist=", strlen("cachelist="));
7530
WriteLocalData(fd, ",", strlen(","));
7534
*logofs << "Loop: Writing entry '" << control -> PersistentCachePath
7535
<< "/" << dirEntry -> d_name << "' to FD#" << fd
7536
<< ".\n" << logofs_flush;
7540
// Write cache file name to the socket,
7541
// including leading 'C-' or 'S-'.
7544
WriteLocalData(fd, dirEntry -> d_name, MD5_LENGTH * 2 + 2);
7556
*logofs << "Loop: Writing an empty list to FD#" << fd
7557
<< ".\n" << logofs_flush;
7560
return WriteLocalData(fd, "cachelist=none ", strlen("cachelist=none "));
7564
return WriteLocalData(fd, " ", 1);
7570
// Send back the selected cache name.
7574
*logofs << "Loop: Going to send the selected cache.\n"
7578
char buffer[DEFAULT_STRING_LENGTH];
7580
if (control -> PersistentCacheName != NULL)
7583
*logofs << "Loop: Name of selected cache file is '"
7584
<< control -> PersistentCacheName << "'.\n"
7588
sprintf(buffer, "cachefile=%s%s ",
7589
*(control -> PersistentCacheName) == 'C' ? "S-" : "C-",
7590
control -> PersistentCacheName + 2);
7595
*logofs << "Loop: No valid cache file was selected.\n"
7599
sprintf(buffer, "cachefile=none ");
7603
*logofs << "Loop: Sending string '" << buffer
7604
<< "' as selected cache file.\n"
7608
return WriteLocalData(fd, buffer, strlen(buffer));
7612
int ReadProxyCaches(int fd)
7614
if (control -> ProxyMode == proxy_client)
7617
*logofs << "Loop: Going to receive the selected proxy cache.\n"
7622
// We will read the name of cache plus the stop character.
7625
char buffer[DEFAULT_STRING_LENGTH];
7628
// Leave space for a trailing null.
7631
int result = ReadRemoteData(fd, buffer, sizeof("cachefile=") + MD5_LENGTH * 2 + 3, ' ');
7638
char *cacheName = strstr(buffer, "cachefile=");
7640
if (cacheName == NULL)
7643
*logofs << "Loop: PANIC! Invalid cache file option '"
7644
<< buffer << "' provided by remote proxy.\n"
7648
cerr << "Error" << ": Invalid cache file option '"
7649
<< buffer << "' provided by remote proxy.\n";
7654
cacheName += strlen("cachefile=");
7656
if (control -> PersistentCacheName != NULL)
7658
delete [] control -> PersistentCacheName;
7661
control -> PersistentCacheName = NULL;
7663
if (strncasecmp(cacheName, "none", strlen("none")) == 0)
7666
*logofs << "Loop: No cache file selected by remote proxy.\n"
7670
else if (strlen(cacheName) != MD5_LENGTH * 2 + 3 ||
7671
*(cacheName + MD5_LENGTH * 2 + 2) != ' ')
7674
*logofs << "Loop: PANIC! Invalid cache file name '"
7675
<< cacheName << "' provided by remote proxy.\n"
7679
cerr << "Error" << ": Invalid cache file name '"
7680
<< cacheName << "' provided by remote proxy.\n";
7687
// It is "C-" + 32 + "\0".
7690
control -> PersistentCacheName = new char[MD5_LENGTH * 2 + 3];
7692
*(cacheName + MD5_LENGTH * 2 + 2) = '\0';
7694
strcpy(control -> PersistentCacheName, cacheName);
7697
*logofs << "Loop: Cache file '" << control -> PersistentCacheName
7698
<< "' selected by remote proxy.\n" << logofs_flush;
7705
*logofs << "Loop: Going to receive the list of remote caches.\n"
7711
int size = ((MD5_LENGTH * 2 + 2) + strlen(",")) * DEFAULT_REMOTE_CACHE_ENTRIES +
7712
strlen("cachelist=") + strlen(" ") + 1;
7714
char *buffer = new char[size];
7716
int result = ReadRemoteData(fd, buffer, size - 1, ' ');
7726
*logofs << "Loop: Read list of caches from remote side as '"
7727
<< buffer << "'.\n" << logofs_flush;
7731
// Prepare the buffer. What we want is a list
7732
// like "cache1,cache2,cache2" terminated by
7736
*(buffer + strlen(buffer) - 1) = '\0';
7738
if (strncasecmp(buffer, "cachelist=", strlen("cachelist=")) != 0)
7741
*logofs << "Loop: Wrong format for list of cache files "
7742
<< "read from FD#" << fd << ".\n" << logofs_flush;
7745
cerr << "Error" << ": Wrong format for list of cache files.\n";
7752
control -> PersistentCacheName = GetLastCache(buffer, control -> PersistentCachePath);
7755
// Get rid of list of caches.
7764
int ReadForwarderVersion(int fd)
7767
*logofs << "Loop: Going to negotiate the forwarder version.\n"
7772
// Check if we actually expect the session cookie.
7775
if (*authCookie == '\0')
7778
*logofs << "Loop: No authentication cookie required "
7779
<< "from FD#" << fd << ".\n" << logofs_flush;
7785
char options[DEFAULT_REMOTE_OPTIONS_LENGTH];
7787
int result = ReadRemoteData(fd, options, sizeof(options), ' ');
7795
*logofs << "Loop: Received forwarder version string '" << options
7796
<< "' from FD#" << fd << ".\n" << logofs_flush;
7799
if (strncmp(options, "NXSSH-", strlen("NXSSH-")) != 0)
7802
*logofs << "Loop: PANIC! Parse error in forwarder options string '"
7803
<< options << "'.\n" << logofs_flush;
7806
cerr << "Error" << ": Parse error in forwarder options string '"
7807
<< options << "'.\n";
7813
// Accept whatever forwarder version.
7816
sscanf(options, "NXSSH-%i.%i.%i", &(control -> RemoteVersionMajor),
7817
&(control -> RemoteVersionMinor), &(control -> RemoteVersionPatch));
7820
*logofs << "Loop: Read forwarder version '" << control -> RemoteVersionMajor
7821
<< "." << control -> RemoteVersionMinor << "." << control -> RemoteVersionPatch
7822
<< "'.\n" << logofs_flush;
7828
int ReadForwarderOptions(int fd)
7831
// Get the forwarder cookie.
7834
if (*authCookie == '\0')
7837
*logofs << "Loop: No authentication cookie required "
7838
<< "from FD#" << fd << ".\n" << logofs_flush;
7844
char options[DEFAULT_REMOTE_OPTIONS_LENGTH];
7846
int result = ReadRemoteData(fd, options, sizeof(options), ' ');
7854
*logofs << "Loop: Received forwarder options string '"
7855
<< options << "' from FD#" << fd << ".\n"
7859
if (ParseForwarderOptions(options) < 0)
7862
*logofs << "Loop: PANIC! Couldn't negotiate a valid "
7863
<< "cookie with the NX forwarder.\n"
7867
cerr << "Error" << ": Couldn't negotiate a valid "
7868
<< "cookie with the NX forwarder.\n";
7876
int ReadRemoteData(int fd, char *buffer, int size, char stop)
7879
*logofs << "Loop: Going to read remote data from FD#"
7880
<< fd << ".\n" << logofs_flush;
7883
if (size >= MAXIMUM_REMOTE_OPTIONS_LENGTH)
7886
*logofs << "Loop: PANIC! Maximum remote options buffer "
7887
<< "limit exceeded.\n" << logofs_flush;
7890
cerr << "Error" << ": Maximum remote options buffer "
7891
<< "limit exceeded.\n";
7896
while (remotePosition < (size - 1))
7898
int result = read(fd, remoteData + remotePosition, 1);
7906
if (EGET() == EAGAIN)
7909
*logofs << "Loop: Reading data from FD#" << fd
7910
<< " would block.\n" << logofs_flush;
7915
else if (EGET() == EINTR)
7917
if (CheckAbort() != 0)
7927
*logofs << "Loop: PANIC! The remote NX proxy closed "
7928
<< "the connection.\n" << logofs_flush;
7931
cerr << "Error" << ": The remote NX proxy closed "
7932
<< "the connection.\n";
7936
else if (*(remoteData + remotePosition) == stop)
7939
*logofs << "Loop: Read stop character from FD#"
7940
<< fd << ".\n" << logofs_flush;
7946
// Copy the fake terminating null
7950
*(remoteData + remotePosition) = '\0';
7952
memcpy(buffer, remoteData, remotePosition + 1);
7955
*logofs << "Loop: Remote string '" << remoteData
7956
<< "' read from FD#" << fd << ".\n"
7960
int t = remotePosition;
7969
// Make sure string received
7970
// from far end is printable.
7973
if (isgraph(*(remoteData + remotePosition)) == 0)
7976
*logofs << "Loop: WARNING! Non printable character decimal '"
7977
<< (unsigned int) *(remoteData + remotePosition)
7978
<< "' received in remote data from FD#"
7979
<< fd << ".\n" << logofs_flush;
7982
cerr << "Warning" << ": Non printable character decimal '"
7983
<< (unsigned int) *(remoteData + remotePosition)
7984
<< "' received in remote data from FD#"
7985
<< fd << ".\n" << logofs_flush;
7987
*(remoteData + remotePosition) = ' ';
7991
*logofs << "Loop: Read a further character "
7992
<< "from FD#" << fd << ".\n"
8000
*(remoteData + remotePosition) = '\0';
8003
*logofs << "Loop: PANIC! Stop character missing "
8004
<< "from FD#" << fd << " after " << remotePosition
8005
<< " characters read in string '" << remoteData
8006
<< "'.\n" << logofs_flush;
8009
cerr << "Error" << ": Stop character missing "
8010
<< "from FD#" << fd << " after " << remotePosition
8011
<< " characters read in string '" << remoteData
8014
memcpy(buffer, remoteData, remotePosition);
8021
int WriteLocalData(int fd, const char *buffer, int size)
8025
while (position < size)
8027
int result = write(fd, buffer + position, size - position);
8033
if (result < 0 && EGET() == EINTR)
8039
*logofs << "Loop: Error writing data to FD#"
8040
<< fd << ".\n" << logofs_flush;
8053
// Parse the string passed by calling process in
8054
// the environment. This is not necessarily the
8055
// content of DISPLAY variable, but can be the
8056
// parameters passed when creating the process
8060
int ParseEnvironmentOptions(const char *env, int force)
8063
// Be sure log file is valid.
8072
// Be sure we have a parameters repository
8073
// and a context to jump into because this
8074
// can be called before creating the proxy.
8077
if (control == NULL)
8079
control = new Control();
8082
if (setjmp(context) == 1)
8085
*logofs << "Loop: Out of the long jump while parsing "
8086
<< "the environment options.\n"
8093
if (force == 0 && parsedOptions == 1)
8096
*logofs << "Loop: Skipping a further parse of environment "
8097
<< "options string '" << (env != NULL ? env : "")
8098
<< "'.\n" << logofs_flush;
8104
if (env == NULL || *env == '\0')
8107
*logofs << "Loop: Nothing to do with empty environment "
8108
<< "options string '" << (env != NULL ? env : "")
8109
<< "'.\n" << logofs_flush;
8116
*logofs << "Loop: Going to parse the environment options "
8117
<< "string '" << env << "'.\n"
8124
// Copy the string passed as parameter
8125
// because we need to modify it.
8128
char opts[DEFAULT_DISPLAY_OPTIONS_LENGTH];
8132
memset(opts, '\0', DEFAULT_DISPLAY_OPTIONS_LENGTH);
8136
if (strlen(env) >= DEFAULT_DISPLAY_OPTIONS_LENGTH)
8139
*logofs << "Loop: PANIC! Environment options string '" << env
8140
<< "' exceeds length of " << DEFAULT_DISPLAY_OPTIONS_LENGTH
8141
<< " characters.\n" << logofs_flush;
8144
cerr << "Error" << ": Environment options string '" << env
8145
<< "' exceeds length of " << DEFAULT_DISPLAY_OPTIONS_LENGTH
8146
<< " characters.\n";
8153
char *nextOpts = opts;
8156
// Ensure that DISPLAY environment variable
8157
// (roughly) follows the X convention for
8158
// transport notation.
8161
if (strncasecmp(opts, "nx/nx,:", 7) == 0 ||
8162
strncasecmp(opts, "nx,:", 4) == 0)
8165
*logofs << "Loop: PANIC! Parse error in options string '"
8166
<< opts << "' at 'nx,:'.\n" << logofs_flush;
8169
cerr << "Error" << ": Parse error in options string '"
8170
<< opts << "' at 'nx,:'.\n";
8174
else if (strncasecmp(opts, "nx/nx,", 6) == 0)
8178
else if (strncasecmp(opts, "nx,", 3) == 0)
8182
else if (strncasecmp(opts, "nx:", 3) == 0)
8186
else if (force == 0)
8189
*logofs << "Loop: Ignoring host X server display string '"
8190
<< opts << "'.\n" << logofs_flush;
8197
// Save here the name of the options file and
8198
// parse it after all the other options.
8201
char fileOptions[DEFAULT_STRING_LENGTH] = { 0 };
8204
// The options string is intended to be a series
8205
// of name/value tuples in the form name=value
8206
// separated by the ',' character ended by a ':'
8207
// followed by remote NX proxy port.
8213
value = rindex(nextOpts, ':');
8217
char *check = value + 1;
8219
if (*check == '\0' || isdigit(*check) == 0)
8222
*logofs << "Loop: PANIC! Can't identify NX port in string '"
8223
<< value << "'.\n" << logofs_flush;
8226
cerr << "Error" << ": Can't identify NX port in string '"
8232
proxyPort = atoi(check);
8235
// Get rid of the port specification.
8240
else if (proxyPort == DEFAULT_NX_PROXY_PORT && force == 0)
8243
// Complain only if user didn't specify
8244
// the port on the command line.
8248
*logofs << "Loop: PANIC! Can't identify NX port in string '"
8249
<< opts << "'.\n" << logofs_flush;
8252
cerr << "Error" << ": Can't identify NX port in string '"
8259
*logofs << "Loop: Parsing options string '"
8260
<< nextOpts << "'.\n" << logofs_flush;
8264
// Now all the other optional parameters.
8267
name = strtok(nextOpts, "=");
8271
value = strtok(NULL, ",");
8273
if (CheckArg("environment", name, value) < 0)
8278
if (strcasecmp(name, "options") == 0)
8280
strncpy(fileOptions, value, DEFAULT_STRING_LENGTH - 1);
8282
else if (strcasecmp(name, "display") == 0)
8284
strncpy(displayHost, value, DEFAULT_STRING_LENGTH - 1);
8286
else if (strcasecmp(name, "link") == 0)
8288
if (control -> ProxyMode == proxy_server)
8290
PrintOptionIgnored("local", name, value);
8292
else if (ParseLinkOption(value) < 0)
8295
*logofs << "Loop: PANIC! Can't identify 'link' option in string '"
8296
<< value << "'.\n" << logofs_flush;
8299
cerr << "Error" << ": Can't identify 'link' option in string '"
8305
else if (strcasecmp(name, "limit") == 0)
8307
if (control -> ProxyMode == proxy_server)
8309
PrintOptionIgnored("local", name, value);
8311
else if (ParseBitrateOption(value) < 0)
8314
*logofs << "Loop: PANIC! Can't identify option 'limit' in string '"
8315
<< value << "'.\n" << logofs_flush;
8318
cerr << "Error" << ": Can't identify option 'limit' in string '"
8324
else if (strcasecmp(name, "type") == 0)
8327
// Type of session, for example "desktop",
8328
// "application", "windows", etc.
8331
if (control -> ProxyMode == proxy_server)
8333
PrintOptionIgnored("local", name, value);
8337
if (strcasecmp(value, "default") == 0)
8339
*sessionType = '\0';
8343
strncpy(sessionType, value, DEFAULT_STRING_LENGTH - 1);
8347
else if (strcasecmp(name, "listen") == 0)
8349
if (*connectHost != '\0')
8352
*logofs << "Loop: PANIC! Can't handle 'listen' and 'connect' parameters "
8353
<< "at the same time.\n" << logofs_flush;
8355
*logofs << "Loop: PANIC! Refusing 'listen' parameter with 'connect' being '"
8356
<< connectHost << "'.\n" << logofs_flush;
8359
cerr << "Error" << ": Can't handle 'listen' and 'connect' parameters "
8360
<< "at the same time.\n";
8362
cerr << "Error" << ": Refusing 'listen' parameter with 'connect' being '"
8363
<< connectHost << "'.\n";
8368
listenPort = ValidateArg("local", name, value);
8370
else if (strcasecmp(name, "accept") == 0)
8372
if (*connectHost != '\0')
8375
*logofs << "Loop: PANIC! Can't handle 'accept' and 'connect' parameters "
8376
<< "at the same time.\n" << logofs_flush;
8378
*logofs << "Loop: PANIC! Refusing 'accept' parameter with 'connect' being '"
8379
<< connectHost << "'.\n" << logofs_flush;
8382
cerr << "Error" << ": Can't handle 'accept' and 'connect' parameters "
8383
<< "at the same time.\n";
8385
cerr << "Error" << ": Refusing 'accept' parameter with 'connect' being '"
8386
<< connectHost << "'.\n";
8391
strncpy(acceptHost, value, DEFAULT_STRING_LENGTH - 1);
8393
else if (strcasecmp(name, "connect") == 0)
8395
if (*acceptHost != '\0')
8398
*logofs << "Loop: PANIC! Can't handle 'connect' and 'accept' parameters "
8399
<< "at the same time.\n" << logofs_flush;
8401
*logofs << "Loop: PANIC! Refusing 'connect' parameter with 'accept' being '"
8402
<< acceptHost << "'.\n" << logofs_flush;
8405
cerr << "Error" << ": Can't handle 'connect' and 'accept' parameters "
8406
<< "at the same time.\n";
8408
cerr << "Error" << ": Refusing 'connect' parameter with 'accept' being '"
8409
<< acceptHost << "'.\n";
8414
strncpy(connectHost, value, DEFAULT_STRING_LENGTH - 1);
8416
else if (strcasecmp(name, "port") == 0)
8418
connectPort = ValidateArg("local", name, value);
8420
else if (strcasecmp(name, "retry") == 0)
8422
control -> OptionProxyRetryConnect = ValidateArg("local", name, value);
8423
control -> OptionServerRetryConnect = ValidateArg("local", name, value);
8425
else if (strcasecmp(name, "session") == 0)
8427
strncpy(sessionFileName, value, DEFAULT_STRING_LENGTH - 1);
8429
else if (strcasecmp(name, "errors") == 0)
8432
// The old name of the parameter was 'log'
8433
// but the default name for the file is
8434
// 'errors' so it is more logical to use
8438
strncpy(errorsFileName, value, DEFAULT_STRING_LENGTH - 1);
8440
else if (strcasecmp(name, "root") == 0)
8442
strncpy(rootDir, value, DEFAULT_STRING_LENGTH - 1);
8444
else if (strcasecmp(name, "id") == 0)
8446
strncpy(sessionId, value, DEFAULT_STRING_LENGTH - 1);
8448
else if (strcasecmp(name, "stats") == 0)
8450
control -> EnableStatistics = 1;
8452
strncpy(statsFileName, value, DEFAULT_STRING_LENGTH - 1);
8454
else if (strcasecmp(name, "cookie") == 0)
8456
LowercaseArg("local", name, value);
8458
strncpy(authCookie, value, DEFAULT_STRING_LENGTH - 1);
8460
else if (strcasecmp(name, "nodelay") == 0)
8462
useNoDelay = ValidateArg("local", name, value);
8464
else if (strcasecmp(name, "policy") == 0)
8466
if (control -> ProxyMode == proxy_server)
8468
PrintOptionIgnored("local", name, value);
8472
usePolicy = ValidateArg("local", name, value);
8475
else if (strcasecmp(name, "render") == 0)
8477
if (control -> ProxyMode == proxy_server)
8479
PrintOptionIgnored("local", name, value);
8483
useRender = ValidateArg("local", name, value);
8486
else if (strcasecmp(name, "taint") == 0)
8488
if (control -> ProxyMode == proxy_server)
8490
PrintOptionIgnored("local", name, value);
8494
useTaint = ValidateArg("local", name, value);
8497
else if (strcasecmp(name, "delta") == 0)
8499
if (control -> ProxyMode == proxy_server)
8501
PrintOptionIgnored("local", name, value);
8505
control -> LocalDeltaCompression = ValidateArg("local", name, value);
8508
else if (strcasecmp(name, "data") == 0)
8510
control -> LocalDataCompressionLevel = ValidateArg("local", name, value);
8512
if (control -> LocalDataCompressionLevel == 0)
8514
control -> LocalDataCompression = 0;
8518
control -> LocalDataCompression = 1;
8521
else if (strcasecmp(name, "stream") == 0)
8523
control -> LocalStreamCompressionLevel = ValidateArg("local", name, value);
8525
if (control -> LocalStreamCompressionLevel == 0)
8527
control -> LocalStreamCompression = 0;
8531
control -> LocalStreamCompression = 1;
8534
else if (strcasecmp(name, "memory") == 0)
8536
control -> LocalMemoryLevel = ValidateArg("local", name, value);
8538
else if (strcasecmp(name, "cache") == 0)
8540
if (control -> ProxyMode == proxy_server)
8542
PrintOptionIgnored("local", name, value);
8544
else if (ParseCacheOption(value) < 0)
8547
*logofs << "Loop: PANIC! Can't identify cache size for string '"
8548
<< value << "'.\n" << logofs_flush;
8551
cerr << "Error" << ": Can't identify cache size for string '"
8557
else if (strcasecmp(name, "images") == 0)
8559
if (control -> ProxyMode == proxy_server)
8561
PrintOptionIgnored("local", name, value);
8563
else if (ParseImagesOption(value) < 0)
8566
*logofs << "Loop: PANIC! Can't identify images cache size for string '"
8567
<< value << "'.\n" << logofs_flush;
8570
cerr << "Error" << ": Can't identify images cache size for string '"
8576
else if (strcasecmp(name, "shseg") == 0)
8579
// The 'shmem' option is used by the agent, together
8580
// with 'shpix' literal. We make the 'shseg' option
8581
// specific to the proxy and use it to determine the
8582
// size of the shared memory segment, or otherwise 0,
8583
// if the use of the shared memory extension should
8584
// not be enabled on the real X server.
8587
if (control -> ProxyMode == proxy_server)
8589
PrintOptionIgnored("local", name, value);
8591
else if (ParseShmemOption(value) < 0)
8594
*logofs << "Loop: PANIC! Can't identify size of shared memory "
8595
<< "segment in string '" << value << "'.\n"
8599
cerr << "Error" << ": Can't identify size of shared memory "
8600
<< "segment in string '" << value << "'.\n";
8605
else if (strcasecmp(name, "load") == 0)
8607
if (control -> ProxyMode == proxy_server)
8609
PrintOptionIgnored("local", name, value);
8613
control -> PersistentCacheEnableLoad = ValidateArg("local", name, value);
8615
if (control -> PersistentCacheEnableLoad > 0)
8617
control -> PersistentCacheEnableLoad = 1;
8621
if (control -> PersistentCacheName != NULL)
8623
delete [] control -> PersistentCacheName;
8626
control -> PersistentCacheName = NULL;
8628
control -> PersistentCacheEnableLoad = 0;
8632
else if (strcasecmp(name, "save") == 0)
8634
if (control -> ProxyMode == proxy_server)
8636
PrintOptionIgnored("local", name, value);
8640
control -> PersistentCacheEnableSave = ValidateArg("local", name, value);
8642
if (control -> PersistentCacheEnableSave > 0)
8644
control -> PersistentCacheEnableSave = 1;
8648
if (control -> PersistentCacheName != NULL)
8650
delete [] control -> PersistentCacheName;
8653
control -> PersistentCacheName = NULL;
8655
control -> PersistentCacheEnableSave = 0;
8659
else if (strcasecmp(name, "cups") == 0)
8661
cupsPort = ValidateArg("local", name, value);
8663
else if (strcasecmp(name, "sync") == 0)
8666
*logofs << "Loop: WARNING! No 'sync' channel in current version. "
8667
<< "Assuming 'cups' channel.\n" << logofs_flush;
8670
cerr << "Warning" << ": No 'sync' channel in current version. "
8671
<< "Assuming 'cups' channel.\n";
8673
cupsPort = ValidateArg("local", name, value);
8675
else if (strcasecmp(name, "keybd") == 0 ||
8676
strcasecmp(name, "aux") == 0)
8678
auxPort = ValidateArg("local", name, value);
8680
else if (strcasecmp(name, "samba") == 0 ||
8681
strcasecmp(name, "smb") == 0)
8683
smbPort = ValidateArg("local", name, value);
8685
else if (strcasecmp(name, "media") == 0)
8687
mediaPort = ValidateArg("local", name, value);
8689
else if (strcasecmp(name, "http") == 0)
8691
httpPort = ValidateArg("local", name, value);
8693
else if (strcasecmp(name, "font") == 0)
8695
strncpy(fontPort, value, DEFAULT_STRING_LENGTH - 1);
8697
else if (strcasecmp(name, "slave") == 0)
8699
slavePort = ValidateArg("local", name, value);
8701
else if (strcasecmp(name, "mask") == 0)
8703
control -> ChannelMask = ValidateArg("local", name, value);
8705
else if (strcasecmp(name, "timeout") == 0)
8707
int timeout = ValidateArg("local", name, value);
8712
*logofs << "Loop: Disabling timeout on broken "
8713
<< "proxy connection.\n" << logofs_flush;
8716
control -> ProxyTimeout = 0;
8720
control -> ProxyTimeout = timeout * 1000;
8723
else if (strcasecmp(name, "cleanup") == 0)
8725
int cleanup = ValidateArg("local", name, value);
8730
*logofs << "Loop: Disabling grace timeout on "
8731
<< "proxy shutdown.\n" << logofs_flush;
8734
control -> CleanupTimeout = 0;
8738
control -> CleanupTimeout = cleanup * 1000;
8741
else if (strcasecmp(name, "pack") == 0)
8743
if (control -> ProxyMode == proxy_server)
8745
PrintOptionIgnored("local", name, value);
8747
else if (ParsePackOption(value) < 0)
8750
*logofs << "Loop: PANIC! Can't identify pack method for string '"
8751
<< value << "'.\n" << logofs_flush;
8754
cerr << "Error" << ": Can't identify pack method for string '"
8760
else if (strcasecmp(name, "core") == 0)
8762
control -> EnableCoreDumpOnAbort = ValidateArg("local", name, value);
8764
else if (strcasecmp(name, "kill") == 0)
8766
if (control -> KillDaemonOnShutdownNumber <
8767
control -> KillDaemonOnShutdownLimit)
8770
*logofs << "Loop: WARNING! Adding process with pid '"
8771
<< ValidateArg("local", name, value) << " to the "
8772
<< "daemons to kill at shutdown.\n"
8776
control -> KillDaemonOnShutdown[control ->
8777
KillDaemonOnShutdownNumber] =
8778
ValidateArg("local", name, value);
8780
control -> KillDaemonOnShutdownNumber++;
8785
*logofs << "Loop: WARNING! Number of daemons to kill "
8786
<< "at shutdown exceeded.\n" << logofs_flush;
8789
cerr << "Warning" << ": Number of daemons to kill "
8790
<< "at shutdown exceeded.\n";
8793
else if (strcasecmp(name, "strict") == 0)
8795
if (control -> ProxyMode == proxy_server)
8797
PrintOptionIgnored("local", name, value);
8801
useStrict = ValidateArg("local", name, value);
8804
else if (strcasecmp(name, "encryption") == 0)
8806
useEncryption = ValidateArg("local", name, value);
8808
else if (strcasecmp(name, "product") == 0)
8810
strncpy(productName, value, DEFAULT_STRING_LENGTH - 1);
8812
else if (strcasecmp(name, "rootless") == 0 ||
8813
strcasecmp(name, "geometry") == 0 ||
8814
strcasecmp(name, "resize") == 0 ||
8815
strcasecmp(name, "fullscreen") == 0 ||
8816
strcasecmp(name, "keyboard") == 0 ||
8817
strcasecmp(name, "clipboard") == 0 ||
8818
strcasecmp(name, "streaming") == 0 ||
8819
strcasecmp(name, "backingstore") == 0)
8822
*logofs << "Loop: Ignoring agent option '" << name
8823
<< "' with value '" << value << "'.\n"
8827
else if (strcasecmp(name, "composite") == 0 ||
8828
strcasecmp(name, "shmem") == 0 ||
8829
strcasecmp(name, "shpix") == 0 ||
8830
strcasecmp(name, "kbtype") == 0 ||
8831
strcasecmp(name, "client") == 0 ||
8832
strcasecmp(name, "shadow") == 0 ||
8833
strcasecmp(name, "shadowuid") == 0 ||
8834
strcasecmp(name, "shadowmode") == 0 ||
8835
strcasecmp(name, "clients") == 0)
8838
*logofs << "Loop: Ignoring agent option '" << name
8839
<< "' with value '" << value << "'.\n"
8843
else if (strcasecmp(name, "defer") == 0 ||
8844
strcasecmp(name, "tile") == 0 ||
8845
strcasecmp(name, "menu") == 0)
8848
*logofs << "Loop: Ignoring agent option '" << name
8849
<< "' with value '" << value << "'.\n"
8856
*logofs << "Loop: WARNING! Ignoring unknown option '"
8857
<< name << "' with value '" << value << "'.\n"
8861
cerr << "Warning" << ": Ignoring unknown option '"
8862
<< name << "' with value '" << value << "'.\n";
8865
name = strtok(NULL, "=");
8867
} // End of while (name) ...
8870
*logofs << "Loop: Completed parsing of string '"
8871
<< env << "'.\n" << logofs_flush;
8874
if (*fileOptions != '\0')
8876
if (strcmp(fileOptions, optionsFileName) != 0)
8879
*logofs << "Loop: Reading options from '" << fileOptions
8880
<< "'.\n" << logofs_flush;
8883
if (ParseFileOptions(fileOptions) < 0)
8891
*logofs << "Loop: WARNING! Name of the options file "
8892
<< "specified multiple times. Not parsing "
8893
<< "again.\n" << logofs_flush;
8897
if (*optionsFileName == '\0')
8899
strncpy(optionsFileName, value, DEFAULT_STRING_LENGTH - 1);
8902
*logofs << "Loop: Assuming name of options file '"
8903
<< optionsFileName << "'.\n"
8910
// If port where proxy is acting as an X server
8911
// was not specified assume the same port where
8912
// proxy is listening for the remote peer.
8915
if (xPort == DEFAULT_NX_X_PORT)
8924
// Parse the command line options passed by user when
8925
// running proxy in stand alone mode. Note that passing
8926
// parameters this way is strongly discouraged. These
8927
// command line switch can change (and they do often).
8928
// Please, use the form "option=value" instead and set
8929
// the DISPLAY environment variable.
8932
int ParseCommandLineOptions(int argc, const char **argv)
8935
// Be sure log file is valid.
8943
if (setjmp(context) == 1)
8946
*logofs << "Loop: Out of the long jump while parsing "
8947
<< "the command line options.\n"
8955
// Be sure we have a parameters repository
8958
if (control == NULL)
8960
control = new Control();
8963
if (parsedCommand == 1)
8966
*logofs << "Loop: Skipping a further parse of command line options.\n"
8974
*logofs << "Loop: Going to parse the command line options.\n"
8981
// Print out arguments.
8986
*logofs << "Loop: Argc is " << argc << ".\n" << logofs_flush;
8988
for (int argi = 0; argi < argc; argi++)
8990
*logofs << "Loop: Argv[" << argi << "] is " << argv[argi]
8991
<< ".\n" << logofs_flush;
8997
// Shall use getopt here.
9000
for (int argi = 1; argi < argc; argi++)
9002
const char *nextArg = argv[argi];
9004
if (*nextArg == '-')
9006
switch (*(nextArg + 1))
9010
PrintUsageInfo(nextArg, 0);
9017
// Start proxy in CLIENT mode.
9020
if (WE_SET_PROXY_MODE == 0)
9023
*logofs << "Loop: Setting local proxy mode to proxy_client.\n"
9027
control -> ProxyMode = proxy_client;
9029
else if (control -> ProxyMode != proxy_client)
9032
*logofs << "Loop: PANIC! Can't redefine local proxy to "
9033
<< "client mode.\n" << logofs_flush;
9036
cerr << "Error" << ": Can't redefine local proxy to "
9037
<< "client mode.\n";
9047
// Start proxy in SERVER mode.
9050
if (WE_SET_PROXY_MODE == 0)
9053
*logofs << "Loop: Setting local proxy mode to proxy_server.\n"
9057
control -> ProxyMode = proxy_server;
9059
else if (control -> ProxyMode != proxy_server)
9062
*logofs << "Loop: PANIC! Can't redefine local proxy to "
9063
<< "server mode.\n" << logofs_flush;
9066
cerr << "Error" << ": Can't redefine local proxy to "
9067
<< "server mode.\n";
9082
PrintUsageInfo(nextArg, 1);
9085
// Function GetArg() is not used anymore.
9086
// Add a dummy call to avoid the warning.
9091
GetArg(argi, argc, argv);
9103
// Try to parse the option as a remote host:port
9104
// specification as in 'localhost:8'. Such a
9105
// parameter can be specified at the end of the
9106
// command line at the connecting side.
9109
if (ParseHostOption(nextArg, connectHost, connectPort) > 0)
9112
// Assume port is at a proxied display offset.
9115
proxyPort = connectPort;
9117
connectPort += DEFAULT_NX_PROXY_PORT_OFFSET;
9119
else if (ParseEnvironmentOptions(nextArg, 1) < 0)
9131
// Set the variable to the values of host and
9132
// port where this proxy is going to hook to
9133
// an existing proxy.
9136
int ParseBindOptions(char **host, int *port)
9138
if (*bindHost != '\0')
9152
// Read options from file and merge with environment.
9155
int ParseFileOptions(const char *file)
9159
if (*file != '/' && *file != '.')
9161
char *filePath = GetSessionPath();
9163
if (filePath == NULL)
9165
cerr << "Error" << ": Cannot determine directory for NX option file.\n";
9170
fileName = new char[strlen(filePath) + strlen("/") +
9173
strcpy(fileName, filePath);
9175
strcat(fileName, "/");
9176
strcat(fileName, file);
9182
fileName = new char[strlen(file) + 1];
9184
strcpy(fileName, file);
9188
*logofs << "Loop: Going to read options from file '"
9189
<< fileName << "'.\n" << logofs_flush;
9192
FILE *filePtr = fopen(fileName, "r");
9194
if (filePtr == NULL)
9197
*logofs << "Loop: PANIC! Can't open options file '" << fileName
9198
<< "'. Error is " << EGET() << " '" << ESTR() << "'.\n"
9202
cerr << "Error" << ": Can't open options file '" << fileName
9203
<< "'. Error is " << EGET() << " '" << ESTR() << "'.\n";
9210
char options[DEFAULT_DISPLAY_OPTIONS_LENGTH];
9214
memset(options, '\0', DEFAULT_DISPLAY_OPTIONS_LENGTH);
9218
if (fgets(options, DEFAULT_DISPLAY_OPTIONS_LENGTH, filePtr) == NULL)
9221
*logofs << "Loop: PANIC! Can't read options from file '" << fileName
9222
<< "'. Error is " << EGET() << " '" << ESTR() << "'.\n"
9226
cerr << "Error" << ": Can't read options from file '" << fileName
9227
<< "'. Error is " << EGET() << " '" << ESTR() << "'.\n";
9239
// Purge the newline and the other non-
9240
// printable characters in the string.
9243
char *next = options;
9245
while (*next != '\0')
9247
if (isprint(*next) == 0)
9256
*logofs << "Loop: Read options '" << options << "' from file '"
9257
<< fileName << "'.\n" << logofs_flush;
9260
if (ParseEnvironmentOptions(options, 1) < 0)
9273
// Parse the option string passed from the
9274
// remote proxy at startup.
9277
int ParseRemoteOptions(char *opts)
9280
*logofs << "Loop: Going to parse the remote options "
9281
<< "string '" << opts << "'.\n"
9289
// The options string is intended to be a series
9290
// of name/value tuples in the form name=value
9291
// separated by the ',' character.
9310
// Get rid of the terminating space.
9313
if (*(opts + strlen(opts) - 1) == ' ')
9315
*(opts + strlen(opts) - 1) = '\0';
9318
name = strtok(opts, "=");
9322
value = strtok(NULL, ",");
9324
if (CheckArg("remote", name, value) < 0)
9329
if (strcasecmp(name, "cookie") == 0)
9331
if (WE_PROVIDE_CREDENTIALS)
9334
*logofs << "Loop: WARNING! Ignoring remote option 'cookie' "
9335
<< "with value '" << value << "' when initiating "
9336
<< "connection.\n" << logofs_flush;
9339
cerr << "Warning" << ": Ignoring remote option 'cookie' "
9340
<< "with value '" << value << "' when initiating "
9343
else if (strncasecmp(authCookie, value, strlen(authCookie)) != 0)
9346
*logofs << "Loop: PANIC! Authentication cookie '" << value
9347
<< "' doesn't match '" << authCookie << "'.\n"
9351
cerr << "Error" << ": Authentication cookie '" << value
9352
<< "' doesn't match '" << authCookie << "'.\n";
9359
else if (strcasecmp(name, "link") == 0)
9361
if (control -> ProxyMode == proxy_client)
9363
PrintOptionIgnored("remote", name, value);
9367
if (*linkSpeedName != '\0' && strcasecmp(linkSpeedName, value) != 0)
9370
*logofs << "Loop: WARNING! Overriding option 'link' "
9371
<< "with new value '" << value << "'.\n"
9375
cerr << "Warning" << ": Overriding option 'link' "
9376
<< "with new value '" << value << "'.\n";
9379
if (ParseLinkOption(value) < 0)
9382
*logofs << "Loop: PANIC! Can't identify remote 'link' "
9383
<< "option in string '" << value << "'.\n"
9387
cerr << "Error" << ": Can't identify remote 'link' "
9388
<< "option in string '" << value << "'.\n";
9396
else if (strcasecmp(name, "pack") == 0)
9398
if (control -> ProxyMode == proxy_client)
9400
PrintOptionIgnored("remote", name, value);
9404
if (*packMethodName != '\0' && strcasecmp(packMethodName, value) != 0)
9407
*logofs << "Loop: WARNING! Overriding option 'pack' "
9408
<< "with remote value '" << value << "'.\n"
9412
cerr << "Warning" << ": Overriding option 'pack' "
9413
<< "with remote value '" << value << "'.\n";
9416
if (ParsePackOption(value) < 0)
9419
*logofs << "Loop: PANIC! Invalid pack option '"
9420
<< value << "' requested by remote.\n"
9424
cerr << "Error" << ": Invalid pack option '"
9425
<< value << "' requested by remote.\n";
9433
else if (strcasecmp(name, "cache") == 0)
9435
if (control -> ProxyMode == proxy_client)
9437
PrintOptionIgnored("remote", name, value);
9442
// Cache size is sent as a hint of how much memory
9443
// the remote proxy is going to consume. A very low
9444
// powered thin client could choose to refuse the
9448
if (ParseCacheOption(value) < 0)
9451
*logofs << "Loop: PANIC! Can't identify remote 'cache' "
9452
<< "option in string '" << value << "'.\n"
9456
cerr << "Error" << ": Can't identify remote 'cache' "
9457
<< "option in string '" << value << "'.\n";
9465
else if (strcasecmp(name, "images") == 0)
9467
if (control -> ProxyMode == proxy_client)
9469
PrintOptionIgnored("remote", name, value);
9474
// Images cache size is sent as a hint.
9475
// There is no obbligation for the local
9476
// proxy to use the persistent cache.
9479
if (ParseImagesOption(value) < 0)
9482
*logofs << "Loop: PANIC! Can't identify remote 'images' "
9483
<< "option in string '" << value << "'.\n"
9487
cerr << "Error" << ": Can't identify remote 'images' "
9488
<< "option in string '" << value << "'.\n";
9496
else if (strcasecmp(name, "limit") == 0)
9498
if (control -> ProxyMode == proxy_client)
9500
PrintOptionIgnored("remote", name, value);
9504
if (*bitrateLimitName != '\0' &&
9505
strcasecmp(bitrateLimitName, value) != 0)
9508
*logofs << "Loop: WARNING! Overriding option 'limit' "
9509
<< "with new value '" << value << "'.\n"
9513
cerr << "Warning" << ": Overriding option 'limit' "
9514
<< "with new value '" << value << "'.\n";
9517
if (ParseBitrateOption(value) < 0)
9520
*logofs << "Loop: PANIC! Can't identify 'limit' "
9521
<< "option in string '" << value << "'.\n"
9525
cerr << "Error" << ": Can't identify 'limit' "
9526
<< "option in string '" << value << "'.\n";
9534
else if (strcasecmp(name, "render") == 0)
9536
if (control -> ProxyMode == proxy_client)
9538
PrintOptionIgnored("remote", name, value);
9542
useRender = ValidateArg("remote", name, value);
9547
else if (strcasecmp(name, "taint") == 0)
9549
if (control -> ProxyMode == proxy_client)
9551
PrintOptionIgnored("remote", name, value);
9555
useTaint = ValidateArg("remote", name, value);
9560
else if (strcasecmp(name, "type") == 0)
9562
if (control -> ProxyMode == proxy_client)
9564
PrintOptionIgnored("remote", name, value);
9568
if (strcasecmp(value, "default") == 0)
9570
*sessionType = '\0';
9574
strncpy(sessionType, value, DEFAULT_STRING_LENGTH - 1);
9580
else if (strcasecmp(name, "strict") == 0)
9582
if (control -> ProxyMode == proxy_client)
9584
PrintOptionIgnored("remote", name, value);
9588
useStrict = ValidateArg("remote", name, value);
9593
else if (strcasecmp(name, "shseg") == 0)
9595
if (control -> ProxyMode == proxy_client)
9597
PrintOptionIgnored("remote", name, value);
9599
else if (ParseShmemOption(value) < 0)
9602
*logofs << "Loop: PANIC! Can't identify size of shared memory "
9603
<< "segment in string '" << value << "'.\n"
9607
cerr << "Error" << ": Can't identify size of shared memory "
9608
<< "segment in string '" << value << "'.\n";
9615
else if (strcasecmp(name, "delta") == 0)
9617
if (control -> ProxyMode == proxy_client)
9619
PrintOptionIgnored("remote", name, value);
9623
control -> RemoteDeltaCompression = ValidateArg("remote", name, value);
9626
// Follow for delta compression the
9627
// same settings as the client proxy.
9630
control -> LocalDeltaCompression = control -> RemoteDeltaCompression;
9635
else if (strcasecmp(name, "stream") == 0)
9638
// If remote side didn't choose its own
9639
// stream compression level then assume
9643
if (strcasecmp(value, "default") == 0)
9646
// This applies only at client side.
9649
control -> RemoteStreamCompression =
9650
control -> LocalStreamCompression;
9652
control -> RemoteStreamCompressionLevel =
9653
control -> LocalStreamCompressionLevel;
9657
control -> RemoteStreamCompressionLevel = ValidateArg("remote", name, value);
9659
if (control -> RemoteStreamCompressionLevel > 0)
9661
control -> RemoteStreamCompression = 1;
9665
control -> RemoteStreamCompression = 0;
9668
if (control -> LocalStreamCompressionLevel < 0)
9670
control -> LocalStreamCompressionLevel = ValidateArg("remote", name, value);
9672
if (control -> LocalStreamCompressionLevel > 0)
9674
control -> LocalStreamCompression = 1;
9678
control -> LocalStreamCompression = 0;
9685
else if (strcasecmp(name, "data") == 0)
9688
// Apply the same to data compression level.
9691
if (strcasecmp(value, "default") == 0)
9693
control -> RemoteDataCompression =
9694
control -> LocalDataCompression;
9696
control -> RemoteDataCompressionLevel =
9697
control -> LocalDataCompressionLevel;
9701
control -> RemoteDataCompressionLevel = ValidateArg("remote", name, value);
9703
if (control -> RemoteDataCompressionLevel > 0)
9705
control -> RemoteDataCompression = 1;
9709
control -> RemoteDataCompression = 0;
9712
if (control -> LocalDataCompressionLevel < 0)
9714
control -> LocalDataCompressionLevel = ValidateArg("remote", name, value);
9716
if (control -> LocalDataCompressionLevel > 0)
9718
control -> LocalDataCompression = 1;
9722
control -> LocalDataCompression = 0;
9729
else if (strcasecmp(name, "flush") == 0)
9732
// This option has no effect in recent
9737
*logofs << "Loop: Ignoring obsolete remote option '"
9738
<< name << "' with value '" << value
9739
<< "'.\n" << logofs_flush;
9745
*logofs << "Loop: WARNING! Ignoring unknown remote option '"
9746
<< name << "' with value '" << value << "'.\n"
9750
cerr << "Warning" << ": Ignoring unknown remote option '"
9751
<< name << "' with value '" << value << "'.\n";
9754
name = strtok(NULL, "=");
9756
} // End of while (name) ...
9759
// If we are client side, we need remote 'stream'
9760
// and 'data' options. If we are server, we need
9761
// all the above plus 'link' and some others.
9764
char missing[DEFAULT_STRING_LENGTH];
9768
if (control -> ProxyMode == proxy_client)
9772
strcpy(missing, "stream");
9774
else if (hasData == 0)
9776
strcpy(missing, "data");
9782
// Don't complain if the optional 'flush',
9783
// 'render' and 'taint' options are not
9789
strcpy(missing, "link");
9791
else if (hasCache == 0)
9793
strcpy(missing, "cache");
9795
else if (hasPack == 0)
9797
strcpy(missing, "pack");
9799
else if (hasDelta == 0)
9801
strcpy(missing, "delta");
9803
else if (hasStream == 0)
9805
strcpy(missing, "stream");
9807
else if (hasData == 0)
9809
strcpy(missing, "data");
9811
else if (hasType == 0)
9813
strcpy(missing, "type");
9815
else if (hasImages == 0)
9817
strcpy(missing, "images");
9821
if (WE_PROVIDE_CREDENTIALS == 0)
9824
// Can be that user doesn't have requested to
9825
// check the authorization cookie provided by
9826
// the connecting peer.
9829
if (hasCookie == 0 && *authCookie != '\0')
9831
strcpy(missing, "cookie");
9835
if (*missing != '\0')
9838
*logofs << "Loop: PANIC! The remote peer didn't specify the option '"
9839
<< missing << "'.\n" << logofs_flush;
9842
cerr << "Error" << ": The remote peer didn't specify the option '"
9843
<< missing << "'.\n";
9852
// Parse the cookie provided by the NX proxy
9853
// connection forwarder.
9856
int ParseForwarderOptions(char *opts)
9859
*logofs << "Loop: Going to parse the forwarder options "
9860
<< "string '" << opts << "'.\n"
9870
// Get rid of the terminating space.
9873
if (*(opts + strlen(opts) - 1) == ' ')
9875
*(opts + strlen(opts) - 1) = '\0';
9878
name = strtok(opts, "=");
9882
value = strtok(NULL, ",");
9884
if (CheckArg("forwarder", name, value) < 0)
9889
if (strcasecmp(name, "cookie") == 0)
9891
if (strncasecmp(authCookie, value, strlen(authCookie)) != 0)
9894
*logofs << "Loop: PANIC! The NX forwarder cookie '" << value
9895
<< "' doesn't match '" << authCookie << "'.\n"
9899
cerr << "Error" << ": The NX forwarder cookie '" << value
9900
<< "' doesn't match '" << authCookie << "'.\n";
9910
*logofs << "Loop: WARNING! Ignoring unknown forwarder option '"
9911
<< name << "' with value '" << value << "'.\n"
9915
cerr << "Warning" << ": Ignoring unknown forwarder option '"
9916
<< name << "' with value '" << value << "'.\n";
9919
name = strtok(NULL, "=");
9921
} // End of while (name) ...
9926
*logofs << "Loop: PANIC! The NX forwarder didn't provide "
9927
<< "the authentication cookie.\n" << logofs_flush;
9930
cerr << "Error" << ": The NX forwarder didn't provide "
9931
<< "the authentication cookie.\n";
9945
if (getrlimit(RLIMIT_CORE, &rlim))
9948
*logofs << "Cannot read RLIMIT_CORE. Error is '"
9949
<< ESTR() << "'.\n" << logofs_flush;
9955
if (rlim.rlim_cur < rlim.rlim_max)
9957
rlim.rlim_cur = rlim.rlim_max;
9959
if (setrlimit(RLIMIT_CORE, &rlim))
9962
*logofs << "Loop: Cannot read RLIMIT_CORE. Error is '"
9963
<< ESTR() << "'.\n" << logofs_flush;
9971
*logofs << "Loop: Set RLIMIT_CORE to "<< rlim.rlim_max
9972
<< ".\n" << logofs_flush;
9975
#endif // #ifdef COREDUMPS
9980
char *GetLastCache(char *listBuffer, const char *searchPath)
9982
if (listBuffer == NULL || searchPath == NULL ||
9983
strncmp(listBuffer, "cachelist=", strlen("cachelist=")) != 0)
9986
*logofs << "Loop: Invalid parameters '" << listBuffer << "' and '"
9987
<< (searchPath != NULL ? searchPath : "")
9988
<< "'. Can't select any cache.\n" << logofs_flush;
9994
char *selectedName = new char[MD5_LENGTH * 2 + 3];
9996
*selectedName = '\0';
10001
if (control -> ProxyMode == proxy_client)
10003
localPrefix = "C-";
10004
remotePrefix = "S-";
10008
localPrefix = "S-";
10009
remotePrefix = "C-";
10013
// Get rid of prefix.
10016
listBuffer += strlen("cachelist=");
10020
fileName = strtok(listBuffer, ",");
10023
// It is "/path/to/file" + "/" + "C-" + 32 + "\0".
10026
char fullPath[strlen(searchPath) + MD5_LENGTH * 2 + 4];
10028
time_t selectedTime = 0;
10030
struct stat fileStat;
10034
if (strncmp(fileName, "none", strlen("none")) == 0)
10037
*logofs << "Loop: No cache files seem to be available.\n"
10041
delete [] selectedName;
10045
else if (strlen(fileName) != MD5_LENGTH * 2 + 2 ||
10046
strncmp(fileName, remotePrefix, 2) != 0)
10049
*logofs << "Loop: PANIC! Bad cache file name '"
10050
<< fileName << "'.\n" << logofs_flush;
10053
cerr << "Error" << ": Bad cache file name '"
10054
<< fileName << "'.\n";
10056
delete [] selectedName;
10062
*logofs << "Loop: Parsing remote cache name '"
10063
<< fileName << "'.\n" << logofs_flush;
10067
// Prefix, received as "S-", becomes
10068
// "C-" and viceversa.
10071
*fileName = *localPrefix;
10073
strcpy(fullPath, searchPath);
10074
strcat(fullPath, "/");
10075
strcat(fullPath, fileName);
10077
if (stat(fullPath, &fileStat) == 0)
10080
*logofs << "Loop: Found a matching cache '"
10081
<< fullPath << "'.\n" << logofs_flush;
10084
if (fileStat.st_mtime >= selectedTime)
10086
strcpy(selectedName, fileName);
10088
selectedTime = fileStat.st_mtime;
10094
*logofs << "Loop: Can't get stats of file '"
10095
<< fullPath << "'.\n" << logofs_flush;
10099
fileName = strtok(NULL, ",");
10102
if (*selectedName != '\0')
10104
return selectedName;
10108
delete [] selectedName;
10114
char *GetTempPath()
10116
if (*tempDir == '\0')
10119
// Check the NX_TEMP environment, first,
10120
// then the TEMP variable.
10123
const char *tempEnv = getenv("NX_TEMP");
10125
if (tempEnv == NULL || *tempEnv == '\0')
10128
*logofs << "Loop: WARNING! No environment for NX_TEMP.\n"
10132
tempEnv = getenv("TEMP");
10134
if (tempEnv == NULL || *tempEnv == '\0')
10137
*logofs << "Loop: WARNING! No environment for TEMP.\n"
10145
if (strlen(tempEnv) > DEFAULT_STRING_LENGTH - 1)
10148
*logofs << "Loop: PANIC! Invalid value for the NX "
10149
<< "temporary directory '" << tempEnv
10150
<< "'.\n" << logofs_flush;
10153
cerr << "Error" << ": Invalid value for the NX "
10154
<< "temporary directory '" << tempEnv
10160
strcpy(tempDir, tempEnv);
10163
*logofs << "Loop: Assuming temporary NX directory '"
10164
<< tempDir << "'.\n" << logofs_flush;
10168
char *tempPath = new char[strlen(tempDir) + 1];
10170
if (tempPath == NULL)
10173
*logofs << "Loop: PANIC! Can't allocate memory "
10174
<< "for the temp path.\n" << logofs_flush;
10177
cerr << "Error" << ": Can't allocate memory "
10178
<< "for the temp path.\n";
10183
strcpy(tempPath, tempDir);
10188
char *GetClientPath()
10190
if (*clientDir == '\0')
10193
// Check the NX_CLIENT environment.
10196
const char *clientEnv = getenv("NX_CLIENT");
10198
if (clientEnv == NULL || *clientEnv == '\0')
10201
*logofs << "Loop: WARNING! No environment for NX_CLIENT.\n"
10206
// Try to guess the location of the client.
10209
clientEnv = "/usr/NX/bin/nxclient";
10213
clientEnv = "/Applications/NX Client for OSX.app/Contents/MacOS/nxclient";
10217
#ifdef __CYGWIN32__
10219
clientEnv = "C:\\Program Files\\NX Client for Windows\\nxclient";
10224
if (strlen(clientEnv) > DEFAULT_STRING_LENGTH - 1)
10227
*logofs << "Loop: PANIC! Invalid value for the NX "
10228
<< "client directory '" << clientEnv
10229
<< "'.\n" << logofs_flush;
10232
cerr << "Error" << ": Invalid value for the NX "
10233
<< "client directory '" << clientEnv
10239
strcpy(clientDir, clientEnv);
10242
*logofs << "Loop: Assuming NX client location '"
10243
<< clientDir << "'.\n" << logofs_flush;
10247
char *clientPath = new char[strlen(clientDir) + 1];
10249
if (clientPath == NULL)
10252
*logofs << "Loop: PANIC! Can't allocate memory "
10253
<< "for the client path.\n" << logofs_flush;
10256
cerr << "Error" << ": Can't allocate memory "
10257
<< "for the client path.\n";
10262
strcpy(clientPath, clientDir);
10267
char *GetSystemPath()
10269
if (*systemDir == '\0')
10272
// Check the NX_SYSTEM environment.
10275
const char *systemEnv = getenv("NX_SYSTEM");
10277
if (systemEnv == NULL || *systemEnv == '\0')
10280
*logofs << "Loop: WARNING! No environment for NX_SYSTEM.\n"
10284
systemEnv = "/usr/NX";
10287
if (strlen(systemEnv) > DEFAULT_STRING_LENGTH - 1)
10290
*logofs << "Loop: PANIC! Invalid value for the NX "
10291
<< "system directory '" << systemEnv
10292
<< "'.\n" << logofs_flush;
10295
cerr << "Error" << ": Invalid value for the NX "
10296
<< "system directory '" << systemEnv
10302
strcpy(systemDir, systemEnv);
10305
*logofs << "Loop: Assuming system NX directory '"
10306
<< systemDir << "'.\n" << logofs_flush;
10310
char *systemPath = new char[strlen(systemDir) + 1];
10312
if (systemPath == NULL)
10315
*logofs << "Loop: PANIC! Can't allocate memory "
10316
<< "for the system path.\n" << logofs_flush;
10319
cerr << "Error" << ": Can't allocate memory "
10320
<< "for the system path.\n";
10325
strcpy(systemPath, systemDir);
10330
char *GetHomePath()
10332
if (*homeDir == '\0')
10335
// Check the NX_HOME environment.
10338
const char *homeEnv = getenv("NX_HOME");
10340
if (homeEnv == NULL || *homeEnv == '\0')
10343
*logofs << "Loop: WARNING! No environment for NX_HOME.\n"
10347
homeEnv = getenv("HOME");
10349
if (homeEnv == NULL || *homeEnv == '\0')
10352
*logofs << "Loop: PANIC! No environment for HOME.\n"
10356
cerr << "Error" << ": No environment for HOME.\n";
10362
if (strlen(homeEnv) > DEFAULT_STRING_LENGTH - 1)
10365
*logofs << "Loop: PANIC! Invalid value for the NX "
10366
<< "home directory '" << homeEnv
10367
<< "'.\n" << logofs_flush;
10370
cerr << "Error" << ": Invalid value for the NX "
10371
<< "home directory '" << homeEnv
10377
strcpy(homeDir, homeEnv);
10380
*logofs << "Loop: Assuming NX user's home directory '"
10381
<< homeDir << "'.\n" << logofs_flush;
10385
char *homePath = new char[strlen(homeDir) + 1];
10387
if (homePath == NULL)
10390
*logofs << "Loop: PANIC! Can't allocate memory "
10391
<< "for the home path.\n" << logofs_flush;
10394
cerr << "Error" << ": Can't allocate memory "
10395
<< "for the home path.\n";
10400
strcpy(homePath, homeDir);
10405
char *GetRootPath()
10407
if (*rootDir == '\0')
10410
// Check the NX_ROOT environment.
10413
const char *rootEnv = getenv("NX_ROOT");
10415
if (rootEnv == NULL || *rootEnv == '\0')
10418
*logofs << "Loop: WARNING! No environment for NX_ROOT.\n"
10423
// We will determine the root NX directory
10424
// based on the NX_HOME or HOME directory
10428
const char *homeEnv = GetHomePath();
10430
if (strlen(homeEnv) > DEFAULT_STRING_LENGTH -
10431
strlen("/.nx") - 1)
10434
*logofs << "Loop: PANIC! Invalid value for the NX "
10435
<< "home directory '" << homeEnv
10436
<< "'.\n" << logofs_flush;
10439
cerr << "Error" << ": Invalid value for the NX "
10440
<< "home directory '" << homeEnv
10447
*logofs << "Loop: Assuming NX root directory in "
10448
<< "the user's home '" << homeEnv
10449
<< "'.\n" << logofs_flush;
10452
strcpy(rootDir, homeEnv);
10453
strcat(rootDir, "/.nx");
10458
// Create the NX root directory.
10461
struct stat dirStat;
10463
if ((stat(rootDir, &dirStat) == -1) && (EGET() == ENOENT))
10465
if (mkdir(rootDir, 0700) < 0 && (EGET() != EEXIST))
10468
*logofs << "Loop: PANIC! Can't create directory '"
10469
<< rootDir << ". Error is " << EGET() << " '"
10470
<< ESTR() << "'.\n" << logofs_flush;
10473
cerr << "Error" << ": Can't create directory '"
10474
<< rootDir << ". Error is " << EGET() << " '"
10475
<< ESTR() << "'.\n";
10483
if (strlen(rootEnv) > DEFAULT_STRING_LENGTH - 1)
10486
*logofs << "Loop: PANIC! Invalid value for the NX "
10487
<< "root directory '" << rootEnv
10488
<< "'.\n" << logofs_flush;
10491
cerr << "Error" << ": Invalid value for the NX "
10492
<< "root directory '" << rootEnv
10498
strcpy(rootDir, rootEnv);
10502
*logofs << "Loop: Assuming NX root directory '"
10503
<< rootDir << "'.\n" << logofs_flush;
10507
char *rootPath = new char[strlen(rootDir) + 1];
10509
if (rootPath == NULL)
10512
*logofs << "Loop: PANIC! Can't allocate memory "
10513
<< "for the root path.\n" << logofs_flush;
10516
cerr << "Error" << ": Can't allocate memory "
10517
<< "for the root path.\n";
10522
strcpy(rootPath, rootDir);
10527
char *GetCachePath()
10529
char *rootPath = GetRootPath();
10533
if (*sessionType != '\0')
10535
cachePath = new char[strlen(rootPath) + strlen("/cache-") +
10536
strlen(sessionType) + 1];
10540
cachePath = new char[strlen(rootPath) + strlen("/cache") + 1];
10543
strcpy(cachePath, rootPath);
10545
if (*sessionType != '\0')
10547
strcat(cachePath, "/cache-");
10549
strcat(cachePath, sessionType);
10553
strcat(cachePath, "/cache");
10557
// Create the cache directory if needed.
10560
struct stat dirStat;
10562
if ((stat(cachePath, &dirStat) == -1) && (EGET() == ENOENT))
10564
if (mkdir(cachePath, 0700) < 0 && (EGET() != EEXIST))
10567
*logofs << "Loop: PANIC! Can't create directory '" << cachePath
10568
<< ". Error is " << EGET() << " '" << ESTR() << "'.\n"
10572
cerr << "Error" << ": Can't create directory '" << cachePath
10573
<< ". Error is " << EGET() << " '" << ESTR() << "'.\n";
10575
delete [] rootPath;
10576
delete [] cachePath;
10582
delete [] rootPath;
10587
char *GetImagesPath()
10589
char *rootPath = GetRootPath();
10591
char *imagesPath = new char[strlen(rootPath) + strlen("/images") + 1];
10593
strcpy(imagesPath, rootPath);
10595
strcat(imagesPath, "/images");
10598
// Create the cache directory if needed.
10601
struct stat dirStat;
10603
if ((stat(imagesPath, &dirStat) == -1) && (EGET() == ENOENT))
10605
if (mkdir(imagesPath, 0700) < 0 && (EGET() != EEXIST))
10608
*logofs << "Loop: PANIC! Can't create directory '" << imagesPath
10609
<< ". Error is " << EGET() << " '" << ESTR() << "'.\n"
10613
cerr << "Error" << ": Can't create directory '" << imagesPath
10614
<< ". Error is " << EGET() << " '" << ESTR() << "'.\n";
10616
delete [] rootPath;
10617
delete [] imagesPath;
10624
// Create 16 directories in the path to
10625
// hold the images whose name begins with
10626
// the corresponding hexadecimal digit.
10629
char *digitPath = new char[strlen(imagesPath) + 5];
10631
strcpy(digitPath, imagesPath);
10634
// Image paths have format "[path][/I-c][\0]",
10635
// where c is the first digit of the checksum.
10638
for (char digit = 0; digit < 16; digit++)
10640
sprintf(digitPath + strlen(imagesPath), "/I-%01X", digit);
10642
if ((stat(digitPath, &dirStat) == -1) && (EGET() == ENOENT))
10644
if (mkdir(digitPath, 0700) < 0 && (EGET() != EEXIST))
10647
*logofs << "Loop: PANIC! Can't create directory '" << digitPath
10648
<< ". Error is " << EGET() << " '" << ESTR() << "'.\n"
10652
cerr << "Error" << ": Can't create directory '" << digitPath
10653
<< ". Error is " << EGET() << " '" << ESTR() << "'.\n";
10655
delete [] rootPath;
10656
delete [] imagesPath;
10657
delete [] digitPath;
10664
delete [] rootPath;
10665
delete [] digitPath;
10670
char *GetSessionPath()
10672
if (*sessionDir == '\0')
10674
char *rootPath = GetRootPath();
10676
strcpy(sessionDir, rootPath);
10678
if (control -> ProxyMode == proxy_client)
10680
strcat(sessionDir, "/C-");
10684
strcat(sessionDir, "/S-");
10687
if (*sessionId == '\0')
10689
char port[DEFAULT_STRING_LENGTH];
10691
sprintf(port, "%d", proxyPort);
10693
strcpy(sessionId, port);
10696
strcat(sessionDir, sessionId);
10698
struct stat dirStat;
10700
if ((stat(sessionDir, &dirStat) == -1) && (EGET() == ENOENT))
10702
if (mkdir(sessionDir, 0700) < 0 && (EGET() != EEXIST))
10705
*logofs << "Loop: PANIC! Can't create directory '" << sessionDir
10706
<< ". Error is " << EGET() << " '" << ESTR() << "'.\n"
10710
cerr << "Error" << ": Can't create directory '" << sessionDir
10711
<< ". Error is " << EGET() << " '" << ESTR() << "'.\n";
10713
delete [] rootPath;
10720
*logofs << "Loop: Root of NX session is '" << sessionDir
10721
<< "'.\n" << logofs_flush;
10724
delete [] rootPath;
10727
char *sessionPath = new char[strlen(sessionDir) + 1];
10729
strcpy(sessionPath, sessionDir);
10731
return sessionPath;
10735
// Identify requested link characteristics
10736
// and set control parameters accordingly.
10739
int ParseLinkOption(const char *opt)
10742
// Normalize the user input.
10745
if (strcasecmp(opt, "modem") == 0 ||
10746
strcasecmp(opt, "33k") == 0 ||
10747
strcasecmp(opt, "56k") == 0)
10749
strcpy(linkSpeedName, "MODEM");
10751
else if (strcasecmp(opt, "isdn") == 0 ||
10752
strcasecmp(opt, "64k") == 0 ||
10753
strcasecmp(opt, "128k") == 0)
10755
strcpy(linkSpeedName, "ISDN");
10757
else if (strcasecmp(opt, "adsl") == 0 ||
10758
strcasecmp(opt, "256k") == 0 ||
10759
strcasecmp(opt, "640k") == 0)
10761
strcpy(linkSpeedName, "ADSL");
10763
else if (strcasecmp(opt, "wan") == 0 ||
10764
strcasecmp(opt, "1m") == 0 ||
10765
strcasecmp(opt, "2m") == 0 ||
10766
strcasecmp(opt, "34m") == 0)
10768
strcpy(linkSpeedName, "WAN");
10770
else if (strcasecmp(opt, "lan") == 0 ||
10771
strcasecmp(opt, "10m") == 0 ||
10772
strcasecmp(opt, "100m") == 0 ||
10773
strcasecmp(opt, "local") == 0)
10775
strcpy(linkSpeedName, "LAN");
10778
if (strcasecmp(linkSpeedName, "modem") != 0 &&
10779
strcasecmp(linkSpeedName, "isdn") != 0 &&
10780
strcasecmp(linkSpeedName, "adsl") != 0 &&
10781
strcasecmp(linkSpeedName, "wan") != 0 &&
10782
strcasecmp(linkSpeedName, "lan") != 0)
10790
int ParsePackOption(const char *opt)
10793
*logofs << "Loop: Pack method is " << packMethod
10794
<< " quality is " << packQuality << ".\n"
10799
*logofs << "Loop: Parsing pack method '" << opt
10800
<< "'.\n" << logofs_flush;
10803
if (strcasecmp(opt, "0") == 0 ||
10804
strcasecmp(opt, "none") == 0 ||
10805
strcasecmp(opt, "nopack") == 0 ||
10806
strcasecmp(opt, "no-pack") == 0)
10808
packMethod = PACK_NONE;
10810
else if (strcasecmp(opt, "8") == 0)
10812
packMethod = PACK_MASKED_8_COLORS;
10814
else if (strcasecmp(opt, "64") == 0)
10816
packMethod = PACK_MASKED_64_COLORS;
10818
else if (strcasecmp(opt, "256") == 0)
10820
packMethod = PACK_MASKED_256_COLORS;
10822
else if (strcasecmp(opt, "512") == 0)
10824
packMethod = PACK_MASKED_512_COLORS;
10826
else if (strcasecmp(opt, "4k") == 0)
10828
packMethod = PACK_MASKED_4K_COLORS;
10830
else if (strcasecmp(opt, "32k") == 0)
10832
packMethod = PACK_MASKED_32K_COLORS;
10834
else if (strcasecmp(opt, "64k") == 0)
10836
packMethod = PACK_MASKED_64K_COLORS;
10838
else if (strcasecmp(opt, "256k") == 0)
10840
packMethod = PACK_MASKED_256K_COLORS;
10842
else if (strcasecmp(opt, "2m") == 0)
10844
packMethod = PACK_MASKED_2M_COLORS;
10846
else if (strcasecmp(opt, "16m") == 0)
10848
packMethod = PACK_MASKED_16M_COLORS;
10850
else if (strncasecmp(opt, "8-jpeg", strlen("8-jpeg")) == 0)
10852
packMethod = PACK_JPEG_8_COLORS;
10854
else if (strncasecmp(opt, "64-jpeg", strlen("64-jpeg")) == 0)
10856
packMethod = PACK_JPEG_64_COLORS;
10858
else if (strncasecmp(opt, "256-jpeg", strlen("256-jpeg")) == 0)
10860
packMethod = PACK_JPEG_256_COLORS;
10862
else if (strncasecmp(opt, "512-jpeg", strlen("512-jpeg")) == 0)
10864
packMethod = PACK_JPEG_512_COLORS;
10866
else if (strncasecmp(opt, "4k-jpeg", strlen("4k-jpeg")) == 0)
10868
packMethod = PACK_JPEG_4K_COLORS;
10870
else if (strncasecmp(opt, "32k-jpeg", strlen("32k-jpeg")) == 0)
10872
packMethod = PACK_JPEG_32K_COLORS;
10874
else if (strncasecmp(opt, "64k-jpeg", strlen("64k-jpeg")) == 0)
10876
packMethod = PACK_JPEG_64K_COLORS;
10878
else if (strncasecmp(opt, "256k-jpeg", strlen("256k-jpeg")) == 0)
10880
packMethod = PACK_JPEG_256K_COLORS;
10882
else if (strncasecmp(opt, "2m-jpeg", strlen("2m-jpeg")) == 0)
10884
packMethod = PACK_JPEG_2M_COLORS;
10886
else if (strncasecmp(opt, "16m-jpeg", strlen("16m-jpeg")) == 0)
10888
packMethod = PACK_JPEG_16M_COLORS;
10890
else if (strncasecmp(opt, "8-png", strlen("8-png")) == 0)
10892
packMethod = PACK_PNG_8_COLORS;
10894
else if (strncasecmp(opt, "64-png", strlen("64-png")) == 0)
10896
packMethod = PACK_PNG_64_COLORS;
10898
else if (strncasecmp(opt, "256-png", strlen("256-png")) == 0)
10900
packMethod = PACK_PNG_256_COLORS;
10902
else if (strncasecmp(opt, "512-png", strlen("512-png")) == 0)
10904
packMethod = PACK_PNG_512_COLORS;
10906
else if (strncasecmp(opt, "4k-png", strlen("4k-png")) == 0)
10908
packMethod = PACK_PNG_4K_COLORS;
10910
else if (strncasecmp(opt, "32k-png", strlen("32k-png")) == 0)
10912
packMethod = PACK_PNG_32K_COLORS;
10914
else if (strncasecmp(opt, "64k-png", strlen("64k-png")) == 0)
10916
packMethod = PACK_PNG_64K_COLORS;
10918
else if (strncasecmp(opt, "256k-png", strlen("256k-png")) == 0)
10920
packMethod = PACK_PNG_256K_COLORS;
10922
else if (strncasecmp(opt, "2m-png", strlen("2m-png")) == 0)
10924
packMethod = PACK_PNG_2M_COLORS;
10926
else if (strncasecmp(opt, "16m-png", strlen("16m-png")) == 0)
10928
packMethod = PACK_PNG_16M_COLORS;
10930
else if (strncasecmp(opt, "16m-rgb", strlen("16m-rgb")) == 0 ||
10931
strncasecmp(opt, "rgb", strlen("rgb")) == 0)
10933
packMethod = PACK_RGB_16M_COLORS;
10935
else if (strncasecmp(opt, "16m-rle", strlen("16m-rle")) == 0 ||
10936
strncasecmp(opt, "rle", strlen("rle")) == 0)
10938
packMethod = PACK_RLE_16M_COLORS;
10940
else if (strncasecmp(opt, "16m-bitmap", strlen("16m-bitmap")) == 0 ||
10941
strncasecmp(opt, "bitmap", strlen("bitmap")) == 0)
10943
packMethod = PACK_BITMAP_16M_COLORS;
10945
else if (strncasecmp(opt, "lossy", strlen("lossy")) == 0)
10947
packMethod = PACK_LOSSY;
10949
else if (strncasecmp(opt, "lossless", strlen("lossless")) == 0)
10951
packMethod = PACK_LOSSLESS;
10953
else if (strncasecmp(opt, "adaptive", strlen("adaptive")) == 0)
10955
packMethod = PACK_ADAPTIVE;
10962
if (packMethod == PACK_NONE)
10964
strcpy(packMethodName, "none");
10968
strcpy(packMethodName, opt);
10971
if (packMethod == PACK_RGB_16M_COLORS ||
10972
packMethod == PACK_RLE_16M_COLORS ||
10973
packMethod == PACK_BITMAP_16M_COLORS ||
10974
(packMethod >= PACK_JPEG_8_COLORS &&
10975
packMethod <= PACK_JPEG_16M_COLORS) ||
10976
(packMethod >= PACK_PNG_8_COLORS &&
10977
packMethod <= PACK_PNG_16M_COLORS) ||
10978
packMethod == PACK_LOSSY ||
10979
packMethod == PACK_LOSSLESS ||
10980
packMethod == PACK_ADAPTIVE)
10982
char *dash = rindex(opt, '-');
10984
if (dash != NULL && strlen(dash) == 2 &&
10985
*(dash + 1) >= '0' && *(dash + 1) <= '9')
10987
packQuality = atoi(dash + 1);
10990
*logofs << "Loop: Using pack quality '"
10991
<< packQuality << "'.\n" << logofs_flush;
11003
int ParsePackMethod(const int method, const int quality)
11009
strcpy(packMethodName, "none");
11013
case PACK_MASKED_8_COLORS:
11015
strcpy(packMethodName, "8");
11019
case PACK_MASKED_64_COLORS:
11021
strcpy(packMethodName, "64");
11025
case PACK_MASKED_256_COLORS:
11027
strcpy(packMethodName, "256");
11031
case PACK_MASKED_512_COLORS:
11033
strcpy(packMethodName, "512");
11037
case PACK_MASKED_4K_COLORS:
11039
strcpy(packMethodName, "4k");
11043
case PACK_MASKED_32K_COLORS:
11045
strcpy(packMethodName, "32k");
11049
case PACK_MASKED_64K_COLORS:
11051
strcpy(packMethodName, "64k");
11055
case PACK_MASKED_256K_COLORS:
11057
strcpy(packMethodName, "256k");
11061
case PACK_MASKED_2M_COLORS:
11063
strcpy(packMethodName, "2m");
11067
case PACK_MASKED_16M_COLORS:
11069
strcpy(packMethodName, "16m");
11073
case PACK_JPEG_8_COLORS:
11075
strcpy(packMethodName, "8-jpeg");
11079
case PACK_JPEG_64_COLORS:
11081
strcpy(packMethodName, "64-jpeg");
11085
case PACK_JPEG_256_COLORS:
11087
strcpy(packMethodName, "256-jpeg");
11091
case PACK_JPEG_512_COLORS:
11093
strcpy(packMethodName, "512-jpeg");
11097
case PACK_JPEG_4K_COLORS:
11099
strcpy(packMethodName, "4k-jpeg");
11103
case PACK_JPEG_32K_COLORS:
11105
strcpy(packMethodName, "32k-jpeg");
11109
case PACK_JPEG_64K_COLORS:
11111
strcpy(packMethodName, "64k-jpeg");
11115
case PACK_JPEG_256K_COLORS:
11117
strcpy(packMethodName, "256k-jpeg");
11121
case PACK_JPEG_2M_COLORS:
11123
strcpy(packMethodName, "2m-jpeg");
11127
case PACK_JPEG_16M_COLORS:
11129
strcpy(packMethodName, "16m-jpeg");
11133
case PACK_PNG_8_COLORS:
11135
strcpy(packMethodName, "8-png");
11139
case PACK_PNG_64_COLORS:
11141
strcpy(packMethodName, "64-png");
11145
case PACK_PNG_256_COLORS:
11147
strcpy(packMethodName, "256-png");
11151
case PACK_PNG_512_COLORS:
11153
strcpy(packMethodName, "512-png");
11157
case PACK_PNG_4K_COLORS:
11159
strcpy(packMethodName, "4k-png");
11163
case PACK_PNG_32K_COLORS:
11165
strcpy(packMethodName, "32k-png");
11169
case PACK_PNG_64K_COLORS:
11171
strcpy(packMethodName, "64k-png");
11175
case PACK_PNG_256K_COLORS:
11177
strcpy(packMethodName, "256k-png");
11181
case PACK_PNG_2M_COLORS:
11183
strcpy(packMethodName, "2m-png");
11187
case PACK_PNG_16M_COLORS:
11189
strcpy(packMethodName, "16m-png");
11193
case PACK_RGB_16M_COLORS:
11195
strcpy(packMethodName, "16m-rgb");
11199
case PACK_RLE_16M_COLORS:
11201
strcpy(packMethodName, "16m-rle");
11205
case PACK_BITMAP_16M_COLORS:
11207
strcpy(packMethodName, "16m-bitmap");
11213
strcpy(packMethodName, "lossy");
11217
case PACK_LOSSLESS:
11219
strcpy(packMethodName, "lossless");
11223
case PACK_ADAPTIVE:
11225
strcpy(packMethodName, "adaptive");
11235
if (quality < 0 || quality > 9)
11240
if (packMethod == PACK_RGB_16M_COLORS ||
11241
packMethod == PACK_RLE_16M_COLORS ||
11242
packMethod == PACK_BITMAP_16M_COLORS ||
11243
(packMethod >= PACK_JPEG_8_COLORS &&
11244
packMethod <= PACK_JPEG_16M_COLORS) ||
11245
(packMethod >= PACK_PNG_8_COLORS &&
11246
packMethod <= PACK_PNG_16M_COLORS) ||
11247
packMethod == PACK_LOSSY ||
11248
packMethod == PACK_LOSSLESS ||
11249
packMethod == PACK_ADAPTIVE)
11251
sprintf(packMethodName + strlen(packMethodName),
11255
packMethod = method;
11256
packQuality = quality;
11258
control -> PackMethod = packMethod;
11259
control -> PackQuality = packQuality;
11264
int SetDirectories()
11267
// Determine the location of the user's NX
11268
// directory and the other relevant paths.
11269
// The functions below will check the pa-
11270
// rameters passed to the program and will
11271
// query the environment, if needed.
11274
control -> HomePath = GetHomePath();
11275
control -> RootPath = GetRootPath();
11276
control -> SystemPath = GetSystemPath();
11277
control -> TempPath = GetTempPath();
11278
control -> ClientPath = GetClientPath();
11286
// So far we used stderr (or stdout under
11287
// WIN32). Now use the files selected by
11291
if (*statsFileName == '\0')
11293
strcpy(statsFileName, "stats");
11296
*logofs << "Loop: Assuming default statistics file '"
11297
<< statsFileName << "'.\n" << logofs_flush;
11303
*logofs << "Loop: Name selected for statistics is '"
11304
<< statsFileName << "'.\n" << logofs_flush;
11308
if (OpenLogFile(statsFileName, statofs) < 0)
11315
if (*errorsFileName == '\0')
11317
strcpy(errorsFileName, "errors");
11320
*logofs << "Loop: Assuming default log file name '"
11321
<< errorsFileName << "'.\n" << logofs_flush;
11327
*logofs << "Loop: Name selected for log file is '"
11328
<< errorsFileName << "'.\n" << logofs_flush;
11333
// Share the bebug output with the nxssh binder
11334
// process. The file must be made writable by
11335
// everybody because the nxssh process is run by
11336
// nxserver as the nx user.
11341
strcpy(errorsFileName, "/tmp/errors");
11343
ostream *tmpfs = new ofstream(errorsFileName, ios::out);
11347
chmod(errorsFileName, S_IRUSR | S_IWUSR | S_IRGRP |
11348
S_IWGRP | S_IROTH | S_IWOTH);
11352
if (OpenLogFile(errorsFileName, logofs) < 0)
11358
// By default the session log is the standard error
11359
// of the process. It is anyway required to set the
11360
// option when running inside SSH, otherwise the
11361
// output will go to the same file as the SSH log,
11362
// depending where the NX client has redirected the
11366
if (*sessionFileName != '\0')
11369
*logofs << "Loop: Name selected for session file is '"
11370
<< sessionFileName << "'.\n" << logofs_flush;
11373
if (errofs != NULL)
11376
*logofs << "Loop: WARNING! Unexpected value for stream errofs.\n"
11380
cerr << "Warning" << ": Unexpected value for stream errofs.\n";
11383
if (errsbuf != NULL)
11386
*logofs << "Loop: WARNING! Unexpected value for buffer errsbuf.\n"
11390
cerr << "Warning" << ": Unexpected value for buffer errsbuf.\n";
11396
if (OpenLogFile(sessionFileName, errofs) < 0)
11402
// Redirect the standard error to the file.
11405
errsbuf = cerr.rdbuf(errofs -> rdbuf());
11416
// Depending on the proxy side, we need to determine on which
11417
// port to listen for the given protocol or to which port we
11418
// will have to forward the connection. Three possibilities
11419
// are given for each supported protocol:
11421
// Port <= 0: Disable port forwarding.
11422
// Port == 1: Use the default port.
11423
// Port > 1: Use the specified port.
11425
// At the connectiong side the user should always explicitly
11426
// set the ports where the connections will be forwarded. This
11427
// is both for security reasons and because, when running both
11428
// proxies on the same host, there is a concrete possibility
11429
// that, by using the default ports, the connection will be
11430
// forwarded to the same port where the peer proxy is listen-
11431
// ing, causing a loop.
11437
*logofs << "Loop: Disabling cups connections.\n"
11447
if (control -> ProxyMode == proxy_client)
11451
cupsPort = DEFAULT_NX_CUPS_PORT_OFFSET + proxyPort;
11461
// Use the well-known 631/tcp port of the
11462
// Internet Printing Protocol.
11472
*logofs << "Loop: Using cups port '" << cupsPort
11473
<< "'.\n" << logofs_flush;
11480
*logofs << "Loop: Disabling auxiliary X11 connections.\n"
11490
if (control -> ProxyMode == proxy_client)
11494
auxPort = DEFAULT_NX_AUX_PORT_OFFSET + proxyPort;
11502
// Auxiliary X connections are always forwarded
11503
// to the display where the session is running.
11504
// The only value accepted is 1.
11510
*logofs << "Loop: WARNING! Overriding auxiliary X11 "
11511
<< "port with new value '" << 1 << "'.\n"
11515
cerr << "Warning" << ": Overriding auxiliary X11 "
11516
<< "port with new value '" << 1 << "'.\n";
11525
*logofs << "Loop: Using auxiliary X11 port '" << auxPort
11526
<< "'.\n" << logofs_flush;
11533
*logofs << "Loop: Disabling SMB connections.\n"
11543
if (control -> ProxyMode == proxy_client)
11547
smbPort = DEFAULT_NX_SMB_PORT_OFFSET + proxyPort;
11557
// Assume the 139/tcp port used for SMB
11558
// over NetBIOS over TCP.
11568
*logofs << "Loop: Using SMB port '" << smbPort
11569
<< "'.\n" << logofs_flush;
11573
if (mediaPort <= 0)
11576
*logofs << "Loop: Disabling multimedia connections.\n"
11582
useMediaSocket = 0;
11586
if (control -> ProxyMode == proxy_client)
11588
if (mediaPort == 1)
11590
mediaPort = DEFAULT_NX_MEDIA_PORT_OFFSET + proxyPort;
11593
useMediaSocket = 1;
11597
if (mediaPort == 1)
11600
// We don't have a well-known port to
11601
// be used for media connections.
11605
*logofs << "Loop: PANIC! No port specified for multimedia connections.\n"
11609
cerr << "Error" << ": No port specified for multimedia connections.\n";
11614
useMediaSocket = 0;
11618
*logofs << "Loop: Using multimedia port '" << mediaPort
11619
<< "'.\n" << logofs_flush;
11626
*logofs << "Loop: Disabling HTTP connections.\n"
11636
if (control -> ProxyMode == proxy_client)
11640
httpPort = DEFAULT_NX_HTTP_PORT_OFFSET + proxyPort;
11650
// Use the well-known 80/tcp port.
11660
*logofs << "Loop: Using HTTP port '" << httpPort
11661
<< "'.\n" << logofs_flush;
11665
if (ParseFontPath(fontPort) <= 0)
11668
*logofs << "Loop: Disabling font server connections.\n"
11679
// We don't know yet if the remote proxy supports
11680
// the font server connections. If needed, we will
11681
// disable the font server connections at later
11685
if (control -> ProxyMode == proxy_server)
11695
*logofs << "Loop: Using font server port '" << fontPort
11696
<< "'.\n" << logofs_flush;
11700
if (slavePort <= 0)
11703
*logofs << "Loop: Disabling slave connections.\n"
11709
useSlaveSocket = 0;
11714
// File transfer connections can
11715
// be originated by both sides.
11718
if (slavePort == 1)
11720
if (control -> ProxyMode == proxy_client)
11722
slavePort = DEFAULT_NX_SLAVE_PORT_CLIENT_OFFSET + proxyPort;
11726
slavePort = DEFAULT_NX_SLAVE_PORT_SERVER_OFFSET + proxyPort;
11730
useSlaveSocket = 1;
11733
*logofs << "Loop: Using slave port '" << slavePort
11734
<< "'.\n" << logofs_flush;
11741
int SetDescriptors()
11743
unsigned int limit = 0;
11745
#ifdef RLIMIT_NOFILE
11749
if (getrlimit(RLIMIT_NOFILE, &limits) == 0)
11751
if (limits.rlim_max == RLIM_INFINITY)
11757
limit = (unsigned int) limits.rlim_max;
11763
#ifdef _SC_OPEN_MAX
11767
limit = sysconf(_SC_OPEN_MAX);
11774
if (limit > FD_SETSIZE)
11776
limit = FD_SETSIZE;
11781
#ifdef RLIMIT_NOFILE
11783
if (limits.rlim_cur < limit)
11785
limits.rlim_cur = limit;
11787
setrlimit(RLIMIT_NOFILE, &limits);
11795
*logofs << "Loop: PANIC! Cannot determine number of available "
11796
<< "file descriptors.\n" << logofs_flush;
11799
cerr << "Error" << ": Cannot determine number of available "
11800
<< "file descriptors.\n";
11809
// Find the directory containing the caches
11810
// matching the session type.
11815
if ((control -> PersistentCachePath = GetCachePath()) == NULL)
11818
*logofs << "Loop: PANIC! Error getting or creating the cache path.\n"
11822
cerr << "Error" << ": Error getting or creating the cache path.\n";
11828
*logofs << "Loop: Path of cache files is '" << control -> PersistentCachePath
11829
<< "'.\n" << logofs_flush;
11836
// Initialize all configuration parameters.
11839
int SetParameters()
11842
// Find out the type of session.
11848
// Initialize the network and compression
11849
// parameters according to the settings
11850
// suggested by the user.
11856
// Set compression according to link speed.
11862
// Be sure that we have a literal for current
11863
// cache size. Value will reflect control's
11864
// default unless we already parsed a 'cache'
11865
// option. Server side has no control on size
11866
// of cache but is informed at session nego-
11867
// tiation about how much memory is going to
11874
// Set size of shared memory segments.
11880
// Make adjustments to cache based
11881
// on the pack method.
11887
// Set disk-based image cache.
11893
// Set CPU and bandwidth limits.
11902
// According to session literal determine
11903
// the type of traffic that is going to be
11904
// transported. Literals should be better
11905
// standardized in future NX versions.
11910
if (strncmp(sessionType, "agent", strlen("agent")) == 0 ||
11911
strncmp(sessionType, "desktop", strlen("desktop")) == 0 ||
11912
strncmp(sessionType, "rootless", strlen("rootless")) == 0 ||
11913
strncmp(sessionType, "console", strlen("console")) == 0 ||
11914
strncmp(sessionType, "default", strlen("default")) == 0 ||
11915
strncmp(sessionType, "gnome", strlen("gnome")) == 0 ||
11916
strncmp(sessionType, "kde", strlen("kde")) == 0 ||
11917
strncmp(sessionType, "cde", strlen("cde")) == 0 ||
11918
strncmp(sessionType, "xdm", strlen("xdm")) == 0)
11920
control -> SessionMode = session_agent;
11922
else if (strncmp(sessionType, "win", strlen("win")) == 0 ||
11923
strncmp(sessionType, "vnc", strlen("vnc")) == 0)
11925
control -> SessionMode = session_agent;
11927
else if (strncmp(sessionType, "shadow", strlen("shadow")) == 0)
11929
control -> SessionMode = session_shadow;
11931
else if (strncmp(sessionType, "proxy", strlen("proxy")) == 0 ||
11932
strncmp(sessionType, "application", strlen("application")) == 0 ||
11933
strncmp(sessionType, "raw", strlen("raw")) == 0)
11935
control -> SessionMode = session_proxy;
11940
// If the session type is not passed or
11941
// it is not among the recognized strings,
11942
// we assume that the proxy is connected
11946
if (*sessionType != '\0' &&
11947
(control -> isProtoStep8() == 1 ||
11948
strncmp(sessionType, "unix-", strlen("unix-")) != 0))
11951
*logofs << "Loop: WARNING! Unrecognized session type '"
11952
<< sessionType << "'. Assuming agent session.\n"
11956
cerr << "Warning" << ": Unrecognized session type '"
11957
<< sessionType << "'. Assuming agent session.\n";
11960
control -> SessionMode = session_agent;
11963
#if defined(TEST) || defined(INFO)
11964
*logofs << "Loop: Assuming session type '"
11965
<< DumpSession(control -> SessionMode) << "' with "
11966
<< "string '" << sessionType << "'.\n"
11971
// By default the policy is immediate. Agents
11972
// will set a different policy, if they like.
11973
// Anyway we need to check if the user has
11974
// provided a custom flush policy.
11977
if (usePolicy != -1)
11981
control -> FlushPolicy = policy_deferred;
11985
control -> FlushPolicy = policy_immediate;
11988
#if defined(TEST) || defined(INFO)
11989
*logofs << "Loop: WARNING! Forcing flush policy to '"
11990
<< DumpPolicy(control -> FlushPolicy)
11991
<< ".\n" << logofs_flush;
11996
control -> FlushPolicy = policy_immediate;
11998
#if defined(TEST) || defined(INFO)
11999
*logofs << "Loop: Setting initial flush policy to '"
12000
<< DumpPolicy(control -> FlushPolicy)
12001
<< "'.\n" << logofs_flush;
12006
// Check if the proxy library is run inside
12007
// another program providing encryption, as
12008
// it is the case of the SSH client.
12011
if (useEncryption != -1)
12013
if (useEncryption > 0)
12015
control -> LinkEncrypted = 1;
12019
control -> LinkEncrypted = 0;
12023
if (control -> LinkEncrypted == 1)
12025
#if defined(TEST) || defined(INFO)
12026
*logofs << "Loop: Proxy running as part of an "
12027
<< "encrypting client.\n"
12033
#if defined(TEST) || defined(INFO)
12034
*logofs << "Loop: Assuming proxy running as a "
12035
<< "standalone program.\n"
12041
// Check if the system administrator has
12042
// enabled the respawn of the client at
12043
// the end of session.
12046
if (control -> ProxyMode == proxy_server)
12048
struct stat fileStat;
12050
char fileName[DEFAULT_STRING_LENGTH];
12052
snprintf(fileName, DEFAULT_STRING_LENGTH - 1,
12053
"%s/share/noexit", control -> SystemPath);
12055
*(fileName + DEFAULT_STRING_LENGTH - 1) = '\0';
12057
if (stat(fileName, &fileStat) == 0)
12060
*logofs << "Loop: Enabling respawn of client at session shutdown.\n"
12064
control -> EnableRestartOnShutdown = 1;
12074
// If differential compression is disabled
12075
// we don't need a cache at all.
12078
if (control -> LocalDeltaCompression == 0)
12080
control -> ClientTotalStorageSize = 0;
12081
control -> ServerTotalStorageSize = 0;
12085
// Set a a cache size literal.
12088
int size = control -> getUpperStorageSize();
12090
if (size / 1024 > 0)
12092
sprintf(cacheSizeName, "%dk", size / 1024);
12096
sprintf(cacheSizeName, "%d", size);
12099
if (control -> ProxyMode == proxy_client)
12101
control -> LocalTotalStorageSize =
12102
control -> ClientTotalStorageSize;
12104
control -> RemoteTotalStorageSize =
12105
control -> ServerTotalStorageSize;
12109
control -> LocalTotalStorageSize =
12110
control -> ServerTotalStorageSize;
12112
control -> RemoteTotalStorageSize =
12113
control -> ClientTotalStorageSize;
12117
*logofs << "Loop: Storage size limit is "
12118
<< control -> ClientTotalStorageSize
12119
<< " at client and "
12120
<< control -> ServerTotalStorageSize
12126
*logofs << "Loop: Storage local limit set to "
12127
<< control -> LocalTotalStorageSize
12128
<< " remote limit set to "
12129
<< control -> RemoteTotalStorageSize
12130
<< ".\n" << logofs_flush;
12134
// Never reserve for split store more than
12135
// half the memory available for messages.
12138
if (size > 0 && control ->
12139
SplitTotalStorageSize > size / 2)
12142
*logofs << "Loop: Reducing size of split store to "
12143
<< size / 2 << " bytes.\n"
12147
control -> SplitTotalStorageSize = size / 2;
12151
// Don't load render from persistent
12152
// cache if extension is hidden or
12153
// not supported by agent.
12156
if (control -> HideRender == 1)
12159
*logofs << "Loop: Not loading render extension "
12160
<< "from persistent cache.\n"
12164
control -> PersistentCacheLoadRender = 0;
12173
// If not set, adjust the size of the shared
12174
// memory segment according to size of the
12178
if (*shsegSizeName == '\0')
12180
int size = control -> getUpperStorageSize();
12182
const int mega = 1048576;
12186
if (size <= 1 * mega)
12190
else if (size <= 2 * mega)
12194
else if (size < 4 * mega)
12203
if (size > 4194304)
12208
control -> ShmemClientSize = size;
12209
control -> ShmemServerSize = size;
12214
// The delta compression is disabled.
12215
// Use a default segment size of 2 MB.
12218
control -> ShmemServerSize = 2 * mega;
12223
// Client side shared memory support is
12224
// not useful and not implemented.
12227
if (control -> ShmemServerSize >= 524288)
12229
control -> ShmemServer = 1;
12231
#if defined(TEST) || defined(INFO)
12232
*logofs << "Loop: Set initial shared memory size "
12233
<< "to " << control -> ShmemServerSize
12234
<< " bytes.\n" << logofs_flush;
12239
#if defined(TEST) || defined(INFO)
12240
*logofs << "Loop: Disabled use of the shared memory "
12241
<< "extension.\n" << logofs_flush;
12244
control -> ShmemServer = 0;
12251
// Adjust the pack method according to the
12252
// type of the session.
12258
*logofs << "Loop: Setting pack with initial method "
12259
<< packMethod << " and quality " << packQuality
12260
<< ".\n" << logofs_flush;
12264
// Check if this is a proxy session and, in
12265
// this case, set the pack method to none.
12266
// Packed images are not supported by plain
12270
if (control -> SessionMode == session_proxy)
12273
*logofs << "Loop: WARNING! Disabling pack with proxy session.\n"
12277
packMethod = PACK_NONE;
12281
// Adjust the internal settings according
12282
// to the newly selected pack method.
12285
ParsePackMethod(packMethod, packQuality);
12288
// Don't load messages from persistent
12289
// cache if packed images are disabled.
12292
if (control -> PackMethod == PACK_NONE)
12294
control -> PersistentCacheLoadPacked = 0;
12297
*logofs << "Loop: Not loading packed images "
12298
<< "from persistent cache.\n"
12307
// Set the disk-based image cache parameters
12308
// according to the user's wishes.
12314
// Be sure we disable the image cache if we
12315
// are connecting to plain X clients.
12318
if (control -> SessionMode == session_proxy)
12321
*logofs << "Loop: Disabling image cache with "
12322
<< "session '" << DumpSession(control ->
12323
SessionMode) << "'.\n" << logofs_flush;
12326
sprintf(imagesSizeName, "0");
12328
control -> ImageCacheEnableLoad = 0;
12329
control -> ImageCacheEnableSave = 0;
12334
int size = control -> ImageCacheDiskLimit;
12336
if (size / 1024 > 0)
12338
sprintf(imagesSizeName, "%dk", size / 1024);
12342
sprintf(imagesSizeName, "%d", size);
12347
control -> ImageCacheEnableLoad = 1;
12348
control -> ImageCacheEnableSave = 1;
12350
if (control -> ProxyMode == proxy_server)
12352
if ((control -> ImageCachePath = GetImagesPath()) == NULL)
12355
*logofs << "Loop: PANIC! Error getting or creating image cache path.\n"
12359
cerr << "Error" << ": Error getting or creating image cache path.\n";
12365
*logofs << "Loop: Path of image cache files is '" << control -> ImageCachePath
12366
<< "'.\n" << logofs_flush;
12373
*logofs << "Loop: Disabling the persistent image cache.\n"
12377
control -> ImageCacheEnableLoad = 0;
12378
control -> ImageCacheEnableSave = 0;
12387
// Normalize the different proxy versions.
12390
int local = (control -> LocalVersionMajor << 24) |
12391
(control -> LocalVersionMinor << 16) |
12392
control -> LocalVersionPatch;
12394
int remote = (control -> RemoteVersionMajor << 24) |
12395
(control -> RemoteVersionMinor << 16) |
12396
control -> RemoteVersionPatch;
12402
if (control -> RemoteVersionMajor <= 1)
12405
// The remote proxy uses a different
12406
// logic to determine the version so
12407
// we default to the compatibility
12411
major = control -> CompatVersionMajor;
12412
minor = control -> CompatVersionMinor;
12413
patch = control -> CompatVersionPatch;
12416
*logofs << "Loop: Using compatibility version '"
12417
<< major << "." << minor << "." << patch
12418
<< "'.\n" << logofs_flush;
12421
else if (control -> LocalVersionMajor >
12422
control -> RemoteVersionMajor)
12425
// We use a more recent version. Let's
12426
// negotiate the version based on the
12427
// version supported by the remote.
12430
major = control -> RemoteVersionMajor;
12431
minor = control -> RemoteVersionMinor;
12432
patch = control -> RemoteVersionPatch;
12435
*logofs << "Loop: Using remote version '"
12436
<< major << "." << minor << "." << patch
12437
<< "'.\n" << logofs_flush;
12443
// We support a major version that is
12444
// equal or older than the remote. We
12445
// assume the smaller version between
12446
// the two, including the minor and
12447
// the patch numbers.
12450
if (local > remote)
12452
major = control -> RemoteVersionMajor;
12453
minor = control -> RemoteVersionMinor;
12454
patch = control -> RemoteVersionPatch;
12457
*logofs << "Loop: Using remote version '"
12458
<< major << "." << minor << "." << patch
12459
<< "'.\n" << logofs_flush;
12464
major = control -> LocalVersionMajor;
12465
minor = control -> LocalVersionMinor;
12466
patch = control -> LocalVersionPatch;
12469
*logofs << "Loop: Using local version '"
12470
<< major << "." << minor << "." << patch
12471
<< "'.\n" << logofs_flush;
12477
// Handle the 1.5.0 versions. The protocol
12478
// step 6 is the minimum supported version.
12490
else if (major == 2)
12494
else if (major == 3)
12500
else if (minor > 0 || patch > 0)
12509
else if (major > 3)
12517
*logofs << "Loop: PANIC! Incompatible remote version "
12518
<< control -> RemoteVersionMajor << "." << control -> RemoteVersionMinor
12519
<< "." << control -> RemoteVersionPatch << " with local version "
12520
<< control -> LocalVersionMajor << "." << control -> LocalVersionMinor
12521
<< "." << control -> LocalVersionPatch << ".\n" << logofs_flush;
12524
cerr << "Error" << ": Incompatible remote version "
12525
<< control -> RemoteVersionMajor << "." << control -> RemoteVersionMinor
12526
<< "." << control -> RemoteVersionPatch << " with local version "
12527
<< control -> LocalVersionMajor << "." << control -> LocalVersionMinor
12528
<< "." << control -> LocalVersionPatch << ".\n";
12534
*logofs << "Loop: Using NX protocol step "
12535
<< step << ".\n" << logofs_flush;
12538
control -> setProtoStep(step);
12541
// Ignore the differences in patch version
12542
// and print a warning if the local version
12543
// is different or obsolete compared to
12547
local &= 0xffff0000;
12548
remote &= 0xffff0000;
12550
if (local != remote)
12553
*logofs << "Loop: WARNING! Connected to remote version "
12554
<< control -> RemoteVersionMajor << "." << control -> RemoteVersionMinor
12555
<< "." << control -> RemoteVersionPatch << " with local version "
12556
<< control -> LocalVersionMajor << "." << control -> LocalVersionMinor
12557
<< "." << control -> LocalVersionPatch << ".\n" << logofs_flush;
12560
cerr << "Warning" << ": Connected to remote version "
12561
<< control -> RemoteVersionMajor << "." << control -> RemoteVersionMinor
12562
<< "." << control -> RemoteVersionPatch << " with local version "
12563
<< control -> LocalVersionMajor << "." << control -> LocalVersionMinor
12564
<< "." << control -> LocalVersionPatch << ".\n" << logofs_flush;
12567
if (local < remote)
12569
cerr << "Warning" << ": Consider checking http://www.nomachine.com/ for updates.\n";
12573
// Now that we are aware of the remote
12574
// version, let's adjust the options to
12575
// be compatible with the remote proxy.
12578
if (control -> ProxyMode == proxy_client)
12580
if (control -> isProtoStep8() == 0)
12582
if (strncmp(sessionType, "shadow", strlen("shadow")) == 0 ||
12583
strncmp(sessionType, "application", strlen("application")) == 0 ||
12584
strncmp(sessionType, "console", strlen("console")) == 0 ||
12585
strncmp(sessionType, "default", strlen("default")) == 0 ||
12586
strncmp(sessionType, "gnome", strlen("gnome")) == 0 ||
12587
strncmp(sessionType, "kde", strlen("kde")) == 0 ||
12588
strncmp(sessionType, "cde", strlen("cde")) == 0 ||
12589
strncmp(sessionType, "xdm", strlen("xdm")) == 0)
12592
#if defined(TEST) || defined(INFO)
12593
*logofs << "Loop: WARNING! Prepending 'unix-' to the "
12594
<< "name of the session.\n" << logofs_flush;
12597
char buffer[DEFAULT_STRING_LENGTH];
12599
snprintf(buffer, DEFAULT_STRING_LENGTH - 1, "unix-%s", sessionType);
12601
strcpy(sessionType, buffer);
12606
// Check if the remote is able to handle
12607
// the selected pack method.
12610
if (control -> isProtoStep8() == 0)
12612
if (packMethod == PACK_ADAPTIVE || packMethod == PACK_LOSSY)
12615
*logofs << "Loop: WARNING! Assuming a lossy encoding with "
12616
<< "an old proxy version.\n" << logofs_flush;
12619
packMethod = PACK_JPEG_16M_COLORS;
12621
else if (packMethod == PACK_LOSSLESS)
12624
*logofs << "Loop: WARNING! Assuming a lossless encoding with "
12625
<< "an old proxy version.\n" << logofs_flush;
12628
if (control -> isProtoStep7() == 1)
12630
packMethod = PACK_RLE_16M_COLORS;
12634
packMethod = PACK_PNG_16M_COLORS;
12640
// If the remote doesn't support the
12641
// selected method use something that
12645
if ((packMethod == PACK_RGB_16M_COLORS ||
12646
packMethod == PACK_RLE_16M_COLORS ||
12647
packMethod == PACK_BITMAP_16M_COLORS) &&
12648
control -> isProtoStep7() == 0)
12651
*logofs << "Loop: WARNING! Setting the pack method to '"
12652
<< PACK_PNG_16M_COLORS << "' with '" << packMethod
12653
<< "' unsupported.\n" << logofs_flush;
12656
packMethod = PACK_PNG_16M_COLORS;
12659
else if (packMethod == PACK_BITMAP_16M_COLORS &&
12660
control -> isProtoStep8() == 0)
12663
*logofs << "Loop: WARNING! Setting the pack method to '"
12664
<< PACK_RLE_16M_COLORS << "' with '" << packMethod
12665
<< "' unsupported.\n" << logofs_flush;
12668
packMethod = PACK_RLE_16M_COLORS;
12673
// Update the pack method name.
12676
ParsePackMethod(packMethod, packQuality);
12680
// At the moment the image cache is not used by the
12681
// agent but we need to take care of the compatibi-
12682
// lity with old versions. Proxy versions older than
12683
// the 3.0.0 assume that it is enabled and will send
12684
// specific bits as part of the encoding. Conversely,
12685
// it is advisable to disable the cache right now.
12686
// By not enabling the image cache, the house-keep-
12687
// ing process will only take care of cleaning up
12688
// the "cache-" directories.
12691
if (control -> isProtoStep8() == 1)
12694
*logofs << "Loop: Disabling image cache with protocol "
12695
<< "step '" << control -> getProtoStep()
12696
<< "'.\n" << logofs_flush;
12699
sprintf(imagesSizeName, "0");
12701
control -> ImageCacheEnableLoad = 0;
12702
control -> ImageCacheEnableSave = 0;
12709
// Identify the requested link settings
12710
// and update the control parameters
12717
*logofs << "Loop: Setting link with initial value "
12718
<< linkSpeedName << ".\n" << logofs_flush;
12721
if (*linkSpeedName == '\0')
12723
strcpy(linkSpeedName, "lan");
12727
*logofs << "Loop: Link speed is " << linkSpeedName
12728
<< ".\n" << logofs_flush;
12731
if (strcasecmp(linkSpeedName, "modem") == 0)
12735
else if (strcasecmp(linkSpeedName, "isdn") == 0)
12739
else if (strcasecmp(linkSpeedName, "adsl") == 0)
12743
else if (strcasecmp(linkSpeedName, "wan") == 0)
12747
else if (strcasecmp(linkSpeedName, "lan") == 0)
12757
// Set TCP_NODELAY according to the user's
12761
if (useNoDelay != -1)
12763
control -> OptionProxyClientNoDelay = useNoDelay;
12764
control -> OptionProxyServerNoDelay = useNoDelay;
12768
// Select the image compression method.
12771
if (packMethod == -1)
12773
packMethod = control -> PackMethod;
12776
if (packQuality == -1)
12778
packQuality = control -> PackQuality;
12781
if (ParsePackMethod(packMethod, packQuality) < 0)
12784
*logofs << "Loop: PANIC! Unrecognized pack method id "
12785
<< packMethod << " with quality " << packQuality
12786
<< ".\n" << logofs_flush;
12789
cerr << "Error" << ": Unrecognized pack method id "
12790
<< packMethod << " with quality " << packQuality
12797
// Check if the user disabled the ability
12798
// to generate simple replies at the client
12802
if (control -> SessionMode == session_proxy)
12804
if (useTaint != -1)
12806
control -> TaintReplies = (useTaint == 1);
12811
*logofs << "Loop: WARNING! Forcing taint of replies "
12812
<< "with a proxy session.\n"
12816
control -> TaintReplies = 1;
12822
// There is no need to taint the
12823
// replies if we have an agent.
12826
control -> TaintReplies = 0;
12830
// Be sure that the requests needing a reply
12831
// are flushed immediately. Normal X clients
12832
// use so many replies to make the queuing
12833
// completely useless.
12836
if (control -> SessionMode == session_proxy)
12839
*logofs << "Loop: WARNING! Forcing flush on priority "
12840
<< "with a proxy session.\n"
12844
control -> FlushPriority = 1;
12851
// Parameters for MODEM 28.8/33.6/56 Kbps.
12857
*logofs << "Loop: Setting parameters for MODEM.\n"
12861
control -> LinkMode = LINK_TYPE_MODEM;
12863
control -> TokenSize = 256;
12864
control -> TokenLimit = 24;
12866
control -> SplitMode = 1;
12867
control -> SplitTotalSize = 128;
12868
control -> SplitTotalStorageSize = 1048576;
12870
control -> SplitTimeout = 50;
12871
control -> MotionTimeout = 50;
12872
control -> IdleTimeout = 50;
12874
control -> PackMethod = PACK_ADAPTIVE;
12875
control -> PackQuality = 3;
12881
// Parameters for ISDN 64/128 Kbps.
12887
*logofs << "Loop: Setting parameters for ISDN.\n"
12891
control -> LinkMode = LINK_TYPE_ISDN;
12893
control -> TokenSize = 384;
12894
control -> TokenLimit = 24;
12896
control -> SplitMode = 1;
12897
control -> SplitTotalSize = 128;
12898
control -> SplitTotalStorageSize = 1048576;
12900
control -> SplitTimeout = 50;
12901
control -> MotionTimeout = 20;
12902
control -> IdleTimeout = 50;
12904
control -> PackMethod = PACK_ADAPTIVE;
12905
control -> PackQuality = 5;
12911
// Parameters for ADSL 256 Kbps.
12917
*logofs << "Loop: Setting parameters for ADSL.\n"
12921
control -> LinkMode = LINK_TYPE_ADSL;
12923
control -> TokenSize = 512;
12924
control -> TokenLimit = 24;
12926
control -> SplitMode = 1;
12927
control -> SplitTotalSize = 128;
12928
control -> SplitTotalStorageSize = 1048576;
12930
control -> SplitTimeout = 50;
12931
control -> MotionTimeout = 10;
12932
control -> IdleTimeout = 50;
12934
control -> PackMethod = PACK_ADAPTIVE;
12935
control -> PackQuality = 7;
12941
// Parameters for XDSL/FDDI/ATM 1/2/34 Mbps WAN.
12947
*logofs << "Loop: Setting parameters for WAN.\n"
12951
control -> LinkMode = LINK_TYPE_WAN;
12953
control -> TokenSize = 768;
12954
control -> TokenLimit = 24;
12956
control -> SplitMode = 1;
12957
control -> SplitTotalSize = 128;
12958
control -> SplitTotalStorageSize = 1048576;
12960
control -> SplitTimeout = 50;
12961
control -> MotionTimeout = 5;
12962
control -> IdleTimeout = 50;
12964
control -> PackMethod = PACK_ADAPTIVE;
12965
control -> PackQuality = 9;
12971
// Parameters for LAN 10/100 Mbps.
12977
*logofs << "Loop: Setting parameters for LAN.\n"
12981
control -> LinkMode = LINK_TYPE_LAN;
12983
control -> TokenSize = 1536;
12984
control -> TokenLimit = 24;
12986
control -> SplitMode = 1;
12987
control -> SplitTotalSize = 128;
12988
control -> SplitTotalStorageSize = 1048576;
12990
control -> SplitTimeout = 50;
12991
control -> MotionTimeout = 0;
12992
control -> IdleTimeout = 50;
12994
control -> PackMethod = PACK_ADAPTIVE;
12995
control -> PackQuality = 9;
13001
// Identify the requested link type and set
13002
// the control parameters accordingly.
13005
int SetCompression()
13007
if (strcasecmp(linkSpeedName, "modem") == 0)
13009
SetCompressionModem();
13011
else if (strcasecmp(linkSpeedName, "isdn") == 0)
13013
SetCompressionIsdn();
13015
else if (strcasecmp(linkSpeedName, "adsl") == 0)
13017
SetCompressionAdsl();
13019
else if (strcasecmp(linkSpeedName, "wan") == 0)
13021
SetCompressionWan();
13023
else if (strcasecmp(linkSpeedName, "lan") == 0)
13025
SetCompressionLan();
13032
if (control -> LocalDeltaCompression < 0)
13034
control -> LocalDeltaCompression = 1;
13038
// If we didn't set remote delta compression
13039
// (as it should always be the case at client
13040
// side) assume value of local side.
13043
if (control -> RemoteDeltaCompression < 0)
13045
control -> RemoteDeltaCompression =
13046
control -> LocalDeltaCompression;
13050
// If we didn't set remote compression levels
13051
// assume values of local side.
13054
if (control -> RemoteStreamCompression < 0)
13056
control -> RemoteStreamCompressionLevel =
13057
control -> LocalStreamCompressionLevel;
13059
if (control -> RemoteStreamCompressionLevel > 0)
13061
control -> RemoteStreamCompression = 1;
13065
control -> RemoteStreamCompression = 0;
13069
if (control -> RemoteDataCompression < 0)
13071
control -> RemoteDataCompressionLevel =
13072
control -> LocalDataCompressionLevel;
13074
if (control -> RemoteDataCompressionLevel > 0)
13076
control -> RemoteDataCompression = 1;
13080
control -> RemoteDataCompression = 0;
13088
// Compression for MODEM.
13091
int SetCompressionModem()
13093
if (control -> LocalDataCompression < 0)
13095
control -> LocalDataCompression = 1;
13096
control -> LocalDataCompressionLevel = 1;
13099
if (control -> LocalDataCompressionThreshold < 0)
13101
control -> LocalDataCompressionThreshold = 32;
13104
if (control -> LocalStreamCompression < 0)
13106
control -> LocalStreamCompression = 1;
13107
control -> LocalStreamCompressionLevel = 9;
13114
// Compression for ISDN.
13117
int SetCompressionIsdn()
13119
if (control -> LocalDataCompression < 0)
13121
control -> LocalDataCompression = 1;
13122
control -> LocalDataCompressionLevel = 1;
13125
if (control -> LocalDataCompressionThreshold < 0)
13127
control -> LocalDataCompressionThreshold = 32;
13130
if (control -> LocalStreamCompression < 0)
13132
control -> LocalStreamCompression = 1;
13133
control -> LocalStreamCompressionLevel = 6;
13140
// Compression for ADSL.
13143
int SetCompressionAdsl()
13145
if (control -> LocalDataCompression < 0)
13147
control -> LocalDataCompression = 1;
13148
control -> LocalDataCompressionLevel = 1;
13151
if (control -> LocalDataCompressionThreshold < 0)
13153
control -> LocalDataCompressionThreshold = 32;
13156
if (control -> LocalStreamCompression < 0)
13158
control -> LocalStreamCompression = 1;
13159
control -> LocalStreamCompressionLevel = 4;
13166
// Compression for WAN.
13169
int SetCompressionWan()
13171
if (control -> LocalDataCompression < 0)
13173
control -> LocalDataCompression = 1;
13174
control -> LocalDataCompressionLevel = 1;
13177
if (control -> LocalDataCompressionThreshold < 0)
13179
control -> LocalDataCompressionThreshold = 32;
13182
if (control -> LocalStreamCompression < 0)
13184
control -> LocalStreamCompression = 1;
13185
control -> LocalStreamCompressionLevel = 1;
13192
// Compression for LAN.
13195
int SetCompressionLan()
13198
// Disable delta compression if not
13199
// explicitly enabled.
13202
if (control -> LocalDeltaCompression < 0)
13204
control -> LocalDeltaCompression = 0;
13207
if (control -> LocalDataCompression < 0)
13209
control -> LocalDataCompression = 0;
13210
control -> LocalDataCompressionLevel = 0;
13213
if (control -> LocalDataCompressionThreshold < 0)
13215
control -> LocalDataCompressionThreshold = 0;
13218
if (control -> LocalStreamCompression < 0)
13220
control -> LocalStreamCompression = 0;
13221
control -> LocalStreamCompressionLevel = 0;
13230
// Check if the user requested strict
13231
// control flow parameters.
13234
if (useStrict == 1)
13236
#if defined(TEST) || defined(INFO)
13237
*logofs << "Loop: LIMIT! Decreasing the token limit "
13238
<< "to " << control -> TokenLimit / 2
13239
<< " with option 'strict'.\n"
13243
control -> TokenLimit /= 2;
13248
control -> TokenLimit = 1;
13250
#if defined(TEST) || defined(INFO)
13251
*logofs << "Loop: WARNING! LIMIT! Setting the token limit "
13252
<< "to " << control -> TokenLimit
13253
<< " to simulate the proxy congestion.\n"
13260
// Reduce the size of the log file.
13265
control -> FileSizeLimit = 8388608;
13270
// Check the bitrate limits.
13273
if (control -> LocalBitrateLimit == -1)
13275
if (control -> ProxyMode == proxy_client)
13277
control -> LocalBitrateLimit =
13278
control -> ClientBitrateLimit;
13282
control -> LocalBitrateLimit =
13283
control -> ServerBitrateLimit;
13287
#if defined(TEST) || defined(INFO)
13288
*logofs << "Loop: LIMIT! Setting client bitrate limit "
13289
<< "to " << control -> ClientBitrateLimit
13290
<< " server bitrate limit to " << control ->
13291
ServerBitrateLimit << " with local limit "
13292
<< control -> LocalBitrateLimit << ".\n"
13300
// These functions are used to parse literal
13301
// values provided by the user and set the
13302
// control parameters accordingly.
13305
int ParseCacheOption(const char *opt)
13307
int size = ParseArg("", "cache", opt);
13312
*logofs << "Loop: PANIC! Invalid value '"
13313
<< opt << "' for option 'cache'.\n"
13317
cerr << "Error" << ": Invalid value '"
13318
<< opt << "' for option 'cache'.\n";
13324
*logofs << "Loop: Setting size of cache to "
13325
<< size << " bytes.\n" << logofs_flush;
13328
control -> ClientTotalStorageSize = size;
13329
control -> ServerTotalStorageSize = size;
13331
strcpy(cacheSizeName, opt);
13336
*logofs << "Loop: WARNING! Disabling NX delta compression.\n"
13340
control -> LocalDeltaCompression = 0;
13343
*logofs << "Loop: WARNING! Disabling use of NX persistent cache.\n"
13347
control -> PersistentCacheEnableLoad = 0;
13348
control -> PersistentCacheEnableSave = 0;
13354
int ParseImagesOption(const char *opt)
13356
int size = ParseArg("", "images", opt);
13361
*logofs << "Loop: PANIC! Invalid value '"
13362
<< opt << "' for option 'images'.\n"
13366
cerr << "Error" << ": Invalid value '"
13367
<< opt << "' for option 'images'.\n";
13373
*logofs << "Loop: Setting size of images cache to "
13374
<< size << " bytes.\n" << logofs_flush;
13377
control -> ImageCacheDiskLimit = size;
13379
strcpy(imagesSizeName, opt);
13384
int ParseShmemOption(const char *opt)
13386
int size = ParseArg("", "shseg", opt);
13391
*logofs << "Loop: PANIC! Invalid value '"
13392
<< opt << "' for option 'shseg'.\n"
13396
cerr << "Error" << ": Invalid value '"
13397
<< opt << "' for option 'shseg'.\n";
13402
control -> ShmemClientSize = size;
13403
control -> ShmemServerSize = size;
13405
#if defined(TEST) || defined(INFO)
13406
*logofs << "Loop: Set shared memory size to "
13407
<< control -> ShmemServerSize << " bytes.\n"
13411
strcpy(shsegSizeName, opt);
13416
int ParseBitrateOption(const char *opt)
13418
int bitrate = ParseArg("", "limit", opt);
13423
*logofs << "Loop: PANIC! Invalid value '"
13424
<< opt << "' for option 'limit'.\n"
13428
cerr << "Error" << ": Invalid value '"
13429
<< opt << "' for option 'limit'.\n";
13434
strcpy(bitrateLimitName, opt);
13439
*logofs << "Loop: Disabling bitrate limit on proxy link.\n"
13443
control -> LocalBitrateLimit = 0;
13448
*logofs << "Loop: Setting bitrate to " << bitrate
13449
<< " bits per second.\n" << logofs_flush;
13453
// Internal representation is in bytes
13457
control -> LocalBitrateLimit = bitrate >> 3;
13463
int ParseHostOption(const char *opt, char *host, int &port)
13466
*logofs << "Loop: Trying to parse options string '" << opt
13467
<< "' as a remote NX host.\n" << logofs_flush;
13470
if (opt == NULL || *opt == '\0')
13473
*logofs << "Loop: PANIC! No host parameter provided.\n"
13479
else if (strlen(opt) >= DEFAULT_STRING_LENGTH)
13482
*logofs << "Loop: PANIC! Host parameter exceeds length of "
13483
<< DEFAULT_STRING_LENGTH << " characters.\n"
13491
// Look for a host name followed
13492
// by a colon followed by port.
13495
int newPort = port;
13497
const char *separator = rindex(opt, ':');
13499
if (separator != NULL)
13501
const char *check = separator + 1;
13503
while (*check != '\0' && *check != ',' &&
13504
*check != '=' && isdigit(*check) != 0)
13509
newPort = atoi(separator + 1);
13511
if (newPort < 0 || *check != '\0')
13514
*logofs << "Loop: Can't identify remote NX port in string '"
13515
<< separator << "'.\n" << logofs_flush;
13521
else if (newPort < 0)
13524
// Complain if port was not passed
13529
*logofs << "Loop: Can't identify remote NX port in string '"
13530
<< opt << "'.\n" << logofs_flush;
13537
separator = opt + strlen(opt);
13540
char newHost[DEFAULT_STRING_LENGTH] = { 0 };
13542
strncpy(newHost, opt, strlen(opt) - strlen(separator));
13544
*(newHost + strlen(opt) - strlen(separator)) = '\0';
13546
const char *check = newHost;
13548
while (*check != '\0' && *check != ',' &&
13554
if (*check != '\0')
13557
*logofs << "Loop: Can't identify remote NX host in string '"
13558
<< newHost << "'.\n" << logofs_flush;
13563
else if (*acceptHost != '\0')
13566
*logofs << "Loop: PANIC! Can't manage to connect and accept connections "
13567
<< "at the same time.\n" << logofs_flush;
13569
*logofs << "Loop: PANIC! Refusing remote NX host with string '"
13570
<< opt << "'.\n" << logofs_flush;
13573
cerr << "Error" << ": Can't manage to connect and accept connections "
13574
<< "at the same time.\n";
13576
cerr << "Error" << ": Refusing remote NX host with string '"
13582
if (*host != '\0' && strcmp(host, newHost) != 0)
13585
*logofs << "Loop: WARNING! Overriding remote NX host '"
13586
<< host << "' with new value '" << newHost
13587
<< "'.\n" << logofs_flush;
13591
strcpy(host, newHost);
13593
if (port != -1 && port != newPort)
13596
*logofs << "Loop: WARNING! Overriding remote NX port '"
13597
<< port << "' with new value '" << newPort
13598
<< "'.\n" << logofs_flush;
13603
*logofs << "Loop: Parsed options string '" << opt
13604
<< "' with host '" << newHost << "' and port '"
13605
<< newPort << "'.\n" << logofs_flush;
13613
int ParseFontPath(char *path)
13615
char oldPath[DEFAULT_STRING_LENGTH];
13617
strcpy(oldPath, path);
13619
if (path == NULL || *path == '\0' || strcmp(path, "0") == 0)
13625
*logofs << "Loop: Parsing font server option '" << path
13626
<< "'.\n" << logofs_flush;
13630
// Convert the value to our default port.
13633
if (strcmp(fontPort, "1") == 0)
13635
if (control -> ProxyMode == proxy_server)
13637
snprintf(fontPort, DEFAULT_STRING_LENGTH - 1, "%d",
13638
DEFAULT_NX_FONT_PORT_OFFSET + proxyPort);
13643
// Let the client use the well-known
13644
// "unix/:7100" font path.
13647
snprintf(fontPort, DEFAULT_STRING_LENGTH - 1, "unix/:7100");
13652
// Check if a simple numaric value was given.
13655
if (atoi(path) > 0)
13658
*logofs << "Loop: Assuming numeric TCP port '" << atoi(path)
13659
<< "' for font server.\n" << logofs_flush;
13666
// Let's assume that a port specification "unix/:7100"
13667
// corresponds to "$TEMP/.font-unix/fs7100" and a port
13668
// "unix/:-1" corresponds to "$TEMP/.font-unix/fs-1".
13671
if (strncmp("unix/:", path, 6) == 0)
13673
snprintf(path, DEFAULT_STRING_LENGTH - 1, "%s/.font-unix/fs%s",
13674
control -> TempPath, oldPath + 6);
13676
*(path + DEFAULT_STRING_LENGTH - 1) = '\0';
13679
*logofs << "Loop: Assuming Unix socket '" << path
13680
<< "' for font server.\n" << logofs_flush;
13683
else if (strncmp("tcp/:", path, 5) == 0)
13685
snprintf(path, DEFAULT_STRING_LENGTH - 1, "%d", atoi(oldPath + 5));
13687
*(path + DEFAULT_STRING_LENGTH - 1) = '\0';
13689
if (atoi(path) <= 0)
13691
goto ParseFontPathError;
13695
*logofs << "Loop: Assuming TCP port '" << atoi(path)
13696
<< "' for font server.\n" << logofs_flush;
13702
// Accept an absolute file path as
13703
// a valid Unix socket.
13708
goto ParseFontPathError;
13712
*logofs << "Loop: Assuming Unix socket '" << path
13713
<< "' for font server.\n" << logofs_flush;
13719
ParseFontPathError:
13722
*logofs << "Loop: Unable to determine the font server "
13723
<< "port in string '" << path << "'.\n"
13730
int ParseListenOption(int &address)
13732
if (*listenHost == '\0')
13735
// On the X client side listen on any address.
13736
// On the X server side listen to the forwarder
13740
if (control -> ProxyMode == proxy_server)
13742
address = (int) inet_addr("127.0.0.1");
13746
address = htonl(INADDR_ANY);
13751
address = inet_addr(listenHost);
13757
int OpenLogFile(char *name, ostream *&stream)
13759
if (name == NULL || *name == '\0')
13762
*logofs << "Loop: WARNING! No name provided for output. Using standard error.\n"
13766
if (stream == NULL)
13774
if (stream == NULL || stream == &cerr)
13776
if (*name != '/' && *name != '.')
13778
char *filePath = GetSessionPath();
13780
if (filePath == NULL)
13783
*logofs << "Loop: PANIC! Cannot determine directory of NX session file.\n"
13787
cerr << "Error" << ": Cannot determine directory of NX session file.\n";
13792
if (strlen(filePath) + strlen("/") +
13793
strlen(name) + 1 > DEFAULT_STRING_LENGTH)
13796
*logofs << "Loop: PANIC! Full name of NX file '" << name
13797
<< " would exceed length of " << DEFAULT_STRING_LENGTH
13798
<< " characters.\n" << logofs_flush;
13801
cerr << "Error" << ": Full name of NX file '" << name
13802
<< " would exceed length of " << DEFAULT_STRING_LENGTH
13803
<< " characters.\n";
13808
char *file = new char[strlen(filePath) + strlen("/") +
13812
// Transform name in a fully qualified name.
13815
strcpy(file, filePath);
13817
strcat(file, name);
13819
strcpy(name, file);
13821
delete [] filePath;
13825
mode_t fileMode = umask(0077);
13829
if ((stream = new ofstream(name, ios::app)) != NULL)
13842
*logofs << "Loop: PANIC! Bad stream provided for output.\n"
13846
cerr << "Error" << ": Bad stream provided for output.\n";
13854
int ReopenLogFile(char *name, ostream *&stream, int limit)
13856
if (*name != '\0' && limit >= 0)
13858
struct stat fileStat;
13863
// This is used for the log file, if the
13864
// size exceeds the limit.
13867
if (stat(name, &fileStat) != 0)
13870
*logofs << "Loop: WARNING! Can't get stats of file '"
13871
<< name << "'. Error is " << EGET()
13872
<< " '" << ESTR() << "'.\n" << logofs_flush;
13877
else if (fileStat.st_size < (long) limit)
13884
*logofs << "Loop: Deleting file '" << name
13885
<< "' with size " << fileStat.st_size
13886
<< ".\n" << logofs_flush;
13890
// Create a new stream over the previous
13891
// file. Trying to delete the file fails
13892
// to work on recent Cygwin installs.
13899
mode_t fileMode = umask(0077);
13903
if ((stream = new ofstream(name, ios::out)) != NULL)
13914
*logofs << "Loop: Reopened file '" << name
13915
<< "'.\n" << logofs_flush;
13922
void PrintProcessInfo()
13926
cerr << "\nNXPROXY - Version " << control -> LocalVersionMajor
13927
<< "." << control -> LocalVersionMinor << "."
13928
<< control -> LocalVersionPatch << "\n\n";
13930
cerr << "Copyright (C) 2001, 2007 NoMachine.\n"
13931
<< "See http://www.nomachine.com/ for more information.\n\n";
13935
// People get confused by the fact that client
13936
// mode is running on NX server and viceversa.
13937
// Let's adopt an user-friendly naming conven-
13941
cerr << "Info: Proxy running in "
13942
<< (control -> ProxyMode == proxy_client ? "server" : "client")
13943
<< " mode with pid '" << getpid() << "'.\n";
13947
cerr << "Session" << ": Starting session at '"
13948
<< strTimestamp() << "'.\n";
13953
if (*errorsFileName != '\0')
13955
cerr << "Info" << ": Using errors file '" << errorsFileName << "'.\n";
13958
if (*statsFileName != '\0')
13960
cerr << "Info" << ": Using stats file '" << statsFileName << "'.\n";
13966
void PrintConnectionInfo()
13968
cerr << "Info" << ": Using "
13969
<< linkSpeedName << " link parameters "
13970
<< control -> TokenSize
13971
<< "/" << control -> TokenLimit
13972
<< "/" << control -> FlushPolicy + 1
13973
<< "/" << control -> FlushPriority
13976
if (control -> ProxyMode == proxy_client)
13978
cerr << "Info" << ": Using agent parameters "
13979
<< control -> PingTimeout
13980
<< "/" << control -> MotionTimeout
13981
<< "/" << control -> IdleTimeout
13982
<< "/" << control -> TaintReplies
13983
<< "/" << control -> HideRender
13987
if (control -> LocalDeltaCompression == 1)
13989
cerr << "Info" << ": Using cache parameters "
13990
<< control -> MinimumMessageSize
13991
<< "/" << control -> MaximumMessageSize / 1024 << "KB"
13992
<< "/" << control -> ClientTotalStorageSize / 1024 << "KB"
13993
<< "/" << control -> ServerTotalStorageSize / 1024 << "KB"
13997
if (control -> ImageCacheEnableLoad == 1 ||
13998
control -> ImageCacheEnableSave == 1)
14000
cerr << "Info" << ": Using image streaming parameters "
14001
<< control -> SplitTimeout
14002
<< "/" << control -> SplitTotalSize
14003
<< "/" << control -> SplitTotalStorageSize / 1024 << "KB"
14004
<< "/" << control -> SplitDataThreshold
14005
<< "/" << control -> SplitDataPacketLimit
14008
cerr << "Info" << ": Using image cache parameters "
14009
<< control -> ImageCacheEnableLoad
14010
<< "/" << control -> ImageCacheEnableSave
14011
<< "/" << control -> ImageCacheDiskLimit / 1024 << "KB"
14015
cerr << "Info" << ": Using pack method '"
14016
<< packMethodName << "' with session '"
14017
<< sessionType << "'.\n";
14019
if (*productName != '\0')
14021
cerr << "Info" << ": Using product '" << productName
14022
<< "'.\n" << logofs_flush;
14025
if (control -> LocalDeltaCompression == 0)
14027
cerr << "Info" << ": Not using NX delta compression.\n";
14030
if (control -> LocalDataCompression == 1 ||
14031
control -> RemoteDataCompression == 1)
14033
cerr << "Info" << ": Using ZLIB data compression "
14034
<< control -> LocalDataCompressionLevel
14035
<< "/" << control -> RemoteDataCompressionLevel
14036
<< "/" << control -> LocalDataCompressionThreshold
14041
cerr << "Info" << ": Not using ZLIB data compression.\n";
14044
if (control -> LocalStreamCompression == 1 ||
14045
control -> RemoteStreamCompression == 1)
14047
cerr << "Info" << ": Using ZLIB stream compression "
14048
<< control -> LocalStreamCompressionLevel
14049
<< "/" << control -> RemoteStreamCompressionLevel
14054
cerr << "Info" << ": Not using ZLIB stream compression.\n";
14057
if (control -> LocalBitrateLimit > 0)
14059
cerr << "Info" << ": Using bandwidth limit of "
14060
<< bitrateLimitName << " bits per second.\n";
14063
if (control -> PersistentCacheName != NULL)
14065
cerr << "Info" << ": Using cache file '"
14066
<< control -> PersistentCachePath << "/"
14067
<< control -> PersistentCacheName << "'.\n";
14071
if (control -> PersistentCacheEnableLoad == 0 ||
14072
control -> LocalDeltaCompression == 0)
14074
cerr << "Info" << ": Not using a persistent cache.\n";
14078
cerr << "Info" << ": No suitable cache file found.\n";
14082
if (control -> ProxyMode == proxy_client &&
14083
useUnixSocket > 0 || useTcpSocket > 0 ||
14084
useAgentSocket > 0)
14086
cerr << "Info" << ": Listening to X11 connections "
14087
<< "on display ':" << xPort << "'.\n";
14089
else if (control -> ProxyMode == proxy_server)
14091
cerr << "Info" << ": Forwarding X11 connections "
14092
<< "to display '" << displayHost << "'.\n";
14095
if (control -> ProxyMode == proxy_client &&
14096
useCupsSocket > 0 && cupsPort > 0)
14098
cerr << "Info" << ": Listening to CUPS connections "
14099
<< "on port '" << cupsPort << "'.\n";
14101
else if (control -> ProxyMode == proxy_server &&
14104
cerr << "Info" << ": Forwarding CUPS connections "
14105
<< "to port '" << cupsPort << "'.\n";
14108
if (control -> ProxyMode == proxy_client &&
14109
useAuxSocket > 0 && auxPort > 0)
14111
cerr << "Info" << ": Listening to auxiliary X11 connections "
14112
<< "on port '" << auxPort << "'.\n";
14114
else if (control -> ProxyMode == proxy_server &&
14117
cerr << "Info" << ": Forwarding auxiliary X11 connections "
14118
<< "to display '" << displayHost << "'.\n";
14121
if (control -> ProxyMode == proxy_client &&
14122
useSmbSocket > 0 && smbPort > 0)
14124
cerr << "Info" << ": Listening to SMB connections "
14125
<< "on port '" << smbPort << "'.\n";
14127
else if (control -> ProxyMode == proxy_server &&
14130
cerr << "Info" << ": Forwarding SMB connections "
14131
<< "to port '" << smbPort << "'.\n";
14134
if (control -> ProxyMode == proxy_client &&
14135
useMediaSocket > 0 && mediaPort > 0)
14137
cerr << "Info" << ": Listening to multimedia connections "
14138
<< "on port '" << mediaPort << "'.\n";
14140
else if (control -> ProxyMode == proxy_server &&
14143
cerr << "Info" << ": Forwarding multimedia connections "
14144
<< "to port '" << mediaPort << "'.\n";
14147
if (control -> ProxyMode == proxy_client &&
14148
useHttpSocket > 0 && httpPort > 0)
14150
cerr << "Info" << ": Listening to HTTP connections "
14151
<< "on port '" << httpPort << "'.\n";
14153
else if (control -> ProxyMode == proxy_server &&
14156
cerr << "Info" << ": Forwarding HTTP connections "
14157
<< "to port '" << httpPort << "'.\n";
14160
if (control -> ProxyMode == proxy_server &&
14161
useFontSocket > 0 && *fontPort != '\0')
14163
cerr << "Info" << ": Listening to font server connections "
14164
<< "on port '" << fontPort << "'.\n";
14166
else if (control -> ProxyMode == proxy_client &&
14169
cerr << "Info" << ": Forwarding font server connections "
14170
<< "to port '" << fontPort << "'.\n";
14173
if (useSlaveSocket > 0 && slavePort > 0)
14175
cerr << "Info" << ": Listening to slave connections "
14176
<< "on port '" << slavePort << "'.\n";
14180
void PrintVersionInfo()
14182
cerr << "NXPROXY - " << "Version "
14183
<< control -> LocalVersionMajor << "."
14184
<< control -> LocalVersionMinor << "."
14185
<< control -> LocalVersionPatch;
14190
void PrintCopyrightInfo()
14194
PrintVersionInfo();
14198
cerr << GetCopyrightInfo();
14201
// Print third party's copyright info.
14206
cerr << GetOtherCopyrightInfo();
14211
void PrintOptionIgnored(const char *type, const char *name, const char *value)
14213
if (control -> ProxyMode == proxy_server)
14216
*logofs << "Loop: WARNING! Ignoring " << type
14217
<< " option '" << name << "' with value '"
14218
<< value << "' at " << "NX client side.\n"
14222
cerr << "Warning" << ": Ignoring " << type
14223
<< " option '" << name << "' with value '"
14224
<< value << "' at " << "NX client side.\n";
14229
*logofs << "Loop: WARNING! Ignoring " << type
14230
<< " option '" << name << "' with value '"
14231
<< value << "' at " << "NX server side.\n"
14235
cerr << "Warning" << ": Ignoring " << type
14236
<< " option '" << name << "' with value '"
14237
<< value << "' at " << "NX server side.\n";
14241
const char *GetOptions(const char *options)
14243
if (options != NULL)
14245
if (strncasecmp(options, "nx/nx,", 6) != 0 &&
14246
strncasecmp(options, "nx,", 3) != 0 &&
14247
strncasecmp(options, "nx:", 3) != 0)
14250
*logofs << "Loop: PANIC! Display options string '" << options
14251
<< "' must start with 'nx' or 'nx/nx' prefix.\n"
14255
cerr << "Error" << ": Display options string '" << options
14256
<< "' must start with 'nx' or 'nx/nx' prefix.\n";
14263
options = getenv("DISPLAY");
14269
const char *GetArg(int &argi, int argc, const char **argv)
14272
// Skip "-" and flag character.
14275
const char *arg = argv[argi] + 2;
14279
if (argi + 1 == argc)
14287
return (*argv[argi] == '-' ? NULL : argv[argi]);
14292
return (*arg == '-' ? NULL : arg);
14296
int CheckArg(const char *type, const char *name, const char *value)
14299
*logofs << "Loop: Parsing " << type << " option '" << name
14300
<< "' with value '" << (value ? value : "(null)")
14301
<< "'.\n" << logofs_flush;
14304
if (value == NULL || strstr(value, "=") != NULL)
14307
*logofs << "Loop: PANIC! Error in " << type << " option '"
14308
<< name << "'. No value found.\n"
14312
cerr << "Error" << ": Error in " << type << " option '"
14313
<< name << "'. No value found.\n";
14317
else if (strstr(name, ",") != NULL)
14320
*logofs << "Loop: PANIC! Parse error at " << type << " option '"
14321
<< name << "'.\n" << logofs_flush;
14324
cerr << "Error" << ": Parse error at " << type << " option '"
14329
else if (strlen(value) >= DEFAULT_STRING_LENGTH)
14332
*logofs << "Loop: PANIC! Value '" << value << "' of "
14333
<< type << " option '" << name << "' exceeds length of "
14334
<< DEFAULT_STRING_LENGTH << " characters.\n"
14338
cerr << "Error" << ": Value '" << value << "' of "
14339
<< type << " option '" << name << "' exceeds length of "
14340
<< DEFAULT_STRING_LENGTH << " characters.\n";
14348
int ParseArg(const char *type, const char *name, const char *value)
14350
if (strcasecmp(value, "0") == 0)
14356
// Find the base factor.
14361
const char *id = value + strlen(value) - 1;
14363
if (strcasecmp(id, "g") == 0)
14365
base = 1024 * 1024 * 1024;
14367
else if (strcasecmp(id, "m") == 0)
14369
base = 1024 * 1024;
14371
else if (strcasecmp(id, "k") == 0)
14375
else if (strcasecmp(id, "b") == 0 || isdigit(*id) == 1)
14384
char *string = new char[strlen(value)];
14386
strncpy(string, value, strlen(value) - 1);
14388
*(string + (strlen(value) - 1)) = '\0';
14392
*logofs << "Loop: Parsing integer option '" << name
14393
<< "' from string '" << string << "' with base set to ";
14395
switch (tolower(*id))
14401
*logofs << (char) toupper(*id);
14406
*logofs << ".\n" << logofs_flush;
14410
double result = atof(string) * base;
14412
if (result < 0 || result > (((unsigned) -1) >> 1))
14422
*logofs << "Loop: Integer option parsed to '"
14423
<< (int) result << "'.\n" << logofs_flush;
14426
return (int) result;
14429
int ValidateArg(const char *type, const char *name, const char *value)
14431
int number = atoi(value);
14436
*logofs << "Loop: PANIC! Invalid " << type
14437
<< " option '" << name << "' with value '"
14438
<< value << "'.\n" << logofs_flush;
14441
cerr << "Error" << ": Invalid " << type
14442
<< " option '" << name << "' with value '"
14443
<< value << "'.\n";
14451
int LowercaseArg(const char *type, const char *name, char *value)
14453
char *next = value;
14455
while (*next != '\0')
14457
*next = tolower(*next);
14465
int CheckSignal(int signal)
14468
// Return 1 if the signal needs to be handled
14469
// by the proxy, 2 if the signal just needs to
14470
// be blocked to avoid interrupting a system
14498
#ifdef __CYGWIN32__
14501
// This signal can be raised by the Cygwin
14517
static void PrintUsageInfo(const char *option, int error)
14521
cerr << "Error" << ": Invalid command line option '" << option << "'.\n";
14524
cerr << GetUsageInfo();
14528
cerr << "Error" << ": NX transport initialization failed.\n";
14532
static void handleCheckSessionInLoop()
14535
// Check if we completed the shutdown procedure
14536
// and the remote confirmed the shutdown. The
14537
// tear down should be always initiated by the
14538
// agent, but the X server side may unilateral-
14539
// ly shut down the link without our permission.
14542
if (proxy -> getShutdown() > 0)
14545
*logofs << "Loop: End of NX transport requested "
14546
<< "by remote.\n" << logofs_flush;
14549
handleTerminatingInLoop();
14551
if (control -> ProxyMode == proxy_server)
14554
*logofs << "Loop: Bytes received so far are "
14555
<< (unsigned long long) statistics -> getBytesIn()
14556
<< ".\n" << logofs_flush;
14559
if (statistics -> getBytesIn() < 1024)
14561
cerr << "Info" << ": Your session was closed before reaching "
14562
<< "a usable state.\n";
14563
cerr << "Info" << ": This can be due to the local X server "
14564
<< "refusing access to the client.\n";
14565
cerr << "Info" << ": Please check authorization provided "
14566
<< "by the remote X application.\n";
14571
*logofs << "Loop: Shutting down the NX transport.\n"
14577
else if (proxy -> handlePing() < 0)
14580
*logofs << "Loop: Failure handling the ping for "
14581
<< "proxy FD#" << proxyFD << ".\n"
14589
// Check if the watchdog has exited and we didn't
14590
// get the SIGCHLD. This can happen if the parent
14591
// has overridden our signal handlers.
14594
if (IsRunning(lastWatchdog) && CheckProcess(lastWatchdog, "watchdog") == 0)
14597
*logofs << "Loop: WARNING! Watchdog is gone unnoticed. "
14598
<< "Setting the last signal to SIGTERM.\n"
14602
lastSignal = SIGTERM;
14605
*logofs << "Loop: WARNING! Resetting pid of last "
14606
<< "watchdog process.\n" << logofs_flush;
14609
SetNotRunning(lastWatchdog);
14613
// Let the client proxy find out if the agent's
14614
// channel is gone. This is the normal shutdown
14615
// procedure in the case of an internal connect-
14616
// ion to the agent.
14621
if (control -> ProxyMode == proxy_client &&
14622
agent != NULL && proxy -> getType(agentFD[1]) ==
14623
channel_none && lastKill == 0 && lastDestroy == 1)
14626
*logofs << "Loop: End of NX transport requested "
14627
<< "by agent.\n" << logofs_flush;
14631
*logofs << "Loop: Bytes sent so far are "
14632
<< (unsigned long long) statistics -> getBytesOut()
14633
<< ".\n" << logofs_flush;
14636
if (statistics -> getBytesOut() < 1024)
14638
cerr << "Info" << ": Your session has died before reaching "
14639
<< "an usable state.\n";
14640
cerr << "Info" << ": This can be due to the remote X server "
14641
<< "refusing access to the client.\n";
14642
cerr << "Info" << ": Please check the authorization provided "
14643
<< "by your X application.\n";
14650
// Check if the user requested the end of the
14651
// session by sending a signal to the proxy.
14652
// All signals are handled in the main loop
14653
// so we need to reset the value to get ready
14654
// for the next iteration.
14659
if (lastSignal != 0)
14661
switch (lastSignal)
14671
signal = lastSignal;
14685
// The first time termination signal is received
14686
// disable all further connections, close down any
14687
// X channel and wait for a second signal.
14693
// Don't print a message if cleanup is
14694
// due to normal termination of agent.
14700
*logofs << "Loop: End of NX transport requested by signal '"
14701
<< signal << "' '" << DumpSignal(signal)
14702
<< "'.\n" << logofs_flush;
14705
handleTerminatingInLoop();
14709
// Disable any further connection.
14712
CleanupListeners();
14715
// Close all the remaining X channels and
14716
// let proxies save their persistent cache
14720
CleanupConnections();
14723
// We'll need to wait for the X channels
14724
// to be shut down before waiting for the
14730
else if (lastKill == 2)
14733
*logofs << "Loop: Shutting down the NX transport.\n"
14737
proxy -> handleShutdown();
14743
if (lastKill == 1 && proxy -> getChannels(channel_x11) == 0)
14746
// Save the message stores to the
14747
// persistent cache.
14750
proxy -> handleSave();
14753
// Run a watchdog process so we can finally
14754
// give up at the time the watchdog exits.
14757
if (IsNotRunning(lastWatchdog))
14759
int timeout = control -> CleanupTimeout;
14763
if (proxy -> getChannels() == 0)
14769
*logofs << "Loop: Starting watchdog process with timeout "
14770
<< "of " << timeout << " Ms.\n"
14777
*logofs << "Loop: Starting watchdog process without "
14778
<< "a timeout.\n" << logofs_flush;
14782
lastWatchdog = NXTransWatchdog(timeout);
14784
if (IsFailed(lastWatchdog))
14787
*logofs << "Loop: PANIC! Can't start the NX watchdog "
14788
<< "process in shutdown.\n" << logofs_flush;
14791
cerr << "Error" << ": Can't start the NX watchdog "
14792
<< "process in shutdown.\n";
14799
*logofs << "Loop: Watchdog started with pid '"
14800
<< lastWatchdog << "'.\n" << logofs_flush;
14807
*logofs << "Loop: PANIC! Previous watchdog detected "
14808
<< "in shutdown with pid '" << lastWatchdog
14809
<< "'.\n" << logofs_flush;
14812
cerr << "Error" << ": Previous watchdog detected "
14813
<< "in shutdown with pid '" << lastWatchdog
14819
if (control -> CleanupTimeout > 0)
14822
*logofs << "Loop: Waiting the cleanup timeout to complete.\n"
14826
cerr << "Info" << ": Waiting the cleanup timeout to complete.\n";
14831
// The NX server will kill the watchdog
14832
// process after having shut down the
14833
// service channels.
14836
cerr << "Info" << ": Watchdog running with pid '" << lastWatchdog
14840
*logofs << "Loop: Waiting the watchdog process to complete.\n"
14844
cerr << "Info" << ": Waiting the watchdog process to complete.\n";
14851
static void handleCheckBitrateInLoop()
14853
static long int slept = 0;
14856
*logofs << "Loop: Bitrate is " << statistics -> getBitrateInShortFrame()
14857
<< " B/s and " << statistics -> getBitrateInLongFrame()
14858
<< " B/s in " << control -> ShortBitrateTimeFrame / 1000
14859
<< "/" << control -> LongBitrateTimeFrame / 1000
14860
<< " seconds timeframes.\n" << logofs_flush;
14864
// This can be improved. We may not jump out
14865
// of the select often enough to guarantee
14866
// the necessary accuracy.
14869
if (control -> LocalBitrateLimit > 0)
14872
*logofs << "Loop: Calculating bandwidth usage with limit "
14873
<< control -> LocalBitrateLimit << ".\n"
14877
int reference = (statistics -> getBitrateInLongFrame() +
14878
statistics -> getBitrateInShortFrame()) / 2;
14880
if (reference > control -> LocalBitrateLimit)
14882
double ratio = ((double) reference) /
14883
((double) control -> LocalBitrateLimit);
14890
slept += (unsigned int) (pow(50000, ratio) / 1000);
14895
*logofs << "Loop: WARNING! Sleeping due to "
14896
<< "reference bitrate of " << reference
14897
<< " B/s.\n" << logofs_flush;
14900
cerr << "Warning" << ": Sleeping due to "
14901
<< "reference bitrate of " << reference
14907
T_timestamp idleTs = getNewTimestamp();
14909
usleep((unsigned int) pow(50000, ratio));
14911
int diffTs = diffTimestamp(idleTs, getNewTimestamp());
14913
statistics -> addIdleTime(diffTs);
14915
statistics -> subReadTime(diffTs);
14920
#if defined(TEST) || defined(INFO)
14922
static void handleCheckStateInLoop(int &setFDs)
14928
for (int j = 0; j < setFDs; j++)
14932
fdPending = proxy -> getPending(j);
14937
*logofs << "Loop: PANIC! Buffer for descriptor FD#"
14938
<< j << " has pending bytes to read.\n"
14945
fdLength = proxy -> getLength(j);
14950
*logofs << "Loop: WARNING! Buffer for descriptor FD#"
14951
<< j << " has " << fdLength << " bytes to write.\n"
14958
fdPending = proxy -> getPending(proxyFD);
14963
*logofs << "Loop: PANIC! Buffer for proxy descriptor FD#"
14964
<< proxyFD << " has pending bytes to read.\n"
14971
fdLength = proxy -> getFlushable(proxyFD);
14975
if (control -> FlushPolicy == policy_immediate &&
14976
proxy -> getBlocked(proxyFD) == 0)
14979
*logofs << "Loop: PANIC! Buffer for proxy descriptor FD#"
14980
<< proxyFD << " has " << fdLength << " bytes "
14981
<< "to write with policy 'immediate'.\n"
14990
*logofs << "Loop: WARNING! Buffer for proxy descriptor FD#"
14991
<< proxyFD << " has " << fdLength << " bytes "
14992
<< "to write.\n" << logofs_flush;
14997
fdSplits = proxy -> getSplitSize();
15002
*logofs << "Loop: WARNING! Proxy descriptor FD#" << proxyFD
15003
<< " has " << fdSplits << " splits to send.\n"
15009
static void handleCheckSelectInLoop(int &setFDs, fd_set &readSet,
15010
fd_set &writeSet, T_timestamp selectTs)
15012
#if defined(TEST) || defined(INFO)
15013
*logofs << "Loop: Maximum descriptors is ["
15014
<< setFDs << "] at " << strMsTimestamp()
15015
<< ".\n" << logofs_flush;
15024
#if defined(TEST) || defined(INFO)
15025
*logofs << "Loop: Selected for read are ";
15028
for (int j = 0; j < setFDs; j++)
15030
if (FD_ISSET(j, &readSet))
15032
#if defined(TEST) || defined(INFO)
15033
*logofs << "[" << j << "]" << logofs_flush;
15042
#if defined(TEST) || defined(INFO)
15043
*logofs << ".\n" << logofs_flush;
15048
#if defined(TEST) || defined(INFO)
15049
*logofs << "[none].\n" << logofs_flush;
15055
#if defined(TEST) || defined(INFO)
15056
*logofs << "Loop: Selected for write are ";
15059
for (int j = 0; j < setFDs; j++)
15061
if (FD_ISSET(j, &writeSet))
15063
#if defined(TEST) || defined(INFO)
15064
*logofs << "[" << j << "]" << logofs_flush;
15073
#if defined(TEST) || defined(INFO)
15074
*logofs << ".\n" << logofs_flush;
15079
#if defined(TEST) || defined(INFO)
15080
*logofs << "[none].\n" << logofs_flush;
15085
#if defined(TEST) || defined(INFO)
15086
*logofs << "Loop: Select timeout is "
15087
<< selectTs.tv_sec << " S and "
15088
<< (double) selectTs.tv_usec / 1000
15089
<< " Ms.\n" << logofs_flush;
15093
static void handleCheckResultInLoop(int &resultFDs, int &errorFDs, int &setFDs, fd_set &readSet,
15094
fd_set &writeSet, struct timeval &selectTs,
15095
struct timeval &startTs)
15097
int diffTs = diffTimestamp(startTs, getNewTimestamp());
15099
#if defined(TEST) || defined(INFO)
15101
if (diffTs >= (control -> PingTimeout -
15102
(control -> LatencyTimeout * 4)))
15104
*logofs << "Loop: Select result is [" << resultFDs
15105
<< "] at " << strMsTimestamp() << " with no "
15106
<< "communication within " << diffTs
15107
<< " Ms.\n" << logofs_flush;
15111
*logofs << "Loop: Select result is [" << resultFDs
15112
<< "] error is [" << errorFDs << "] at "
15113
<< strMsTimestamp() << " after " << diffTs
15114
<< " Ms.\n" << logofs_flush;
15125
#if defined(TEST) || defined(INFO)
15126
*logofs << "Loop: Selected for read are ";
15129
for (int j = 0; j < setFDs; j++)
15131
if (FD_ISSET(j, &readSet))
15133
#if defined(TEST) || defined(INFO)
15134
*logofs << "[" << j << "]" << logofs_flush;
15143
#if defined(TEST) || defined(INFO)
15144
*logofs << ".\n" << logofs_flush;
15149
#if defined(TEST) || defined(INFO)
15150
*logofs << "[none].\n" << logofs_flush;
15156
#if defined(TEST) || defined(INFO)
15157
*logofs << "Loop: Selected for write are ";
15160
for (int j = 0; j < setFDs; j++)
15162
if (FD_ISSET(j, &writeSet))
15164
#if defined(TEST) || defined(INFO)
15165
*logofs << "[" << j << "]" << logofs_flush;
15174
#if defined(TEST) || defined(INFO)
15175
*logofs << ".\n" << logofs_flush;
15180
#if defined(TEST) || defined(INFO)
15181
*logofs << "[none].\n" << logofs_flush;
15189
static void handleCheckSessionInConnect()
15192
*logofs << "Loop: Going to check session in connect.\n"
15196
if (control -> ProxyMode == proxy_client)
15198
HandleAlert(FAILED_PROXY_CONNECTION_CLIENT_ALERT, 1);
15200
else if (IsNotRunning(lastDialog))
15202
HandleAlert(FAILED_PROXY_CONNECTION_SERVER_ALERT, 1);
15205
handleAlertInLoop();
15208
static void handleStatisticsInLoop()
15210
if (lastSignal == 0)
15215
int mode = NO_STATS;
15217
if (control -> EnableStatistics == 1)
15219
if (lastSignal == SIGUSR1)
15222
// Print overall statistics.
15225
mode = TOTAL_STATS;
15227
else if (lastSignal == SIGUSR2)
15230
// Print partial statistics.
15233
mode = PARTIAL_STATS;
15236
if (mode == TOTAL_STATS || mode == PARTIAL_STATS)
15239
*logofs << "Loop: Going to request proxy statistics "
15240
<< "with signal '" << DumpSignal(lastSignal)
15241
<< "'.\n" << logofs_flush;
15246
if (ReopenLogFile(statsFileName, statofs, 0) < 0)
15251
proxy -> handleStatistics(mode, statofs);
15257
static void handleNegotiationInLoop(int &setFDs, fd_set &readSet,
15258
fd_set &writeSet, T_timestamp &selectTs)
15265
*logofs << "Loop: Going to run a new negotiation loop "
15266
<< "with stage " << control -> ProxyStage
15267
<< " at " << strMsTimestamp() << ".\n"
15271
switch (control -> ProxyStage)
15273
case stage_undefined:
15276
*logofs << "Loop: Handling negotiation with '"
15277
<< "stage_undefined" << "'.\n"
15281
control -> ProxyStage = stage_initializing;
15285
case stage_initializing:
15288
*logofs << "Loop: Handling negotiation with '"
15289
<< "stage_initializing" << "'.\n"
15293
InitBeforeNegotiation();
15295
control -> ProxyStage = stage_connecting;
15299
case stage_connecting:
15302
*logofs << "Loop: Handling negotiation with '"
15303
<< "stage_connecting" << "'.\n"
15307
SetupProxyConnection();
15309
control -> ProxyStage = stage_connected;
15313
case stage_connected:
15316
*logofs << "Loop: Handling negotiation with '"
15317
<< "stage_connected" << "'.\n"
15322
// Server side proxy must always be the one that
15323
// sends its version and options first, so, in
15324
// some way, client side can be the the one that
15325
// has the last word on the matter.
15328
if (control -> ProxyMode == proxy_server)
15331
// Check if we have been listening for a
15332
// forwarder. In this case it will have to
15333
// authenticate itself.
15336
if (WE_LISTEN_FORWARDER)
15338
control -> ProxyStage = stage_waiting_forwarder_version;
15343
control -> ProxyStage = stage_sending_proxy_options;
15348
// The X client side is the side that has to wait
15349
// for the authorization cookie and any remote
15353
control -> ProxyStage = stage_waiting_proxy_version;
15358
case stage_sending_proxy_options:
15361
*logofs << "Loop: Handling negotiation with '"
15362
<< "stage_sending_proxy_options" << "'.\n"
15366
if (SendProxyOptions(proxyFD) < 0)
15368
goto handleNegotiationInLoopError;
15371
if (control -> ProxyMode == proxy_server)
15373
control -> ProxyStage = stage_waiting_proxy_version;
15377
control -> ProxyStage = stage_sending_proxy_caches;
15382
case stage_waiting_forwarder_version:
15385
*logofs << "Loop: Handling negotiation with '"
15386
<< "stage_waiting_forwarder_version" << "'.\n"
15390
int result = ReadForwarderVersion(proxyFD);
15396
else if (result == 1)
15398
control -> ProxyStage = stage_waiting_forwarder_options;
15402
goto handleNegotiationInLoopError;
15407
case stage_waiting_forwarder_options:
15410
*logofs << "Loop: Handling negotiation with '"
15411
<< "stage_waiting_forwarder_options" << "'.\n"
15415
int result = ReadForwarderOptions(proxyFD);
15421
else if (result == 1)
15423
control -> ProxyStage = stage_sending_proxy_options;
15427
goto handleNegotiationInLoopError;
15432
case stage_waiting_proxy_version:
15435
*logofs << "Loop: Handling negotiation with '"
15436
<< "stage_waiting_proxy_version" << "'.\n"
15440
int result = ReadProxyVersion(proxyFD);
15446
else if (result == 1)
15448
control -> ProxyStage = stage_waiting_proxy_options;
15452
goto handleNegotiationInLoopError;
15457
case stage_waiting_proxy_options:
15460
*logofs << "Loop: Handling negotiation with '"
15461
<< "stage_waiting_proxy_options" << "'.\n"
15465
int result = ReadProxyOptions(proxyFD);
15471
else if (result == 1)
15473
if (control -> ProxyMode == proxy_server)
15475
control -> ProxyStage = stage_waiting_proxy_caches;
15479
control -> ProxyStage = stage_sending_proxy_options;
15484
goto handleNegotiationInLoopError;
15489
case stage_sending_proxy_caches:
15492
*logofs << "Loop: Handling negotiation with '"
15493
<< "stage_sending_proxy_caches" << "'.\n"
15497
if (SendProxyCaches(proxyFD) < 0)
15499
goto handleNegotiationInLoopError;
15502
if (control -> ProxyMode == proxy_server)
15504
control -> ProxyStage = stage_operational;
15508
control -> ProxyStage = stage_waiting_proxy_caches;
15513
case stage_waiting_proxy_caches:
15516
*logofs << "Loop: Handling negotiation with '"
15517
<< "stage_waiting_proxy_caches" << "'.\n"
15521
int result = ReadProxyCaches(proxyFD);
15527
else if (result == 1)
15529
if (control -> ProxyMode == proxy_server)
15531
control -> ProxyStage = stage_sending_proxy_caches;
15535
control -> ProxyStage = stage_operational;
15540
goto handleNegotiationInLoopError;
15545
case stage_operational:
15548
*logofs << "Loop: Handling negotiation with '"
15549
<< "stage_operational" << "'.\n"
15553
InitAfterNegotiation();
15562
*logofs << "Loop: PANIC! Unmanaged case '" << control -> ProxyStage
15563
<< "' while handling negotiation.\n" << logofs_flush;
15566
cerr << "Error" << ": Unmanaged case '" << control -> ProxyStage
15567
<< "' while handling negotiation.\n";
15575
// Check if the user requested the end of
15579
if (CheckAbort() != 0)
15585
// Select the proxy descriptor so that we
15586
// can proceed negotiating the session.
15589
FD_SET(proxyFD, &readSet);
15591
if (proxyFD >= setFDs)
15593
setFDs = proxyFD + 1;
15596
setMinTimestamp(selectTs, control -> PingTimeout);
15599
*logofs << "Loop: Selected proxy FD#" << proxyFD << " in negotiation "
15600
<< "phase with timeout of " << selectTs.tv_sec << " S and "
15601
<< selectTs.tv_usec << " Ms.\n" << logofs_flush;
15606
handleNegotiationInLoopError:
15609
*logofs << "Loop: PANIC! Failure negotiating the session in stage '"
15610
<< control -> ProxyStage << "'.\n" << logofs_flush;
15613
cerr << "Error" << ": Failure negotiating the session in stage '"
15614
<< control -> ProxyStage << "'.\n";
15617
if (control -> ProxyMode == proxy_server &&
15618
control -> ProxyStage == stage_waiting_proxy_version)
15621
*logofs << "Loop: PANIC! Wrong version or invalid session "
15622
<< "authentication cookie.\n" << logofs_flush;
15625
cerr << "Error" << ": Wrong version or invalid session "
15626
<< "authentication cookie.\n";
15629
handleTerminatingInLoop();
15634
static void handleTerminatingInLoop()
15636
if (getpid() == lastProxy)
15638
if (control -> ProxyStage < stage_terminating)
15642
cerr << "Session" << ": Terminating session at '"
15643
<< strTimestamp() << "'.\n";
15646
control -> ProxyStage = stage_terminating;
15651
static void handleTerminatedInLoop()
15653
if (getpid() == lastProxy)
15655
if (control -> ProxyStage < stage_terminated)
15659
cerr << "Session" << ": Session terminated at '"
15660
<< strTimestamp() << "'.\n";
15663
control -> ProxyStage = stage_terminated;
15668
static void handleAlertInLoop()
15670
if (lastAlert.code == 0)
15675
if (lastAlert.local == 0 &&
15676
(lastAlert.code > LAST_PROTO_STEP_6_ALERT &&
15677
control -> isProtoStep7() == 0))
15680
// The remote proxy would be unable
15681
// to handle the alert.
15685
*logofs << "Loop: WARNING! Ignoring unsupported alert "
15686
<< "with code '" << lastAlert.code << "'.\n"
15690
else if (lastAlert.local == 0)
15694
#if defined(TEST) || defined(INFO)
15695
*logofs << "Loop: Requesting a remote alert with code '"
15696
<< lastAlert.code << "'.\n" << logofs_flush;
15699
if (proxy -> handleAlert(lastAlert.code) < 0)
15707
#if defined(TEST) || defined(INFO)
15708
*logofs << "Loop: Handling a local alert with code '"
15709
<< lastAlert.code << "'.\n" << logofs_flush;
15712
if (control -> ProxyMode == proxy_client)
15715
// If we are at X client side and server
15716
// proxy is not responding, we don't have
15717
// any possibility to interact with user.
15720
if (lastAlert.code != CLOSE_DEAD_PROXY_CONNECTION_CLIENT_ALERT &&
15721
lastAlert.code != RESTART_DEAD_PROXY_CONNECTION_CLIENT_ALERT &&
15722
lastAlert.code != FAILED_PROXY_CONNECTION_CLIENT_ALERT)
15725
// Let the server proxy show the dialog.
15728
if (proxy != NULL &&
15729
proxy -> handleAlert(lastAlert.code) < 0)
15737
char caption[DEFAULT_STRING_LENGTH];
15739
strcpy(caption, ALERT_CAPTION_PREFIX);
15741
int length = strlen(sessionId);
15744
// Get rid of the trailing MD5 from session id.
15747
if (length > (MD5_LENGTH * 2 + 1) &&
15748
*(sessionId + (length - (MD5_LENGTH * 2 + 1))) == '-')
15750
strncat(caption, sessionId, length - (MD5_LENGTH * 2 + 1));
15754
strcat(caption, sessionId);
15758
// Use the display to which we are forwarding
15759
// the remote X connections.
15762
char *display = displayHost;
15770
switch (lastAlert.code)
15772
case CLOSE_DEAD_X_CONNECTION_CLIENT_ALERT:
15774
message = CLOSE_DEAD_X_CONNECTION_CLIENT_ALERT_STRING;
15775
type = CLOSE_DEAD_X_CONNECTION_CLIENT_ALERT_TYPE;
15779
case CLOSE_DEAD_X_CONNECTION_SERVER_ALERT:
15781
message = CLOSE_DEAD_X_CONNECTION_SERVER_ALERT_STRING;
15782
type = CLOSE_DEAD_X_CONNECTION_SERVER_ALERT_TYPE;
15786
case CLOSE_DEAD_PROXY_CONNECTION_SERVER_ALERT:
15788
message = CLOSE_DEAD_PROXY_CONNECTION_SERVER_ALERT_STRING;
15789
type = CLOSE_DEAD_PROXY_CONNECTION_SERVER_ALERT_TYPE;
15793
case RESTART_DEAD_PROXY_CONNECTION_SERVER_ALERT:
15795
message = RESTART_DEAD_PROXY_CONNECTION_SERVER_ALERT_STRING;
15796
type = RESTART_DEAD_PROXY_CONNECTION_SERVER_ALERT_TYPE;
15800
case CLOSE_UNRESPONSIVE_X_SERVER_ALERT:
15802
message = CLOSE_UNRESPONSIVE_X_SERVER_ALERT_STRING;
15803
type = CLOSE_UNRESPONSIVE_X_SERVER_ALERT_TYPE;
15807
case WRONG_PROXY_VERSION_ALERT:
15809
message = WRONG_PROXY_VERSION_ALERT_STRING;
15810
type = WRONG_PROXY_VERSION_ALERT_TYPE;
15814
case FAILED_PROXY_CONNECTION_SERVER_ALERT:
15816
message = FAILED_PROXY_CONNECTION_SERVER_ALERT_STRING;
15817
type = FAILED_PROXY_CONNECTION_SERVER_ALERT_TYPE;
15821
case MISSING_PROXY_CACHE_ALERT:
15823
message = MISSING_PROXY_CACHE_ALERT_STRING;
15824
type = MISSING_PROXY_CACHE_ALERT_TYPE;
15828
case ABORT_PROXY_CONNECTION_ALERT:
15830
message = ABORT_PROXY_CONNECTION_ALERT_STRING;
15831
type = ABORT_PROXY_CONNECTION_ALERT_TYPE;
15835
case DISPLACE_MESSAGE_ALERT:
15837
message = DISPLACE_MESSAGE_ALERT_STRING;
15838
type = DISPLACE_MESSAGE_ALERT_TYPE;
15842
case GREETING_MESSAGE_ALERT:
15844
message = GREETING_MESSAGE_ALERT_STRING;
15845
type = GREETING_MESSAGE_ALERT_TYPE;
15849
case START_RESUME_SESSION_ALERT:
15851
message = START_RESUME_SESSION_ALERT_STRING;
15852
type = START_RESUME_SESSION_ALERT_TYPE;
15856
case FAILED_RESUME_DISPLAY_ALERT:
15858
message = FAILED_RESUME_DISPLAY_ALERT_STRING;
15859
type = FAILED_RESUME_DISPLAY_ALERT_TYPE;
15863
case FAILED_RESUME_DISPLAY_BROKEN_ALERT:
15865
message = FAILED_RESUME_DISPLAY_BROKEN_STRING;
15866
type = FAILED_RESUME_DISPLAY_BROKEN_TYPE;
15870
case FAILED_RESUME_VISUALS_ALERT:
15872
message = FAILED_RESUME_VISUALS_ALERT_STRING;
15873
type = FAILED_RESUME_VISUALS_ALERT_TYPE;
15877
case FAILED_RESUME_COLORMAPS_ALERT:
15879
message = FAILED_RESUME_COLORMAPS_ALERT_STRING;
15880
type = FAILED_RESUME_COLORMAPS_ALERT_TYPE;
15884
case FAILED_RESUME_PIXMAPS_ALERT:
15886
message = FAILED_RESUME_PIXMAPS_ALERT_STRING;
15887
type = FAILED_RESUME_PIXMAPS_ALERT_TYPE;
15891
case FAILED_RESUME_DEPTHS_ALERT:
15893
message = FAILED_RESUME_DEPTHS_ALERT_STRING;
15894
type = FAILED_RESUME_DEPTHS_ALERT_TYPE;
15898
case FAILED_RESUME_RENDER_ALERT:
15900
message = FAILED_RESUME_RENDER_ALERT_STRING;
15901
type = FAILED_RESUME_RENDER_ALERT_TYPE;
15905
case FAILED_RESUME_FONTS_ALERT:
15907
message = FAILED_RESUME_FONTS_ALERT_STRING;
15908
type = FAILED_RESUME_FONTS_ALERT_TYPE;
15912
case INTERNAL_ERROR_ALERT:
15914
message = INTERNAL_ERROR_ALERT_STRING;
15915
type = INTERNAL_ERROR_ALERT_TYPE;
15919
case ABORT_PROXY_NEGOTIATION_ALERT:
15921
message = ABORT_PROXY_NEGOTIATION_ALERT_STRING;
15922
type = ABORT_PROXY_NEGOTIATION_ALERT_TYPE;
15926
case ABORT_PROXY_SHUTDOWN_ALERT:
15928
message = ABORT_PROXY_SHUTDOWN_ALERT_STRING;
15929
type = ABORT_PROXY_SHUTDOWN_ALERT_TYPE;
15933
case FAILED_XDMCP_CONNECTION_ALERT:
15935
message = FAILED_XDMCP_CONNECTION_ALERT_STRING;
15936
type = FAILED_XDMCP_CONNECTION_ALERT_TYPE;
15942
if (lastAlert.code > LAST_PROTO_STEP_7_ALERT)
15945
*logofs << "Loop: WARNING! An unrecognized alert type '"
15946
<< lastAlert.code << "' was requested.\n"
15950
cerr << "Warning" << ": An unrecognized alert type '"
15951
<< lastAlert.code << "' was requested.\n";
15956
*logofs << "Loop: WARNING! Ignoring obsolete alert type '"
15957
<< lastAlert.code << "'.\n" << logofs_flush;
15970
if (replace == 1 && IsRunning(lastDialog))
15972
#if defined(TEST) || defined(INFO)
15973
*logofs << "Loop: Killing the previous dialog with pid '"
15974
<< lastDialog << "'.\n" << logofs_flush;
15978
// The client ignores the TERM signal
15982
#ifdef __CYGWIN32__
15984
KillProcess(lastDialog, "dialog", SIGKILL, 0);
15988
KillProcess(lastDialog, "dialog", SIGTERM, 0);
15992
SetNotRunning(lastDialog);
15996
proxy -> handleResetAlert();
16000
if (message != NULL && type != NULL)
16002
lastDialog = NXTransDialog(caption, message, 0, type, local, display);
16004
if (IsFailed(lastDialog))
16007
*logofs << "Loop: PANIC! Can't start the NX dialog process.\n"
16011
SetNotRunning(lastDialog);
16013
#if defined(TEST) || defined(INFO)
16016
*logofs << "Loop: Dialog started with pid '"
16017
<< lastDialog << "'.\n" << logofs_flush;
16021
#if defined(TEST) || defined(INFO)
16024
*logofs << "Loop: No new dialog required for code '"
16025
<< lastAlert.code << "'.\n" << logofs_flush;
16035
lastAlert.code = 0;
16036
lastAlert.local = 0;
16039
static inline void handleSetAgentInLoop(int &setFDs, fd_set &readSet,
16040
fd_set &writeSet, struct timeval &selectTs)
16043
*logofs << "Loop: Preparing the masks for the agent descriptors.\n"
16047
agent -> saveChannelState();
16049
agent -> saveReadMask(&readSet);
16050
agent -> saveWriteMask(&writeSet);
16052
if (control -> ProxyStage >= stage_operational)
16054
if (agent -> remoteCanRead(&readSet) ||
16055
agent -> remoteCanWrite(&writeSet) ||
16056
agent -> localCanRead() ||
16057
agent -> proxyCanRead())
16060
*logofs << "Loop: Setting a null timeout with agent descriptors ready.\n"
16065
// Force a null timeout so we'll bail out
16066
// of the select immediately. We will ac-
16067
// comodate the result code later.
16070
selectTs.tv_sec = 0;
16071
selectTs.tv_usec = 0;
16076
*logofs << "Loop: Clearing the read and write agent descriptors.\n"
16080
agent -> clearReadMask(&readSet);
16081
agent -> clearWriteMask(&writeSet);
16084
static inline void handleAgentInLoop(int &resultFDs, int &errorFDs, int &setFDs, fd_set &readSet,
16085
fd_set &writeSet, struct timeval &selectTs)
16087
#if defined(TEST) || defined(INFO)
16088
*logofs << "Loop: Setting proxy and local agent descriptors.\n"
16093
// Check if I/O is possible on the local
16094
// agent or the proxy descriptor.
16097
if (resultFDs >= 0)
16100
// Save if the proxy can read from the
16101
// the agent descriptor.
16104
agent -> saveChannelState();
16106
#if defined(TEST) || defined(INFO)
16107
*logofs << "Loop: Values were resultFDs " << resultFDs
16108
<< " errorFDs " << errorFDs << " setFDs "
16109
<< setFDs << ".\n" << logofs_flush;
16112
if (agent -> localCanRead() == 1)
16114
#if defined(TEST) || defined(INFO)
16115
*logofs << "Loop: Setting agent descriptor FD#" << agent ->
16116
getLocalFd() << " as ready to read.\n"
16120
agent -> setLocalRead(&readSet, &resultFDs);
16123
#if defined(TEST) || defined(INFO)
16125
if (agent -> proxyCanRead(&readSet) == 0 &&
16126
agent -> proxyCanRead() == 1)
16128
*logofs << "Loop: WARNING! Can read from proxy FD#"
16129
<< proxyFD << " but the descriptor "
16130
<< "is not selected.\n" << logofs_flush;
16133
if (agent -> proxyCanRead(&readSet) == 1)
16135
*logofs << "Loop: Setting proxy descriptor FD#" << agent ->
16136
getProxyFd() << " as ready to read.\n"
16142
#if defined(TEST) || defined(INFO)
16143
*logofs << "Loop: Values are now resultFDs " << resultFDs
16144
<< " errorFDs " << errorFDs << " setFDs "
16145
<< setFDs << ".\n" << logofs_flush;
16150
static inline void handleAgentLateInLoop(int &resultFDs, int &errorFDs, int &setFDs, fd_set &readSet,
16151
fd_set &writeSet, struct timeval &selectTs)
16153
#if defined(TEST) || defined(INFO)
16154
*logofs << "Loop: Setting remote agent descriptors.\n"
16159
// We reset the masks before calling our select.
16160
// We now set the descriptors that are ready but
16161
// only if they were set in the original mask.
16162
// We do this after having executed our loop as
16163
// we may have produced more data and the agent
16164
// descriptors may have become readable or writ-
16165
// able in the meanwhile.
16168
if (resultFDs >= 0)
16171
// Save if the proxy can read from the
16172
// the agent descriptor.
16175
agent -> saveChannelState();
16177
#if defined(TEST) || defined(INFO)
16178
*logofs << "Loop: Values were resultFDs " << resultFDs
16179
<< " errorFDs " << errorFDs << " setFDs "
16180
<< setFDs << ".\n" << logofs_flush;
16183
if (agent -> remoteCanRead(agent ->
16184
getSavedReadMask()) == 1)
16186
#if defined(TEST) || defined(INFO)
16187
*logofs << "Loop: Setting agent descriptor FD#" << agent ->
16188
getRemoteFd() << " as ready to read.\n"
16192
agent -> setRemoteRead(&readSet, &resultFDs);
16195
if (agent -> remoteCanWrite(agent ->
16196
getSavedWriteMask()) == 1)
16198
#if defined(TEST) || defined(INFO)
16199
*logofs << "Loop: Setting agent descriptor FD#" << agent ->
16200
getRemoteFd() << " as ready to write.\n"
16204
agent -> setRemoteWrite(&writeSet, &resultFDs);
16207
#if defined(TEST) || defined(INFO)
16208
*logofs << "Loop: Values are now resultFDs " << resultFDs
16209
<< " errorFDs " << errorFDs << " setFDs "
16210
<< setFDs << ".\n" << logofs_flush;
16215
static inline void handleReadableInLoop(int &resultFDs, fd_set &readSet)
16219
T_channel_type type = channel_none;
16221
char *label = NULL;
16225
if (tcpFD != -1 && FD_ISSET(tcpFD, &readSet))
16227
type = channel_x11;
16235
if (unixFD != -1 && FD_ISSET(unixFD, &readSet))
16237
type = channel_x11;
16245
if (cupsFD != -1 && FD_ISSET(cupsFD, &readSet))
16247
type = channel_cups;
16255
if (auxFD != -1 && FD_ISSET(auxFD, &readSet))
16258
// Starting from version 1.5.0 we create real X
16259
// connections for the keyboard channel, so they
16260
// can use the fake authorization cookie. This
16261
// means that there is not such a thing like a
16262
// channel_aux anymore.
16265
type = channel_x11;
16266
label = "auxiliary X11";
16273
if (smbFD != -1 && FD_ISSET(smbFD, &readSet))
16275
type = channel_smb;
16283
if (mediaFD != -1 && FD_ISSET(mediaFD, &readSet))
16285
type = channel_media;
16293
if (httpFD != -1 && FD_ISSET(httpFD, &readSet))
16295
type = channel_http;
16303
if (fontFD != -1 && FD_ISSET(fontFD, &readSet))
16305
type = channel_font;
16306
label = "font server";
16313
if (slaveFD != -1 && FD_ISSET(slaveFD, &readSet))
16315
type = channel_slave;
16323
if (type != channel_none)
16325
int newFD = AcceptConnection(fd, domain, label);
16329
if (proxy -> handleNewConnection(type, newFD) < 0)
16332
*logofs << "Loop: PANIC! Error creating new " << label
16333
<< " connection.\n" << logofs_flush;
16336
cerr << "Error" << ": Error creating new " << label
16337
<< " connection.\n";
16342
// Don't kill the proxy in the case of an error.
16344
// HandleCleanup();
16347
else if (proxy -> getReadable(newFD) > 0)
16350
// Add the descriptor, so we can try
16351
// to read immediately.
16355
*logofs << "Loop: Trying to read immediately "
16356
<< "from descriptor FD#" << newFD
16357
<< ".\n" << logofs_flush;
16360
FD_SET(newFD, &readSet);
16367
*logofs << "Loop: Nothing to read immediately "
16368
<< "from descriptor FD#" << newFD
16369
<< ".\n" << logofs_flush;
16377
*logofs << "Loop: Going to check the readable descriptors.\n"
16381
if (proxy -> handleRead(resultFDs, readSet) < 0)
16384
*logofs << "Loop: Failure reading from descriptors "
16385
<< "for proxy FD#" << proxyFD << ".\n"
16393
static inline void handleWritableInLoop(int &resultFDs, fd_set &writeSet)
16396
*logofs << "Loop: Going to check the writable descriptors.\n"
16400
if (resultFDs > 0 && proxy -> handleFlush(resultFDs, writeSet) < 0)
16403
*logofs << "Loop: Failure writing to descriptors "
16404
<< "for proxy FD#" << proxyFD << ".\n"
16412
static inline void handleFlushInLoop()
16415
*logofs << "Loop: Going to flush any data to the proxy.\n"
16419
if (agent == NULL || control ->
16420
FlushPolicy == policy_immediate)
16422
#if defined(TEST) || defined(INFO)
16424
if (usePolicy == -1 && control ->
16425
ProxyMode == proxy_client)
16427
*logofs << "Loop: WARNING! Flushing the proxy link "
16428
<< "on behalf of the agent.\n" << logofs_flush;
16433
if (proxy -> handleFlush() < 0)
16436
*logofs << "Loop: Failure flushing the proxy FD#"
16437
<< proxyFD << ".\n" << logofs_flush;
16445
static inline void handleRotateInLoop()
16448
*logofs << "Loop: Going to rotate channels "
16449
<< "for proxy FD#" << proxyFD << ".\n"
16453
proxy -> handleRotate();
16456
static inline void handleEventsInLoop()
16459
*logofs << "Loop: Going to check channel events "
16460
<< "for proxy FD#" << proxyFD << ".\n"
16464
if (proxy -> handleEvents() < 0)
16467
*logofs << "Loop: Failure handling channel events "
16468
<< "for proxy FD#" << proxyFD << ".\n"
16476
static void handleLogReopenInLoop(T_timestamp &logsTs, T_timestamp &nowTs)
16479
// If need to limit the size of the
16480
// log file, check the size at each
16486
if (diffTimestamp(logsTs, nowTs) > control -> FileSizeCheckTimeout)
16491
*logofs << "Loop: Checking size of log file '"
16492
<< errorsFileName << "'.\n" << logofs_flush;
16497
if (ReopenLogFile(errorsFileName, logofs, control -> FileSizeLimit) < 0)
16505
// Reset to current timestamp.
16512
static inline void handleSetReadInLoop(fd_set &readSet, int &setFDs, struct timeval &selectTs)
16514
proxy -> setReadDescriptors(&readSet, setFDs, selectTs);
16517
static inline void handleSetWriteInLoop(fd_set &writeSet, int &setFDs, struct timeval &selectTs)
16519
proxy -> setWriteDescriptors(&writeSet, setFDs, selectTs);
16522
static void handleSetListenersInLoop(fd_set &readSet, int &setFDs)
16525
// Set descriptors of listening sockets.
16528
if (control -> ProxyMode == proxy_client)
16530
if (useTcpSocket == 1)
16532
FD_SET(tcpFD, &readSet);
16534
if (tcpFD >= setFDs)
16536
setFDs = tcpFD + 1;
16540
*logofs << "Loop: Selected listener tcpFD " << tcpFD
16541
<< " with setFDs " << setFDs << ".\n"
16546
if (useUnixSocket == 1)
16548
FD_SET(unixFD, &readSet);
16550
if (unixFD >= setFDs)
16552
setFDs = unixFD + 1;
16556
*logofs << "Loop: Selected listener unixFD " << unixFD
16557
<< " with setFDs " << setFDs << ".\n"
16562
if (useCupsSocket == 1)
16564
FD_SET(cupsFD, &readSet);
16566
if (cupsFD >= setFDs)
16568
setFDs = cupsFD + 1;
16572
*logofs << "Loop: Selected listener cupsFD " << cupsFD
16573
<< " with setFDs " << setFDs << ".\n"
16578
if (useAuxSocket == 1)
16580
FD_SET(auxFD, &readSet);
16582
if (auxFD >= setFDs)
16584
setFDs = auxFD + 1;
16588
*logofs << "Loop: Selected listener auxFD " << auxFD
16589
<< " with setFDs " << setFDs << ".\n"
16594
if (useSmbSocket == 1)
16596
FD_SET(smbFD, &readSet);
16598
if (smbFD >= setFDs)
16600
setFDs = smbFD + 1;
16604
*logofs << "Loop: Selected listener smbFD " << smbFD
16605
<< " with setFDs " << setFDs << ".\n"
16610
if (useMediaSocket == 1)
16612
FD_SET(mediaFD, &readSet);
16614
if (mediaFD >= setFDs)
16616
setFDs = mediaFD + 1;
16620
*logofs << "Loop: Selected listener mediaFD " << mediaFD
16621
<< " with setFDs " << setFDs << ".\n"
16626
if (useHttpSocket == 1)
16628
FD_SET(httpFD, &readSet);
16630
if (httpFD >= setFDs)
16632
setFDs = httpFD + 1;
16636
*logofs << "Loop: Selected listener httpFD " << httpFD
16637
<< " with setFDs " << setFDs << ".\n"
16644
if (useFontSocket == 1)
16646
FD_SET(fontFD, &readSet);
16648
if (fontFD >= setFDs)
16650
setFDs = fontFD + 1;
16654
*logofs << "Loop: Selected listener fontFD " << fontFD
16655
<< " with setFDs " << setFDs << ".\n"
16661
if (useSlaveSocket == 1)
16663
FD_SET(slaveFD, &readSet);
16665
if (slaveFD >= setFDs)
16667
setFDs = slaveFD + 1;
16671
*logofs << "Loop: Selected listener slaveFD " << slaveFD
16672
<< " with setFDs " << setFDs << ".\n"