php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Request #32491 Solaris: Running out of file descriptors..
Submitted: 2005-03-29 21:55 UTC Modified: 2005-04-04 17:01 UTC
From: Oscar dot Castillo at jpl dot nasa dot gov Assigned:
Status: Closed Package: Feature/Change Request
PHP Version: 5CVS-2005-03-31 OS: Solaris 9
Private report: No CVE-ID:
 [2005-03-29 21:55 UTC] Oscar dot Castillo at jpl dot nasa dot gov
Description:
------------
I am using iPlanet 6.0 SP5, Zend 2.5.7 and PHP 5.0.3 on a 64 bit Solaris 9 SunFire V880 (4x4GHz CPU, 8Gb RAM). I have a problem with an HTTP POST command that attempts to upload a file onto the web server and a consistent "File upload error - unable to create a temporary file in Unknown on line 0" error message appears in the error logs. The upload_tmp_dir directory is set to /tmp. The error message disappears with a web server daemon reset (stop/start the web server daemon), but the error begins to occur within 2 to 24 hours again. When the errors occur, the /tmp directory begins to accumulate with php[web_server_pid] files that are zero bytes in size. My upload_max_filesize parameter is configured to 2Mb, however the uploaded files are never above 50kb. I've tried many suggested fixes from the php.net bug reports area, but to no avail. Thanks in advance for your help!

Reproduce code:
---------------
<?php
$debug_level = 2;		// 0 = no debug; 10 = most debug
if ($debug_level > 9) { phpinfo(); }
//
// Configurable variables
//
  $BaseDir = './';
  $LogFileName = $BaseDir . "RTIU_translator.log";
  $SCLK_SCET_Dir = "/afs/jpl/group/casops/dom/data/main/sclkscet/";  // placed in executed shell script
  $UploadDir = $BaseDir . "Temp/" ;		//	Where the posted files are placed
  $TransferDir = $BaseDir . "Xfer/";	//  Where the tar file for the translator is created

//
// Verify resources are available
//

$LogFile = fopen( $LogFileName,'a');
if (! $LogFile)
{ print("Logging is disabled, please save all displayed messages if help is needed from IO support<br>");
}

// Verify UPLOAD directory
if (!file_exists($UploadDir))
{ if ($LogFile)
  { $TmpStr = sprintf("%s VERIFY Upload directory does not exist\n",
					  gmdate("Y-m-d H:i:s") );
//                      strftime("%Y-%m-%d %T"));
    fwrite($LogFile, $TmpStr);
  }
  if(!mkdir($UploadDir,0774))
  { DisplayError("Could not create Upload directory",0);
    return;
  }
}
// Verify TRANSFER directory
if (!file_exists($TransferDir))
{ if ($LogFile)
  { $TmpStr = sprintf("%s VERIFY Transfer directory does not exist\n",
					  gmdate("Y-m-d H:i:s") );
//                      strftime("%Y-%m-%d %T"));
    fwrite($LogFile, $TmpStr);
  }
  print( "Missing Transfer Directory: $TransferDir <br>");
  if (!mkdir($TransferDir,0774))
  { DisplayError("Could not create Transfer directory",0);
    return;
  }
}

//
// Capture form values
//

$BaseName = escapeshellcmd($_POST['BaseName']);
$SequenceID = $_POST['SequenceID'];
$SCLK_SCET_File = $_POST['SCLK_SCET_File'];
$StartTimeUTC = $_POST['StartTimeUTC'];
$StopTimeUTC = $_POST['StopTimeUTC'];
$InputType = $_POST['input_type'];			// SASF or PRT
$OutputType = $_POST['output_type'];		// SEQ or IEB
$UserFileName = $_FILES['UploadFile']['name'];
$StartALF = $_POST['StartALF'];				// 0 <= ?
$ALF_Partition = $_POST['ALF_Partition'];	// DEFAULT or NON_DEFAULT
$ALF_Step = $_POST['ALF_Step'];				// 4 or 8

$CAS_User = $_SERVER['REMOTE_USER'];
$HostSource = $_SERVER['REMOTE_HOST'];

//
//  Debug stuff (POST data)
//
if ($debug_level > 3)
{ print( "Upload Directory: $UploadDir <br>");
  print( "SCLK/SCET Directory: $SCLK_SCET_Dir <br>");

  if ($debug_level > 8)
  { print ("<pre>");
    var_dump($_FILES);

    var_dump($_POST);
    print ("</pre>");
  }
  print( "Input Type: $InputType <br>");
  print( "Basename: $BaseName <br>");
  print( "Upload File: $UserFileName <br>");
  print( "Sequence ID: $SequenceID <br>");
  print( "SCLK/SCET File: $SCLK_SCET_File <br>");
  print( "Start UTC: $StartTimeUTC <br>");
  print( "Stop UTC: $StopTimeUTC <br>");

  print( "Output Type: $OutputType <br>");
  print( "Start ALF: $StartALF <br>");
  print( "Partition: $ALF_Partition <br>");
  print( "ALF Step: $ALF_Step <br>");
}

//
// Verify parameter values
//
print("<H2>Processing an $InputType upload</H2><br>");

//
//  Log an attempted posting (user & host)
//
if ($LogFile)
{ $TmpStr = sprintf("%s REQUEST (%s) %s from %s\n",
//                    strftime("%Y-%m-%d %T"), $BaseName, $CAS_User, $HostSource);
                    gmdate("Y-m-d H:i:s"), $BaseName, $CAS_User, $HostSource);
  fwrite($LogFile, $TmpStr);
}
  
// Check Basename
if (strlen($BaseName) == 0)
{ DisplayError("Basename must be specified to translate a sequence",0);
  return;
} else
{ if (strlen($BaseName) != 6)
  { DisplayError("Basename invalid length: '$BaseName'",
                 "Must be 6 alpha-numeric characters");
    return;
  }
  if (strspn($BaseName,"1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") != 6)
  { DisplayError("Basename invalid character(s): '$BaseName'",
                 "Must be 6 alpha-numeric characters");
    return;
  }
}
if (is_file("$TransferDir/$BaseName.tar") || is_dir("$TransferDir/$BaseName"))
{ DisplayError("The specified basename has been previously used",
               "Please select a different name: $BaseName");
  return;
}

// Check for uploaded file
if ($_FILES['UploadFile']['error'] != UPLOAD_ERR_OK)
{ $TmpStr = $_FILES['UploadFile']['name'];
  DisplayError("Error uploading sequence file", "$TmpStr");
  if ($debug_level > 1)
  { var_dump($_FILES);
    var_dump($_POST);
  }
  return;
}

// Verify SASF Parameters
if ($InputType == "SASF")
{ print("<H3>Verifying parameters for SASF processing</H3>");
  //  Start Times (only if SASF input)
  if (($StartUTC=Verify_UTC($StartTimeUTC,"Start Time")))
  { if (($StopUTC=Verify_UTC($StopTimeUTC,"Stop Time")))
    { if ($StopUTC <= $StartUTC)
      { DisplayError("'Stop Time' not later than 'Start Time'",
                     "Start: $StartTimeUTC<br>Stop: $StopTimeUTC");
        return;
      }
    } else	// Bad Stop Time
    { return;
    }
  } else	// Bad Start Time
  { return;
  }
} else		// InputType is PRT
{
}
//
// Build translation script
//
  $UploadFile = $UploadDir . "/" . $UserFileName;
  if (move_uploaded_file($_FILES['UploadFile']['tmp_name'], $UploadFile))
  { // print ("Uploaded the $InputType file as $UploadFile<br>");
  } else
  { DisplayError("Could not get uploaded file: '$UserFileName'",0);
    if ($debug_level > 1)
    { var_dump($_FILES);
      var_dump($_POST);
    }
   return;
  }

  $ScriptFile = "$UploadDir/$BaseName.csh";
  $FileHandle = fopen($ScriptFile,"wb");
  if (! $FileHandle)
  { DisplayError("Could not create script file", 0);
    return;
  }
  fwrite($FileHandle,"#!/bin/csh\n");
  fwrite($FileHandle,"#\n");
  fwrite($FileHandle,"# Script is passed the two programs to be used as parameters.\n");
  fwrite($FileHandle,"# The script is started with the working directory containing\n");
  fwrite($FileHandle,"# and the uploaded user file (SASF or PRTSEQ)\n\n");
  
  $PrtseqFile = $BaseName . "prtseq";
  if ($InputType == "SASF")
  { fwrite($FileHandle,"\$1 $UserFileName $SequenceID $BaseName $SCLK_SCET_Dir/$SCLK_SCET_File $StartTimeUTC $StopTimeUTC\n");
  } else
  { fwrite($FileHandle,"cp $UserFileName $PrtseqFile\n");
  }
  fwrite($FileHandle,"# Include error check\n\n");
  
  if ($OutputType == "IEB")
  { fwrite($FileHandle,"\$2 $BaseName $StartALF $ALF_Partition $ALF_Step < $PrtseqFile > $BaseName.seq\n");
  } else
  { fwrite($FileHandle,"\$2 $BaseName < $PrtseqFile > $BaseName.seq\n");
  }
  fwrite($FileHandle,"# Include error check\n");
  fwrite($FileHandle,"\n# Calling script is responsible for moving tar.gz to the download directory\n");
  fclose($FileHandle);							// End of ScriptFile
  
  if ($debug_level > 4)
  { print ("<pre>\n\nScript File Commands ...\n\n");
    readfile($ScriptFile);
    print ("</pre>");
  }
  
  $TargetDir = $TransferDir . '/' . $BaseName;
  $BuildCmd = "mkdir -p $TargetDir";
  $CmdOutput = system($BuildCmd,$ReturnValue);
  if ($ReturnValue != 0)
  { DisplayError("ERROR making workspace for '$BaseName'","Error: '$ReturnValue'");
    return;
  }

  $BuildCmd = "mv $ScriptFile $UploadFile $TargetDir";
  $CmdOutput = system($BuildCmd,$ReturnValue);
  if ($ReturnValue != 0)
  { DisplayError("ERROR moving '$UserFileName' to workspace","Error: '$ReturnValue'");
    return;
  }

  $BuildCmd = "(cd $TransferDir; tar -cf $BaseName.tar $BaseName)";
  $CmdOutput = system($BuildCmd,$ReturnValue);
  if ($ReturnValue != 0)
  { DisplayError("ERROR transfering '$BaseName' package","Error: '$ReturnValue'");
    return;
  }

  print ("<H3>Upload successfully completed</H3>");
  print ("<p>");
  print ("Processing will begin shortly");
  print ("</p><p>");
  print ("Please check back in a few minutes on the download tab to get ");
  print ("the translated sequence.<br>It will be called '$BaseName.tar.gz'");
  print ("</p>");

  //
  //  Log successful upload
  //
  if ( $LogFile )
  { $TmpStr = sprintf("%s SUBMIT (%s) by %s successfully\n",
//                      strftime("%Y-%m-%d %T"), $BaseName, $CAS_User );
                      gmdate("Y-m-d H:i:s"), $BaseName, $CAS_User );
    fwrite($LogFile,$TmpStr);
    fclose( $LogFile );
  }

  return;

//////
//  Verify_UTC function
//////
function Verify_UTC ( &$Time, $FieldName )
{
  $Status = sscanf($Time,"%d-%dT%d:%d:%d",$Year,$Day,$Hour,$Min,$Sec);
  if ($Status != 5)
  { 
    DisplayError ("Could not parse $FieldName: '$Time'",
                  "Must be in the form: 'yyyy-dddThh:mm:ss.mmm'");
    return 0;
  }

  $GMT_Time = mktime($Hour,$Min,$Sec,1,$Day,$Year);
  $CalcTime = strftime("%Y-%jT%T.000",$GMT_Time);

  if (strncmp($CalcTime,$Time,(strlen($CalcTime)-4)) == 0)
  { $Time = $CalcTime;
    return $GMT_Time;
  } else
  { DisplayError ("$FieldName interpreted as invalid: '$Time'",0);
    return 0;
  }
}

//////
//  DisplayError function
//////
function DisplayError( $ErrorMsg, $Ancillary )
{ global $LogFile, $CAS_User, $BaseName;

  print("<br><big> *** Processing Error ***</big><br>");
  print("$ErrorMsg<br>");
  if ($Ancillary != NULL)
  { print("<br>$Ancillary<br>");
  }
  
  //
  //  Log error in post (user and reason)
  //
  if ( $LogFile )
  { $TmpStr = sprintf("%s ERROR (%s) %s [%s]\n",
//                      strftime("%Y-%m-%d %T"), $BaseName, $ErrorMsg );
					  gmdate("Y-m-d H:i:s"), $BaseName, $ErrorMsg, $Ancillary );
    fwrite($LogFile,$TmpStr);
    fclose( $LogFile );
  }

  return;
}
?>

Expected result:
----------------
The uploaded file is transfered from a client machine onto the web server's local filesystem structure.

Actual result:
--------------
PHP Warning: File upload error - unable to create a temporary file in Unknown on line 0

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2005-03-31 11:17 UTC] thetaphi@php.net
Again the old STDIO problem: There are some places in PHP where the C library stdio functions are used instead of posix io (popen in all excute functions/sendmail functions, uploading of temporary files, some 3rd party extensions). Under Solaris with the AT&T libc stdio is limited to 255 file descriptors, if the current posix file descriptor gets larger than 255 all further fopens or fdopens fail because the field in the FILE* struct is a unsigned char. Using Posix IO has no limit here (descriptor is int).
There are 2 solutions:
a) limiting the accepting threads in sunone/iplanet and disabling java in webserver completely (java is very descriptor intensive -> descriptors for PHP get > 255)
b) using PHP as CGI/FastCGI (look for Sun PHP enabler at zend.com) or my CGI enabler: http://www.thetaphi.de/php-ressources/

Changing this in PHP is a heavy task, the zend engine itsself is safe since 4.3.3, other parts should be changed in the future to POSIX IO.

This is a Solaris problem which gets important in a multithreaded and heavy file descriptor using webserver like sunone/iplanet
 [2005-03-31 19:47 UTC] Oscar dot Castillo at jpl dot nasa dot gov
I have 3 web server environments setup with the same version of PHP, a development environment, a test environment and a production environment. Your description of the common Solaris /iPlanet problem explains why the same PHP script works well in the development and test environment, but not the production environment. I do have Java enabled on all 3 web server environments, but the only difference is that our production is obviously much more heavily utilized than the test and development environment. I thank you very much for the explanation.

Since Java is required on our web servers, I obviously cannot disable Java. I will try loading the fastcgi API. I'll keep you posted.
 [2005-04-04 17:01 UTC] thetaphi@php.net
This bug has been fixed in CVS.

Snapshots of the sources are packaged every three hours; this change
will be in the next snapshot. You can grab the snapshot at
http://snaps.php.net/.
 
Thank you for the report, and for helping us make PHP better.

For file upload the fd problem is fixed - but it can still happen with other stdio functions: php exec() which uses stdio popen()
 
PHP Copyright © 2001-2014 The PHP Group
All rights reserved.
Last updated: Thu Apr 17 21:01:56 2014 UTC