EPICS Controls Argonne National Laboratory

Experimental Physics and
Industrial Control System

1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  2014  <20152016  2017  2018  2019  2020  2021  2022  2023  2024  Index 1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  2014  <20152016  2017  2018  2019  2020  2021  2022  2023  2024 
<== Date ==> <== Thread ==>

Subject: RE: procServ+devIocStats questions
From: <[email protected]>
To: <[email protected]>, <[email protected]>
Date: Wed, 25 Mar 2015 13:16:54 +0000
> 
> I've made a couple of other small changes to procServ for running on
> Windows which I've been meaning to post for a while, I'll collect them
> together and post them later
> 

I enclose a diff of the changes I have to procServ 2.6.0 (compiled under Cygwin) which I found useful when using it to manage native windows IOCs. In summary they:

* add spawned process to a windows job object. Killing the "process group" from Cygwin doesn't always clean everything up. For example, if the CS Studio ArchiveEngine is being managed it is killed, but not the java.exe JVM process it spawns. Job objects are inherited by spawned processes, so both are now killed automatically.
* hide console command window that is created
* fix small memory leak in clientItem() destructor
* automatic daily log file rotation - log file name is parsed by strftime() to determine filename format
* updated libtelnet to never version (fixes an off by one malloc error)
* Added remakeConnection() method to acceptItem - after a lot of connect/disconnect calls  I found procServ stopped accepting connections.
* windows does not do line buffering, so need to look for EOL to write timestamps in correct place on output line
* handle SIGTTIN, set SNDTIMEO on socket and also do select() before write to process. I had some hangs in cygwin   fhandler_pty_master::doecho()   and these changes were part of debugging this, probably not necessary anymore. 

Regards,

Freddie

Index: procServ.cc
===================================================================
--- procServ.cc	(revision 408)
+++ procServ.cc	(working copy)
@@ -34,7 +34,7 @@
 bool   ctlPortLocal = true;      // Restrict control connections to localhost
 bool   autoRestart = true;       // Enables instant restart of exiting child
 bool   waitForManualStart = false;  // Waits for telnet cmd to manually start child
-bool   shutdownServer = false;   // To keep the server from shutting down
+volatile bool   shutdownServer = false;   // To keep the server from shutting down
 bool   quiet = false;            // Suppress info output (server)
 bool   setCoreSize = false;      // Set core size for child
 char   *procservName;            // The name of this beast (server)
@@ -71,6 +71,8 @@
 int    logPort;                  // Port no. for logger connections
 int    debugFD=-1;               // FD for debug output
 
+static int save_tm_yday = -1;    // current day, used for log rotating
+
 #define MAX_CONNECTIONS 64
 
 // mLoop runs the program
@@ -85,11 +87,14 @@
 void ttySetCharNoEcho(bool save);
 
 // Signal handlers
-void OnSigPipe(int);
-void OnSigTerm(int);
-void OnSigHup(int);
+static void OnSigPipe(int);
+static void OnSigTerm(int);
+static void OnSigHup(int);
+static void OnSigTtin(int);
+
 // Counter used for communication between sig handler and main()
-int sigPipeSet;
+static volatile sig_atomic_t sigPipeSet = 0;  
+static volatile sig_atomic_t sigTtinSet = 0;  
 
 void writePidFile()
 {
@@ -379,7 +384,6 @@
 	printUsage();
 	exit(1);
     }
-
     // Single command characters should be ignored, too
     if (ignChars == NULL && (killChar || toggleRestartChar || logoutChar))
         ignChars = (char*) calloc(1 + ONE_CHAR_COMMANDS, 1);
@@ -436,6 +440,8 @@
     sigaction(SIGTERM, &sig, NULL);
     sig.sa_handler = &OnSigHup;
     sigaction(SIGHUP, &sig, NULL);
+    sig.sa_handler = &OnSigTtin;    // If we handle it, caller gets EINTR; if we ignore it, caller gets EIO
+    sigaction(SIGTTIN, &sig, NULL);
     sig.sa_handler = SIG_IGN;
     sigaction(SIGXFSZ, &sig, NULL);
     if (inFgMode) {
@@ -528,6 +534,11 @@
             SendToAll( buf, strlen(buf), NULL );
             sigPipeSet--;
         }
+        if (sigTtinSet > 0) {
+            sprintf( buf, "@@@ Got a sigTtin signal" NL);
+            SendToAll( buf, strlen(buf), NULL );
+            sigTtinSet--;
+        }
 
         // Prepare FD set for select()
         p = connectionItem::head;
@@ -587,19 +598,49 @@
     int len = 0;
     time_t now;
     struct tm now_tm;
+	static bool log_stamp_sent = false;
 
-    if (stampLog) {
-        time(&now);
-        localtime_r(&now, &now_tm);
+    time(&now);
+    localtime_r(&now, &now_tm);
+	if (stampLog)
+	{
         strftime(stamp, sizeof(stamp)-1, stampFormat, &now_tm);
         len = strlen(stamp);
+	}
+    if (now_tm.tm_yday != save_tm_yday)
+    {
+        openLogFile();  // reopen log file on day change to allow for log file rotation
+		log_stamp_sent = false;
     }
     // Log the traffic to file / stdout (debug)
     if (sender==NULL || sender->isProcess())
     {
         if (logFileFD > 0) {
-            if (stampLog) write(logFileFD, stamp, len);
-            write(logFileFD, message, count);
+            if (stampLog) 
+			{
+			    // Windows does not support line buffering, so we can get parts of lines - hence need to track of when to send timestamp
+			    int i = 0, j = 0;
+			    for(i = 0; i < count; ++i)
+				{
+					if (!log_stamp_sent)
+					{
+						write(logFileFD, stamp, len);
+						log_stamp_sent = true;
+					}
+				    if (message[i] == '\n')
+					{
+                        write(logFileFD, message+j, i-j+1);
+						j = i + 1;
+						log_stamp_sent = false;
+					}
+				}
+                write(logFileFD, message+j, count-j);  // finish off rest of line with no newline at end
+			}
+			else
+			{
+                write(logFileFD, message, count);
+			}
+            fsync(logFileFD);
         }
         if (false == inFgMode && debugFD > 0) write(debugFD, message, count);
     }
@@ -702,13 +743,13 @@
 	assert(connectionNo>=0);
 }
 
-void OnSigPipe(int)
+static void OnSigPipe(int)
 {
     PRINTF("SigPipe received\n");
     sigPipeSet++;
 }
 
-void OnSigTerm(int)
+static void OnSigTerm(int)
 {
     PRINTF("SigTerm received\n");
     processFactorySendSignal(killSig);
@@ -715,12 +756,18 @@
     shutdownServer = true;
 }
 
-void OnSigHup(int)
+static void OnSigHup(int)
 {
     PRINTF("SigHup received\n");
     openLogFile();
 }
 
+static void OnSigTtin(int)
+{
+    PRINTF("SigTtin received\n");
+    sigTtinSet++;
+}
+
 // Fork the daemon and exit the parent
 void forkAndGo()
 {
@@ -754,6 +801,8 @@
 
         // Make sure we are not attached to a terminal
         setsid();
+		
+		hideWindow();
     }
 }
 
@@ -763,8 +812,6 @@
 int checkCommandFile(const char * command)
 {
     struct stat s;
-    mode_t must_perm   =         S_IXUSR        |S_IXGRP        |S_IXOTH;
-    mode_t should_perm = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
 
     PRINTF("Checking command file validity\n");
 
@@ -784,6 +831,10 @@
         return -1;
     }
 
+// if we are using cygwin to run windows executables, permission bits are meaningless
+#ifndef __CYGWIN__
+    mode_t must_perm   =         S_IXUSR        |S_IXGRP        |S_IXOTH;
+    mode_t should_perm = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
     if (must_perm != (s.st_mode & must_perm)) {
         fprintf(stderr, "%s: Error - Please change permissions on %s to at least ---x--x--x\n"
                 "procServ is not able to continue without execute permission\n",
@@ -797,6 +848,7 @@
                 procservName, command);
         return 0;
     }
+#endif /* __CYGWIN__ */
 
     return 0;
 }
@@ -803,17 +855,28 @@
 
 void openLogFile()
 {
+    char logFileName[1024];
+    time_t now;
+    struct tm now_tm;
     if (-1 != logFileFD) {
         close(logFileFD);
     }
+    time(&now);
+    localtime_r(&now, &now_tm);
+    save_tm_yday = now_tm.tm_yday;
     if (logFile) {
-        logFileFD = open(logFile, O_CREAT|O_WRONLY|O_APPEND, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
+        if (0 == strftime(logFileName, sizeof(logFileName)-1, logFile, &now_tm))
+        {
+            strncpy(logFileName, logFile, sizeof(logFileName)-1);
+        }
+        logFileName[sizeof(logFileName)-1] = '\0';
+        logFileFD = open(logFileName, O_CREAT|O_WRONLY|O_APPEND, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
         if (-1 == logFileFD) {         // Don't stop here - just go without
             fprintf(stderr,
-                    "%s: unable to open log file %s\n",
-                    procservName, logFile);
+                    "%s: unable to open log file %s (%s)\n",
+                    procservName, logFileName, logFile);
         } else {
-            PRINTF("Opened file %s for logging\n", logFile);
+            PRINTF("Opened file %s for logging\n", logFileName);
         }
     }
 }
@@ -837,6 +900,17 @@
     }
 }
 
+void hideWindow()
+{
+#ifdef __CYGWIN__
+		HWND conwin = GetConsoleWindow();
+		if ( conwin != NULL )
+		{
+			ShowWindowAsync(conwin, SW_HIDE);
+		}
+#endif
+}
+
 connectionItem * connectionItem::head;
 // Globals:
 time_t procServStart; // Time when this IOC started
Index: procServ.h
===================================================================
--- procServ.h	(revision 408)
+++ procServ.h	(working copy)
@@ -17,6 +17,10 @@
 #include <stdio.h>
 #include <time.h>
 
+#ifdef __CYGWIN__
+#include <windows.h>
+#endif /* __CYGWIN__ */
+
 #ifndef PRINTF
 #define PRINTF if (inDebugMode) printf
 #endif
@@ -27,7 +31,7 @@
 extern bool   logPortLocal;
 extern bool   autoRestart;
 extern bool   waitForManualStart;
-extern bool   shutdownServer;
+extern volatile bool   shutdownServer;
 extern bool   setCoreSize;
 extern char   *procservName;
 extern char   *childName;
@@ -127,4 +131,6 @@
     };
 };
 
+void hideWindow();
+
 #endif /* #ifndef procServH */
Index: clientFactory.cc
===================================================================
--- clientFactory.cc	(revision 408)
+++ clientFactory.cc	(working copy)
@@ -58,6 +58,10 @@
 clientItem::~clientItem()
 {
     if ( _fd >= 0 ) close( _fd );
+    if (_telnet != NULL)
+    {
+        telnet_free(_telnet);
+    }
     PRINTF("~clientItem()\n");
     if (_readonly) _loggers--;
     else _users--;
@@ -65,6 +69,7 @@
 
 // Client item constructor
 // This sets KEEPALIVE on the socket and displays the greeting
+// Also sets the socket SNDTIMEO
 clientItem::clientItem(int socketIn, bool readonly)
 {
     assert(socketIn>=0);
@@ -77,6 +82,9 @@
     char buf1[512], buf2[512];
     char greeting1[] = "@@@ Welcome to procServ (" PROCSERV_VERSION_STRING ")" NL;
     char greeting2[256] = "";
+	struct timeval send_timeout; 
+	send_timeout.tv_sec = 10;
+    send_timeout.tv_usec = 0;
 
     PRINTF("New clientItem %p\n", this);
     if ( killChar ) {
@@ -118,6 +126,8 @@
              _users, _loggers );
 
     setsockopt( socketIn, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval) );
+	setsockopt( socketIn, SOL_SOCKET, SO_SNDTIMEO, &send_timeout, sizeof(send_timeout) );
+
     _fd = socketIn;
     _readonly = readonly;
 
@@ -165,6 +175,7 @@
         PRINTF("clientItem:: Got error reading input connection: %s\n", strerror(errno));
         _markedForDeletion = true;
     } else if (false == _readonly) {
+		buf[len] = '\0';
         telnet_recv(_telnet, buf, len);
     }
 }
Index: processClass.h
===================================================================
--- processClass.h	(revision 408)
+++ processClass.h	(working copy)
@@ -29,6 +29,10 @@
     pid_t _pid;
     static processClass * _runningItem;
     static time_t _restartTime;
+	void terminateJob();
+#ifdef __CYGWIN__
+	HANDLE _hwinjob;
+#endif /* __CYGWIN__ */
 };
 
 
Index: processFactory.cc
===================================================================
--- processFactory.cc	(revision 408)
+++ processFactory.cc	(working copy)
@@ -16,6 +16,10 @@
 #include <string.h>
 #include <strings.h>
 
+#ifdef __CYGWIN__
+#include <sys/cygwin.h>
+#endif /* __CYGWIN__ */
+
 // forkpty()
 #ifdef HAVE_LIBUTIL_H      // FreeBSD
 #include <libutil.h>
@@ -102,7 +106,8 @@
 
                                 // Negative PID sends signal to all members of process group
     if ( _pid > 0 ) kill( -_pid, SIGKILL );
-    if ( _fd > 0 ) close( _fd );
+	terminateJob();
+	if ( _fd > 0 ) close( _fd );
     _runningItem = NULL;
 }
 
@@ -118,17 +123,66 @@
     struct rlimit corelimit;
     char buf[128];
 
+#ifdef __CYGWIN__
+	_hwinjob = CreateJobObject(NULL, NULL);
+	if (_hwinjob == NULL)
+	{
+            fprintf(stderr, "CreateJobObject failed\n");
+	}
+	// set processes assigned to a job to automatically terminate when the job handle object is closed.
+	// This is for additional security - we call TerminateJobObject() as well 
+	JOBOBJECT_EXTENDED_LIMIT_INFORMATION job_limits;
+	if (QueryInformationJobObject(_hwinjob, JobObjectExtendedLimitInformation, &job_limits, sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION), NULL) != 0)
+	{
+		job_limits.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
+		if (SetInformationJobObject(_hwinjob, JobObjectExtendedLimitInformation, &job_limits, sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION)) == 0)
+		{
+            fprintf(stderr, "SetInformationJobObject failed\n");
+		}
+	}
+	else
+	{
+            fprintf(stderr, "QueryInformationJobObject failed\n");
+	}
+#endif /* __CYGWIN__ */
+	
+	
     _pid = forkpty(&_fd, factoryName, NULL, NULL);
 
     _markedForDeletion = _pid <= 0;
     if (_pid)                               // I am the parent
     {
-	if(_pid < 0) {
+	    if(_pid < 0) {
             fprintf(stderr, "Fork failed: %s\n", errno == ENOENT ? "No pty" : strerror(errno));
         } else {
             PRINTF("Created process %ld on %s\n", (long) _pid, factoryName);
         }
+#ifdef __CYGWIN__
+	    int winpid = cygwin_internal(CW_CYGWIN_PID_TO_WINPID, _pid);
+        PRINTF("Created win process %ld on %s\n", (long) winpid, factoryName);
 
+		HANDLE hprocess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, winpid); 
+		if (hprocess != NULL)
+		{
+			if (AssignProcessToJobObject(_hwinjob, hprocess) != 0)
+			{
+				PRINTF("Assigned win process %ld to job object\n", (long)winpid);
+			}
+			else
+			{
+				fprintf(stderr, "AssignProcessToJobObject failed\n");
+			}
+			if (CloseHandle(hprocess) == 0)
+			{
+				fprintf(stderr, "CloseHandle(hprocess) failed\n");
+			}
+		}
+		else
+		{
+			fprintf(stderr, "OpenProcess failed for win process %ld\n", (long)winpid);
+		}
+#endif /* __CYGWIN__ */
+
         // Don't start a new one before this time:
         _restartTime = holdoffTime + time(0);
 
@@ -139,9 +193,12 @@
         SendToAll( buf, strlen(buf), this );
         strcpy(buf, "@@@ @@@ @@@ @@@ @@@" NL);
         SendToAll( buf, strlen(buf), this );
+
+		
     }
     else                                    // I am the child
     {
+	    sleep(1);   // to allow AssignProcessToJobObject() to happen - the process we spawn may spawn other processes so we want it to inherit
         setsid();                                  // Become process group leader
         if ( setCoreSize ) {                       // Set core size limit?
             getrlimit( RLIMIT_CORE, &corelimit );
@@ -169,8 +226,8 @@
     char  buf[1600];
 
     int len = read(_fd, buf, sizeof(buf)-1);
-    if (len < 1) {
-        PRINTF("processItem: Got error reading input connection\n");
+    if (len < 0) {
+        PRINTF("processItem: Got error reading input connection: %s\n", strerror(errno));
         _markedForDeletion = true;
     } else if (len == 0) {
         PRINTF("processItem: Got EOF reading input connection\n");
@@ -185,9 +242,16 @@
 int processClass::Send( const char * buf, int count )
 {
     int status = 0;
-    int i, j, ign = 0;
+    int i, j, nsend, ign = 0;
     char buf3[LINEBUF_LENGTH+1];
     char *buf2 = buf3;
+	fd_set writefds;
+	struct timeval select_timeout;
+	FD_ZERO(&writefds);
+	FD_SET(_fd, &writefds);
+	int wait_time = 10;
+	select_timeout.tv_sec = wait_time;
+	select_timeout.tv_usec = 0;
 
                                 // Create working copy of buffer
     if ( count > LINEBUF_LENGTH ) buf2 = (char*) calloc (count + 1, 1);
@@ -202,14 +266,39 @@
     } else {                    // Plain buffer copy
         strncpy (buf2, buf, count);
     }
-        buf2[count - ign] = '\0';
+	nsend = count - ign;
+    buf2[nsend] = '\0';
 
-    if ( count > 0 )
+	// we seem to get occasional hangs in cygwin fhandler_pty_master::doecho()
+	// not sure is using select() will help, but will try
+    if ( nsend > 0 )
     {
-	status = write( _fd, buf2, count - ign );
-	if ( status < 0 ) _markedForDeletion = true;
-    }
-
+		if ( (status = select(_fd+1, NULL, &writefds, NULL, &select_timeout)) > 0 )
+		{
+			status = write( _fd, buf2, nsend );
+			if ( status < 0 ) // write() failed
+			{
+                PRINTF("processItem: Got error writing to input connection: %s\n", strerror(errno));
+				_markedForDeletion = true;
+			}
+			else if ( status != nsend ) // write() did not fully complete
+			{
+				PRINTF("processItem: only wrote %d of %d characters\n", status, nsend);
+				_markedForDeletion = true;
+			}
+		}
+		else if ( 0 == status ) // select() timeout
+		{
+			PRINTF("processItem: select timeout after %d seconds\n", wait_time);
+			status = -1;
+			_markedForDeletion = true;
+		}
+		else // status < 0, select() error
+		{
+			PRINTF("processItem: select error: %s\n", strerror(errno));
+			_markedForDeletion = true;
+		}
+	}
     if ( count > LINEBUF_LENGTH ) free( buf2 );
     return status;
 }
@@ -223,6 +312,11 @@
 	PRINTF("Sending signal %d to pid %ld\n",
 		signal, (long) processClass::_runningItem->_pid);
 	kill(-processClass::_runningItem->_pid,signal);
+	if (signal == killSig)
+	{
+	    processClass::_runningItem->terminateJob();
+	}
+	
     }
 }
 
@@ -230,3 +324,21 @@
 {
     _restartTime = 0;
 }
+
+void processClass::terminateJob()
+{
+#ifdef __CYGWIN__
+	if (_hwinjob != NULL)
+	{
+	    if (TerminateJobObject(_hwinjob, 1) == 0)
+		{
+			fprintf(stderr, "TerminateJobObject failed\n");
+		}
+	    if (CloseHandle(_hwinjob) == 0)
+		{
+			fprintf(stderr, "CloseHandle(_hwinjob) failed\n");
+		}
+		_hwinjob = NULL;
+	}
+#endif /* __CYGWIN__ */
+}
Index: configure.ac
===================================================================
--- configure.ac	(revision 408)
+++ configure.ac	(working copy)
@@ -9,8 +9,8 @@
 AC_INIT([procServ Process Server], [2.6.0], [[email protected]], [procServ],
 	[http://sourceforge.net/projects/procserv/])
 AC_SUBST([PACKAGE_DATE],[04/16/2012])
-
 AC_CONFIG_AUX_DIR([build-aux])
+AC_CANONICAL_HOST
 AM_INIT_AUTOMAKE([1.10 -Wall])
 
 AC_CONFIG_SRCDIR([connectionItem.cc])
@@ -17,8 +17,14 @@
 # we don't need config.h for now
 #AC_CONFIG_HEADER([config.h])
 
-CFLAGS="$CFLAGS -Wall"
-CXXFLAGS="$CXXFLAGS -Wall"
+#DEBUG_TYPE="-g -O0 -fno-omit-frame-pointer" # -gcoff
+DEBUG_TYPE="-g" # -gcoff
+CFLAGS="$CFLAGS -Wall $DEBUG_TYPE"
+CXXFLAGS="$CXXFLAGS -Wall $DEBUG_TYPE"
+if test "$host_os" = "cygwin"; then
+    LDFLAGS="$LDFLAGS -static -Wl,-Map,procServ.map"
+fi
+LIBS="$LIBS"
 
 # Checks for programs.
 AC_PROG_CXX
Index: libtelnet.c
===================================================================
--- libtelnet.c	(revision 408)
+++ libtelnet.c	(working copy)
@@ -769,7 +769,7 @@
 		char *name;
 
 		/* allocate space for name */
-		if ((name = (char *)malloc(size - 1)) == 0) {
+		if ((name = (char *)malloc(size)) == 0) {
 			_error(telnet, __LINE__, __func__, TELNET_ENOMEM, 0,
 					"malloc() failed: %s", strerror(errno));
 			return 0;
Index: acceptFactory.cc
===================================================================
--- acceptFactory.cc	(revision 408)
+++ acceptFactory.cc	(working copy)
@@ -22,6 +22,12 @@
 
 class acceptItem : public connectionItem
 {
+private:
+	int _port;
+	bool _local;
+private:
+	void remakeConnection();
+
 public:
     acceptItem ( int port, bool local, bool readonly );
     void readFromFd(void);
@@ -50,23 +56,34 @@
 // Accept item constructor
 // This opens a socket, binds it to the decided port,
 // and sets it to listen mode
-acceptItem::acceptItem(int port, bool local, bool readonly)
+acceptItem::acceptItem(int port, bool local, bool readonly) : connectionItem(-1, readonly), _port(port), _local(local)
 {
-    int optval = 1;
+	remakeConnection();
+}
+
+void acceptItem::remakeConnection()
+{
+	int optval = 1;
     struct sockaddr_in addr;
     int bindStatus;
+	if ( _fd >=0 )
+	{
+		close(_fd);
+	}
 
-    _readonly = readonly;
-
     _fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
-    assert(_fd>0);
+	if (_fd < 0)
+	{
+        PRINTF("Socket error: %s\n", strerror(errno));
+        throw errno;
+	}
 
     setsockopt(_fd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval));
 
     memset(&addr, 0, sizeof(addr));
     addr.sin_family = AF_INET;
-    addr.sin_port = htons(port);
-    if ( local )
+    addr.sin_port = htons(_port);
+    if ( _local )
         inet_aton( "127.0.0.1", &addr.sin_addr );
     else 
         addr.sin_addr.s_addr = htonl( INADDR_ANY );
@@ -80,8 +97,12 @@
     else
         PRINTF("Bind returned %d\n", bindStatus);
 
+    if ( listen(_fd, 5) < 0 )
+	{
+        PRINTF("Listen error: %s\n", strerror(errno));
+        throw errno;
+	}
     PRINTF("Listening on fd %d\n", _fd);
-    listen(_fd, 5);
     return; 
 }
 
@@ -93,8 +114,16 @@
     socklen_t len = sizeof(addr);
 
     newFd = accept( _fd, &addr, &len );
-    PRINTF( "acceptItem: Accepted connection on handle %d\n", newFd );
-    AddConnection( clientFactory(newFd, _readonly) );
+	if ( newFd >= 0 )
+	{
+		PRINTF( "acceptItem: Accepted connection on handle %d\n", newFd );
+		AddConnection( clientFactory(newFd, _readonly) );
+	}
+	else
+	{
+        PRINTF("Accept error: %s\n", strerror(errno)); // on cygwin got error was EINVAL
+		remakeConnection();
+	}
 }
 
 // Send characters to client
Index: README_cygwin.txt
===================================================================
--- README_cygwin.txt	(revision 0)
+++ README_cygwin.txt	(revision 2777)
@@ -0,0 +1,39 @@
+Building
+--------
+
+In general:
+
+    sh configure
+	make
+	
+should be enough. If you have autoconf and automake packages, then for a really
+clean build type:
+
+    autoreconf -fvi
+    sh configure
+    make clean
+    make
+
+If you plan to control procServ from a non-localhost address, you will need to use:
+
+	sh configure --enable-access-from-anywhere
+
+in place of "sh configure" above
+
+Running on Win32
+----------------
+
+In the .bat file to launch procServ you should add:
+
+    set CYGWIN=nodosfilewarning
+	
+to suppress warnings about using windows style paths.
+
+Using windows style paths in arguments to procServ is ususally OK, but if you have problems try replacing them
+with cygwin syntax i.e. "/cygdrive/c/my/path" rather than "c:\my\path"
+
+If you wish to run a .bat file rather than an executable under procServ then you should use something along the lines of:
+
+    %ComSpec% /c runIOC.bat st.cmd
+
+as arguments to procServ to launch your .bat file

Replies:
Re: procServ+devIocStats questions Ralph Lange
References:
procServ+devIocStats questions Alireza Panna
RE: procServ+devIocStats questions freddie.akeroyd

Navigate by Date:
Prev: RE: procServ+devIocStats questions freddie.akeroyd
Next: Re: procServ+devIocStats questions Kasemir, Kay
Index: 1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  2014  <20152016  2017  2018  2019  2020  2021  2022  2023  2024 
Navigate by Thread:
Prev: RE: procServ+devIocStats questions freddie.akeroyd
Next: Re: procServ+devIocStats questions Ralph Lange
Index: 1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  2014  <20152016  2017  2018  2019  2020  2021  2022  2023  2024 
ANJ, 16 Dec 2015 Valid HTML 4.01! · Home · News · About · Base · Modules · Extensions · Distributions · Download ·
· Search · EPICS V4 · IRMIS · Talk · Bugs · Documents · Links · Licensing ·