972f44485e27c59335c665585715be63a2d224df
galt
  Thu Oct 1 17:40:11 2020 -0700
Adding timeout for gfServer read and write. Hopefully this will fix both the recent problem from Wuhan killing all our translated blat servers, as well as the mysterious hangs which happen occasionally and end up causing the admins to have to restart the server. refs #26285

diff --git src/gfServer/gfServer.c src/gfServer/gfServer.c
index 45717e0..84c435e 100644
--- src/gfServer/gfServer.c
+++ src/gfServer/gfServer.c
@@ -33,52 +33,55 @@
     {"maxDnaHits", OPTION_INT},
     {"maxGap", OPTION_INT},
     {"maxNtSize", OPTION_INT},
     {"maxTransHits", OPTION_INT},
     {"minMatch", OPTION_INT},
     {"repMatch", OPTION_INT},
     {"seqLog", OPTION_BOOLEAN},
     {"ipLog", OPTION_BOOLEAN},
     {"debugLog", OPTION_BOOLEAN},
     {"stepSize", OPTION_INT},
     {"tileSize", OPTION_INT},
     {"trans", OPTION_BOOLEAN},
     {"syslog", OPTION_BOOLEAN},
     {"perSeqMax", OPTION_STRING},
     {"noSimpRepMask", OPTION_BOOLEAN},
+    {"timeout", OPTION_INT},
     {NULL, 0}
 };
 
 
 int maxNtSize = 40000;
 int maxAaSize = 8000;
 
 int minMatch = gfMinMatch;	/* Can be overridden from command line. */
 int tileSize = gfTileSize;	/* Can be overridden from command line. */
 int stepSize = 0;		/* Can be overridden from command line. */
 boolean doTrans = FALSE;	/* Do translation? */
 boolean allowOneMismatch = FALSE; 
 boolean noSimpRepMask = FALSE;
 int repMatch = 1024;    /* Can be overridden from command line. */
 int maxDnaHits = 100;   /* Can be overridden from command line. */
 int maxTransHits = 200; /* Can be overridden from command line. */
 int maxGap = gfMaxGap;
 boolean seqLog = FALSE;
 boolean ipLog = FALSE;
 boolean doMask = FALSE;
 boolean canStop = FALSE;
 
+int timeout = 10;  // default timeout in seconds
+
 void usage()
 /* Explain usage and exit. */
 {
 errAbort(
   "gfServer v %s - Make a server to quickly find where DNA occurs in genome\n"
   "   To set up a server:\n"
   "      gfServer start host port file(s)\n"
   "      where the files are .2bit or .nib format files specified relative to the current directory\n"
   "   To remove a server:\n"
   "      gfServer stop host port\n"
   "   To query a server with DNA sequence:\n"
   "      gfServer query host port probe.fa\n"
   "   To query a server with protein sequence:\n"
   "      gfServer protQuery host port probe.fa\n"
   "   To query a server with translated DNA sequence:\n"
@@ -114,48 +117,60 @@
   "                   tile. Default is %d.\n"
   "   -noSimpRepMask  Suppresses simple repeat masking.\n"
   "   -maxDnaHits=N   Maximum number of hits for a DNA query that are sent from the server.\n"
   "                   Default is %d.\n"
   "   -maxTransHits=N Maximum number of hits for a translated query that are sent from the server.\n"
   "                   Default is %d.\n"
   "   -maxNtSize=N    Maximum size of untranslated DNA query sequence.\n"
   "                   Default is %d.\n"
   "   -maxAaSize=N    Maximum size of protein or translated DNA queries.\n"
   "                   Default is %d.\n"
   "   -perSeqMax=file File contains one seq filename (possibly with ':seq' suffix) per line.\n"
   "                   -maxDnaHits will be applied to each filename[:seq] separately: each may\n"
   "                   have at most maxDnaHits/2 hits.\n"
   "                   Useful for assemblies with many alternate/patch sequences.\n"
   "   -canStop        If set, a quit message will actually take down the server.\n"
-  ,	gfVersion, repMatch, maxDnaHits, maxTransHits, maxNtSize, maxAaSize
+  "   -timeout=N      Timeout in seconds.\n"
+  "                   Default is %d.\n"
+  ,	gfVersion, repMatch, maxDnaHits, maxTransHits, maxNtSize, maxAaSize, timeout
   );
 
 }
 /*
   Note about file(s) specified in the start command:
       The path(s) specified here are sent back exactly as-is
       to clients such as gfClient, hgBlat, webBlat.
       It is intended that relative paths are used.
       Absolute paths starting with '/' tend not to work
       unless the client is on the same machine as the server.
       For use with hgBlat and webBlat, cd to the directory where the file is
       and use the plain file name with no slashes.
         hgBlat will append the path(s) given to dbDb.nibPath.
        webBlat will append the path(s) given to path specified in webBlat.cfg.
       gfClient will append the path(s) given to the seqDir path specified.
 */
 
+static void setSocketTimeout(int sockfd, int delayInSeconds)
+// put socket read and write timeout so it will not take forever to timeout during a read or write
+{
+struct timeval tv;
+tv.tv_sec = delayInSeconds;
+tv.tv_usec = 0;
+setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv);
+setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv, sizeof tv);
+}
+
 void genoFindDirect(char *probeName, int fileCount, char *seqFiles[])
 /* Don't set up server - just directly look for matches. */
 {
 struct genoFind *gf = NULL;
 struct lineFile *lf = lineFileOpen(probeName, TRUE);
 struct dnaSeq seq;
 int hitCount = 0, clumpCount = 0, oneHit;
 ZeroVar(&seq);
 
 if (doTrans)
     errAbort("Don't support translated direct stuff currently, sorry");
 
 gf = gfIndexNibsAndTwoBits(fileCount, seqFiles, minMatch, maxGap, 
 	tileSize, repMatch, FALSE,
 	allowOneMismatch, stepSize, noSimpRepMask);
@@ -612,30 +627,31 @@
     fromLen = sizeof(fromAddr);
     connectionHandle = accept(socketHandle, (struct sockaddr*)&fromAddr, &fromLen);
     if (connectionHandle < 0)
         {
 	warn("Error accepting the connection");
 	++warnCount;
         ++connectFailCount;
         if (connectFailCount >= 100)
 	    errAbort("100 continuous connection failures, no point in filling up the log in an infinite loop.");
 	continue;
 	}
     else
 	{
 	connectFailCount = 0;
 	}
+    setSocketTimeout(connectionHandle, timeout);
     if (ipLog)
 	{
 	struct sockaddr_in6 clientAddr;
 	unsigned int addrlen=sizeof(clientAddr);
 	getpeername(connectionHandle, (struct sockaddr *)&clientAddr, &addrlen);
 	char ipStr[NI_MAXHOST];
 	getAddrAsString6n4((struct sockaddr_storage *)&clientAddr, ipStr, sizeof ipStr);
 	logInfo("gfServer version %s on host %s, port %s connection from %s", 
 	    gfVersion, hostName, portName, ipStr);
 	}
     readSize = read(connectionHandle, buf, sizeof(buf)-1);
     if (readSize < 0)
         {
 	warn("Error reading from socket: %s", strerror(errno));
 	++warnCount;
@@ -1010,30 +1026,31 @@
 stepSize = optionInt("stepSize", tileSize);
 if (optionExists("repMatch"))
     repMatch = optionInt("repMatch", 0);
 else
     repMatch = gfDefaultRepMatch(tileSize, stepSize, doTrans);
 minMatch = optionInt("minMatch", minMatch);
 maxDnaHits = optionInt("maxDnaHits", maxDnaHits);
 maxTransHits = optionInt("maxTransHits", maxTransHits);
 maxNtSize = optionInt("maxNtSize", maxNtSize);
 maxAaSize = optionInt("maxAaSize", maxAaSize);
 seqLog = optionExists("seqLog");
 ipLog = optionExists("ipLog");
 doMask = optionExists("mask");
 canStop = optionExists("canStop");
 noSimpRepMask = optionExists("noSimpRepMask");
+timeout = optionInt("timeout", timeout);
 if (argc < 2)
     usage();
 if (optionExists("log"))
     logOpenFile(argv[0], optionVal("log", NULL));
 if (optionExists("syslog"))
     logOpenSyslog(argv[0], optionVal("logFacility", NULL));
 if (optionExists("debugLog"))
     logSetMinPriority("debug");
 
 if (sameWord(command, "direct"))
     {
     if (argc < 4)
         usage();
     genoFindDirect(argv[2], argc-3, argv+3);
     }